/*
 * Copyright (c) 2001,2000,1999 Sasha Vasko <sasha at aftercode.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef _WIN32
#include "win32/config.h"
#else
#include "config.h"
#endif

#define LOCAL_DEBUG

#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
 
#ifdef _WIN32
# include "win32/afterbase.h"
#else
# include "afterbase.h"
#endif
#include "asvisual.h"

#ifdef XSHMIMAGE
# include <sys/ipc.h>
# include <sys/shm.h>
# include <X11/extensions/XShm.h>
#endif

#ifdef HAVE_GLX
# include <GL/gl.h>
# include <GL/glx.h>
#endif


CARD32 color2pixel32bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel32rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel24bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel24rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel16bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel16rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel15bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel15rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel);
CARD32 color2pixel_pseudo3bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel );
CARD32 color2pixel_pseudo6bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel );
CARD32 color2pixel_pseudo12bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel );

void pixel2color32rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color32bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color24rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color24bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color16rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color16bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color15rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);
void pixel2color15bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue);

void ximage2scanline32( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void ximage2scanline16( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void ximage2scanline15( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void ximage2scanline_pseudo3bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void ximage2scanline_pseudo6bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void ximage2scanline_pseudo12bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );

void scanline2ximage32( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void scanline2ximage16( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void scanline2ximage15( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void scanline2ximage_pseudo3bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void scanline2ximage_pseudo6bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );
void scanline2ximage_pseudo12bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data );

#ifndef X_DISPLAY_MISSING
static int  get_shifts (unsigned long mask);
static int  get_bits (unsigned long mask);

void _XInitImageFuncPtrs(XImage*);

int
asvisual_empty_XErrorHandler (Display * dpy, XErrorEvent * event)
{
    return 0;
}
/***************************************************************************/
#ifdef LOCAL_DEBUG
Status
debug_AllocColor( const char *file, const char *func, int line, ASVisual *asv, Colormap cmap, XColor *pxcol )
{
	Status sret ;
	sret = XAllocColor( asv->dpy, cmap, pxcol );
	show_progress( " XAllocColor in %s:%s():%d has %s -> cmap = %lX, pixel = %d, color = 0x%4.4lX, 0x%4.4lX, 0x%4.4lX",
				   file, func, line, (sret==0)?"failed":"succeeded", cmap, pxcol->pixel, pxcol->red, pxcol->green, pxcol->blue );
	return sret;
}
#define ASV_ALLOC_COLOR(asv,cmap,pxcol)  debug_AllocColor(__FILE__, __FUNCTION__, __LINE__, (asv),(cmap),(pxcol))
#else
#define ASV_ALLOC_COLOR(asv,cmap,pxcol)  XAllocColor((asv)->dpy,(cmap),(pxcol))
#endif

#else
#define ASV_ALLOC_COLOR(asv,cmap,pxcol)   0
#endif   /* ndef X_DISPLAY_MISSING */

/**********************************************************************/
/* returns the maximum number of true colors between a and b          */
long
ARGB32_manhattan_distance (long a, long b)
{
	register int d = (int)ARGB32_RED8(a)   - (int)ARGB32_RED8(b);
	register int t = (d < 0 )? -d : d ;

	d = (int)ARGB32_GREEN8(a) - (int)ARGB32_GREEN8(b);
	t += (d < 0)? -d : d ;
	d = (int)ARGB32_BLUE8(a)  - (int)ARGB32_BLUE8(b);
	return (t+((d < 0)? -d : d)) ;
}



/***************************************************************************
 * ASVisual :
 * encoding/decoding/querying/setup
 ***************************************************************************/
int get_bits_per_pixel(Display *dpy, int depth)
{
#if 0
#ifndef X_DISPLAY_MISSING
 	register ScreenFormat *fmt = dpy->pixmap_format;
 	register int i;

 	for (i = dpy->nformats + 1; --i; ++fmt)
 		if (fmt->depth == depth)
 			return(fmt->bits_per_pixel);
#endif
#endif
	if (depth <= 4)
	    return 4;
	if (depth <= 8)
	    return 8;
	if (depth <= 16)
	    return 16;
	return 32;
 }

/* ********************* ASScanline ************************************/
ASScanline*
prepare_scanline( unsigned int width, unsigned int shift, ASScanline *reusable_memory, Bool BGR_mode  )
{
	register ASScanline *sl = reusable_memory ;
	size_t aligned_width;
	void *ptr;

	if( sl == NULL )
		sl = safecalloc( 1, sizeof( ASScanline ) );
	else
		memset( sl, 0x00, sizeof(ASScanline));

	if( width == 0 ) width = 1 ;
	sl->width 	= width ;
	sl->shift   = shift ;
	/* we want to align data by 8 byte boundary (double)
	 * to allow for code with less ifs and easier MMX/3Dnow utilization :*/
	aligned_width = width + (width&0x00000001);
	sl->buffer = ptr = safemalloc (((aligned_width*4)+4)*sizeof(CARD32));

	sl->xc1 = sl->red 	= (CARD32*)(((long)ptr>>3)*8);
	sl->xc2 = sl->green = sl->red   + aligned_width;
	sl->xc3 = sl->blue 	= sl->green + aligned_width;
	sl->alpha 	= sl->blue  + aligned_width;

	sl->channels[IC_RED] = sl->red ;
	sl->channels[IC_GREEN] = sl->green ;
	sl->channels[IC_BLUE] = sl->blue ;
	sl->channels[IC_ALPHA] = sl->alpha ;

	if( BGR_mode )
	{
		sl->xc1 = sl->blue ;
		sl->xc3 = sl->red ;
	}
	/* this way we can be sure that our buffers have size of multiplies of 8s
	 * and thus we can skip unneeded checks in code */
	/* initializing padding into 0 to avoid any garbadge carry-over
	 * bugs with diffusion: */
	sl->red[aligned_width-1]   = 0;
	sl->green[aligned_width-1] = 0;
	sl->blue[aligned_width-1]  = 0;
	sl->alpha[aligned_width-1] = 0;
	
	sl->back_color = ARGB32_DEFAULT_BACK_COLOR;

	return sl;
}

void
free_scanline( ASScanline *sl, Bool reusable )
{
	if( sl )
	{
		if( sl->buffer )
			free( sl->buffer );
		if( !reusable )
			free( sl );
	}
}

/* ********************* ASVisual ************************************/
ASVisual *_set_default_asvisual( ASVisual *new_v );

#ifndef X_DISPLAY_MISSING
static XColor black_xcol = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue };
static XColor white_xcol = { 0, 0xFFFF, 0xFFFF, 0xFFFF, DoRed|DoGreen|DoBlue };

static void find_useable_visual( ASVisual *asv, Display *dpy, int screen,
	                             Window root, XVisualInfo *list, int nitems,
								 XSetWindowAttributes *attr )
{
	int k ;
	int (*oldXErrorHandler) (Display *, XErrorEvent *) =
						XSetErrorHandler (asvisual_empty_XErrorHandler);
	Colormap orig_cmap = attr->colormap ;

	for( k = 0  ; k < nitems ; k++ )
	{
		Window       w = None, wjunk;
		unsigned int width, height, ujunk ;
		int          junk;
		/* try and use default colormap when possible : */
		if( orig_cmap == None )
		{
  			if( list[k].visual == DefaultVisual( dpy, (screen) ) )
			{
				attr->colormap = DefaultColormap( dpy, screen );
				LOCAL_DEBUG_OUT( "Using Default colormap %lX", attr->colormap );
			}else
			{
				attr->colormap = XCreateColormap( dpy, root, list[k].visual, AllocNone);
				LOCAL_DEBUG_OUT( "DefaultVisual is %p, while ours is %p, so Created new colormap %lX", DefaultVisual( dpy, (screen) ), list[k].visual, attr->colormap );
			}
		}
		ASV_ALLOC_COLOR( asv, attr->colormap, &black_xcol );
		ASV_ALLOC_COLOR( asv, attr->colormap, &white_xcol );
		attr->border_pixel = black_xcol.pixel ;

/*fprintf( stderr, "checking out visual ID %d, class %d, depth = %d mask = %X,%X,%X\n", list[k].visualid, list[k].class, list[k].depth, list[k].red_mask, list[k].green_mask, list[k].blue_mask 	);*/
		w = XCreateWindow (dpy, root, -10, -10, 10, 10, 0, list[k].depth, CopyFromParent, list[k].visual, CWColormap|CWBorderPixel, attr );
		if( w != None && XGetGeometry (dpy, w, &wjunk, &junk, &junk, &width, &height, &ujunk, &ujunk))
		{
			/* don't really care what's in it since we do not use it anyways : */
			asv->visual_info = list[k] ;
			XDestroyWindow( dpy, w );
			asv->colormap = attr->colormap ;
			asv->own_colormap = (attr->colormap != DefaultColormap( dpy, screen ));
			asv->white_pixel = white_xcol.pixel ;
			asv->black_pixel = black_xcol.pixel ;
			break;
		}
		if( orig_cmap == None )
		{
			if( attr->colormap != DefaultColormap( dpy, screen ))
				XFreeColormap( dpy, attr->colormap );
			attr->colormap = None ;
		}
	}
	XSetErrorHandler(oldXErrorHandler);
}
#endif

