/* make emcas happy -*-C-*-  make emacs happy */
#undef IMAGECACHETRACE
/* 
   Copyright for the image resolving functions as well as for all of
   the html widget is the following (With kind permission of Mark
   Andreessen from the NCSA). We use the contents of img.c and picread.c:
 */

/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

#define HIGHWATERIMAGESIZE 300000

#include <values.h>
#include <HTMLP.h>
#include <ctype.h>

typedef struct ele_rec  HTMLElementRecord;

#define DEF_BLACK       BlackPixel(dsp, DefaultScreen(dsp))
#define DEF_WHITE       WhitePixel(dsp, DefaultScreen(dsp))
/*#define	MAX_LINE	81*/
#define	MAX_LINE	256
#define NO_VISUAL       200


unsigned long fg_pixel, bg_pixel;
XColor fg_color, bg_color;

char nibMask[8] = {
	1, 2, 4, 8, 16, 32, 64, 128
};


/* Caching Mechanism is a must, since Conversion Procedure is called
   for each resize (and we use opaqueResize ....)
   
 */

typedef struct _imgDataRec
    {
    XrmQuark              qFilename;
    int                   count;
    ImageInfo             *imgData;    
    long                  size;
    struct _imgDataRec    *next;
    } imgDataRec, *imgDataRecPtr;

typedef struct _htmlImgCache 
    {
    imgDataRecPtr         dataRec;
    struct _htmlImgCache  *next;
    } htmlImgCacheStruct, *htmlImgCachePtr;

imgDataRecPtr     imgList  = NULL;



#ifdef IMAGECACHETRACE
void
printWidgetInfo(text,w,ptr)
char   *text;
Widget w;
htmlImgCachePtr ptr;
    {
    fprintf(stderr, "----%s(%p)--%s--WidgetInfo Ptr: %p------------\n", 
	    XtName(w), w, text, ptr);
    
    while(ptr)
	{
	fprintf(stderr, "   Data %p contains filename %s, count %d (next %p)\n",
		ptr, XrmQuarkToString(ptr->dataRec->qFilename),
		ptr->dataRec->count, ptr->next);
	ptr=ptr->next;
	}
    }

void
printCacheInfo(text)
char *text;
    {
    imgDataRecPtr ptr = imgList;
    
    fprintf(stderr, "****%s*****CacheInfo ImgList: %p------------\n", text, ptr);

    while(ptr)
	{
	fprintf(stderr, "   Data %p contains filename %s, count %d (next %p)\n",
		ptr, XrmQuarkToString(ptr->qFilename),
		ptr->count, ptr->next);
	ptr=ptr->next;
	}
    }
#endif


/* Add imgData to global list and return address.
 * Does NOT check wether imgData is allready in the list 
 */

static imgDataRecPtr 
htmlAddImgData(qFilename, imgData)
XrmQuark   qFilename;
ImageInfo  *imgData;
    {
    imgDataRecPtr    ptr;

    DBUG_ENTER("htmlAddImgData");

    ptr = (imgDataRecPtr)XtMalloc(sizeof(imgDataRec));
    ptr->next = imgList;
    ptr->qFilename = qFilename;
    ptr->count = 0;
    ptr->imgData = imgData;
    ptr->size = imgData->num_colors * 3 * sizeof(int) + 
	        imgData->width * imgData->height;
    imgList = ptr;

#ifdef IMAGECACHETRACE
    printCacheInfo("Nach AddImgData");
#endif
    DBUG_RETURN(ptr);
    } 


/* actually free colors and image data */

static void
free_image_data (img)
ImageInfo *img;
    {
    if (!img)
	return;

    if (img->reds)
      {
	free (img->reds);
	img->reds = NULL;
      }
    if (img->greens)
      {
	free (img->greens);
	img->greens = NULL;
      }
    if (img->blues)
      {
	free (img->blues);
	img->blues = NULL;
      }
    if (img->image_data)
      {
	free (img->image_data);
	img->image_data = NULL;
      }

    return;
    }


/* 
 * Search all available imgData Structures for
 * matching entry, return NULL if not found, the data 
 * structure otherwise.
 */



