/* 
 * tkBitmap.c --
 *
 *	This file maintains a database of read-only bitmaps for the Tk
 *	toolkit.  This allows bitmaps to be shared between widgets and
 *	also avoids interactions with the X server.
 *
 * Copyright (c) 1990-1994 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

/* TkWin version 0.3 -- Tk for Windows
 * Copyright (C) 1994 K. K. Kubota
 * Mathematical Sciences Computing Facility, University of Kentucky,
 * Lexington, KY 40506, USA (e-mail: ken@ms.uky.edu)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose is granted without fee, provided that the above copyright notice
 * and the following paragraph appear in all copies of this software
 * and supporting documentation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  In no event shall the author be
 * liable to any party for direct, indirect, special, incidental, or
 * consequential damages arising out of the use of this software and its
 * documentation.
 */

static char sccsid[] = "@(#) tkBitmap.c 1.27 94/12/17 16:31:01";

#include "tkPort.h"
#ifdef TCL_MSDOS_PORT
#include "tkInt.h"
#else
#include "tk.h"
#endif

/*
 * The includes below are for pre-defined bitmaps.
 */

#include "bitmaps/error"
#include "bitmaps/gray25"
#include "bitmaps/gray50"
#include "bitmaps/hourglass"
#include "bitmaps/info"
#include "bitmaps/questhead"
#include "bitmaps/question"
#include "bitmaps/warning"

/*
 * One of the following data structures exists for each bitmap that is
 * currently in use.  Each structure is indexed with both "idTable" and
 * "nameTable".
 */

typedef struct {
    Pixmap bitmap;		/* X identifier for bitmap.  None means this
				 * bitmap was created by Tk_DefineBitmap
				 * and it isn't currently in use. */
    unsigned int width, height;	/* Dimensions of bitmap. */
#ifndef TCL_MSDOS_PORT
    Display *display;		/* Display for which bitmap is valid. */
#endif
    int refCount;		/* Number of active uses of bitmap. */
    Tcl_HashEntry *hashPtr;	/* Entry in nameTable for this structure
				 * (needed when deleting). */
} TkBitmap;

/*
 * Hash table to map from a textual description of a bitmap to the
 * TkBitmap record for the bitmap, and key structure used in that
 * hash table:
 */

static Tcl_HashTable nameTable;
typedef struct {
    Tk_Uid name;		/* Textual name for desired bitmap. */
#ifdef TCL_MSDOS_PORT
    char *dummy;
#else
    Screen *screen;		/* Screen on which bitmap will be used. */
#endif
} NameKey;

/*
 * Hash table that maps from <display + bitmap id> to the TkBitmap structure
 * for the bitmap.  This table is used by Tk_FreeBitmap.
 */

static Tcl_HashTable idTable;
typedef struct {
#ifdef TCL_MSDOS_PORT
    char *dummy;
#else
    Display *display;		/* Display for which bitmap was allocated. */
#endif
    Pixmap pixmap;		/* X identifier for pixmap. */
} IdKey;

/*
 * For each call to Tk_DefineBitmap one of the following structures is
 * created to hold information about the bitmap.
 */

typedef struct {
    char *source;		/* Bits for bitmap. */
    unsigned int width, height;	/* Dimensions of bitmap. */
} PredefBitmap;

/*
 * Hash table create by Tk_DefineBitmap to map from a name to a
 * collection of in-core data about a bitmap.  The table is
 * indexed by the address of the data for the bitmap, and the entries
 * contain pointers to PredefBitmap structures.
 */

static Tcl_HashTable predefTable;

/*
 * Hash table used by Tk_GetBitmapFromData to map from a collection
 * of in-core data about a bitmap to a Tk_Uid giving an automatically-
 * generated name for the bitmap:
 */

static Tcl_HashTable dataTable;
typedef struct {
    char *source;		/* Bitmap bits. */
    unsigned int width, height;	/* Dimensions of bitmap. */
} DataKey;

static int initialized = 0;	/* 0 means static structures haven't been
				 * initialized yet. */

/*
 * Forward declarations for procedures defined in this file:
 */