/* Main procedure finding and querying the best visual */
Bool
query_screen_visual_id( ASVisual *asv, Display *dpy, int screen, Window root, int default_depth, VisualID visual_id, Colormap cmap )
{
#ifndef X_DISPLAY_MISSING
	int nitems = 0 ;
	/* first  - attempt locating 24bpp TrueColor or DirectColor RGB or BGR visuals as the best cases : */
	/* second - attempt locating 32bpp TrueColor or DirectColor RGB or BGR visuals as the next best cases : */
	/* third  - lesser but still capable 16bpp 565 RGB or BGR modes : */
	/* forth  - even more lesser 15bpp 555 RGB or BGR modes : */
	/* nothing nice has been found - use whatever X has to offer us as a default :( */
	int i ;

	XVisualInfo *list = NULL;
	XSetWindowAttributes attr ;
	static XVisualInfo templates[] =
		/* Visual, visualid, screen, depth, class      , red_mask, green_mask, blue_mask, colormap_size, bits_per_rgb */
		{{ NULL  , 0       , 0     , 24   , TrueColor  , 0xFF0000, 0x00FF00  , 0x0000FF , 0            , 0 },
		 { NULL  , 0       , 0     , 24   , TrueColor  , 0x0000FF, 0x00FF00  , 0xFF0000 , 0            , 0 },
		 { NULL  , 0       , 0     , 24   , TrueColor  , 0x0     , 0x0       , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , TrueColor  , 0xFF0000, 0x00FF00  , 0x0000FF , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , TrueColor  , 0x0000FF, 0x00FF00  , 0xFF0000 , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , TrueColor  , 0x0     , 0x0       , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0xF800  , 0x07E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0x001F  , 0x07E0    , 0xF800   , 0            , 0 },
		 /* big endian or MBR_First modes : */
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0x0     , 0xE007    , 0x0      , 0            , 0 },
		 /* some misrepresented modes that really are 15bpp : */
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0x7C00  , 0x03E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0x001F  , 0x03E0    , 0x7C00   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , TrueColor  , 0x0     , 0xE003    , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , TrueColor  , 0x7C00  , 0x03E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , TrueColor  , 0x001F  , 0x03E0    , 0x7C00   , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , TrueColor  , 0x0     , 0xE003    , 0x0      , 0            , 0 },
/* no suitable TrueColorMode found - now do the same thing to DirectColor :*/
		 { NULL  , 0       , 0     , 24   , DirectColor, 0xFF0000, 0x00FF00  , 0x0000FF , 0            , 0 },
		 { NULL  , 0       , 0     , 24   , DirectColor, 0x0000FF, 0x00FF00  , 0xFF0000 , 0            , 0 },
		 { NULL  , 0       , 0     , 24   , DirectColor, 0x0     , 0x0       , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , DirectColor, 0xFF0000, 0x00FF00  , 0x0000FF , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , DirectColor, 0x0000FF, 0x00FF00  , 0xFF0000 , 0            , 0 },
		 { NULL  , 0       , 0     , 32   , DirectColor, 0x0     , 0x0       , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0xF800  , 0x07E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0x001F  , 0x07E0    , 0xF800   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0x0     , 0xE007    , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0x7C00  , 0x03E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0x001F  , 0x03E0    , 0x7C00   , 0            , 0 },
		 { NULL  , 0       , 0     , 16   , DirectColor, 0x0     , 0xE003    , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , DirectColor, 0x7C00  , 0x03E0    , 0x001F   , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , DirectColor, 0x001F  , 0x03E0    , 0x7C00   , 0            , 0 },
		 { NULL  , 0       , 0     , 15   , DirectColor, 0x0     , 0xE003    , 0x0      , 0            , 0 },
		 { NULL  , 0       , 0     , 0    , 0          , 0       , 0         , 0        , 0            , 0 },
		} ;
#endif /*ifndef X_DISPLAY_MISSING */
	if( asv == NULL )
		return False ;
	memset( asv, 0x00, sizeof(ASVisual));

	asv->dpy = dpy ;

#ifndef X_DISPLAY_MISSING
	memset( &attr, 0x00, sizeof( attr ));
	attr.colormap = cmap ;

	if( visual_id == 0 )
	{
		for( i = 0 ; templates[i].depth != 0 ; i++ )
		{
			int mask = VisualScreenMask|VisualDepthMask|VisualClassMask ;

			templates[i].screen = screen ;
			if( templates[i].red_mask != 0 )
				mask |= VisualRedMaskMask;
			if( templates[i].green_mask != 0 )
				mask |= VisualGreenMaskMask ;
			if( templates[i].blue_mask != 0 )
				mask |= VisualBlueMaskMask ;

			if( (list = XGetVisualInfo( dpy, mask, &(templates[i]), &nitems ))!= NULL )
			{
				find_useable_visual( asv, dpy, screen, root, list, nitems, &attr );
				XFree( list );
				list = NULL ;
				if( asv->visual_info.visual != NULL )
					break;
			}
		}
	}else
	{
		templates[0].visualid = visual_id ;
		if( (list = XGetVisualInfo( dpy, VisualIDMask, &(templates[0]), &nitems )) != NULL )
		{
			find_useable_visual( asv, dpy, screen, root, list, nitems, &attr );
		 	XFree( list );
			list = NULL ;
		}
		if( asv->visual_info.visual == NULL )
			show_error( "Visual with requested ID of 0x%X is unusable - will try default instead.", visual_id );
	}

	if( asv->visual_info.visual == NULL )
	{  /* we ain't found any decent Visuals - that's some crappy screen you have! */
		register int vclass = 6 ;
		while( --vclass >= 0 )
			if( XMatchVisualInfo( dpy, screen, default_depth, vclass, &(asv->visual_info) ) )
				break;
		if( vclass < 0 )
			return False;
		/* try and use default colormap when possible : */
		if( asv->visual_info.visual == DefaultVisual( dpy, screen ) )
			attr.colormap = DefaultColormap( dpy, screen );
		else
			attr.colormap = XCreateColormap( dpy, root, asv->visual_info.visual, AllocNone);
		ASV_ALLOC_COLOR( asv, attr.colormap, &black_xcol );
		ASV_ALLOC_COLOR( asv, attr.colormap, &white_xcol );
		asv->colormap = attr.colormap ;
		asv->own_colormap = (attr.colormap != DefaultColormap( dpy, screen ));
		asv->white_pixel = white_xcol.pixel ;
		asv->black_pixel = black_xcol.pixel ;
	}
	if( get_output_threshold() >= OUTPUT_VERBOSE_THRESHOLD )
	{
		fprintf( stderr, "Selected visual 0x%lx: depth %d, class %d\n RGB masks: 0x%lX, 0x%lX, 0x%lX, Byte Ordering: %s\n",
				 asv->visual_info.visualid,
				 asv->visual_info.depth,
				 asv->visual_info.class,
				 asv->visual_info.red_mask,
				 asv->visual_info.green_mask,
				 asv->visual_info.blue_mask,
				 (ImageByteOrder(asv->dpy)==MSBFirst)?"MSBFirst":"LSBFirst" );
	}
#else
	asv->white_pixel = ARGB32_White ;
	asv->black_pixel = ARGB32_Black ;
#endif /*ifndef X_DISPLAY_MISSING */
	return True;
}

ASVisual *
create_asvisual_for_id( Display *dpy, int screen, int default_depth, VisualID visual_id, Colormap cmap, ASVisual *reusable_memory )
{
	ASVisual *asv = reusable_memory ;
#ifndef X_DISPLAY_MISSING
    Window root = dpy?RootWindow(dpy,screen):None;
#endif /*ifndef X_DISPLAY_MISSING */

	if( asv == NULL )
        asv = safecalloc( 1, sizeof(ASVisual) );
#ifndef X_DISPLAY_MISSING
    if( dpy )
    {
        if( query_screen_visual_id( asv, dpy, screen, root, default_depth, visual_id, cmap ) )
        {   /* found visual - now off to decide about color handling on it : */
            if( !setup_truecolor_visual( asv ) )
            {  /* well, we don't - lets try and preallocate as many colors as we can but up to
                * 1/4 of the colorspace or 12bpp colors, whichever is smaller */
                setup_pseudo_visual( asv );
                if( asv->as_colormap == NULL )
                    setup_as_colormap( asv );
            }
        }else
        {
            if( reusable_memory != asv )
                free( asv );
            asv = NULL ;
        }
    }
#endif /*ifndef X_DISPLAY_MISSING */
	_set_default_asvisual( asv );
	return asv;
}

ASVisual *
create_asvisual( Display *dpy, int screen, int default_depth, ASVisual *reusable_memory )
{
	VisualID visual_id = 0;
	char *id_env_var ;

	if( (id_env_var = getenv( ASVISUAL_ID_ENVVAR )) != NULL )
		visual_id = strtol(id_env_var,NULL,16);

	return create_asvisual_for_id( dpy, screen, default_depth, visual_id, None, reusable_memory );
}


void
destroy_asvisual( ASVisual *asv, Bool reusable )
{
	if( asv )
	{
		if( get_default_asvisual() == asv )
			_set_default_asvisual( NULL );
#ifndef X_DISPLAY_MISSING
	 	if( asv->own_colormap )
	 	{
	 		if( asv->colormap )
	 			XFreeColormap( asv->dpy, asv->colormap );
	 	}
	 	if( asv->as_colormap )
		{
	 		free( asv->as_colormap );
			if( asv->as_colormap_reverse.xref != NULL )
			{
				if( asv->as_colormap_type == ACM_12BPP )
					destroy_ashash( &(asv->as_colormap_reverse.hash) );
				else
					free( asv->as_colormap_reverse.xref );
			}
		}
#ifdef HAVE_GLX
		if( asv->glx_scratch_gc_direct )
			glXDestroyContext(dpy, asv->glx_scratch_gc_direct );
		if( asv->glx_scratch_gc_indirect )
			glXDestroyContext(dpy, asv->glx_scratch_gc_indirect );
#endif

#endif /*ifndef X_DISPLAY_MISSING */
		if( !reusable )
			free( asv );
	}
}

int as_colormap_type2size( int type )
{
	switch( type )
	{
		case ACM_3BPP :
			return 8 ;
		case ACM_6BPP :
			return 64 ;
		case ACM_12BPP :
			return 4096 ;
		default:
			return 0 ;
	}
}

