/*	ParseBitmap.cpp
 *
 *		This contains the code necessary to parse the Windows 4 color bitmap
 *	and blow the bits into an X image
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or 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 MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library 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.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++

#if OPT_XWIN
#include <XDraw.h>
#include <XDataUtil.h>

/************************************************************************/
/*																		*/
/*	Color Table															*/
/*																		*/
/************************************************************************/

static bool				GTableInit = false;			// true if initialized
static Color			GTableColor[16];
static unsigned long	GTablePixel[16];

static short			GTransparent;
static unsigned long	GConvertTable[16];			// convert color table

/************************************************************************/
/*																		*/
/*	Color table initialization											*/
/*																		*/
/************************************************************************/

/*	InitColorTable
 *
 *		Initialize the color table
 */

static void InitColorTable()
{
	extern unsigned long AllocColor(Color);
	int i;
	
	if (!GTableInit) {
		GTableInit = true;
		
		GTableColor[0]  = RGB(0x00,0x00,0x00);
		GTableColor[1]  = RGB(0x80,0x80,0x80);
		GTableColor[2]  = RGB(0xDD,0xDD,0xDD);
		GTableColor[3]  = RGB(0xFF,0xFF,0xFF);		// greys
		
		GTableColor[4]  = RGB(0x80,0x00,0x00);
		GTableColor[5]  = RGB(0xFF,0x00,0x00);
		GTableColor[6]  = RGB(0x00,0x80,0x00);
		GTableColor[7]  = RGB(0x00,0xFF,0x00);
		GTableColor[8]  = RGB(0x00,0x00,0x80);
		GTableColor[9]  = RGB(0x00,0x00,0xFF);
		GTableColor[10] = RGB(0x80,0x80,0x00);
		GTableColor[11] = RGB(0xFF,0xFF,0x00);
		GTableColor[12] = RGB(0x80,0x00,0x80);
		GTableColor[13] = RGB(0xFF,0x00,0xFF);
		GTableColor[14] = RGB(0x00,0x80,0x80);
		GTableColor[15] = RGB(0x00,0xFF,0xFF);
		
		for (i = 0; i < 16; i++) GTablePixel[i] = AllocColor(GTableColor[i]);
	}
}

/*	InvertColor
 *
 *		Find the closest color to the one in the table
 */

static int InvertColor(Color c)
{
	int i,j;
	long dr,dg,db;
	long max;
	
	j = -1;
	max = 0;
	for (i = 0; i < 16; i++) {
		dr = GetRValue(c);
		dr -= GetRValue(GTableColor[i]);
		dg = GetGValue(c);
		dg -= GetGValue(GTableColor[i]);
		db = GetBValue(c);
		db -= GetBValue(GTableColor[i]);
		
		dr = dr * dr + dg * dg + db * db;
		if ((j == -1) || (max > dr)) {
			j = i;
			max = dr;
		}
	}
	return j;
}


/************************************************************************/
/*																		*/
/*	Bitmap parser														*/
/*																		*/
/************************************************************************/

/*	VerifyColorBitmap
 *
 *		Return true if this is a color bitmap I can deal with
 */

static bool VerifyColorBitmap(void *bmap)
{
	char *h = (char *)bmap;
	long tmp;
	
	if ((h[0] != 'B') || (h[1] != 'M')) return false;
	tmp = ESwapLong(*((long *)(h + 0x000E)));
	if (tmp != 0x28) return false;				// not the right header
	
	tmp = ESwapWord(*((short *)(h + 0x001A)));	// not the right color
	if (tmp != 1) return false;					// depth
	tmp = ESwapWord(*((short *)(h + 0x001C)));
	if (tmp != 4) return false;
	
	tmp = ESwapLong(*((short *)(h + 0x001E)));
	if (tmp != 0) return false;					// compressed? yeeech!
	
	return true;
}

/*	GetBitmapSize
 *
 *		Get the size of this bitmap
 */

static void GetBitmapSize(void *bmap, short *x, short *y)
{
	char *h = (char *)bmap;
	
	*x = ESwapLong(*((long *)(h + 0x0012)));
	*y = ESwapLong(*((long *)(h + 0x0016)));
}

/*	BuildColorTable
 *
 *		Given the bitmap, builds the color conversion table
 */

static void BuildColorTable(void *bmap)
{
	Color c;
	unsigned char *h = (unsigned char *)bmap;
	int i,j;
	
	/*
	 *	Initialize stuff
	 */
	
	InitColorTable();
	h += 0x0036;					// start of color table
	
	/*
	 *	Build inverted color table
	 */
	
	GTransparent = -1;
	for (i = 0; i < 16; i++) {
		c = RGB(h[2],h[1],h[0]);
		h += 4;
		j = InvertColor(c);
		if (j == 2) GTransparent = i;
		GConvertTable[i] = GTablePixel[j];
	}
}