static imgDataRecPtr
htmlLocateImgInfo(qFilename)
XrmQuark   qFilename;
    {
    imgDataRecPtr    ptr, *reference;
    long             memUsed;

    DBUG_ENTER("htmlLocateImgInfo");

#ifdef IMAGECACHETRACE
    printCacheInfo("Vor Locate");
    fprintf(stderr,"   looking for %s\n",XrmQuarkToString(qFilename));
#endif

    for (ptr = imgList, memUsed = 0; 
	 ((ptr != NULL) && (ptr->qFilename != qFilename));
	 ptr = ptr->next)
	memUsed += ptr->size;

    if (ptr)
	DBUG_RETURN(ptr);

    /* fprintf(stderr,"memused=%ld\n",memUsed); */

    if (memUsed > HIGHWATERIMAGESIZE)
	{
	for (reference = &imgList, ptr = imgList; 
	     ptr != NULL; 
	     reference = &ptr)
	    {
	    if (ptr->count == 0)
		{
		/*
		fprintf(stderr,"freeing ptr=%p, name=%s, count=%d\n",
			ptr,XrmQuarkToString(ptr->qFilename),ptr->count);
		*/

		free_image_data(ptr->imgData);
		memUsed -= ptr->size;

		*reference = ptr->next;
		XtFree((char*)ptr);
		/* fprintf(stderr,"after XtFree, memUsed=%d\n",memUsed); */
		ptr = *reference;
		}
	    else
		ptr = ptr->next;
	    }
	}
#ifdef IMAGECACHETRACE
    printCacheInfo("Nach Loesch");
#endif
    DBUG_RETURN(NULL);
    }

/* This function is called by the MM* routines and frees 
 * the reference list of a widget. The imageInfo is NOT freed, we'll
 * only decrement the counter;
 */

void
wafeHTMLfreeImageData(ptr)
char *ptr;
    {
    htmlImgCachePtr iPtr = (htmlImgCachePtr) ptr;
    htmlImgCachePtr nextPtr;

    DBUG_ENTER("wafeHTMLfreeImageData");

    while (iPtr != NULL)
	{
	nextPtr = iPtr->next;
	iPtr->dataRec->count--;
	XtFree((char *)iPtr);
	iPtr = nextPtr;
	}
    DBUG_VOID_RETURN;
    }




/*
 * Add a new ref to a widget's image-list if not yet there
 */

static void
htmlAddRefToWidget(w, qFilename, dataPtr)
Widget          w;
XrmQuark        qFilename;
imgDataRecPtr   dataPtr;
    {
    MMattribListPtr     *al = wafeMMgetAttribList(w,True);
    htmlImgCachePtr     imgCachePtr = (htmlImgCachePtr)wafeMMgetValue(al, qimageList);
    htmlImgCachePtr     iPtr;
    
    DBUG_ENTER("htmlAddRefToWidgetCache");

#ifdef IMAGECACHETRACE    
    printWidgetInfo("Beginn:", w, imgCachePtr);
#endif

    if (!imgCachePtr) 
	{  /* First image ref for this widget */
	imgCachePtr = (htmlImgCachePtr) XtMalloc(sizeof(htmlImgCacheStruct));
	imgCachePtr->next = NULL;
	dataPtr->count++;
/*
 	fprintf(stderr, "Ptr: %p; Incrementing count for %s to %d\n",
		imgCachePtr,
		XrmQuarkToString(qFilename), dataPtr->count);
*/
	imgCachePtr->dataRec = dataPtr;
	wafeMMreplace(al, qimageList, (char *) imgCachePtr, wafeHTMLfreeImageData);
	}
    else
	{ /* scan existing image refs */

	for (iPtr = imgCachePtr;
	     ((iPtr->next != NULL) && (iPtr->dataRec->qFilename != qFilename));
	     iPtr = iPtr->next);
	
	if (iPtr->dataRec->qFilename != qFilename)
	    { /* first display of this image for widget: add image ref */
	    iPtr->next = (htmlImgCachePtr) XtMalloc(sizeof(htmlImgCacheStruct));
	    iPtr = iPtr->next;
	    dataPtr->count++;
/*
	    fprintf(stderr, "New href: Incrementing count for %s to %d\n", 
		    XrmQuarkToString(qFilename), dataPtr->count);
*/

	    iPtr->dataRec = dataPtr;
	    iPtr->next = NULL;
	    }
	}

#ifdef IMAGECACHETRACE    
    printWidgetInfo("End: ", w, imgCachePtr);
#endif
    
    DBUG_VOID_RETURN;
    }
    

/* this function is essentially from NCSA Mosaic */