Bool
visual2visual_prop( ASVisual *asv, size_t *size,
								   unsigned long *version,
								   unsigned long **data )
{
	int cmap_size = 0 ;
	unsigned long *prop ;

	if( asv == NULL || data == NULL)
		return False;

	cmap_size = as_colormap_type2size( asv->as_colormap_type );

	if( cmap_size > 0 && asv->as_colormap == NULL )
		return False ;

	prop = safemalloc( *size ) ;
#ifndef X_DISPLAY_MISSING
	prop[0] = asv->visual_info.visualid ;
	prop[1] = asv->colormap ;
	prop[2] = asv->black_pixel ;
	prop[3] = asv->white_pixel ;
	prop[4] = asv->as_colormap_type ;
	if( cmap_size > 0 )
	{
		register int i;
		for( i = 0 ; i < cmap_size ; i++ )
			prop[i+5] = asv->as_colormap[i] ;
	}
	if( size )
		*size = (1+1+2+1+cmap_size)*sizeof(unsigned long);
#endif /*ifndef X_DISPLAY_MISSING */
	if( version )
		*version = (1<<16)+0;                        /* version is 1.0 */
	*data = prop ;
	return True;
}

Bool
visual_prop2visual( ASVisual *asv, Display *dpy, int screen,
								   size_t size,
								   unsigned long version,
								   unsigned long *data )
{
#ifndef X_DISPLAY_MISSING
	XVisualInfo templ, *list ;
	int nitems = 0 ;
	int cmap_size = 0 ;
#endif /*ifndef X_DISPLAY_MISSING */

	if( asv == NULL )
		return False;

	asv->dpy = dpy ;

	if( size < (1+1+2+1)*sizeof(unsigned long) ||
		(version&0x00FFFF) != 0 || (version>>16) != 1 || data == NULL )
		return False;

	if( data[0] == None || data[1] == None ) /* we MUST have valid colormap and visualID !!!*/
		return False;

#ifndef X_DISPLAY_MISSING
	templ.screen = screen ;
	templ.visualid = data[0] ;

	list = XGetVisualInfo( dpy, VisualScreenMask|VisualIDMask, &templ, &nitems );
	if( list == NULL || nitems == 0 )
		return False;   /* some very bad visual ID has been requested :( */

	asv->visual_info = *list ;
	XFree( list );

	if( asv->own_colormap && asv->colormap )
		XFreeColormap( dpy, asv->colormap );

	asv->colormap = data[1] ;
	asv->own_colormap = False ;
	asv->black_pixel = data[2] ;
	asv->white_pixel = data[3] ;
	asv->as_colormap_type = data[4];

	cmap_size = as_colormap_type2size( asv->as_colormap_type );

	if( cmap_size > 0 )
	{
		register int i ;
		if( asv->as_colormap )
			free( asv->as_colormap );
		asv->as_colormap = safemalloc( cmap_size );
		for( i = 0 ; i < cmap_size ; i++ )
			asv->as_colormap[i] = data[i+5];
	}else
		asv->as_colormap_type = ACM_None ;     /* just in case */
#else

#endif /*ifndef X_DISPLAY_MISSING */
	return True;
}

Bool
setup_truecolor_visual( ASVisual *asv )
{
#ifndef X_DISPLAY_MISSING
	XVisualInfo *vi = &(asv->visual_info) ;

	if( vi->class != TrueColor )
		return False;

#ifdef HAVE_GLX
	if( glXQueryExtension (dpy, NULL, NULL))
	{
		int val = False;
		glXGetConfig(dpy, vi, GLX_USE_GL, &val);		
		if( val ) 
		{
			asv->glx_scratch_gc_indirect = glXCreateContext (dpy, &(asv->visual_info), NULL, False);
			if( asv->glx_scratch_gc_indirect ) 
			{	
				set_flags( asv->glx_support, ASGLX_Available );
				if( glXGetConfig(dpy, vi, GLX_RGBA, &val) == 0 )
					if( val ) set_flags( asv->glx_support, ASGLX_RGBA );
				if( glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &val) == 0 )
					if( val ) set_flags( asv->glx_support, ASGLX_DoubleBuffer );
				if( glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &val) == 0 )
					if( val ) set_flags( asv->glx_support, ASGLX_DoubleBuffer );
				
				if( (asv->glx_scratch_gc_direct = glXCreateContext (dpy, &(asv->visual_info), NULL, True)) != NULL ) 
					if( !glXIsDirect( dpy, asv->glx_scratch_gc_direct ) )
					{	
						glXDestroyContext(dpy, asv->glx_scratch_gc_direct );
						asv->glx_scratch_gc_direct = NULL ;
					}
#if 0                          /* that needs some more research :  */
				/* under Cygwin that seems to be 40% faster then regular XImage for some reason */
				set_flags( asv->glx_support, ASGLX_UseForImageTx );
#endif
			}
		}	 
	}	 
#endif

	asv->BGR_mode = ((vi->red_mask&0x0010)!=0) ;
	asv->rshift = get_shifts (vi->red_mask);
	asv->gshift = get_shifts (vi->green_mask);
	asv->bshift = get_shifts (vi->blue_mask);
	asv->rbits = get_bits (vi->red_mask);
	asv->gbits = get_bits (vi->green_mask);
	asv->bbits = get_bits (vi->blue_mask);
	asv->true_depth = vi->depth ;
	asv->msb_first = (ImageByteOrder(asv->dpy)==MSBFirst);

	if( asv->true_depth == 16 && ((vi->red_mask|vi->blue_mask)&0x8000) == 0 )
		asv->true_depth = 15;
	/* setting up conversion handlers : */
	switch( asv->true_depth )
	{
		case 24 :
		case 32 :
			asv->color2pixel_func     = (asv->BGR_mode)?color2pixel32bgr:color2pixel32rgb ;
			asv->pixel2color_func     = (asv->BGR_mode)?pixel2color32bgr:pixel2color32rgb ;
			asv->ximage2scanline_func = ximage2scanline32 ;
			asv->scanline2ximage_func = scanline2ximage32 ;
		    break ;
/*		case 24 :
			scr->color2pixel_func     = (bgr_mode)?color2pixel24bgr:color2pixel24rgb ;
			scr->pixel2color_func     = (bgr_mode)?pixel2color24bgr:pixel2color24rgb ;
			scr->ximage2scanline_func = ximage2scanline24 ;
			scr->scanline2ximage_func = scanline2ximage24 ;
		    break ;
  */	case 16 :
			asv->color2pixel_func     = (asv->BGR_mode)?color2pixel16bgr:color2pixel16rgb ;
			asv->pixel2color_func     = (asv->BGR_mode)?pixel2color16bgr:pixel2color16rgb ;
			asv->ximage2scanline_func = ximage2scanline16 ;
			asv->scanline2ximage_func = scanline2ximage16 ;
		    break ;
		case 15 :
			asv->color2pixel_func     = (asv->BGR_mode)?color2pixel15bgr:color2pixel15rgb ;
			asv->pixel2color_func     = (asv->BGR_mode)?pixel2color15bgr:pixel2color15rgb ;
			asv->ximage2scanline_func = ximage2scanline15 ;
			asv->scanline2ximage_func = scanline2ximage15 ;
		    break ;
	}
#endif /*ifndef X_DISPLAY_MISSING */
	return (asv->ximage2scanline_func != NULL) ;
}

ARGB32 *
make_reverse_colormap( unsigned long *cmap, size_t size, int depth, unsigned short mask, unsigned short shift )
{
	unsigned int max_pixel = 0x01<<depth ;
	ARGB32 *rcmap = safecalloc( max_pixel, sizeof( ARGB32 ) );
	register int i ;

	for( i = 0 ; i < (int)size ; i++ )
		if( cmap[i] < max_pixel )
			rcmap[cmap[i]] = MAKE_ARGB32( 0xFF, (i>>(shift<<1))& mask, (i>>(shift))&mask, i&mask);
	return rcmap;
}

ASHashTable *
make_reverse_colorhash( unsigned long *cmap, size_t size, int depth, unsigned short mask, unsigned short shift )
{
	ASHashTable *hash = create_ashash( 0, NULL, NULL, NULL );
	register unsigned int i ;

	if( hash )
	{
		for( i = 0 ; i < size ; i++ )
			add_hash_item( hash, (ASHashableValue)cmap[i], (void*)((long)MAKE_ARGB32( 0xFF, (i>>(shift<<1))& mask, (i>>(shift))&mask, i&mask)) );
	}
	return hash;
}

void
setup_pseudo_visual( ASVisual *asv  )
{
#ifndef X_DISPLAY_MISSING
	XVisualInfo *vi = &(asv->visual_info) ;

	/* we need to allocate new usable list of colors based on available bpp */
	asv->true_depth = vi->depth ;
	if( asv->as_colormap == NULL )
	{
		if( asv->true_depth < 8 )
			asv->as_colormap_type = ACM_3BPP ;
		else if( asv->true_depth < 12 )
			asv->as_colormap_type = ACM_6BPP ;
		else
			asv->as_colormap_type = ACM_12BPP ;
	}
	/* then we need to set up hooks : */
	switch( asv->as_colormap_type )
	{
		case ACM_3BPP:
			asv->ximage2scanline_func = ximage2scanline_pseudo3bpp ;
			asv->scanline2ximage_func = scanline2ximage_pseudo3bpp ;
			asv->color2pixel_func = color2pixel_pseudo3bpp ;
		    break ;
		case ACM_6BPP:
			asv->ximage2scanline_func = ximage2scanline_pseudo6bpp ;
			asv->scanline2ximage_func = scanline2ximage_pseudo6bpp ;
			asv->color2pixel_func = color2pixel_pseudo6bpp ;
		    break ;
		default:
			asv->as_colormap_type = ACM_12BPP ;
		case ACM_12BPP:
			asv->ximage2scanline_func = ximage2scanline_pseudo12bpp ;
			asv->scanline2ximage_func = scanline2ximage_pseudo12bpp ;
			asv->color2pixel_func = color2pixel_pseudo12bpp ;
		    break ;
	}
	if( asv->as_colormap != NULL )
	{
		if( asv->as_colormap_type == ACM_3BPP || asv->as_colormap_type == ACM_6BPP )
		{
			unsigned short mask = 0x0003, shift = 2 ;
			if( asv->as_colormap_type==ACM_3BPP )
			{
				mask = 0x0001 ;
				shift = 1 ;
			}
			asv->as_colormap_reverse.xref = make_reverse_colormap( asv->as_colormap,
															  as_colormap_type2size( asv->as_colormap_type ),
															  asv->true_depth, mask, shift );
		}else if( asv->as_colormap_type == ACM_12BPP )
		{
			asv->as_colormap_reverse.hash = make_reverse_colorhash( asv->as_colormap,
															  as_colormap_type2size( asv->as_colormap_type ),
															  asv->true_depth, 0x000F, 4 );
		}
	}
#endif /*ifndef X_DISPLAY_MISSING */
}