static void		BitmapInit _ANSI_ARGS_((void));
#ifdef TCL_MSDOS_PORT
static char *ReadBitmapFile _ANSI_ARGS_((char *filename, unsigned int *width,
    unsigned int *height, int *x_hot, int *y_hot));
#endif

#ifdef TCL_MSDOS_PORT
/* Create Device Independent Bitmap from bit array */
HBITMAP CreateBitmapFromData(tkwin, width, height, source)
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    int width, height;
    char *source;
	{
	    HBITMAP bitmap;
#ifdef NOTDEF
	    bitmap = CreateBitmap(width, height, 1, 1, (short *)source);    
#endif
	    /* First creating the bitmap and then initializing it allows
	     * for larger bitmaps than doing it all at once
	     */
	    bitmap = CreateBitmap(width, height, 1, 1, NULL);    
	    if(bitmap != NULL){
		long ret;
		ret = SetBitmapBits(bitmap, (DWORD)(((width+15L)/16)*2)*(long)(height),
		    source);
		if(!ret){
		    /* MessageBeep(0); */
		    return NULL;
		}
	    }
#ifdef NOTDEF
	    BITMAP bm;
	    bm.bmType = 0;
	    bm.bmWidth = width;
	    bm.bmHeight = height;
	    bm.bmWidthBytes = ((width + 15) >> 4)*2;
	    bm.bmPlanes = 1;
	    bm.bmBitsPixel = 1;
	    bm.bmBits = source;
	    bitmap = CreateBitmapIndirect(&bm);
#endif
#ifdef NOTDEF
	    HDC hdc = GetDC(((TkWindow *)tkwin)->window);
	    BITMAPINFO *bmip;
	    RGBQUAD argbq[] = {{0, 0, 0, 0}, {255, 255, 255, 0}};
	    bmip = ckalloc(sizeof(BITMAPINFO)+sizeof(RGBQUAD));
	    bmip->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	    bmip->bmiHeader.biWidth = width;
	    bmip->bmiHeader.biHeight = height;
	    bmip->bmiHeader.biPlanes = 1;
	    bmip->bmiHeader.biBitCount = 1;
	    bmip->bmiHeader.biCompression = BI_RGB;
	    bmip->bmiHeader.biSizeImage = 0;
	    bmip->bmiHeader.biXPelsPerMeter = 
		WidthOfScreen()/WidthMMOfScreen();
	    bmip->bmiHeader.biYPelsPerMeter =
		HeightOfScreen()/HeightMMOfScreen();
	    bmip->bmiHeader.biClrUsed = 2;
	    bmip->bmiHeader.biClrImportant = 2;
	    memcpy(bmip->bmiColors, argbq, sizeof(RGBQUAD)*2);
	    bitmap = CreateDIBitmap(hdc, &(bmip->bmiHeader), CBM_INIT, source,
		bmip, DIB_RGB_COLORS);
	    ckfree(bmip);
	    ReleaseDC(((TkWindow *)tkwin)->window, hdc);
#endif
	    return bitmap;
	}
#endif

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetBitmap --
 *
 *	Given a string describing a bitmap, locate (or create if necessary)
 *	a bitmap that fits the description.
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(i.e. a Pixmap with a single plane), unless string couldn't be
 *	parsed correctly.  In this case, None is returned and an error
 *	message is left in interp->result.  The caller should never
 *	modify the bitmap that is returned, and should eventually call
 *	Tk_FreeBitmap when the bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps
 *	aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Pixmap _export