static unsigned char *
ReadXpmPixmap(view, fp, datafile, w, h, colrs, Colors, CharsPP)
Widget view;
FILE *fp;
char *datafile;
int *w, *h;
XColor *colrs;
int Colors, CharsPP;
{
	unsigned char *pixels;
	char **Color_Vals;
	XColor tmpcolr;
	int i, j, k;
	int found;
	char line[BUFSIZ], name_and_type[MAX_LINE];
	unsigned char *dataP;
	unsigned char *bitp;
	int tchar;
	char *t;
	char *t2;
	Display *dsp = XtDisplay(view);

	if (Colors == 0)
	{
		/* fprintf(stderr, "Can't find Colors.\n"); */
		return((unsigned char *)NULL);
	}
	if (*w == 0)
	{
		/* fprintf(stderr, "Can't read image.\n"); */
		return((unsigned char *)NULL);
	}
	if (*h == 0)
	{
		/* fprintf(stderr, "Can't read image.\n"); */
		return((unsigned char *)NULL);
	}

	Color_Vals = (char **)XtMalloc(sizeof(char *) * Colors);
	for (i=0; i<Colors; i++)
	{
		tchar = getc(fp);
		while ((tchar != '"')&&(tchar != EOF))
		{
			tchar = getc(fp);
		}
		Color_Vals[i] = (char *)XtMalloc(sizeof(char) * (CharsPP + 1));
		j = 0;
		tchar = getc(fp);
		while ((tchar != '"')&&(tchar != EOF)&&(j < CharsPP))
		{
			Color_Vals[i][j] = (char)tchar;
			tchar = getc(fp);
			j++;
		}
		Color_Vals[i][j] = '\0';
		if (tchar != '"')
		{
			tchar = getc(fp);
			while ((tchar != '"')&&(tchar != EOF))
			{
				tchar = getc(fp);
			}
		}
		tchar = getc(fp);
		while ((tchar != '"')&&(tchar != EOF))
		{
			tchar = getc(fp);
		}
		j = 0;
		tchar = getc(fp);
		while ((tchar != '"')&&(tchar != EOF))
		{
			line[j] = (char)tchar;
			tchar = getc(fp);
			j++;
		}
		line[j] = '\0';
		XParseColor(dsp, DefaultColormap(dsp, DefaultScreen(dsp)),
			line, &tmpcolr);
		colrs[i].red = tmpcolr.red;
		colrs[i].green = tmpcolr.green;
		colrs[i].blue = tmpcolr.blue;
		colrs[i].pixel = i;
		colrs[i].flags = DoRed|DoGreen|DoBlue;
	}
	for (i=Colors; i<256; i++)
	{
		colrs[i].red = 0;
		colrs[i].green = 0;
		colrs[i].blue = 0;
		colrs[i].pixel = i;
		colrs[i].flags = DoRed|DoGreen|DoBlue;
	}
	tchar = getc(fp);
	while ((tchar != ';')&&(tchar != EOF))
	{
		tchar = getc(fp);
	}

	for ( ; ; )
	{
		if (!(fgets(line, MAX_LINE, fp)))
		{
			fprintf(stderr, "Can't find Pixels\n");
			return((unsigned char *)NULL);
		}
		else if (sscanf(line,"static char * %s = {",name_and_type) == 1)
		{
			if ((t = strrchr(name_and_type, '_')) == NULL)
			{
				t = name_and_type;
			}
			else
			{
				t++;
			}
			if ((t2 = strchr(name_and_type, '[')) != NULL)
			{
				*t2 = '\0';
			}
			if (!strcmp("pixels", t))
			{
				break;
			}
		}
	}
	pixels = (unsigned char *)XtMalloc((*w) * (*h));
	if (pixels == NULL)
	{
		fprintf(stderr, "Not enough memory for data.\n");
		return((unsigned char *)NULL);
	}

	line[0] = '\0';
	t = line;
	dataP = pixels;
	tchar = getc(fp);
	while ((tchar != '"')&&(tchar != EOF))
	{
		tchar = getc(fp);
	}
	tchar = getc(fp);
	for (j=0; j<(*h); j++)
	{
		for (i=0; i<(*w); i++)
		{
			k = 0;
			while ((tchar != '"')&&(tchar != EOF)&&(k < CharsPP))
			{
				line[k] = (char)tchar;
				tchar = getc(fp);
				k++;
			}
			if ((k == 0)&&(tchar == '"'))
			{
				tchar = getc(fp);
				while ((tchar != '"')&&(tchar != EOF))
				{
					tchar = getc(fp);
				}
				k = 0;
				tchar = getc(fp);
				while ((tchar != '"')&&(tchar != EOF)&&
					(k < CharsPP))
				{
					line[k] = (char)tchar;
					tchar = getc(fp);
					k++;
				}
			}
			line[k] = '\0';
			found = 0;
			for (k=0; k<Colors; k++)
			{
				if (strncmp(Color_Vals[k], line, CharsPP) == 0)
				{
					*dataP++ = (unsigned char)k;
					found = 1;
					break;
				}
			}
			if (found == 0)
			{
				fprintf(stderr, "Invalid Pixel (%2s) in file %s\n", line, datafile);
				*dataP++ = (unsigned char)0;
			}
		}
	}

	bitp = pixels;
	for (i=0; i<((*w) * (*h)); i++)
	{
		if ((int)*bitp > (256 - 1))
			*bitp = (unsigned char)0;
		bitp++;
	}

	for (i=0; i<Colors; i++)
	{
		XtFree((char *)Color_Vals[i]);
	}
	XtFree((char *)Color_Vals);
	return(pixels);
}