#ifndef X_DISPLAY_MISSING
static unsigned long*
make_3bpp_colormap( ASVisual *asv )
{
	XColor colors_3bpp[8] =
	/* list of non-white, non-black colors in order of decreasing importance: */
	{   { 0, 0, 0xFFFF, 0, DoRed|DoGreen|DoBlue, 0},
		{ 0, 0xFFFF, 0, 0, DoRed|DoGreen|DoBlue, 0},
		{ 0, 0, 0, 0xFFFF, DoRed|DoGreen|DoBlue, 0},
	 	{ 0, 0xFFFF, 0xFFFF, 0, DoRed|DoGreen|DoBlue, 0},
	    { 0, 0, 0xFFFF, 0xFFFF, DoRed|DoGreen|DoBlue, 0},
	    { 0, 0xFFFF, 0, 0xFFFF, DoRed|DoGreen|DoBlue, 0}} ;
	unsigned long *cmap ;

	cmap = safemalloc( 8 * sizeof(unsigned long) );
	/* fail safe code - if any of the alloc fails - colormap entry will still have
	 * most suitable valid value ( black or white in 1bpp mode for example ) : */
	cmap[0] = cmap[1] = cmap[2] = cmap[3] = asv->black_pixel ;
	cmap[7] = cmap[6] = cmap[5] = cmap[4] = asv->white_pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[0] ))  /* pure green */
		cmap[0x02] = cmap[0x03] = cmap[0x06] = colors_3bpp[0].pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[1] ))  /* pure red */
		cmap[0x04] = cmap[0x05] = colors_3bpp[1].pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[2] ))  /* pure blue */
		cmap[0x01] = colors_3bpp[2].pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[3] ))  /* yellow */
		cmap[0x06] = colors_3bpp[3].pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[4] ))  /* cyan */
		cmap[0x03] = colors_3bpp[4].pixel ;
	if( ASV_ALLOC_COLOR( asv, asv->colormap, &colors_3bpp[5] ))  /* magenta */
		cmap[0x05] = colors_3bpp[5].pixel ;
	return cmap;
}

static unsigned long*
make_6bpp_colormap( ASVisual *asv, unsigned long *cmap_3bpp )
{
	unsigned short red, green, blue ;
	unsigned long *cmap = safemalloc( 0x0040*sizeof( unsigned long) );
	XColor xcol ;

	cmap[0] = asv->black_pixel ;

	xcol.flags = DoRed|DoGreen|DoBlue ;
	for( blue = 0 ; blue <= 0x0003 ; blue++ )
	{
		xcol.blue = (0xFFFF*blue)/3 ;
		for( red = 0 ; red <= 0x0003 ; red++ )
		{	                                /* red has highier priority then blue */
			xcol.red = (0xFFFF*red)/3 ;
/*			green = ( blue == 0 && red == 0 )?1:0 ; */
			for( green = 0 ; green <= 0x0003 ; green++ )
			{                                  /* green has highier priority then red */
				unsigned short index_3bpp = ((red&0x0002)<<1)|(green&0x0002)|((blue&0x0002)>>1);
				unsigned short index_6bpp = (red<<4)|(green<<2)|blue;
				xcol.green = (0xFFFF*green)/3 ;

				if( (red&0x0001) == ((red&0x0002)>>1) &&
					(green&0x0001) == ((green&0x0002)>>1) &&
					(blue&0x0001) == ((blue&0x0002)>>1) )
					cmap[index_6bpp] = cmap_3bpp[index_3bpp];
				else
				{
					if( ASV_ALLOC_COLOR( asv, asv->colormap, &xcol) != 0 )
						cmap[index_6bpp] = xcol.pixel ;
					else
						cmap[index_6bpp] = cmap_3bpp[index_3bpp] ;
				}
			}
		}
	}
	return cmap;
}

static unsigned long*
make_9bpp_colormap( ASVisual *asv, unsigned long *cmap_6bpp )
{
	unsigned long *cmap = safemalloc( 512*sizeof( unsigned long) );
	unsigned short red, green, blue ;
	XColor xcol ;

	cmap[0] = asv->black_pixel ;               /* just in case  */

	xcol.flags = DoRed|DoGreen|DoBlue ;
	for( blue = 0 ; blue <= 0x0007 ; blue++ )
	{
		xcol.blue = (0xFFFF*blue)/7 ;
		for( red = 0 ; red <= 0x0007 ; red++ )
		{	                                /* red has highier priority then blue */
			xcol.red = (0xFFFF*red)/7 ;
			for( green = 0 ; green <= 0x0007 ; green++ )
			{                                  /* green has highier priority then red */
				unsigned short index_6bpp = ((red&0x0006)<<3)|((green&0x0006)<<1)|((blue&0x0006)>>1);
				unsigned short index_9bpp = (red<<6)|(green<<3)|blue;
				xcol.green = (0xFFFF*green)/7 ;

				if( (red&0x0001) == ((red&0x0002)>>1) &&
					(green&0x0001) == ((green&0x0002)>>1) &&
					(blue&0x0001) == ((blue&0x0002)>>1) )
					cmap[index_9bpp] = cmap_6bpp[index_6bpp];
				else
				{
					if( ASV_ALLOC_COLOR( asv, asv->colormap, &xcol) != 0 )
						cmap[index_9bpp] = xcol.pixel ;
					else
						cmap[index_9bpp] = cmap_6bpp[index_6bpp] ;
				}
			}
		}
	}
	return cmap;
}

static unsigned long*
make_12bpp_colormap( ASVisual *asv, unsigned long *cmap_9bpp )
{
	unsigned long *cmap = safemalloc( 4096*sizeof( unsigned long) );
	unsigned short red, green, blue ;
	XColor xcol ;

	cmap[0] = asv->black_pixel ;               /* just in case  */

	xcol.flags = DoRed|DoGreen|DoBlue ;
	for( blue = 0 ; blue <= 0x000F ; blue++ )
	{
		xcol.blue = (0xFFFF*blue)/15 ;
		for( red = 0 ; red <= 0x000F ; red++ )
		{	                                /* red has highier priority then blue */
			xcol.red = (0xFFFF*red)/15 ;
			for( green = 0 ; green <= 0x000F ; green++ )
			{                                  /* green has highier priority then red */
				unsigned short index_9bpp = ((red&0x000E)<<5)|((green&0x000E)<<2)|((blue&0x000E)>>1);
				unsigned short index_12bpp = (red<<8)|(green<<4)|blue;
				xcol.green = (0xFFFF*green)/15 ;

				if( (red&0x0001) == ((red&0x0002)>>1) &&
					(green&0x0001) == ((green&0x0002)>>1) &&
					(blue&0x0001) == ((blue&0x0002)>>1) )
					cmap[index_12bpp] = cmap_9bpp[index_9bpp];
				else
				{
					if( ASV_ALLOC_COLOR( asv, asv->colormap, &xcol) != 0 )
						cmap[index_12bpp] = xcol.pixel ;
					else
						cmap[index_12bpp] = cmap_9bpp[index_9bpp] ;
				}
			}
		}
	}
	return cmap;
}
#endif /*ifndef X_DISPLAY_MISSING */

void
setup_as_colormap( ASVisual *asv )
{
#ifndef X_DISPLAY_MISSING
	unsigned long *cmap_lower, *cmap ;

	if( asv == NULL || asv->as_colormap != NULL )
		return ;

	cmap = make_3bpp_colormap( asv );
	if( asv->as_colormap_type == ACM_3BPP )
	{
		asv->as_colormap = cmap ;
		asv->as_colormap_reverse.xref = make_reverse_colormap( cmap, 8, asv->true_depth, 0x0001, 1 );
		return ;
	}
	cmap_lower = cmap ;
	cmap = make_6bpp_colormap( asv, cmap_lower );
	free( cmap_lower );
	if( asv->as_colormap_type == ACM_6BPP )
	{
		asv->as_colormap = cmap ;
		asv->as_colormap_reverse.xref = make_reverse_colormap( cmap, 64, asv->true_depth, 0x0003, 2 );
	}else
	{
		cmap_lower = cmap ;
		cmap = make_9bpp_colormap( asv, cmap_lower );
		free( cmap_lower );
		cmap_lower = cmap ;
		cmap = make_12bpp_colormap( asv, cmap_lower );
		free( cmap_lower );

		asv->as_colormap = cmap ;
		asv->as_colormap_reverse.hash = make_reverse_colorhash( cmap, 4096, asv->true_depth, 0x000F, 4 );
	}
#endif /*ifndef X_DISPLAY_MISSING */
}

