#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkprivate.h>
#if defined(_WIN32)
# include <gdk/gdkwin32.h>
#else
# include <gdk/gdkx.h>
# define HAVE_X
#endif

#include "guirgbimg.h"


static void GUIImagePixelRGBABlend(
	const gint bpp, guint8 *tar, const guint8 *src
);
static void GUIImagePixelGreyscaleAlphaBlend(
	const gint bpp, guint8 *tar, const guint8 *src
);
static void GUIImagePixelAnySet(
	const gint bpp, guint8 *tar, const guint8 *src
);

void GUIImageBufferCopyArea(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl,
	gint tar_x, gint tar_y,
	const gboolean blend
);
void GUIImageBufferResize(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
);
void GUIImageBufferRotateCW90(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
);
void GUIImageBufferRotateCCW90(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
);
void GUIImageBufferRotateCW180(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
);
void GUIImageBufferMirrorH(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data, 
	gint tar_width, gint tar_height, gint tar_bpl
);
void GUIImageBufferMirrorV(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data, 
	gint tar_width, gint tar_height, gint tar_bpl
);

void GUIImageBufferFlattenWithBG(
	gint bpp,
	guint8 *data,
	gint width, gint height, gint bpl,
	const guint8 *bg_color
);

guint8 *GUIGetRGBImage(
	GdkDrawable *drawable,
	const GdkRectangle *rect,
	gint *width_rtn, gint *height_rtn, gint *bpl_rtn
);
guint8 *GUIGetRGBAImage(
	GdkDrawable *drawable,
	const GdkRectangle *rect,
	gint *width_rtn, gint *height_rtn, gint *bpl_rtn  
);


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)	(((a) > (b)) ? (a) : (b))
#define MIN(a,b)	(((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


static void GUIImagePixelRGBABlend(
	const gint bpp, guint8 *tar, const guint8 *src
)
{
	const guint32 src_alpha = src[3];
	if(src_alpha == 0xff)
	{
	    *(guint32 *)tar = *(guint32 *)src;
	}
	else if(src_alpha > 0x00)
	{
	    const guint32 tar_alpha = 0xff - src_alpha;
	    tar[0] = (guint8)(
		((guint32)tar[0] * tar_alpha / 0xff) +
		((guint32)src[0] * src_alpha / 0xff)
	    );
	    tar[1] = (guint8)(
		((guint32)tar[1] * tar_alpha / 0xff) +
		((guint32)src[1] * src_alpha / 0xff)
	    );
	    tar[2] = (guint8)(
		((guint32)tar[2] * tar_alpha / 0xff) +
		((guint32)src[2] * src_alpha / 0xff)
	    );
	    tar[3] = (guint8)MIN(
		(guint32)tar[3] + src_alpha,
		0xff
	    );
	}
}

static void GUIImagePixelGreyscaleAlphaBlend(
	const gint bpp, guint8 *tar, const guint8 *src
)
{
	const guint32 src_alpha = src[1];
	if(src_alpha == 0xff)
	{
	    *(guint16 *)tar = *(guint16 *)src;
	}
	else if(src_alpha > 0x00)
	{
	    const guint32 tar_alpha = 0xff - src_alpha;
	    tar[0] = (guint8)(
		((guint32)tar[0] * tar_alpha / 0xff) +
		((guint32)src[0] * src_alpha / 0xff)
	    );
	    tar[1] = (guint8)MIN(
		(guint32)tar[1] + src_alpha,
		0xff
	    );
	}
}

static void GUIImagePixelAnySet(
	const gint bpp, guint8 *tar, const guint8 *src
)
{
	memcpy(tar, src, bpp);
}




/*
 *	Coppies an area of the source image to the target image.
 *
 *	The bpp specifies the bytes per pixel.
 *
 *	The src_data, src_width, and src_height specifies the
 *	source image.
 *
 *	The src_bpl specifies the number of bytes per line on the
 *	source image.
 *
 *	The tar_data, tar_width, and tar_height specifies the
 *	source image.
 *
 *	The tar_bpl specifies the number of bytes per line on the
 *	source image.
 *
 *	The tar_x and tar_y specifies the coordinates on the target
 *	image to copy the source image to.
 *
 *	If blend is TRUE then if bpp is 4 or 2 (if there is an alpha
 *	channel) then the source image will be blended on to the
 *	target image, otherwise the source image's pixel is set to
 *	the target image's pixel.
 */