/* This function is essentially from NCSA Mosaic */

static unsigned char *
ReadXbmBitmap(view, fp, datafile, w, h, colrs)
Widget view;
FILE *fp;
char *datafile;
int *w, *h;
XColor *colrs;
{
	char line[MAX_LINE], name_and_type[MAX_LINE];
	char *t;
	char *t2;
	unsigned char *ptr, *dataP;
	int bytes_per_line, version10p, raster_length, padding;
	int i, bytes, temp, value;
	int Ncolors, charspp, xpmformat;
        static unsigned long fg_pixel, bg_pixel;
        static int done_fetch_colors = 0;
        extern XColor fg_color, bg_color;
	int blackbit;
	int whitebit;

        if (!done_fetch_colors)
	    {
             /* First, go fetch the pixels. */
#ifndef MOTIF11	    
	    XtVaGetValues (view, 
			   XtNforeground, &fg_pixel,
			   XtNbackground, &bg_pixel, 
			   NULL);
#else
	    XtVaGetValues (view, 
			   XtNbackground, &bg_pixel, 
			   NULL);
	    XtVaGetValues (XtParent(view),
			   XtNforeground, &fg_pixel,
			   NULL);
#endif
             
	    /* Now, load up fg_color and bg_color. */
	    fg_color.pixel = fg_pixel;
	    bg_color.pixel = bg_pixel;
	    
	    /* Now query for the full color info. */
	    XQueryColor(XtDisplay (view), 
			DefaultColormap (XtDisplay (view),
					 DefaultScreen (XtDisplay (view))),
			&fg_color);
             XQueryColor(XtDisplay (view), 
			 DefaultColormap (XtDisplay (view),
					  DefaultScreen (XtDisplay (view))),
			 &bg_color);
 
	    done_fetch_colors = 1;

#ifdef MORETHAN256COLORS
            /*
             * For a TrueColor visual, we can't use the pixel value as
             * the color index because it is > 255.  Arbitrarily assign
             * 0 to foreground, and 1 to background.
             */
            if ((Vclass == TrueColor) ||(Vclass == DirectColor))
		{
                fg_color.pixel = 0;
		bg_color.pixel = 1;
                }
#endif
	    }

 /*
	if (Rdata.reverse_inlined_bitmap_colors)
	    {
	    blackbit = bg_color.pixel;
	    whitebit = fg_color.pixel;
	    }
	else
*/
	    {
	    blackbit = fg_color.pixel;
	    whitebit = bg_color.pixel;
	    }

	/*
	 * Error out here on visuals we can't handle so we won't core dump
	 * later.
	
	if (((blackbit > 255)||(whitebit > 255))&&(Vclass != TrueColor))
	  {
		fprintf(stderr, "Error:  cannot deal with default colormap that is deeper than 8, and not TrueColor\n");
                fprintf(stderr, "        If you actually have such a system, please notify mosaic-x@ncsa.uiuc.edu.\n");
                fprintf(stderr, "        We thank you for your support.\n");
		exit(1);
	  }
*/
/*        if (Rdata.reverse_inlined_bitmap_colors)
          {
            colrs[blackbit].red = bg_color.red;
            colrs[blackbit].green = bg_color.green;
            colrs[blackbit].blue = bg_color.blue;
            colrs[blackbit].pixel = bg_color.pixel;
            colrs[blackbit].flags = DoRed|DoGreen|DoBlue;
            
            colrs[whitebit].red = fg_color.red;
            colrs[whitebit].green = fg_color.green;
            colrs[whitebit].blue = fg_color.blue;
            colrs[whitebit].pixel = fg_color.pixel;
            colrs[whitebit].flags = DoRed|DoGreen|DoBlue;
          }
        else
          { */
            colrs[blackbit].red = fg_color.red;
            colrs[blackbit].green = fg_color.green;
            colrs[blackbit].blue = fg_color.blue;
            colrs[blackbit].pixel = fg_color.pixel;
            colrs[blackbit].flags = DoRed|DoGreen|DoBlue;
            
            colrs[whitebit].red = bg_color.red;
            colrs[whitebit].green = bg_color.green;
            colrs[whitebit].blue = bg_color.blue;
            colrs[whitebit].pixel = bg_color.pixel;
            colrs[whitebit].flags = DoRed|DoGreen|DoBlue;

	*w = 0;
	*h = 0;
	Ncolors = 0;
	charspp = 0;
	xpmformat = 0;
	for ( ; ; )
	{
		if (!(fgets(line, MAX_LINE, fp)))
			break;
		if (strlen(line) == (MAX_LINE - 1))
		{
#ifdef DEBUG
			fprintf(stderr, "Line too long.\n");
#endif
			return((unsigned char *)NULL);
		}
		if (sscanf(line, "#define %s %d", name_and_type, &value) == 2)
		{
			if (!(t = strrchr(name_and_type, '_')))
				t = name_and_type;
			else
				t++;
			if (!strcmp("width", t))
				*w= value;
			if (!strcmp("height", t))
				*h= value;
			if (!strcmp("ncolors", t))
				Ncolors = value;
			if (!strcmp("pixel", t))
				charspp = value;
			continue;
		}
		if (sscanf(line, "static short %s = {", name_and_type) == 1)
		{
			version10p = 1;
			break;
		}
		else if 
		  (sscanf(line,"static char * %s = {",name_and_type) == 1 || 
		   sscanf(line,"static unsigned char * %s = {",name_and_type) == 1 )
		{
			xpmformat = 1;
			if (!(t = strrchr(name_and_type, '_')))
				t = name_and_type;
			else
				t++;
			if ((t2 = strchr(name_and_type, '[')) != NULL)
				*t2 = '\0';
			if (!strcmp("mono", t))
				continue;
			else
				break;
		}
		else if 
		  (sscanf(line, "static char %s = {", name_and_type) == 1 ||
		   sscanf(line, "static unsigned char %s = {", name_and_type) == 1)
		{
			version10p = 0;
			break;
		}
		else
			continue;
	}
	if (xpmformat)
	{
		dataP = ReadXpmPixmap(view, fp, datafile, w, h, colrs, Ncolors, charspp);
		return(dataP);
	}
	if (*w == 0)
	{
		/* fprintf(stderr, "Can't read image.\n"); */
		return((unsigned char *)NULL);
	}
	if (*h == 0)
	{
		/* fprintf(stderr, "Can't read image.\n"); */
		return((unsigned char *)NULL);
	}
	padding = 0;
	if (((*w % 16) >= 1)&&((*w % 16) <= 8)&&version10p)
	{
		padding = 1;
	}
	bytes_per_line = ((*w + 7) / 8) + padding;
	raster_length =  bytes_per_line * *h;
	dataP = (unsigned char *)XtMalloc((*w) * (*h));
	if (dataP == NULL)
	{
		fprintf(stderr, "Not enough memory.\n");
		return((unsigned char *)NULL);
	}
	ptr = dataP;
	if (version10p)
	{
		int cnt = 0;
		int lim = (bytes_per_line - padding) * 8;
		for (bytes = 0; bytes < raster_length; bytes += 2)
		{
			if (fscanf(fp, " 0x%x%*[,}]%*[ \r\n]", &value) != 1)
			{
				fprintf(stderr, "Error scanning bits item.\n");
				return((unsigned char *)NULL);
			}
			temp = value;
			value = temp & 0xff;
			for (i = 0; i < 8; i++)
			{
				if (cnt < (*w))
				{
					if (value & nibMask[i])
						*ptr++ = blackbit;
					else
						*ptr++ = whitebit;
				}
				if (++cnt >= lim)
					cnt = 0;
			}
			if ((!padding)||((bytes+2) % bytes_per_line))
			{
				value = temp >> 8;
				for (i = 0; i < 8; i++)
				{
					if (cnt < (*w))
					{
						if (value & nibMask[i])
							*ptr++ = blackbit;
						else
							*ptr++ = whitebit;
					}
					if (++cnt >= lim)
						cnt = 0;
				}
			}
		}
	}
	else
	{
		int cnt = 0;
		int lim = bytes_per_line * 8;
		for (bytes = 0; bytes < raster_length; bytes++)
		{
			if (fscanf(fp, " 0x%x%*[,}]%*[ \r\n]", &value) != 1)
			{
				fprintf(stderr, "Error scanning bits item.\n");
				return((unsigned char *)NULL);
			}
			for (i = 0; i < 8; i++)
			{
				if (cnt < (*w))
				{
					if (value & nibMask[i])
						*ptr++ = blackbit;
					else
						*ptr++ = whitebit;
				}
				if (++cnt >= lim)
					cnt = 0;
			}
		}
	}
	return(dataP);
}