/*********************************************************************/
/* handy utility functions for creation of windows/pixmaps/XImages : */
/*********************************************************************/
Window
create_visual_window( ASVisual *asv, Window parent,
					  int x, int y, unsigned int width, unsigned int height,
					  unsigned int border_width, unsigned int wclass,
 					  unsigned long mask, XSetWindowAttributes *attributes )
{
#ifndef X_DISPLAY_MISSING
	XSetWindowAttributes my_attr ;
	int depth = 0;

	if( asv == NULL || parent == None )
		return None ;
LOCAL_DEBUG_OUT( "Colormap %lX, parent %lX, %ux%u%+d%+d, bw = %d, class %d",
				  asv->colormap, parent, width, height, x, y, border_width,
				  wclass );
	if( attributes == NULL )
	{
		attributes = &my_attr ;
		memset( attributes, 0x00, sizeof(XSetWindowAttributes));
		mask = 0;
	}

	if( width < 1 )
		width = 1 ;
	if( height < 1 )
		height = 1 ;

	if( wclass == InputOnly )
	{
		border_width = 0 ;
		if( (mask&INPUTONLY_LEGAL_MASK) != mask )
			show_warning( " software BUG detected : illegal InputOnly window's mask 0x%lX - overriding", mask );
		mask &= INPUTONLY_LEGAL_MASK ;
	}else
	{
		depth = asv->visual_info.depth ;
		if( !get_flags(mask, CWColormap ) )
		{
			attributes->colormap = asv->colormap ;
			set_flags(mask, CWColormap );
		}
		if( !get_flags(mask, CWBorderPixmap ) )
		{
			attributes->border_pixmap = None ;
			set_flags(mask, CWBorderPixmap );
		}

		clear_flags(mask, CWBorderPixmap );
		if( !get_flags(mask, CWBorderPixel ) )
		{
			attributes->border_pixel = asv->black_pixel ;
			set_flags(mask, CWBorderPixel );
		}
		/* If the parent window and the new window have different bit
		** depths (such as on a Solaris box with 8bpp root window and
		** 24bpp child windows), ParentRelative will not work. */
		if ( get_flags(mask, CWBackPixmap) && attributes->background_pixmap == ParentRelative &&
			 asv->visual_info.visual != DefaultVisual( dpy, DefaultScreen(dpy) ))
		{
			clear_flags(mask, CWBackPixmap);
		}
	}
	LOCAL_DEBUG_OUT( "parent = %lX, mask = 0x%lX, VisualID = 0x%lX, Border Pixel = %ld, colormap = %lX",
					  parent, mask, asv->visual_info.visual->visualid, attributes->border_pixel, attributes->colormap );
	return XCreateWindow (asv->dpy, parent, x, y, width, height, border_width, depth,
						  wclass, asv->visual_info.visual,
	                      mask, attributes);
#else
	return None ;
#endif /*ifndef X_DISPLAY_MISSING */

}


GC
create_visual_gc( ASVisual *asv, Window root, unsigned long mask, XGCValues *gcvalues )
{
   	GC gc = NULL ;

#ifndef X_DISPLAY_MISSING
	if( asv )
	{
		Window scratch_window = 0 ;
		XGCValues scratch_gcv ;
		if( (scratch_window = create_visual_window( asv, root, -20, -20, 10, 10, 0, InputOutput, 0, NULL )) != None )
		{
			gc = XCreateGC( asv->dpy, scratch_window, mask, gcvalues?gcvalues:&scratch_gcv );
			XDestroyWindow( asv->dpy, scratch_window );
		}
	}
#endif
	return gc;
}

Pixmap
create_visual_pixmap( ASVisual *asv, Window root, unsigned int width, unsigned int height, unsigned int depth )
{
#ifndef X_DISPLAY_MISSING
	Pixmap p = None ;
	if( asv != NULL )
		p = XCreatePixmap( asv->dpy, root, MAX(width,(unsigned)1), MAX(height,(unsigned)1), (depth==0)?asv->true_depth:depth );
	return p;
#else
	return None ;
#endif /*ifndef X_DISPLAY_MISSING */
}

void
destroy_visual_pixmap( ASVisual *asv, Pixmap *ppmap )
{
	if( asv && ppmap )
		if( *ppmap )
		{
#ifndef X_DISPLAY_MISSING
			XFreePixmap( asv->dpy, *ppmap );
			*ppmap = None ;
#endif
		}
}

#ifndef X_DISPLAY_MISSING
static unsigned char *scratch_ximage_data = NULL ;
static int scratch_use_count = 0 ;
static size_t scratch_ximage_allocated_size = 0;  
#endif 
static size_t scratch_ximage_max_size = ASSHM_SAVED_MAX*2;  /* maximum of 512 KBytes is default  */  
static size_t scratch_ximage_normal_size = ASSHM_SAVED_MAX;  /* normal usage of scratch pool is 256 KBytes is default  */  

int
set_scratch_ximage_max_size( int new_max_size )
{
	int tmp = scratch_ximage_max_size ;
	scratch_ximage_max_size = new_max_size ;
	return tmp;
}

int
set_scratch_ximage_normal_size( int new_normal_size )
{
	int tmp = scratch_ximage_normal_size ;
	scratch_ximage_normal_size = new_normal_size ;
	return tmp;
}

#ifndef X_DISPLAY_MISSING
static void*
get_scratch_data(size_t size)
{
	if( scratch_ximage_max_size < size || scratch_use_count > 0) 
		return NULL;
	if( scratch_ximage_allocated_size < size ) 
	{
		scratch_ximage_allocated_size = size ;
		scratch_ximage_data = realloc( scratch_ximage_data, size );
	}
	
	++scratch_use_count;
	return scratch_ximage_data ; 
}

static Bool
release_scratch_data( void *data )
{
	if( scratch_use_count == 0 || data != scratch_ximage_data )
		return False;
	--scratch_use_count ;
	if( scratch_use_count == 0 )
	{
		/* want to deallocate if too much is allocated ? */
		
	}	 
	return True;
}	 
#endif 

#ifdef XSHMIMAGE

int	(*orig_XShmImage_destroy_image)(XImage *ximage) = NULL ;

typedef struct ASXShmImage
{
	XImage 			*ximage ;
	XShmSegmentInfo *segment ;
	int 			 ref_count ;
	Bool			 wait_completion_event ;
	unsigned int 	 size ;
}ASXShmImage;

typedef struct ASShmArea
{
	unsigned int 	 size ;
	char *shmaddr ;
	int shmid ;
	struct ASShmArea *next, *prev ;
}ASShmArea;

static ASHashTable	*xshmimage_segments = NULL ;
static ASHashTable	*xshmimage_images = NULL ;
/* attempt to reuse 256 Kb of shmem - no reason to reuse more than that,
 * since most XImages will be in range of 20K-100K */
static ASShmArea  *shm_available_mem_head = NULL ;
static int shm_available_mem_used = 0 ;

static Bool _as_use_shm_images = False ;

void really_destroy_shm_area( char *shmaddr, int shmid )
{
	shmdt (shmaddr);
	shmctl (shmid, IPC_RMID, 0);
	LOCAL_DEBUG_OUT("XSHMIMAGE> DESTROY_SHM : freeing shmid = %d, remaining in cache = %d bytes ", shmid, shm_available_mem_used );
}

void remove_shm_area( ASShmArea *area, Bool free_resources )
{
	if( area )
	{
		if( area == shm_available_mem_head )
			shm_available_mem_head = area->next ;
		if( area->next )
			area->next->prev = area->prev ;
		if( area->prev )
			area->prev->next = area->next ;
		shm_available_mem_used -= area->size ;
		if( free_resources )
			really_destroy_shm_area( area->shmaddr, area->shmid );
		else
		{
			LOCAL_DEBUG_OUT("XSHMIMAGE> REMOVE_SHM : reusing shmid = %d, size %d, remaining in cache = %d bytes ", area->shmid, area->size, shm_available_mem_used );
		}
		free( area );
	}

}

void flush_shm_cache( )
{
	if( xshmimage_images ) 
		destroy_ashash( &xshmimage_images );
	if( xshmimage_segments )
		destroy_ashash( &xshmimage_segments );
	while( shm_available_mem_head != NULL )
		remove_shm_area( shm_available_mem_head, True );
}

void save_shm_area( char *shmaddr, int shmid, int size )
{
	ASShmArea *area;

	if( shm_available_mem_used+size >= ASSHM_SAVED_MAX )
	{
	  	really_destroy_shm_area( shmaddr, shmid );
		return ;
	}

	shm_available_mem_used+=size ;
	area = safecalloc( 1, sizeof(ASShmArea) );

	area->shmaddr = shmaddr ;
	area->shmid = shmid ;
	area->size = size ;
	LOCAL_DEBUG_OUT("XSHMIMAGE> SAVE_SHM : saving shmid = %d, size %d, remaining in cache = %d bytes ", area->shmid, area->size, shm_available_mem_used );

	area->next = shm_available_mem_head ;
	if( shm_available_mem_head )
		shm_available_mem_head->prev = area ;
	shm_available_mem_head = area ;
}

char *get_shm_area( int size, int *shmid )
{
	ASShmArea *selected = NULL, *curr = shm_available_mem_head;

	while( curr != NULL )
	{
		if( curr->size >= size && curr->size < (size * 4)/3 )
		{
			if( selected == NULL )
				selected = curr ;
			else if( selected->size > curr->size )
				selected = curr ;
		}
		curr = curr->next ;
	}
	if( selected != NULL )
	{
		char *tmp = selected->shmaddr ;
		*shmid = selected->shmid ;
		remove_shm_area( selected, False );
		return tmp ;
	}

	*shmid = shmget (IPC_PRIVATE, size, IPC_CREAT|0666);
	return shmat (*shmid, 0, 0);
}