void GUIImageBufferCopyArea(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl,
	gint tar_x, gint tar_y,
	const gboolean blend
)
{
	gint x, y;
	const guint8 *src_line, *src_line_end, *src_ptr, *src_end;
	void (*pixel_op_func)(const gint, guint8 *, const guint8 *);

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Determine the pixel operation function */
	switch(bpp)
	{
	  case 4:
	    if(blend)
		pixel_op_func = GUIImagePixelRGBABlend;
	    else
		pixel_op_func = GUIImagePixelAnySet;
	    break;
	  case 2:
	    if(blend)
		pixel_op_func = GUIImagePixelGreyscaleAlphaBlend;
	    else
		pixel_op_func = GUIImagePixelAnySet;
	    break;
	  default:
	    pixel_op_func = GUIImagePixelAnySet;
	    break;
	}

	/* Iterate through source image */
	for(y = tar_y,
	    src_line = src_data,
	    src_line_end = src_line + (src_bpl * src_height);
	    src_line < src_line_end;
	    y++,
	    src_line += src_bpl
	)
	{
	    if(y < 0)
		continue;
	    if(y >= tar_height)
		break;

	    x = tar_x;
	    src_ptr = src_line;
	    src_end = src_ptr + (src_width * bpp);
	    while(src_ptr < src_end)
	    {
		/* Current target coordinates fall in bounds on the
		 * target image?
		 */
		if((x >= 0) && (x < tar_width))
		{
		    /* Target coordinates are in bounds, set this
		     * pixel
		     */
		    pixel_op_func(
			bpp,
			tar_data + (y * tar_bpl) + (x * bpp),
			src_ptr
		    );
		    src_ptr += bpp;
		}
		else
		{
		    /* Target coordinates out of bounds, skip this
		     * pixel
		     */
 		    src_ptr += bpp;
		}

		x++;
	    }
	}
}


/*
 *	Copy/resize the source image data to the target image data of
 *	a different size.
 */