static unsigned char *
ReadBitmap(html, datafile, w, h, colrs, bg)
Widget html;
char *datafile;
int *w, *h;
XColor *colrs;
int *bg;
{
	unsigned char *bit_data;
	FILE *fp;

	*bg = -1;

        fp = fopen(datafile, "r");
	if (fp != NULL)
	{
	/* First, go fetch foreground and background colors */
	XtVaGetValues (html, XtNforeground, &fg_pixel,
		       XtNbackground, &bg_pixel, NULL);

	/* Now, load up fg_color and bg_color. */
	fg_color.pixel = fg_pixel;
	bg_color.pixel = bg_pixel;
            
	/* Now query for the full color info. */
	XQueryColor 
	    (XtDisplay (html), 
	     DefaultColormap (XtDisplay (html),
			      DefaultScreen (XtDisplay (html))),
	                      &fg_color);

	XQueryColor 
	    (XtDisplay (html), 
	     DefaultColormap (XtDisplay (html),
			      DefaultScreen (XtDisplay (html))),
	                      &bg_color);

	bit_data = ReadGIF(fp, w, h, colrs);
	if (bit_data != NULL)
	    {
	    if (fp != stdin) fclose(fp);
	    return(bit_data);
	    }
	rewind(fp);

	bit_data = ReadXbmBitmap(html,fp, datafile, w, h, colrs);
	if (bit_data != NULL)
	    {
	    if (fp != stdin) fclose(fp);
	    return(bit_data);
	    }
	rewind(fp);
	
	bit_data = ReadXpm3Pixmap(html,fp, datafile, w, h, colrs, bg);
	if (bit_data != NULL)
	    {
	    if (fp != stdin) fclose(fp);
	    return(bit_data);
	    }
	}
	if (fp != stdin) fclose(fp);
	return((unsigned char *)NULL);
}