void
destroy_xshmimage_segment(ASHashableValue value, void *data)
{
	ASXShmImage *img_data = (ASXShmImage*)data ;
	if( img_data->segment != NULL )
	{
		LOCAL_DEBUG_OUT( "XSHMIMAGE> FREE_SEG : img_data = %p : segent to be freed: shminfo = %p ", img_data, img_data->segment );
		XShmDetach (dpy, img_data->segment);
		save_shm_area( img_data->segment->shmaddr, img_data->segment->shmid, img_data->size );
		free( img_data->segment );
		img_data->segment = NULL ;
		if( img_data->ximage == NULL )
			free( img_data );
	}else
	{
		LOCAL_DEBUG_OUT( "XSHMIMAGE> FREE_SEG : img_data = %p : segment data is NULL already value = %ld!!", img_data, value );
	}
}

Bool destroy_xshm_segment( ShmSeg shmseg )
{
	if( xshmimage_segments )
	{
		if(remove_hash_item( xshmimage_segments, AS_HASHABLE(shmseg), NULL, True ) == ASH_Success)
		{
			LOCAL_DEBUG_OUT( "XSHMIMAGE> REMOVE_SEG : segment %ld removed from the hash successfully!", shmseg );
			return True ;
		}
		LOCAL_DEBUG_OUT( "XSHMIMAGE> ERROR : could not find segment %ld(0x%lX) in the hash!", shmseg, shmseg );
	}else
	{
		LOCAL_DEBUG_OUT( "XSHMIMAGE> ERROR : segments hash is %p!!", xshmimage_segments );
	}

	return False ;
}


void
destroy_xshmimage_image(ASHashableValue value, void *data)
{
	ASXShmImage *img_data = (ASXShmImage*)data ;
	if( img_data->ximage != NULL )
	{
		if( orig_XShmImage_destroy_image )
			orig_XShmImage_destroy_image( img_data->ximage );
		else
			XFree ((char *)img_data->ximage);
		LOCAL_DEBUG_OUT( "XSHMIMAGE> FREE_XIM : ximage freed: img_data = %p, xim = %p", img_data, img_data->ximage);
		img_data->ximage = NULL ;
		if( img_data->segment != NULL && !img_data->wait_completion_event )
		{
			if( destroy_xshm_segment( img_data->segment->shmseg ) )
				return ;
			img_data->segment = NULL ;
		}
		if( img_data->segment == NULL )
			free( img_data );
	}
}

Bool enable_shmem_images ( )
{
#ifndef DEBUG_ALLOCS
	if( XShmQueryExtension (dpy) )
	{
		_as_use_shm_images = True ;
		if( xshmimage_segments == NULL )
			xshmimage_segments = create_ashash( 0, NULL, NULL, destroy_xshmimage_segment );
		if( xshmimage_images == NULL )
			xshmimage_images = create_ashash( 0, pointer_hash_value, NULL, destroy_xshmimage_image );
	}else
#endif
		_as_use_shm_images = False ;
	return _as_use_shm_images;
}

void disable_shmem_images()
{
	_as_use_shm_images = False ;
}

Bool 
check_shmem_images_enabled()
{
	return _as_use_shm_images ;
}


int destroy_xshm_image( XImage *ximage )
{
	if( xshmimage_images )
	{
		if( remove_hash_item( xshmimage_images, AS_HASHABLE(ximage), NULL, True ) != ASH_Success )
		{
			if (ximage->data != NULL)
				free ((char *)ximage->data);
			if (ximage->obdata != NULL)
				free ((char *)ximage->obdata);
			XFree ((char *)ximage);
			LOCAL_DEBUG_OUT( "XSHMIMAGE> FREE_XIM : ximage freed: xim = %p", ximage);
		}
	}
	return 1;
}

unsigned long 
ximage2shmseg( XImage *xim )
{
	ASXShmImage *data = NULL ;
	if( get_hash_item( xshmimage_images, AS_HASHABLE(xim), (void**)&data ) == ASH_Success )		
	{
		if( data->segment ) 
			return data->segment->shmseg;	
	}	
	return 0; 
}	 

void registerXShmImage( XImage *ximage, XShmSegmentInfo* shminfo )
{
	ASXShmImage *data = safecalloc( 1, sizeof(ASXShmImage));
	LOCAL_DEBUG_OUT( "XSHMIMAGE> CREATE_XIM : img_data = %p : image created: xiom = %p, shminfo = %p, segment = %d, data = %p", data, ximage, shminfo, shminfo->shmid, ximage->data );
	data->ximage = ximage ;
	data->segment = shminfo ;
	data->size = ximage->bytes_per_line * ximage->height ;

	orig_XShmImage_destroy_image = ximage->f.destroy_image ;
	ximage->f.destroy_image = destroy_xshm_image ;

	add_hash_item( xshmimage_images, AS_HASHABLE(ximage), data );
	add_hash_item( xshmimage_segments, AS_HASHABLE(shminfo->shmseg), data );
}

void *
check_XImage_shared( XImage *xim )
{
	ASXShmImage *img_data = NULL ;
	if( _as_use_shm_images )
	{
		ASHashData hdata ;
		if(get_hash_item( xshmimage_images, AS_HASHABLE(xim), &hdata.vptr ) != ASH_Success)
			img_data = NULL ;
		else
			img_data = hdata.vptr ;
	}
	return img_data ;
}

Bool ASPutXImage( ASVisual *asv, Drawable d, GC gc, XImage *xim,
                  int src_x, int src_y, int dest_x, int dest_y,
				  unsigned int width, unsigned int height )
{
	ASXShmImage *img_data = NULL ;
	if( xim == NULL || asv == NULL )
		return False ;

	if( ( img_data = check_XImage_shared( xim )) != NULL )
	{
		LOCAL_DEBUG_OUT( "XSHMIMAGE> PUT_XIM : using shared memory Put = %p", xim );
		if( XShmPutImage( asv->dpy, d, gc, xim, src_x, src_y, dest_x, dest_y,width, height, True ) )
		{
			img_data->wait_completion_event = True ;
			return True ;
		}
	}
	LOCAL_DEBUG_OUT( "XSHMIMAGE> PUT_XIM : using normal Put = %p", xim );
	return XPutImage( asv->dpy, d, gc, xim, src_x, src_y, dest_x, dest_y,width, height );
}

XImage *ASGetXImage( ASVisual *asv, Drawable d,
                  int x, int y, unsigned int width, unsigned int height,
				  unsigned long plane_mask )
{
	XImage *xim = NULL ;

	if( asv == NULL || d == None )
		return NULL ;
	if( _as_use_shm_images && width*height > 4000)
	{
		unsigned int depth ;
		Window        root;
		unsigned int  ujunk;
		int           junk;
		if(XGetGeometry (dpy, d, &root, &junk, &junk, &ujunk, &ujunk, &ujunk, &depth) == 0)
			return NULL ;

		xim = create_visual_ximage(asv,width,height,depth);
		XShmGetImage( asv->dpy, d, xim, x, y, plane_mask );

	}else
		xim = XGetImage( asv->dpy, d, x, y, width, height, plane_mask, ZPixmap );
	return xim ;
}
#else

Bool enable_shmem_images (){return False; }
void disable_shmem_images(){}
void *check_XImage_shared( XImage *xim ) {return NULL ; }

Bool ASPutXImage( ASVisual *asv, Drawable d, GC gc, XImage *xim,
                  int src_x, int src_y, int dest_x, int dest_y,
				  unsigned int width, unsigned int height )
{
#ifndef X_DISPLAY_MISSING
	if( xim == NULL || asv == NULL )
		return False ;
	return XPutImage( asv->dpy, d, gc, xim, src_x, src_y, dest_x, dest_y,width, height );
#else
	return False;
#endif
}

XImage * ASGetXImage( ASVisual *asv, Drawable d,
                  int x, int y, unsigned int width, unsigned int height,
				  unsigned long plane_mask )
{
#ifndef X_DISPLAY_MISSING
	if( asv == NULL || d == None )
		return NULL ;
	return XGetImage( asv->dpy, d, x, y, width, height, plane_mask, ZPixmap );
#else
	return NULL ;
#endif	
}

#endif                                         /* XSHMIMAGE */

#ifndef X_DISPLAY_MISSING
int
My_XDestroyImage (XImage *ximage)
{
	if( !release_scratch_data(ximage->data) )
		if (ximage->data != NULL)
			free (ximage->data);
	if (ximage->obdata != NULL)
		free (ximage->obdata);
	XFree (ximage);
	return 1;
}
#endif /*ifndef X_DISPLAY_MISSING */


XImage*
create_visual_ximage( ASVisual *asv, unsigned int width, unsigned int height, unsigned int depth )
{
#ifndef X_DISPLAY_MISSING
	register XImage *ximage = NULL;
	unsigned long dsize;
	char         *data;
	int unit ;

	if( asv == NULL )
		return NULL;

#if 0
	unit = asv->dpy->bitmap_unit;
#else
	unit = (asv->true_depth+7)&0x0038;
	if( unit == 24 )
		unit = 32 ;
#endif
#ifdef XSHMIMAGE
	if( _as_use_shm_images && width*height > 4000 )
	{
		XShmSegmentInfo *shminfo = safecalloc( 1, sizeof(XShmSegmentInfo));

		ximage = XShmCreateImage (asv->dpy, asv->visual_info.visual,
			                      (depth==0)?asv->visual_info.depth/*true_depth*/:depth,
								  ZPixmap, NULL, shminfo,
								  MAX(width,(unsigned int)1), MAX(height,(unsigned int)1));
		if( ximage == NULL )
			free( shminfo );
		else
		{
			shminfo->shmaddr = ximage->data = get_shm_area( ximage->bytes_per_line * ximage->height, &(shminfo->shmid) );
			if( shminfo->shmid == -1 )
			{
				static int shmem_failure_count = 0 ;
			    show_warning( "unable to allocate %d bytes of shared image memory", ximage->bytes_per_line * ximage->height ) ;
				if( ximage->bytes_per_line * ximage->height < 100000 || ++shmem_failure_count > 10 )
				{
					show_error( "too many shared memory failures - disabling" ) ;
					_as_use_shm_images = False ;
				}
				free( shminfo );
				shminfo = NULL ;
				XFree( ximage );
				ximage = NULL ;
			}else
			{
				shminfo->readOnly = False;
				XShmAttach (asv->dpy, shminfo);
				registerXShmImage( ximage, shminfo );
			}
		}
	}
#endif
	if( ximage == NULL )
	{
		ximage = XCreateImage (asv->dpy, asv->visual_info.visual, (depth==0)?asv->visual_info.depth/*true_depth*/:depth, ZPixmap, 0, NULL, MAX(width,(unsigned int)1), MAX(height,(unsigned int)1),
						   	unit, 0);
		if (ximage != NULL)
		{
			_XInitImageFuncPtrs (ximage);
			ximage->obdata = NULL;
			ximage->f.destroy_image = My_XDestroyImage;
			dsize = ximage->bytes_per_line*ximage->height;
	    	if (((data = (char *)safemalloc (dsize)) == NULL) && (dsize > 0))
			{
				XFree ((char *)ximage);
				return (XImage *) NULL;
			}
			ximage->data = data;
		}
	}
	return ximage;
#else
	return NULL ;
#endif /*ifndef X_DISPLAY_MISSING */
}
/* this is the vehicle to use static allocated buffer for temporary XImages 
 * in order to reduce XImage meory allocation overhead */