#else
Pixmap
#endif
Tk_GetBitmap(interp, tkwin, string)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    Tk_Uid string;		/* Description of bitmap.  See manual entry
				 * for details on legal syntax. */
{
    NameKey nameKey;
    IdKey idKey;
    Tcl_HashEntry *nameHashPtr, *idHashPtr, *predefHashPtr;
    register TkBitmap *bitmapPtr;
    PredefBitmap *predefPtr;
    int new;
    Pixmap bitmap;
    unsigned int width, height;
    int dummy2;
#ifdef TCL_MSDOS_PORT
    char *source;
#endif

    if (!initialized) {
	BitmapInit();
    }

    nameKey.name = string;
#ifdef TCL_MSDOS_PORT
    nameKey.dummy = NULL;
#else
    nameKey.screen = Tk_Screen(tkwin);
#endif
    nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);
    if (!new) {
	bitmapPtr = (TkBitmap *) Tcl_GetHashValue(nameHashPtr);
	bitmapPtr->refCount++;
	return bitmapPtr->bitmap;
    }

    /*
     * No suitable bitmap exists.  Create a new bitmap from the
     * information contained in the string.  If the string starts
     * with "@" then the rest of the string is a file name containing
     * the bitmap.  Otherwise the string must refer to a bitmap
     * defined by a call to Tk_DefineBitmap.
     */

    if (*string == '@') {
	Tcl_DString buffer;
	int result;

	string = Tcl_TildeSubst(interp, string + 1, &buffer);
	if (string == NULL) {
	    goto error;
	}
#ifdef TCL_MSDOS_PORT
	source = ReadBitmapFile(string, &width, &height, &dummy2, &dummy2);
	if(source != NULL){
	    bitmap = CreateBitmapFromData(tkwin, width, height, source);
	}
#else
	result = XReadBitmapFile(Tk_Display(tkwin),
		RootWindowOfScreen(nameKey.screen), string, &width,
		&height, &bitmap, &dummy2, &dummy2);
#endif
	Tcl_DStringFree(&buffer);
#ifdef TCL_MSDOS_PORT
	if (source == NULL || bitmap == NULL)
#else
	if (result != BitmapSuccess)
#endif
	{
	    Tcl_AppendResult(interp, "error reading bitmap file \"", string,
		    "\"", (char *) NULL);
	    goto error;
	}
    } else {
	predefHashPtr = Tcl_FindHashEntry(&predefTable, string);
	if (predefHashPtr == NULL) {
	    Tcl_AppendResult(interp, "bitmap \"", string,
		    "\" not defined", (char *) NULL);
	    goto error;
	}
	predefPtr = (PredefBitmap *) Tcl_GetHashValue(predefHashPtr);
	width = predefPtr->width;
	height = predefPtr->height;
#ifdef TCL_MSDOS_PORT
	bitmap = CreateBitmapFromData(tkwin, width, height, predefPtr->source);
#else
	bitmap = XCreateBitmapFromData(Tk_Display(tkwin),
		RootWindowOfScreen(nameKey.screen), predefPtr->source,
		width, height);
#endif
    }

    /*
     * Add information about this bitmap to our database.
     */

    bitmapPtr = (TkBitmap *) ckalloc(sizeof(TkBitmap));
    bitmapPtr->bitmap = bitmap;
    bitmapPtr->width = width;
    bitmapPtr->height = height;
#ifndef TCL_MSDOS_PORT
    bitmapPtr->display = Tk_Display(tkwin);
#endif
    bitmapPtr->refCount = 1;
    bitmapPtr->hashPtr = nameHashPtr;
#ifdef TCL_MSDOS_PORT
    idKey.dummy = NULL;
#else
    idKey.display = bitmapPtr->display;
#endif
    idKey.pixmap = bitmap;
    idHashPtr = Tcl_CreateHashEntry(&idTable, (char *) &idKey,
	    &new);
    if (!new) {
	panic("bitmap already registered in Tk_GetBitmap");
    }
    Tcl_SetHashValue(nameHashPtr, bitmapPtr);
    Tcl_SetHashValue(idHashPtr, bitmapPtr);
    return bitmapPtr->bitmap;

    error:
    Tcl_DeleteHashEntry(nameHashPtr);
    return None;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DefineBitmap --
 *
 *	This procedure associates a textual name with a binary bitmap
 *	description, so that the name may be used to refer to the
 *	bitmap in future calls to Tk_GetBitmap.
 *
 * Results:
 *	A standard Tcl result.  If an error occurs then TCL_ERROR is
 *	returned and a message is left in interp->result.
 *
 * Side effects:
 *	"Name" is entered into the bitmap table and may be used from
 *	here on to refer to the given bitmap.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