void GUIImageBufferResize(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
)
{
	const guint8 *src_ptr8;
	guint8 *tar_ptr8;
	const guint16 *src_ptr16;
	guint16 *tar_ptr16;
	const guint32 *src_ptr32;
	guint32 *tar_ptr32;

	/* Current positions */
	gint tar_x_col, tar_y_row;
	gint src_x_col, src_y_row;	/* In units of 256 */

	/* Increments, in units of 256 */
	gint src_dh_x_col_inc, src_dh_y_row_inc;
	gint src_dh_width, src_dh_height;


	if((tar_width <= 0) || (tar_height <= 0) ||
	   (src_width <= 0) || (src_height <= 0) ||
	   (tar_data == NULL) || (src_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* 8 bits */
	if(bpp == 1)
	{
	    src_dh_x_col_inc = (gint)(src_width * 256) / tar_width;
	    src_dh_y_row_inc = (gint)(src_height * 256) / tar_height;

	    src_dh_width = src_width * 256;
	    src_dh_height = src_height * 256;

	    tar_x_col = 0;
	    tar_y_row = 0;
	    src_x_col = 0 * 256;
	    src_y_row = 0 * 256;

	    /* Begin copying source buffer to target buffer */
	    while((tar_y_row < tar_height) &&
		  (src_y_row < src_dh_height)
	    )
	    {
		/* Get buffer position */
		src_ptr8 = (const guint8 *)(src_data +
		    ((src_y_row >> 8) * src_bpl) +
		    ((src_x_col >> 8) * bpp)
		);
		tar_ptr8 = (guint8 *)(tar_data +
		    (tar_y_row * tar_bpl) +
		    (tar_x_col * bpp)
		);
		*tar_ptr8 = *src_ptr8;


		/* Increment colum positions */
		tar_x_col += 1;
		src_x_col += src_dh_x_col_inc;

		/* Go to next line? */
		if((tar_x_col >= tar_width) ||
		   (src_x_col >= src_dh_width)
		)
		{
		    tar_x_col = 0;
		    src_x_col = 0;

		    tar_y_row += 1;
		    src_y_row += src_dh_y_row_inc;
		}
	    }
	}
	/* 16 bits */
	else if(bpp == 2)
	{
	    src_dh_x_col_inc = (gint)(src_width * 256) / tar_width;
	    src_dh_y_row_inc = (gint)(src_height * 256) / tar_height;

	    src_dh_width = src_width * 256;
	    src_dh_height = src_height * 256;

	    tar_x_col = 0;
	    tar_y_row = 0;
	    src_x_col = 0 * 256;
	    src_y_row = 0 * 256;

	    /* Begin copying source buffer to target buffer */
	    while((tar_y_row < tar_height) &&
		  (src_y_row < src_dh_height)
	    )
	    {
		/* Get buffer position */
		src_ptr16 = (const guint16 *)(src_data +
		    ((src_y_row >> 8) * src_bpl) +
		    ((src_x_col >> 8) * bpp)
		);
		tar_ptr16 = (guint16 *)(tar_data +
		    (tar_y_row * tar_bpl) +
		    (tar_x_col * bpp)
		);
		*tar_ptr16 = *src_ptr16;


		/* Increment colum positions */
		tar_x_col += 1;
		src_x_col += src_dh_x_col_inc;

		/* Go to next line? */
		if((tar_x_col >= tar_width) ||
		   (src_x_col >= src_dh_width)
		)
		{
		    tar_x_col = 0;
		    src_x_col = 0;

		    tar_y_row += 1;
		    src_y_row += src_dh_y_row_inc;
		}
	    }
	}
	/* 24 bits */
	else if(bpp == 3)
	{
	    src_dh_x_col_inc = (gint)(src_width * 256) / tar_width;
	    src_dh_y_row_inc = (gint)(src_height * 256) / tar_height;

	    src_dh_width = src_width * 256;
	    src_dh_height = src_height * 256;

	    tar_x_col = 0;
	    tar_y_row = 0;
	    src_x_col = 0 * 256;
	    src_y_row = 0 * 256;

	    /* Begin copying source buffer to target buffer */
	    while((tar_y_row < tar_height) &&
		  (src_y_row < src_dh_height)
	    )
	    {
		/* Get buffer position */
		src_ptr8 = (const guint8 *)(src_data +
		    ((src_y_row >> 8) * src_bpl) +
		    ((src_x_col >> 8) * bpp)
		);
		tar_ptr8 = (guint8 *)(tar_data +
		    (tar_y_row * tar_bpl) +
		    (tar_x_col * bpp)
		);
		*tar_ptr8++ = *src_ptr8++;
		*tar_ptr8++ = *src_ptr8++;
		*tar_ptr8 = *src_ptr8;

		/* Increment colum positions */
		tar_x_col += 1;
		src_x_col += src_dh_x_col_inc;

		/* Go to next line? */
		if((tar_x_col >= tar_width) ||
		   (src_x_col >= src_dh_width)
		)
		{
		    tar_x_col = 0;
		    src_x_col = 0;

		    tar_y_row += 1;
		    src_y_row += src_dh_y_row_inc;
		}
	    }
	}
	/* 32 bits */
	else if(bpp == 4)
	{
	    src_dh_x_col_inc = (gint)(src_width * 256) / tar_width;
	    src_dh_y_row_inc = (gint)(src_height * 256) / tar_height;

	    src_dh_width = src_width * 256;
	    src_dh_height = src_height * 256;

	    tar_x_col = 0;
	    tar_y_row = 0;
	    src_x_col = 0 * 256;
	    src_y_row = 0 * 256;

	    /* Begin copying source buffer to target buffer */
	    while((tar_y_row < tar_height) &&
		  (src_y_row < src_dh_height)
	    )
	    {
		/* Get buffer position */
		src_ptr32 = (const guint32 *)(src_data +
		    ((src_y_row >> 8) * src_bpl) +
		    ((src_x_col >> 8) * bpp)
		);
		tar_ptr32 = (guint32 *)(tar_data +
		    (tar_y_row * tar_bpl) +
		    (tar_x_col * bpp)
		);
		*tar_ptr32 = *src_ptr32;


		/* Increment colum positions */
		tar_x_col += 1;
		src_x_col += src_dh_x_col_inc;

		/* Go to next line? */
		if((tar_x_col >= tar_width) ||
		   (src_x_col >= src_dh_width)
		)
		{
		    tar_x_col = 0;
		    src_x_col = 0;

		    tar_y_row += 1;
		    src_y_row += src_dh_y_row_inc;
		}
	    }
	}
}

/*
 *	Copy/rotates the source image data to the target image data
 *	clockwise 90 degrees.
 */
void GUIImageBufferRotateCW90(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
)
{
	gint sx, sy, tx, ty;
	const guint8 *src_line;

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Rotated geometry not correct? */
	if((src_width != tar_height) || (src_height != tar_width))
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Copy/rotate clockwise 90 degrees */
	for(sy = 0, tx = tar_width - 1;
	    sy < src_height;
	    sy++, tx--
	)
	{
	    src_line = src_data + (sy * src_bpl);
	    for(sx = 0, ty = 0;
		sx < src_width;
		sx++, ty++
	    )
		memcpy(
		    tar_data + (ty * tar_bpl) + (tx * bpp),
		    src_line + (sx * bpp),
		    bpp
		);
	}
}

/*
 *	Copy/rotates the source image data to the target image data
 *	counter-clockwise 90 degrees.
 */
void GUIImageBufferRotateCCW90(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
)
{
	gint sx, sy, tx, ty;
	const guint8 *src_line;

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Rotated geometry not correct? */
	if((src_width != tar_height) || (src_height != tar_width))
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Copy/rotate counter-clockwise 90 degrees */
	for(sy = 0, tx = 0;
	    sy < src_height;
	    sy++, tx++
	)
	{
	    src_line = src_data + (sy * src_bpl);
	    for(sx = 0, ty = tar_height - 1;
		sx < src_width;
		sx++, ty--
	    )
		memcpy(
		    tar_data + (ty * tar_bpl) + (tx * bpp),
		    src_line + (sx * bpp),
		    bpp
		);
	}
}

/*
 *	Copy/rotates the source image data to the target image data
 *	clockwise 180 degrees.
 */
void GUIImageBufferRotateCW180(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
) 
{
	gint sx, sy, tx, ty;
	const guint8 *src_line;

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Rotated geometry not correct? */
	if((src_width != tar_width) || (src_height != tar_height))
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Copy/rotate clockwise 90 degrees */
	for(sy = 0, ty = tar_height - 1;
	    sy < src_height;
	    sy++, ty--
	)
	{
	    src_line = src_data + (sy * src_bpl);
	    for(sx = 0, tx = tar_width - 1;
		sx < src_width;
		sx++, tx--
	    )
		memcpy(
		    tar_data + (ty * tar_bpl) + (tx * bpp),
		    src_line + (sx * bpp),
		    bpp
		);
	}
}

/*
 *	Copy/mirrors the source image data to the target image data
 *	horizontally.
 */
void GUIImageBufferMirrorH(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
)
{
	gint sx, sy, tx, ty;
	const guint8 *src_line;

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Mirror geometry not correct? */
	if((src_width != tar_width) || (src_height != tar_height))
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Copy/mirror horizontally */
	for(sy = 0, ty = 0;
	    sy < src_height;
	    sy++, ty++
	)
	{
	    src_line = src_data + (sy * src_bpl);
	    for(sx = 0, tx = tar_width - 1;
		sx < src_width;
		sx++, tx--
	    )
		memcpy(
		    tar_data + (ty * tar_bpl) + (tx * bpp),
		    src_line + (sx * bpp),
		    bpp
		);
	}
}

/*
 *	Copy/mirrors the source image data to the target image data
 *	vertically.
 */
void GUIImageBufferMirrorV(
	gint bpp,
	const guint8 *src_data,
	gint src_width, gint src_height, gint src_bpl,
	guint8 *tar_data,
	gint tar_width, gint tar_height, gint tar_bpl
)
{
	gint sx, sy, tx, ty;
	const guint8 *src_line;

	if((src_width <= 0) || (src_height <= 0) ||
	   (tar_width <= 0) || (tar_height <= 0) ||
	   (src_data == NULL) || (tar_data == NULL) ||
	   (bpp <= 0)
	)
	    return;

	/* Mirror geometry not correct? */
	if((src_width != tar_width) || (src_height != tar_height))
	    return;

	/* Calculate bytes per line values as needed */
	if(src_bpl <= 0)
	    src_bpl = src_width * bpp;
	if(tar_bpl <= 0)
	    tar_bpl = tar_width * bpp;

	/* Copy/mirror horizontally */
	for(sy = 0, ty = tar_height - 1;
	    sy < src_height;
	    sy++, ty--
	)
	{
	    src_line = src_data + (sy * src_bpl);
	    for(sx = 0, tx = 0;
		sx < src_width;
		sx++, tx++
	    )
		memcpy(
		    tar_data + (ty * tar_bpl) + (tx * bpp),
		    src_line + (sx * bpp),
		    bpp
		);
	}
}

/*
 *	Combines the image data with the specified background color.
 *
 *	Only bpp=2 (Greyscale Alpha) and bpp=4 (RGBA) are supported.
 */
void GUIImageBufferFlattenWithBG(
	gint bpp,
	guint8 *data,
	gint width, gint height, gint bpl,
	const guint8 *bg_color
)
{
	guint tar_alpha, bg_alpha;
	guint8 *line, *line_end, *ptr, *ptr_end;

	if((data == NULL) || (bg_color == NULL))
	    return;

	/* Calculate bytes per line value as needed */
	if(bpl <= 0)
	    bpl = width * bpp;

	/* Greyscale Alpha? */
	if(bpp == 2)
	{
	    for(line = data,
		line_end = line + (bpl * height);
		line < line_end;
		line += bpl
	    )
	    {
		ptr = line;
		ptr_end = ptr + (width * bpp);

		while(ptr < ptr_end)
		{
		    tar_alpha = (guint)ptr[3];
		    if(tar_alpha == 0xff)
		    {
			ptr += bpp;
		    }
		    else if(tar_alpha > 0x00)
		    {
			bg_alpha = 0xff - tar_alpha;
			*ptr++ = (guint8)(
			    ((guint)(*ptr) * tar_alpha / 0xff) +
			    ((guint)(bg_color[0]) * bg_alpha / 0xff)
			);
			*ptr++ = 0xff;
		    }
		    else
		    {
			*(guint16 *)ptr = *(guint16 *)bg_color;
			ptr += bpp;
		    }
		}
	    }
	}
	/* RGBA */
	else if(bpp == 4)
	{
	    for(line = data, 
		line_end = line + (bpl * height);
		line < line_end;
		line += bpl
	    )
	    {
		ptr = line;
		ptr_end = ptr + (width * bpp);

		while(ptr < ptr_end)
		{
		    tar_alpha = (guint)ptr[3];
		    if(tar_alpha == 0xff)
		    {
			ptr += bpp;
		    }
		    else if(tar_alpha > 0x00)
		    {
			bg_alpha = 0xff - tar_alpha;
			*ptr++ = (guint8)(
			    ((guint)(*ptr) * tar_alpha / 0xff) +
			    ((guint)(bg_color[0]) * bg_alpha / 0xff)
			);
			*ptr++ = (guint8)(
			    ((guint)(*ptr) * tar_alpha / 0xff) +
			    ((guint)(bg_color[1]) * bg_alpha / 0xff)
			);
			*ptr++ = (guint8)(
			    ((guint)(*ptr) * tar_alpha / 0xff) +
			    ((guint)(bg_color[2]) * bg_alpha / 0xff)
			);
			*ptr++ = 0xff;
		    }
		    else
		    {
			*(guint32 *)ptr = *(guint32 *)bg_color;
			ptr += bpp;
		    }
		}
	    }
	}
}


/*
 *	Gets a RGB image buffer from the specified drawable with the
 *	specified rectangular area.
 *
 *	If rect is NULL then the entire drawable is obtained.
 */
guint8 *GUIGetRGBImage(
	GdkDrawable *drawable, 
	const GdkRectangle *rect,
	gint *width_rtn, gint *height_rtn, gint *bpl_rtn  
)
{
#if defined(__GDK_X_H__)
	gint width, height, src_bpp, src_bpl, tar_bpp, tar_bpl;
	const guint8 *src_data;
	guint8 *tar_data;
	Display *xdpy;
	XImage *ximg;
	GdkRectangle lrect;
	GdkWindowPrivate *private = (GdkWindowPrivate *)drawable;
	Window xwin = (private != NULL) ? private->xwindow : None;

	if(width_rtn != NULL)
	    *width_rtn = 0;
	if(height_rtn != NULL)
	    *height_rtn = 0;
	if(bpl_rtn != NULL)
	    *bpl_rtn = 0;

	if(xwin == None)
	    return(NULL);

	xdpy = private->xdisplay;
	if(xdpy == NULL)
	    return(NULL);

	/* If no rectangle is specified then set rectangle to cover the
	 * entire drawable
	 */
	if(rect == NULL)
	{
	    rect = &lrect;
	    lrect.x = private->x;
	    lrect.y = private->y;
	    lrect.width = private->width;
	    lrect.height = private->height;
	}

	if((rect->width <= 0) || (rect->height <= 0))
	    return(NULL);

	/* Get an XImage from the GdkDrawable's Window */
	ximg = XGetImage(
	    xdpy, xwin,
	    (int)rect->x, (int)rect->y,
	    (unsigned int)rect->width, (unsigned int)rect->height,
	    AllPlanes,
	    ZPixmap
	);
	if((ximg != NULL) ? (ximg->data == NULL) : TRUE)
	{
	    if(ximg != NULL)
		XDestroyImage(ximg);
	    return(NULL);
	}

	/* Get values from XImage */
	width = ximg->width;
	height = ximg->height;
	src_bpl = ximg->bytes_per_line;
	src_data = (const guint8 *)ximg->data;

	/* Allocate the target RGB image data */
	tar_bpp = 3;
	tar_bpl = width * tar_bpp;
	tar_data = (guint8 *)g_malloc(tar_bpl * height);
	if(tar_data == NULL)
	{
	    XDestroyImage(ximg);
	    return(NULL);
	}

	/* Begin copying the XImage data to the return data by the
	 * XImage's depth
	 */
	if(ximg->depth == 8)
	{
	    gint xoffset_bytes, x, y;
	    const gint	r_div = 7,
			g_div = 7,
			b_div = 3;
	    guint src_v;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 1;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 8 Bits Format: rrrgggbb */
		    src_v = (guint)*src_ptr++;
		    *tar_ptr++ = (guint8)(
		((src_v & 0xE0) >> 5) * 0xff / r_div  
		    );
		    *tar_ptr++ = (guint8)(
		((src_v & 0x1C) >> 2) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((src_v & 0x03) >> 0) * 0xff / b_div
		    );
		}
	    }
	}
	else if(ximg->depth == 15)
	{
	    gint xoffset_bytes, x, y;
	    const gint  r_div = 31,
			g_div = 31,
			b_div = 31;
	    const guint16 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 2;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint16 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 16 Bits Format: arrrrrgg gggbbbbb */
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x7C00) >> 10) * 0xff / r_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x03E0) >> 5) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x001F) >> 0) * 0xff / b_div
		    );

		    src_ptr++;
		}
	    }
	}
	else if(ximg->depth == 16)
	{
	    gint xoffset_bytes, x, y;
	    const gint  r_div = 31,
			g_div = 63,
			b_div = 31;
	    const guint16 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 2;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint16 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 16 Bits Format: rrrrrggg gggbbbbb */
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0xF800) >> 11) * 0xff / r_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x07E0) >> 5) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x001F) >> 0) * 0xff / b_div
		    );

		    src_ptr++;
		}
	    }
	}
	else if(ximg->depth == 24)
	{
	    gint xoffset_bytes, x, y;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 3;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 32 Bits Format: rrrrrrrr gggggggg bbbbbbbb */
		    *tar_ptr++ = src_ptr[2];
		    *tar_ptr++ = src_ptr[1];
		    *tar_ptr++ = src_ptr[0];

		    src_ptr += src_bpp;
		}
	    }
	}
	else if(ximg->depth == 32)
	{
	    gint xoffset_bytes, x, y;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 4;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 32 Bits Format: aaaaaaaa rrrrrrrr gggggggg bbbbbbbb */
		    *tar_ptr++ = src_ptr[2];
		    *tar_ptr++ = src_ptr[1];
		    *tar_ptr++ = src_ptr[0];

		    src_ptr += src_bpp;
		}
	    }
	}

	XDestroyImage(ximg);

	if(width_rtn != NULL)
	    *width_rtn = width;
	if(height_rtn != NULL)
	    *height_rtn = height;
	if(bpl_rtn != NULL)
	    *bpl_rtn = tar_bpl;

	return(tar_data);