/* Image resolution function. in wide parts from NCSA Mosaic, cache is ours */
static ImageInfo *
Resolve (w, src, noload)
Widget w;
char *src;
int noload;
{
  int i, cnt;
  unsigned char *data;
  unsigned char *bg_map;
  unsigned char *bgptr;
  unsigned char *ptr;
  int width, height;
  int widthbyheight = 0;
  int Used[256];
  XColor colrs[256];
  ImageInfo *img_data;
  int bg, bg_red, bg_green, bg_blue;
#ifdef MORETHAN256COLORS
  extern int Vclass;
#endif
  XrmQuark   qFilename;
  char    *origSrc = src;
  imgDataRecPtr  dataPtr;

  DBUG_ENTER("Resolve");

  if (!src)
    DBUG_RETURN(NULL);

  /*
   * If we allready have this imgInfo in cache => just update the widget's 
   * image list and increment the reference counter....
   */ 

  qFilename = XrmStringToQuark(src);
  if ((dataPtr = htmlLocateImgInfo(qFilename)) != NULL)
	{
	htmlAddRefToWidget(w, qFilename, dataPtr);
	DBUG_RETURN(dataPtr->imgData);
	}

  DBUG_PRINT("html", ("img source=<%s>, filesearchpath=<%s>", 
		      origSrc,wafeFileSearchPath));
  src = XtResolvePathname(XtDisplay(w), "bitmaps", origSrc, "", 
			  wafeFileSearchPath, NULL, 0, NULL);

  if (src == NULL)
        DBUG_RETURN(NULL);

  DBUG_PRINT("html", ("img resolved: <%s>", src));

  /* If we don't have the image cached and noload is high,
     then just return NULL to avoid doing a network load. */
  /* Also return if interrupted is high. */
  if (noload /*|| interrupted*/)
    {
      XtFree (src);
      DBUG_RETURN(NULL);
    }

  /*
   * No transparent background by default
   */
  bg = -1;
  bg_map = NULL;
    {
      /* We have to load the image. */
      
      data = ReadBitmap(w, src, &width, &height, colrs, &bg);
      XtFree (src);
      /* if we have a transparent background, prepare for it */
      if ((bg >= 0)&&(data != NULL))
      {
        bg_red = colrs[bg].red;
        bg_green = colrs[bg].green;
        bg_blue = colrs[bg].blue;
        bg_map = (unsigned char *)malloc(width * height);
      }
      
      if (data == NULL)
            DBUG_RETURN(NULL);
      
      img_data = (ImageInfo *)malloc(sizeof(ImageInfo));
      img_data->width = width;
      img_data->height = height;
      img_data->image_data = data;
      img_data->image = (Pixmap)NULL;
      /* Bandaid for bug afflicting Eric's code, apparently. */
      img_data->internal = 0;
    }

  widthbyheight = img_data->width * img_data->height;

  /* Fill out used array. */
  for (i=0; i < 256; i++)
    {
      Used[i] = 0;
    }
  cnt = 1;
  bgptr = bg_map;
  ptr = img_data->image_data;
  for (i=0; i < widthbyheight; i++)
    {
      if (Used[(int)*ptr] == 0)
        {
          Used[(int)*ptr] = cnt;
          cnt++;
        }
      if (bg >= 0)
        {
                if (*ptr == bg)
                {
                        *bgptr = (unsigned char)1;
                }
                else
                {
                        *bgptr = (unsigned char)0;
                }
                bgptr++;
        }
      ptr++;
    }
  cnt--;
#ifdef MORETHAN256COLORS  
  /*
   * If the image has too many colors, apply a median cut algorithm to
   * reduce the color usage, and then reprocess it.
   * Don't cut colors for direct mapped visuals like TrueColor.
   */
  if ((cnt > Rdata.colors_per_inlined_image)&&(Vclass != TrueColor))
    {
      MedianCut(img_data->image_data, &img_data->width, 
                &img_data->height, colrs, cnt, 
                Rdata.colors_per_inlined_image);
      
      for (i=0; i < 256; i++)
        Used[i] = 0;
      cnt = 1;
      ptr = img_data->image_data;
      for (i=0; i < widthbyheight; i++)
        {
          if (Used[(int)*ptr] == 0)
            {
              Used[(int)*ptr] = cnt;
              cnt++;
            }
          ptr++;
        }
      cnt--;

      /* if we had a transparent bg, MedianCut used it.  Get a new one */
      if (bg >= 0)
      {
        cnt++;
        bg = 256;
      }
    }
#endif

  img_data->num_colors = cnt;
    {
      img_data->reds = (int *)malloc(sizeof(int) * cnt);
      img_data->greens = (int *)malloc(sizeof(int) * cnt);
      img_data->blues = (int *)malloc(sizeof(int) * cnt);
    }

  for (i=0; i < 256; i++)
    {
      int indx;
      
      if (Used[i] != 0)
        {
          indx = Used[i] - 1;
          img_data->reds[indx] = colrs[i].red;
          img_data->greens[indx] = colrs[i].green;
          img_data->blues[indx] = colrs[i].blue;
          /* squeegee in the background color */
          if ((bg >= 0)&&(i == bg))
          {
                img_data->reds[indx] = bg_red;
                img_data->greens[indx] = bg_green;
                img_data->blues[indx] = bg_blue;
          }
        }
    }
#ifdef MORETHAN256COLORS  
    /* if MedianCut ate our background, add the new one now. */
    if (bg == 256)
    {
        img_data->reds[cnt - 1] = bg_red;
        img_data->greens[cnt - 1] = bg_green;
        img_data->blues[cnt - 1] = bg_blue;
    }
#endif
  bgptr = bg_map;
  ptr = img_data->image_data;
  for (i=0; i < widthbyheight; i++)
    {
      *ptr = (unsigned char)(Used[(int)*ptr] - 1);
      /* if MedianCut ate the background, enforce it here */
      if (bg == 256)
      {
        if ((int)*bgptr == 1)
        {
                *ptr = (unsigned char)(cnt - 1);
        }
        bgptr++;
      }
      ptr++;
    }

  /* free the background map if we have one */
  if (bg_map != NULL)
  {
    free(bg_map);
  }
  
  dataPtr = htmlAddImgData(qFilename, img_data);
  htmlAddRefToWidget(w, qFilename, dataPtr);  

  DBUG_RETURN(img_data);
}