int _export
#else
int
#endif
Tk_DefineBitmap(interp, name, source, width, height)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    Tk_Uid name;		/* Name to use for bitmap.  Must not already
				 * be defined as a bitmap. */
    char *source;		/* Address of bits for bitmap. */
    unsigned int width;		/* Width of bitmap. */
    unsigned int height;	/* Height of bitmap. */
{
    int new;
    Tcl_HashEntry *predefHashPtr;
    PredefBitmap *predefPtr;

    if (!initialized) {
	BitmapInit();
    }

    predefHashPtr = Tcl_CreateHashEntry(&predefTable, name, &new);
    if (!new) {
        Tcl_AppendResult(interp, "bitmap \"", name,
		"\" is already defined", (char *) NULL);
	return TCL_ERROR;
    }
    predefPtr = (PredefBitmap *) ckalloc(sizeof(PredefBitmap));
    predefPtr->source = source;
    predefPtr->width = width;
    predefPtr->height = height;
    Tcl_SetHashValue(predefHashPtr, predefPtr);
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_NameOfBitmap --
 *
 *	Given a bitmap, return a textual string identifying the
 *	bitmap.
 *
 * Results:
 *	The return value is the string name associated with bitmap.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
Tk_Uid _export
Tk_NameOfBitmap(bitmap)
#else
Tk_Uid
Tk_NameOfBitmap(display, bitmap)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
#endif
    Pixmap bitmap;			/* Bitmap whose name is wanted. */
{
    IdKey idKey;
    Tcl_HashEntry *idHashPtr;
    TkBitmap *bitmapPtr;

    if (!initialized) {
	unknown:
	panic("Tk_NameOfBitmap received unknown bitmap argument");
    }

#ifdef TCL_MSDOS_PORT
    idKey.dummy = NULL;
#else
    idKey.display = display;
#endif
    idKey.pixmap = bitmap;
    idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);
    if (idHashPtr == NULL) {
	goto unknown;
    }
    bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);
    return ((NameKey *) bitmapPtr->hashPtr->key.words)->name;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_SizeOfBitmap --
 *
 *	Given a bitmap managed by this module, returns the width
 *	and height of the bitmap.
 *
 * Results:
 *	The words at *widthPtr and *heightPtr are filled in with
 *	the dimenstions of bitmap.
 *
 * Side effects:
 *	If bitmap isn't managed by this module then the procedure
 *	panics..
 *
 *--------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
Tk_SizeOfBitmap(bitmap, widthPtr, heightPtr)
#else
void
Tk_SizeOfBitmap(display, bitmap, widthPtr, heightPtr)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
#endif
    Pixmap bitmap;			/* Bitmap whose size is wanted. */
    unsigned int *widthPtr;		/* Store bitmap width here. */
    unsigned int *heightPtr;		/* Store bitmap height here. */
{
    IdKey idKey;
    Tcl_HashEntry *idHashPtr;
    TkBitmap *bitmapPtr;

    if (!initialized) {
	unknownBitmap:
	panic("Tk_SizeOfBitmap received unknown bitmap argument");
    }

#ifdef TCL_MSDOS_PORT
    idKey.dummy = NULL;
#else
    idKey.display = display;
#endif
    idKey.pixmap = bitmap;
    idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);
    if (idHashPtr == NULL) {
	goto unknownBitmap;
    }
    bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);
    *widthPtr = bitmapPtr->width;
    *heightPtr = bitmapPtr->height;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_FreeBitmap --
 *
 *	This procedure is called to release a bitmap allocated by
 *	Tk_GetBitmap or TkGetBitmapFromData.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The reference count associated with bitmap is decremented, and
 *	it is officially deallocated if no-one is using it anymore.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MSDOS_PORT
void _export
Tk_FreeBitmap(bitmap)
#else
void
Tk_FreeBitmap(display, bitmap)
    Display *display;			/* Display for which bitmap was
					 * allocated. */