#else
	if(width_rtn != NULL)
	    *width_rtn = 0;
	if(height_rtn != NULL)
	    *height_rtn = 0;
	if(bpl_rtn != NULL)
	    *bpl_rtn = 0;
	return(NULL);
#endif
}

/*
 *	Gets a RGB image buffer from the specified drawable with the 
 *	specified rectangular area.
 *
 *	If rect is NULL then the entire drawable is obtained.
 */
guint8 *GUIGetRGBAImage(
	GdkDrawable *drawable,
	const GdkRectangle *rect,
	gint *width_rtn, gint *height_rtn, gint *bpl_rtn  
)
{
#if defined(__GDK_X_H__)
	gint width, height, src_bpp, src_bpl, tar_bpp, tar_bpl;
	const guint8 *src_data;
	guint8 *tar_data;
	Display *xdpy;
	XImage *ximg;
	GdkRectangle lrect;
	GdkWindowPrivate *private = (GdkWindowPrivate *)drawable;
	Window xwin = (private != NULL) ? private->xwindow : None;

	if(width_rtn != NULL)
	    *width_rtn = 0;
	if(height_rtn != NULL)
	    *height_rtn = 0;
	if(bpl_rtn != NULL)
	    *bpl_rtn = 0;

	if(xwin == None)
	    return(NULL);

	xdpy = private->xdisplay;
	if(xdpy == NULL)
	    return(NULL);

	/* If no rectangle is specified then set rectangle to cover the
	 * entire drawable
	 */
	if(rect == NULL)
	{
	    rect = &lrect;
	    lrect.x = private->x;
	    lrect.y = private->y;
	    lrect.width = private->width;
	    lrect.height = private->height;
	}

	if((rect->width <= 0) || (rect->height <= 0))
	    return(NULL);

	/* Get an XImage from the GdkDrawable's Window */
	ximg = XGetImage(
	    xdpy, xwin,
	    (int)rect->x, (int)rect->y,
	    (unsigned int)rect->width, (unsigned int)rect->height,
	    AllPlanes,
	    ZPixmap
	);
	if((ximg != NULL) ? (ximg->data == NULL) : TRUE)
	{
	    if(ximg != NULL)
		XDestroyImage(ximg);
	    return(NULL);
	}

	/* Get values from XImage */
	width = ximg->width;
	height = ximg->height;
	src_bpl = ximg->bytes_per_line;
	src_data = (const guint8 *)ximg->data;

	/* Allocate the target RGBA image data */
	tar_bpp = 4;
	tar_bpl = width * tar_bpp;
	tar_data = (guint8 *)g_malloc(tar_bpl * height);
	if(tar_data == NULL)
	{
	    XDestroyImage(ximg);
	    return(NULL);
	}

	/* Begin copying the XImage data to the return data */
	if(ximg->depth == 8)
	{
	    gint xoffset_bytes, x, y;
	    const gint	r_div = 7,
			g_div = 7,
			b_div = 3;
	    guint src_v;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 1;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 8 Bits Format: rrrgggbb */
		    src_v = (guint)*src_ptr++;
		    *tar_ptr++ = (guint8)(
		((src_v & 0xE0) >> 5) * 0xff / r_div
		    );
		    *tar_ptr++ = (guint8)(
		((src_v & 0x1C) >> 2) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((src_v & 0x03) >> 0) * 0xff / b_div
		    );
		    *tar_ptr++ = 0xff;
		}
	    }
	}
	else if(ximg->depth == 15)
	{
	    gint xoffset_bytes, x, y;
	    const gint	r_div = 31,
			g_div = 31,
			b_div = 31;
	    const guint16 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 2;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint16 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 16 Bits Format: arrrrrgg gggbbbbb */
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x7C00) >> 10) * 0xff / r_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x03E0) >> 5) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x001F) >> 0) * 0xff / b_div
		    );