static int
visitedTestHandler(w, href)
Widget w;
char   *href;
    {
    extern char *wafeHandlers[];
    DBUG_ENTER("visitedTestHandler");

    if(wafeHandlers[HTMLVISITED])
	{
	int result;

	Tcl_SetVar(wafeInterpreter, "HTMLWIDGET", XtName(w), TCL_GLOBAL_ONLY);
	Tcl_SetVar(wafeInterpreter, "HTMLHREF", href, TCL_GLOBAL_ONLY);
	(void) wafeEval(wafeInterpreter, wafeHandlers[HTMLVISITED], "HTMLVISITED");
	
	if (sscanf(wafeInterpreter->result, "%d", &result) != 1)
	    {
	    fprintf(stderr, 
	    "HTMLVISITED Handler Warning: Procedure should return 0 or 1, assuming 0\n");
	    result = 0;
	    }
	DBUG_RETURN(result);
	}
    else
	{
/*	fprintf(stderr, "No handler registered...\n");*/
	DBUG_RETURN(0);
	}
    
    }



static Widget
crtHtmlWidget(father, name, args, numArgs)
Widget   father;
char     *name;
ArgList  args;
Cardinal numArgs;
    {
    Widget newWidget;
    Arg    arg[2];

    /* registering resolveImageFunction before user can use a picture */
    XtSetArg(arg[0], WbNresolveImageFunction, Resolve);
    XtSetArg(arg[1], WbNpreviouslyVisitedTestFunction, visitedTestHandler);

    newWidget = XtCreateWidget(name, htmlWidgetClass, father, arg, 2);

    XtSetValues(newWidget, args, numArgs);
    return(newWidget);
    }