XImage*
create_visual_scratch_ximage( ASVisual *asv, unsigned int width, unsigned int height, unsigned int depth )
{
#ifndef X_DISPLAY_MISSING
	register XImage *ximage = NULL;
	char         *data;
	int unit ;

	if( asv == NULL )
		return NULL;

#if 0
	unit = asv->dpy->bitmap_unit;
#else
	unit = (asv->true_depth+7)&0x0038;
	if( unit == 24 )
		unit = 32 ;
#endif

	/* for shared memory XImage we already do caching - no need for scratch ximage */	   
#ifdef XSHMIMAGE
	if( _as_use_shm_images )
		return create_visual_ximage( asv, width, height, depth );
#endif
		   
	if( ximage == NULL )
	{
		ximage = XCreateImage (asv->dpy, asv->visual_info.visual, 
			                   (depth==0)?asv->visual_info.depth/*true_depth*/:depth, ZPixmap, 
							   0, NULL, MAX(width,(unsigned int)1), MAX(height,(unsigned int)1),
						   	   unit, 0);
		if (ximage != NULL)
		{
			data = get_scratch_data(ximage->bytes_per_line * ximage->height);
			if( data == NULL ) 
			{
				XFree ((char *)ximage);	
				return create_visual_ximage( asv, width, height, depth );/* fall back */
			}	 
			_XInitImageFuncPtrs (ximage);
			ximage->obdata = NULL;
			ximage->f.destroy_image = My_XDestroyImage;
			ximage->data = data;
		}
	}
	return ximage;
#else
	return NULL ;
#endif /*ifndef X_DISPLAY_MISSING */
}



/****************************************************************************/
/* Color manipulation functions :                                           */
/****************************************************************************/


/* misc function to calculate number of bits/shifts */
#ifndef X_DISPLAY_MISSING
static int
get_shifts (unsigned long mask)
{
	register int  i = 1;

	while (mask >> i)
		i++;

	return i - 1;							   /* can't be negative */
}

static int
get_bits (unsigned long mask)
{
	register int  i;

	for (i = 0; mask; mask >>= 1)
		if (mask & 1)
			i++;

	return i;								   /* can't be negative */
}
#endif
/***************************************************************************/
/* Screen color format -> AS color format conversion ; 					   */
/***************************************************************************/
/***************************************************************************/
/* Screen color format -> AS color format conversion ; 					   */
/***************************************************************************/
/* this functions convert encoded color values into real pixel values, and
 * return half of the quantization error so we can do error diffusion : */
/* encoding scheme : 0RRRrrrr rrrrGGgg ggggggBB bbbbbbbb
 * where RRR, GG and BB are overflow bits so we can do all kinds of funky
 * combined adding, note that we don't use 32'd bit as it is a sign bit */

#ifndef X_DISPLAY_MISSING
static inline void
query_pixel_color( ASVisual *asv, unsigned long pixel, CARD32 *r, CARD32 *g, CARD32 *b )
{
	XColor xcol ;
	xcol.flags = DoRed|DoGreen|DoBlue ;
	xcol.pixel = pixel ;
	if( XQueryColor( asv->dpy, asv->colormap, &xcol ) != 0 )
	{
		*r = xcol.red>>8 ;
		*g = xcol.green>>8 ;
		*b = xcol.blue>>8 ;
	}
}
#endif /*ifndef X_DISPLAY_MISSING */


CARD32 color2pixel32bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	*pixel = ARGB32_RED8(encoded_color)|(ARGB32_GREEN8(encoded_color)<<8)|(ARGB32_BLUE8(encoded_color)<<16);
	return 0;
}
CARD32 color2pixel32rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	*pixel = encoded_color&0x00FFFFFF;
	return 0;
}
CARD32 color2pixel24bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	*pixel = encoded_color&0x00FFFFFF;
	return 0;
}
CARD32 color2pixel24rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	*pixel = encoded_color&0x00FFFFFF;
	return 0;
}
CARD32 color2pixel16bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	register CARD32 c = encoded_color ;
    *pixel = ((c&0x000000F8)<<8)|((c&0x0000FC00)>>5)|((c&0x00F80000)>>19);
	return (c>>1)&0x00030103;
}
CARD32 color2pixel16rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	register CARD32 c = encoded_color ;
    *pixel = ((c&0x00F80000)>>8)|((c&0x0000FC00)>>5)|((c&0x000000F8)>>3);
	return (c>>1)&0x00030103;
}
CARD32 color2pixel15bgr(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	register CARD32 c = encoded_color ;
    *pixel = ((c&0x000000F8)<<7)|((c&0x0000F800)>>6)|((c&0x00F80000)>>19);
	return (c>>1)&0x00030303;
}
CARD32 color2pixel15rgb(ASVisual *asv, CARD32 encoded_color, unsigned long *pixel)
{
	register CARD32 c = encoded_color ;
    *pixel = ((c&0x00F80000)>>9)|((c&0x0000F800)>>6)|((c&0x000000F8)>>3);
	return (c>>1)&0x00030303;
}

CARD32 color2pixel_pseudo3bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel )
{
	register CARD32 c = encoded_color ;
	*pixel = asv->as_colormap[((c>>25)&0x0008)|((c>>16)&0x0002)|((c>>7)&0x0001)];
	return (c>>1)&0x003F3F3F;
}

CARD32 color2pixel_pseudo6bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel )
{
	register CARD32 c = encoded_color ;
	*pixel = asv->as_colormap[((c>>22)&0x0030)|((c>>14)&0x000C)|((c>>6)&0x0003)];
	return (c>>1)&0x001F1F1F;
}

CARD32 color2pixel_pseudo12bpp( ASVisual *asv, CARD32 encoded_color, unsigned long *pixel )
{
	register CARD32 c = encoded_color ;
	*pixel = asv->as_colormap[((c>>16)&0x0F00)|((c>>10)&0x00F0)|((c>>4)&0x000F)];
	return (c>>1)&0x00070707;
}

void pixel2color32rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color32bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color24rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color24bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color16rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color16bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color15rgb(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}
void pixel2color15bgr(ASVisual *asv, unsigned long pixel, CARD32 *red, CARD32 *green, CARD32 *blue)
{}

#ifndef X_DISPLAY_MISSING
void ximage2scanline32(ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x);
	register CARD8 *src = (CARD8*)(xim_data+(i-1)*4) ;
	if( asv->msb_first )
		do
		{
			--i ;
			{
				r[i] = src[1];
				g[i] = src[2];
				b[i] = src[3];
				src -= 4 ;
			}
		}while(i);
	else
		do
		{
			--i ;
			{
				r[i] = src[2];
				g[i] = src[1];
				b[i] = src[0];
				src -= 4 ;
			}
		}while(i);
}

void ximage2scanline16( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD16 *src = (CARD16*)xim_data ;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
#ifdef WORDS_BIGENDIAN
	if( !asv->msb_first )
#else
	if( asv->msb_first )
#endif
		do
		{
#define ENCODE_MSBF_565(r,gh3,gl3,b)	(((gh3)&0x0007)|((gl3)&0xE000)|((r)&0x00F8)|((b)&0x1F00))
			r[i] =  (src[i]&0x00F8);
			g[i] = ((src[i]&0x0007)<<5)|((src[i]&0xE000)>>11);
			b[i] =  (src[i]&0x1F00)>>5;
		}while( --i >= 0);
	else
		do
		{
#define ENCODE_LSBF_565(r,g,b) (((g)&0x07E0)|((r)&0xF800)|((b)&0x001F))
			r[i] =  (src[i]&0xF800)>>8;
			g[i] =  (src[i]&0x07E0)>>3;
			b[i] =  (src[i]&0x001F)<<3;
		}while( --i >= 0);

}
void ximage2scanline15( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD16 *src = (CARD16*)xim_data ;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
#ifdef WORDS_BIGENDIAN
	if( !asv->msb_first )
#else
	if( asv->msb_first )
#endif
		do
		{
#define ENCODE_MSBF_555(r,gh2,gl3,b)	(((gh2)&0x0003)|((gl3)&0xE000)|((r)&0x007C)|((b)&0x1F00))
			r[i] =  (src[i]&0x007C)<<1;
			g[i] = ((src[i]&0x0003)<<6)|((src[i]&0xE000)>>10);
			b[i] =  (src[i]&0x1F00)>>5;
		}while( --i >= 0);
	else
		do
		{
#define ENCODE_LSBF_555(r,g,b) (((g)&0x03E0)|((r)&0x7C00)|((b)&0x001F))
			r[i] =  (src[i]&0x7C00)>>7;
			g[i] =  (src[i]&0x03E0)>>2;
			b[i] =  (src[i]&0x001F)<<3;
		}while( --i >= 0);
}