/* Alpha channel may not be defined
		    *tar_ptr++ = (((guint)*src_ptr) & (1 << 15)) ?
			0xff : 0x00;
 */
		    *tar_ptr++ = 0xff;

		    src_ptr++;
		}
	    }
	}
	else if(ximg->depth == 16)
	{
	    gint xoffset_bytes, x, y;
	    const gint  r_div = 31,
			g_div = 63,
			b_div = 31;
	    const guint16 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 2;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint16 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 16 Bits Format: rrrrrggg gggbbbbb */
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0xF800) >> 11) * 0xff / r_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x07E0) >> 5) * 0xff / g_div
		    );
		    *tar_ptr++ = (guint8)(
		((((guint)*src_ptr) & 0x001F) >> 0) * 0xff / b_div
		    );
		    *tar_ptr++ = 0xff;

		    src_ptr++;
		}
	    }
	}	    
	else if(ximg->depth == 24)
	{                         
	    gint xoffset_bytes, x, y;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 3;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 32 Bits Format: rrrrrrrr gggggggg bbbbbbbb */
		    *tar_ptr++ = src_ptr[2];
		    *tar_ptr++ = src_ptr[1];
		    *tar_ptr++ = src_ptr[0];
		    *tar_ptr++ = 0xff;

		    src_ptr += src_bpp;
		}
	    }
	}
	else if(ximg->depth == 32)
	{
	    gint xoffset_bytes, x, y;
	    const guint8 *src_ptr;
	    guint8 *tar_ptr;

	    src_bpp = 4;
	    xoffset_bytes = ximg->xoffset * src_bpp;

	    for(y = 0; y < height; y++)
	    {
		src_ptr = (const guint8 *)(
		    src_data + (y * src_bpl) + xoffset_bytes
		);
		tar_ptr = tar_data + (y * tar_bpl);

		for(x = 0; x < width; x++)
		{
		    /* 32 Bits Format: aaaaaaaa rrrrrrrr gggggggg bbbbbbbb */
		    *tar_ptr++ = src_ptr[2];
		    *tar_ptr++ = src_ptr[1];
		    *tar_ptr++ = src_ptr[0];
/* Alpha channel may not be defined
		    *tar_ptr++ = src_ptr[3];
 */
		    *tar_ptr++ = 0xff;

		    src_ptr += src_bpp;
		}
	    }
	}

	XDestroyImage(ximg);

	if(width_rtn != NULL)
	    *width_rtn = width;
	if(height_rtn != NULL)
	    *height_rtn = height;
	if(bpl_rtn != NULL)
	    *bpl_rtn = tar_bpl;

	return(tar_data);
#else
	if(width_rtn != NULL)
	    *width_rtn = 0;
	if(height_rtn != NULL)
	    *height_rtn = 0;
	if(bpl_rtn != NULL)
	    *bpl_rtn = 0;
	return(NULL);
#endif
}