/*
 * Convenience function to return the position of the element
 * based on the element id passed in.
 * Function returns the page number and fills in x,y pixel values.
 * If there is no such element, 0,0 of page 1 is returned.
 */
static Boolean
#ifdef _NO_PROTO
HTMLIdToElement(w, element_id, returnRec)
	Widget w;
	int element_id;
        HTMLElementRecord ** returnRec;
#else
HTMLIdToElement(Widget w, int element_id, HTMLElementRecord ** returnRec)
#endif
    {
    HTMLWidget hw = (HTMLWidget)w;
    struct ele_rec *start;
    struct ele_rec *eptr;
    
    eptr = NULL;
    start = hw->html.formatted_elements;
	
    while (start != NULL)
	{
	fprintf(stderr,"HTMLIdToElement: comparing %d with %d\n", 
		start->ele_id, element_id);
	if (start->ele_id == element_id)
	    {
	    *returnRec = start;
	    return True;
	    }
	start = start->next;
	}
    return False;
    }

static void
#ifdef _NO_PROTO
HTMLsetElementBg(w, element_id, colorString)
	Widget w;
	int element_id;
        char * colorString;
#else
HTMLsetElementBg(Widget w, int element_id, char * colorString)
#endif
    { 
    HTMLElementRecord *returnRec;    
    Boolean validId = HTMLIdToElement(w, element_id, &returnRec);
    fprintf(stderr,"HTMLsetElementBg: id: %d is a validId = %d\n", element_id,
	    (int) validId);
    
    if (validId)
	{
	XrmValue input,output;

	input.addr = colorString;
	input.size = strlen(colorString)+1;
	output.addr =  NULL;

	XtConvert(w, "String", &input, "Pixel", &output);
	if (output.addr)
	    {
	    fprintf(stderr,"background pixel was %p\n", (XtPointer)returnRec->bg);
	    returnRec->bg = *(Pixel *) output.addr;
	    fprintf(stderr,"background pixel is  %p\n", (XtPointer)returnRec->bg);
	    RefreshTextRange(w,returnRec,returnRec);
	    }
	}
    }


static String
hTMLGetHRefs(w) 
Widget w;
    {
    int num;
    char **p = HTMLGetHRefs((w),&num);
    fprintf(stderr, "returned from get hrefs, num=%d\n",num);
    return Tcl_Merge(num,p);
    }