void
ximage2scanline_pseudo3bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;

	do
	{
		unsigned long pixel = XGetPixel( xim, i, y );
		ARGB32 c = asv->as_colormap_reverse.xref[pixel] ;
		if( c == 0 )
			query_pixel_color( asv, pixel, r+i, g+i, b+i );
		else
		{
			r[i] =  ARGB32_RED8(c);
			g[i] =  ARGB32_GREEN8(c);
			b[i] =  ARGB32_BLUE8(c);
		}
	}while( --i >= 0);

}

void
ximage2scanline_pseudo6bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;

	if( xim->bits_per_pixel == 8 )
	{
		register CARD8 *src = (CARD8*)xim_data ;
		do
		{
			ARGB32 c = asv->as_colormap_reverse.xref[src[i]] ;
			if( c == 0 )
				query_pixel_color( asv, src[i], r+i, g+i, b+i );
			else
			{
				r[i] =  ARGB32_RED8(c);
				g[i] =  ARGB32_GREEN8(c);
				b[i] =  ARGB32_BLUE8(c);
			}
		}while( --i >= 0);

	}else
		do
		{
			unsigned long pixel = XGetPixel( xim, i, y );
			ARGB32 c = asv->as_colormap_reverse.xref[pixel] ;
			if( c == 0 )
				query_pixel_color( asv, pixel, r+i, g+i, b+i );
			else
			{
				r[i] =  ARGB32_RED8(c);
				g[i] =  ARGB32_GREEN8(c);
				b[i] =  ARGB32_BLUE8(c);
			}
		}while( --i >= 0);
}

void
ximage2scanline_pseudo12bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;

	if( xim->bits_per_pixel == 16 )
	{
		register CARD16 *src = (CARD16*)xim_data ;
		do
		{
            ASHashData hdata ;
            ARGB32 c ;
            if( get_hash_item( asv->as_colormap_reverse.hash, AS_HASHABLE((unsigned long)src[i]), &hdata.vptr ) != ASH_Success )
				query_pixel_color( asv, src[i], r+i, g+i, b+i );
			else
			{
                c = hdata.c32;
				r[i] =  ARGB32_RED8(c);
				g[i] =  ARGB32_GREEN8(c);
				b[i] =  ARGB32_BLUE8(c);
			}
		}while( --i >= 0);

	}else
		do
		{
			unsigned long pixel = XGetPixel( xim, i, y );
            ASHashData hdata ;
			ARGB32 c ;
            if( get_hash_item( asv->as_colormap_reverse.hash, (ASHashableValue)pixel, &hdata.vptr ) != ASH_Success )
				query_pixel_color( asv, pixel, r+i, g+i, b+i );
			else
			{
                c = hdata.c32;
				r[i] =  ARGB32_RED8(c);
				g[i] =  ARGB32_GREEN8(c);
				b[i] =  ARGB32_BLUE8(c);
			}
		}while( --i >= 0);
}


void scanline2ximage32( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x);
	register CARD8 *src = (CARD8*)(xim_data+(i-1)*4) ;
	if( asv->msb_first )
		do
		{
			--i ;
			{
				src[1] = r[i];
 				src[2] = g[i];
				src[3] = b[i];
				src -= 4 ;
			}
		}while(i);
	else
		do
		{
			--i ;
			{
				src[2] = r[i];
				src[1] = g[i];
				src[0] = b[i];
				src -= 4 ;
			}
		}while(i);
#ifdef DEBUG_SL2XIMAGE
	i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x);
	src = (CARD8*)(xim_data+(i-1)*4);
	printf( "xim->width = %d, sl->width = %d, sl->offset = %d\n", xim->width, sl->width, sl->offset_x );
	while(--i>=0 )
	{
		printf( "%2.2X.%2.2X.%2.2X.%2.2X ", src[0], src[1], src[2], src[3] );
	}
	printf( "\n" );
#endif
}

void scanline2ximage16( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD16 *src = (CARD16*)xim_data ;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register CARD32 c = (r[i]<<20) | (g[i]<<10) | (b[i]);
#ifdef WORDS_BIGENDIAN
	if( !asv->msb_first )
#else
	if( asv->msb_first )
#endif
		do
		{
			src[i] = ENCODE_MSBF_565((c>>20),(c>>15),(c<<1),(c<<5));
			if( --i < 0 )
				break;
			/* carry over quantization error allow for error diffusion:*/
			c = ((c>>1)&0x00300403)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
/*fprintf( stderr, "c = 0x%X, d = 0x%X, c^d = 0x%X\n", c, d, c^d );*/
			}
		}while(1);
	else
		do
		{
			src[i] = ENCODE_LSBF_565((c>>12),(c>>7),(c>>3));
			if( --i < 0 )
				break;
			/* carry over quantization error allow for error diffusion:*/
			c = ((c>>1)&0x00300403)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
/*fprintf( stderr, "c = 0x%X, d = 0x%X, c^d = 0x%X\n", c, d, c^d );*/
			}
		}while(1);
}

void scanline2ximage15( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD16 *src = (CARD16*)xim_data ;
    register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register CARD32 c = (r[i]<<20) | (g[i]<<10) | (b[i]);
#ifdef WORDS_BIGENDIAN
	if( !asv->msb_first )
#else
	if( asv->msb_first )
#endif
		do
		{
			src[i] = ENCODE_MSBF_555((c>>21),(c>>16),c/*(c>>2)*/,(c<<5));
			if( --i < 0 )
				break;
			/* carry over quantization error allow for error diffusion:*/
			c = ((c>>1)&0x00300C03)+((r[i]<<20) | (g[i]<<10) | (b[i]));
/*fprintf( stderr, "%s:%d src[%d] = 0x%4.4X, c = 0x%X, color[%d] = #%2.2X%2.2X%2.2X\n", __FUNCTION__, __LINE__, i+1, src[i+1], c, i, r[i], g[i], b[i]);*/
			{
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
/*fprintf( stderr, "%s:%d c = 0x%X, d = 0x%X, c^d = 0x%X\n", __FUNCTION__, __LINE__, c, d, c^d );*/
			}
		}while(1);
	else
	{
		do
		{
			src[i] = ENCODE_LSBF_555((c>>13),(c>>8),(c>>3));
			if( --i < 0 )
				break;
			/* carry over quantization error allow for error diffusion:*/
			c = ((c>>1)&0x00300C03)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
			}
		}while(1);
	}
}

void
scanline2ximage_pseudo3bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD32 c = (r[i]<<20) | (g[i]<<10) | (b[i]);

	do
	{
		XPutPixel( xim, i, y, asv->as_colormap[((c>>25)&0x0008)|((c>>16)&0x0002)|((c>>7)&0x0001)] );
		if( --i < 0 )
			break;
		c = ((c>>1)&0x03F0FC3F)+((r[i]<<20) | (g[i]<<10) | (b[i]));
		{/* handling possible overflow : */
			register CARD32 d = c&0x300C0300 ;
			if( d )
			{
				if( c&0x30000000 )
					d |= 0x0FF00000;
				if( c&0x000C0000 )
					d |= 0x0003FC00 ;
				if( c&0x00000300 )
					d |= 0x000000FF ;
				c ^= d;
			}
		}
	}while(i);
}

void
scanline2ximage_pseudo6bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD32 c = (r[i]<<20) | (g[i]<<10) | (b[i]);

	if( xim->bits_per_pixel == 8 )
	{
		register CARD8 *dst = (CARD8*)xim_data ;
		do
		{
			dst[i] = asv->as_colormap[((c>>22)&0x0030)|((c>>14)&0x000C)|((c>>6)&0x0003)];
			if( --i < 0 )
				break;
			c = ((c>>1)&0x01F07C1F)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{/* handling possible overflow : */
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
			}
		}while(i);
	}else
	{
		do
		{
			XPutPixel( xim, i, y, asv->as_colormap[((c>>22)&0x0030)|((c>>14)&0x000C)|((c>>6)&0x0003)] );
			if( --i < 0 )
				break;
			c = ((c>>1)&0x01F07C1F)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{/* handling possible overflow : */
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
			}
		}while(i);
	}
}

void
scanline2ximage_pseudo12bpp( ASVisual *asv, XImage *xim, ASScanline *sl, int y,  register unsigned char *xim_data )
{
	register CARD32 *r = sl->xc1+sl->offset_x, *g = sl->xc2+sl->offset_x, *b = sl->xc3+sl->offset_x;
	register int i = MIN((unsigned int)(xim->width),sl->width-sl->offset_x)-1;
	register CARD32 c = (r[i]<<20) | (g[i]<<10) | (b[i]);

	if( xim->bits_per_pixel == 16 )
	{
		register CARD16 *dst = (CARD16*)xim_data ;
		do
		{
			dst[i] = asv->as_colormap[((c>>16)&0x0F00)|((c>>10)&0x00F0)|((c>>4)&0x000F)];
			if( --i < 0 )
				break;
			c = ((c>>1)&0x00701C07)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{/* handling possible overflow : */
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
			}
		}while(i);
	}else
	{
		do
		{
			XPutPixel( xim, i, y, asv->as_colormap[((c>>16)&0x0F00)|((c>>10)&0x00F0)|((c>>4)&0x000F)] );
			if( --i < 0 )
				break;
			c = ((c>>1)&0x00701C07)+((r[i]<<20) | (g[i]<<10) | (b[i]));
			{/* handling possible overflow : */
				register CARD32 d = c&0x300C0300 ;
				if( d )
				{
					if( c&0x30000000 )
						d |= 0x0FF00000;
					if( c&0x000C0000 )
						d |= 0x0003FC00 ;
					if( c&0x00000300 )
						d |= 0x000000FF ;
					c ^= d;
				}
			}
		}while(i);
	}
}
#endif /* ifndef X_DISPLAY_MISSING */