#endif
    Pixmap bitmap;			/* Bitmap to be released. */
{
    Tcl_HashEntry *idHashPtr;
    register TkBitmap *bitmapPtr;
    IdKey idKey;

    if (!initialized) {
	panic("Tk_FreeBitmap called before Tk_GetBitmap");
    }

#ifdef TCL_MSDOS_PORT
    idKey.dummy = NULL;
#else
    idKey.display = display;
#endif
    idKey.pixmap = bitmap;
    idHashPtr = Tcl_FindHashEntry(&idTable, (char *) &idKey);
    if (idHashPtr == NULL) {
	panic("Tk_FreeBitmap received unknown bitmap argument");
    }
    bitmapPtr = (TkBitmap *) Tcl_GetHashValue(idHashPtr);
    bitmapPtr->refCount--;
    if (bitmapPtr->refCount == 0) {
#ifdef TCL_MSDOS_PORT
	DeleteObject(bitmapPtr->bitmap);
#else
	XFreePixmap(bitmapPtr->display, bitmapPtr->bitmap);
	Tk_FreeXId(bitmapPtr->display, (XID) bitmapPtr->bitmap);
#endif
	Tcl_DeleteHashEntry(idHashPtr);
	Tcl_DeleteHashEntry(bitmapPtr->hashPtr);
	ckfree((char *) bitmapPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetBitmapFromData --
 *
 *	Given a description of the bits for a bitmap, make a bitmap that
 *	has the given properties. *** NOTE:  this procedure is obsolete
 *	and really shouldn't be used anymore. ***
 *
 * Results:
 *	The return value is the X identifer for the desired bitmap
 *	(a one-plane Pixmap), unless it couldn't be created properly.
 *	In this case, None is returned and an error message is left in
 *	interp->result.  The caller should never modify the bitmap that
 *	is returned, and should eventually call Tk_FreeBitmap when the
 *	bitmap is no longer needed.
 *
 * Side effects:
 *	The bitmap is added to an internal database with a reference count.
 *	For each call to this procedure, there should eventually be a call
 *	to Tk_FreeBitmap, so that the database can be cleaned up when bitmaps
 *	aren't needed anymore.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
#ifdef TCL_MSDOS_PORT
Pixmap _export
#else
Pixmap
#endif
Tk_GetBitmapFromData(interp, tkwin, source, width, height)
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
    Tk_Window tkwin;		/* Window in which bitmap will be used. */
    char *source;		/* Bitmap data for bitmap shape. */
    unsigned int width, height;	/* Dimensions of bitmap. */
{
    DataKey nameKey;
    Tcl_HashEntry *dataHashPtr;
    Tk_Uid name = NULL;		/* Initialization need only to prevent
				 * compiler warning. */
    int new;
    static autoNumber = 0;
    char string[20];

    if (!initialized) {
	BitmapInit();
    }

    nameKey.source = source;
    nameKey.width = width;
    nameKey.height = height;
    dataHashPtr = Tcl_CreateHashEntry(&dataTable, (char *) &nameKey, &new);
    if (!new) {
	name = (Tk_Uid) Tcl_GetHashValue(dataHashPtr);
    } else {
	autoNumber++;
	sprintf(string, "_tk%d", autoNumber);
	name = Tk_GetUid(string);
	Tcl_SetHashValue(dataHashPtr, name);
	if (Tk_DefineBitmap(interp, name, source, width, height) != TCL_OK) {
	    Tcl_DeleteHashEntry(dataHashPtr);
	    return TCL_ERROR;
	}
    }
    return Tk_GetBitmap(interp, tkwin, name);
}

/*
 *----------------------------------------------------------------------
 *
 * BitmapInit --
 *
 *	Initialize the structures used for bitmap management.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Read the code.
 *
 *----------------------------------------------------------------------
 */

static void
BitmapInit()
{
    Tcl_Interp *dummy;

    dummy = Tcl_CreateInterp();
    initialized = 1;
    Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));
    Tcl_InitHashTable(&dataTable, sizeof(DataKey)/sizeof(int));
    Tcl_InitHashTable(&predefTable, TCL_ONE_WORD_KEYS);

    /*
     * The call below is tricky:  can't use sizeof(IdKey) because it
     * gets padded with extra unpredictable bytes on some 64-bit
     * machines.
     */

#ifdef TCL_MSDOS_PORT
    Tcl_InitHashTable(&idTable, (sizeof(char *) + sizeof(Pixmap))/sizeof(int));
#else
    Tcl_InitHashTable(&idTable, (sizeof(Display *) + sizeof(Pixmap))
	    /sizeof(int));
#endif

    Tk_DefineBitmap(dummy, Tk_GetUid("error"), (char *) error_bits,
	    error_width, error_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("gray50"), (char *) gray50_bits,
	    gray50_width, gray50_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("gray25"), (char *) gray25_bits,
	    gray25_width, gray25_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("hourglass"), (char *) hourglass_bits,
	    hourglass_width, hourglass_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("info"), (char *) info_bits,
	    info_width, info_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("questhead"), (char *) questhead_bits,
	    questhead_width, questhead_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("question"), (char *) question_bits,
	    question_width, question_height);
    Tk_DefineBitmap(dummy, Tk_GetUid("warning"), (char *) warning_bits,
	    warning_width, warning_height);
    Tcl_DeleteInterp(dummy);
}

#ifdef TCL_MSDOS_PORT
/* The code below is slightly modified from the X11R5 Xlib source file
 * XRdBitF.c
 */
/* $XFree86: mit/lib/X/XRdBitF.c,v 1.4 1993/03/27 09:14:21 dawes Exp $ */
/* $XConsortium: XRdBitF.c,v 1.15 91/02/01 16:34:46 gildea Exp $ */
/* Copyright, 1987, Massachusetts Institute of Technology */

/*
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  M.I.T. makes no representations about the
suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.
*/

#define MAX_SIZE 255

/* shared data for the image read/parse logic */
static short hexTable[256];		/* conversion value */
static Bool Xinitialized = False;	/* easier to fill in at run time */


/*
 *	Table index for the hex values. Initialized once, first time.
 *	Used for translation value or delimiter significance lookup.
 */
static void initHexTable()
{
    hexTable['0'] = 0;	hexTable['1'] = 1;
    hexTable['2'] = 2;	hexTable['3'] = 3;
    hexTable['4'] = 4;	hexTable['5'] = 5;
    hexTable['6'] = 6;	hexTable['7'] = 7;
    hexTable['8'] = 8;	hexTable['9'] = 9;
    hexTable['A'] = 10;	hexTable['B'] = 11;
    hexTable['C'] = 12;	hexTable['D'] = 13;
    hexTable['E'] = 14;	hexTable['F'] = 15;
    hexTable['a'] = 10;	hexTable['b'] = 11;
    hexTable['c'] = 12;	hexTable['d'] = 13;
    hexTable['e'] = 14;	hexTable['f'] = 15;

    /* delimiters of significance are flagged w/ negative value */
    hexTable[' '] = -1;	hexTable[','] = -1;
    hexTable['}'] = -1;	hexTable['\n'] = -1;
    hexTable['\t'] = -1;
	
    Xinitialized = True;
}

/*
 *	read next hex value in the input stream, return -1 if EOF
 */
static long NextInt (fstream)
    FILE *fstream;
{
    int	ch;
    long value = 0;
    int gotone = 0;
    int done = 0;
    
    /* loop, accumulate hex value until find delimiter  */
    /* skip any initial delimiters found in read stream */

    while (!done) {
	ch = getc(fstream);
	if (ch == EOF) {
	    value	= -1L;
	    done++;
	} else {
	    /* trim high bits, check type and accumulate */
	    ch &= 0xff;
	    if (isascii(ch) && isxdigit(ch)) {
		value = (value << 4) + hexTable[ch];
		gotone++;
	    } else if ((hexTable[ch]) < 0 && gotone)
	      done++;
	}
    }
    return value;
}


/* invert the order of the bits in a character, and complement */
static swap(int c){
    int i, r = 0;
    for(i=0; i < 8; i++){
	r <<= 1;
	if(c&1) r |= 1;
	c >>= 1;
    }
    return (~r)&0xff;
}

/* $XConsortium: Xlib.h,v 11.215 91/07/22 15:42:38 rws Exp $ */
/* 
 * Copyright 1985, 1986, 1987, 1991 by the Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided 
 * that the above copyright notice appear in all copies and that both that 
 * copyright notice and this permission notice appear in supporting 
 * documentation, and that the name of M.I.T. not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission. M.I.T. makes no representations about the 
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * X Window System is a Trademark of MIT.
 *
 */

/* read bitmap from X10 or X11 bitmap files and return
 * array of data bits in format appropriate for Microsoft Windows
 */
static char *ReadBitmapFile (filename, width, height, x_hot, y_hot)
    char *filename;
    unsigned int *width, *height;       /* RETURNED */
    int *x_hot, *y_hot;                 /* RETURNED */
{
    FILE *fstream;			/* handle on file  */
    char *data = NULL;			/* working variable */
    char line[MAX_SIZE];		/* input line from file */
#ifdef TCL_MSDOS_PORT
    unsigned size;			/* number of bytes of data */
#else
    int size;				/* number of bytes of data */
#endif
    char name_and_type[MAX_SIZE];	/* an input line */
    char *type;				/* for parsing */
    long value;				/* from an input line */
    int val;
    int version10p;			/* boolean, old format */
    int padding;			/* to handle alignment */
    int bytes_per_line;			/* per scanline of data */
    unsigned int ww = 0;		/* width */
    unsigned int hh = 0;		/* height */
    int hx = -1;			/* x hotspot */
    int hy = -1;			/* y hotspot */

    /* first time initialization */
    if (Xinitialized == False) initHexTable();

    if ((fstream = fopen(filename, "rt")) == NULL) {
	return NULL;
    }

    /* error cleanup and return macro	*/
#define	RETURN(code) { if (data) ckfree (data); fclose (fstream); return code; }

    while (fgets(line, MAX_SIZE, fstream)) {
	if (strlen(line) == MAX_SIZE-1) {
	    RETURN (NULL);
	}
	if (sscanf(line,"#define %s %d",name_and_type,&val) == 2) {
	    if ((type = strrchr(name_and_type, '_')) == NULL)
	      type = name_and_type;
	    else
	      type++;

	    if (!strcmp("width", type))
	      ww = (unsigned int) val;
	    if (!strcmp("height", type))
	      hh = (unsigned int) val;
	    if (!strcmp("hot", type)) {
		if (type-- == name_and_type || type-- == name_and_type)
		  continue;
		if (!strcmp("x_hot", type))
		  hx = val;
		if (!strcmp("y_hot", type))
		  hy = val;
	    }
	    continue;
	}
    
	if (sscanf(line, "static short %s = {", name_and_type) == 1)
	  version10p = 1;
	else if (sscanf(line,"static unsigned char %s = {",name_and_type) == 1)
	  version10p = 0;
	else if (sscanf(line, "static char %s = {", name_and_type) == 1)
	  version10p = 0;
	else
	  continue;

	if ((type = strrchr(name_and_type, '_')) == NULL)
	  type = name_and_type;
	else
	  type++;

	if (strcmp("bits[]", type))
	  continue;
    
	if (!ww || !hh)
	  RETURN (NULL);

	if ((ww % 16) && ((ww % 16) < 9) && !version10p)
	  padding = 1;
	else padding = 0;

	bytes_per_line = (ww+7)/8 + padding;

	size = bytes_per_line * hh;
/* xxxxxxxxxx added +hh into size; but then removed it */
	data = (char *) ckalloc ((unsigned int) (size));
	if (!data) 
	  RETURN (NULL);

	if (version10p) {
	    unsigned char *ptr;
	    int bytes;

	    for (bytes=0, ptr=data; bytes<size; (bytes += 2)) {
		if ((value = NextInt(fstream)) < 0)
		  RETURN (NULL);
		*(ptr++) = swap(value & 0xff);
		*(ptr++) = swap((value >> 8)&0xff);
	    }
	} else {
	    unsigned char *ptr;
	    int bytes;

	    for (bytes=0, ptr=data; bytes<size; bytes++, ptr++) {
		if ((value = NextInt(fstream)) < 0) 
		  RETURN (NULL);
		*ptr=swap(value&0xff);
		if(padding && ((bytes+2)%bytes_per_line) == 0){
		    *ptr++ = '\377';
		    bytes++;
		}
	    }
	}
    }					/* end while */


    *width = ww;
    *height = hh;
    if (x_hot) *x_hot = hx;
    if (y_hot) *y_hot = hy;

    RETURN (data);
}
#endif