/*	Parse4ColorBitmap
 *
 *		This parses the 4 color bitmap, and construct a mapping from
 *	the pixels in that bitmap to the 16 colors 
 */

static void Parse4ColorBitmap(void *src, XImage *dest, bool trans)
{
	short width,height;
	short x,y;
	unsigned char *data;
	unsigned char *ptr;
	long bytes;
	short pixel;
	
	/*
	 *	Find where the bits start
	 */
	
	if (src == NULL) return;
	GetBitmapSize(src,&width,&height);
	data = (unsigned char *)src;
	data += ESwapLong(*((long *)(data + 0x000A)));
	
	/*
	 *	Find the width of this thing
	 */

	bytes = (width+1)/2;
	if (bytes % 4) bytes += 4 - bytes % 4;	

	for (y = 0; y < height; y++) {
		ptr = y * bytes + data;
		for (x = 0; x < width; x++) {
			pixel = ptr[x >> 1];
			if (x & 1) pixel &= 0x0F;
			else pixel = 0x0F & (pixel >> 4);
			
			if (!trans || (pixel != GTransparent)) {
				XPutPixel(dest,x,height-y-1,GConvertTable[pixel]);
			}
		}
	}
}
 
/************************************************************************/
/*																		*/
/*	X Drawing															*/
/*																		*/
/************************************************************************/

/*	@F"DrawBitmap"
 *	@H"XDrawData.h"
 *	@I
 *
 *		(X) This is the routine used to draw a bitmap onto the display 
 *	surface specified
 */

void DrawBitmap(Display *display, Drawable drawable, GC gc, void *data, short x, short y, bool trans)
{
	short width, height;
	XImage *image;
	Visual *visual;
	char *dmem;
	long size;
	long depth;
	
	/*
	 *	See if the bitmap is even reasonable
	 */
	
	if (!data) return;
	if (!VerifyColorBitmap(data)) return;
	GetBitmapSize(data,&width,&height);
	BuildColorTable(data);
	
	/*
	 *	Create the image
	 */
	
	visual = DefaultVisual(display,0);
	depth = DefaultDepth(display,0);
	
	size = 32 / depth;
	size = (4 * width) / size;
	if (size % 4) size += 4 - size % 4;
	
	dmem = malloc(size * height);
	
	if (trans) {
		/* Necessary for transparency processing */
		image = XGetImage(display,
						  drawable,
						  x, y,
						  width, height,
						  AllPlanes, ZPixmap);
	} else {
		/* No transparency processing */
		image = XCreateImage(display,
							 visual,
							 depth,
							 ZPixmap,
							 0,
							 dmem,
							 width,
							 height,
							 32,
							 size);
	}
	
	/*
	 *	Now blow the bits across into this image
	 */
	
	Parse4ColorBitmap(data,image,trans);
	
	/*
	 *	And blow the bits onto the screen
	 */
	
	XPutImage(display,
			  drawable,
			  gc,
			  image,
			  0, 0,					// source
			  x, y,					// destination
			  width,height);		// size

	/*
	 *	Done. Fry image
	 */
	
	XDestroyImage(image);
}

/*	@F"DrawXBitmap""
 *	@H"XDrawData.h"
 *	@I
 *
 *		(X) Draw the bitmap. This gets the bitmap from the 'bmap' resource
 */

void DrawXBitmap(Display *display, Drawable drawable, GC gc, short resID, short x, short y)
{
	DrawBitmap(display,drawable,gc,
			   ::GetResourceData('bmap',resID),
			   x,
			   y,
			   false);
}

/*	@F"DrawXIcon"
 *	@H"XDrawData.h"
 *	@I
 *
 *		(X) Draw the icon. This is identical to the above (and means that
 *	my icons are small bitmaps), but the resource comes from the 'icon'
 *	resource identifier
 */

void DrawXIcon(Display *display, Drawable drawable, GC gc, short resID, short x, short y)
{
	DrawBitmap(display,drawable,gc,
			   ::GetResourceData('icon',resID),
			   x,
			   y,
			   true);
}

/*	@F"GetBitmapSize"
 *	@H"XDrawData.h"
 *	@I
 *
 *		(X) Get the size of the specified resource
 */

Point GetBitmapSize(short resID)
{
	Point size;
	short x,y;
	
	GetBitmapSize(::GetResourceData('bmap',resID),&x,&y);
	size.h = x;
	size.v = y;
	return size;
}

#endif // OPT_XWIN
