/****************************************************************************
 * 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.                                                  *
 ****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include "HTMLP.h"
#include "NoImage.xbm"
#include "DelayedImage.xbm"
#include "AnchoredImage.xbm"

/* SWP -- Now use eptr->bwidth -- 02/08/96
#define IMAGE_BORDER	2 */
#define IMAGE_DEFAULT_BORDER 2

ImageInfo no_image;
ImageInfo delayed_image;
ImageInfo anchored_image;

static int allocation_index[256];

#ifndef DISABLE_TRACE
extern int htmlwTrace;
#endif

extern int installed_colormap;
extern Colormap installed_cmap;

/*
 * Free all the colors in the default colormap that we have allocated so far.
 */
void
FreeColors(dsp, colormap)
	Display *dsp;
	Colormap colormap;
{
	int i, j;
	unsigned long pix;

	for (i=0; i<256; i++)
	{
		if (allocation_index[i])
		{
			pix = (unsigned long)i;
			/*
			 * Because X is stupid, we have to Free the color
			 * once for each time we've allocated it.
			 */
			for (j=0; j<allocation_index[i]; j++)
			{
				XFreeColors(dsp, colormap, &pix, 1, 0L);
			}
		}
		allocation_index[i] = 0;
	}
}


void
FreeBodyImages(hw)
	HTMLWidget hw;
{

	if (hw->html.bgmap_SAVE!=None) {
		XFreePixmap(XtDisplay(hw),
			    hw->html.bgmap_SAVE);
		hw->html.bgmap_SAVE=None;
	}
	if (hw->html.bgclip_SAVE!=None) {
		XFreePixmap(XtDisplay(hw),
			    hw->html.bgclip_SAVE);
		hw->html.bgclip_SAVE=None;
	}

	return;
}


/*
 * Free up all the pixmaps allocated for this document.
 */
void
FreeImages(hw)
	HTMLWidget hw;
{
	struct ele_rec *eptr;

	eptr = hw->html.formatted_elements;
	while (eptr != NULL)
	{
		if ((eptr->type == E_IMAGE)&&(eptr->pic_data != NULL))
		{
			/*
			 * Don't free the no_image default image
			 */
			if ((eptr->pic_data->image != (Pixmap)NULL)&&
			    (eptr->pic_data->image != delayed_image.image)&&
			    (eptr->pic_data->image != anchored_image.image)&&
			    (eptr->pic_data->image != no_image.image))
			{
				/*
				 * Don't free internal images
				 */
				if ((eptr->edata != NULL)&&
					(strncmp(eptr->edata, INTERNAL_IMAGE,
					strlen(INTERNAL_IMAGE)) == 0))
				{
				}
				else
				{
					XFreePixmap(XtDisplay(hw),
						eptr->pic_data->image);
					eptr->pic_data->image = (Pixmap)NULL;
					if (eptr->pic_data->transparent) {
						XFreePixmap(XtDisplay(hw),
							    eptr->pic_data->clip);
						eptr->pic_data->clip = None;
					}
				}
			}
		}
		eptr = eptr->next;
	}
}


/*
 * Find the closest color by allocating it, or picking an already allocated
 * color
 */
void
FindColor(dsp, colormap, colr)
	Display *dsp;
	Colormap colormap;
	XColor *colr;
{
	int i, match;
#ifdef MORE_ACCURATE
	double rd, gd, bd, dist, mindist;
#else
	int rd, gd, bd, dist, mindist;
#endif /* MORE_ACCURATE */
	int cindx;
static XColor def_colrs[256];
static int have_colors = 0;
	int NumCells;

	match = XAllocColor(dsp, colormap, colr);
	if (match == 0)
	{
		NumCells = DisplayCells(dsp, DefaultScreen(dsp));
		if (!have_colors)
		{
			for (i=0; i<NumCells; i++)
			{
				def_colrs[i].pixel = i;
			}
			XQueryColors(dsp, colormap, def_colrs, NumCells);
			have_colors = 1;
		}
#ifdef MORE_ACCURATE
		mindist = 196608.0;		/* 256.0 * 256.0 * 3.0 */
		cindx = colr->pixel;
		for (i=0; i<NumCells; i++)
		{
			rd = (def_colrs[i].red - colr->red) / 256.0;
			gd = (def_colrs[i].green - colr->green) / 256.0;
			bd = (def_colrs[i].blue - colr->blue) / 256.0;
			dist = (rd * rd) +
				(gd * gd) +
				(bd * bd);
			if (dist < mindist)
			{
				mindist = dist;
				cindx = def_colrs[i].pixel;
				if (dist == 0.0)
				{
					break;
				}
			}
		}
#else
		mindist = 196608;		/* 256 * 256 * 3 */
		cindx = colr->pixel;
		for (i=0; i<NumCells; i++)
		{
			rd = ((int)(def_colrs[i].red >> 8) -
				(int)(colr->red >> 8));
			gd = ((int)(def_colrs[i].green >> 8) -
				(int)(colr->green >> 8));
			bd = ((int)(def_colrs[i].blue >> 8) -
				(int)(colr->blue >> 8));
			dist = (rd * rd) +
				(gd * gd) +
				(bd * bd);
			if (dist < mindist)
			{
				mindist = dist;
				cindx = def_colrs[i].pixel;
				if (dist == 0)
				{
					break;
				}
			}
		}
#endif /* MORE_ACCURATE */
		colr->pixel = cindx;
		colr->red = def_colrs[cindx].red;
		colr->green = def_colrs[cindx].green;
		colr->blue = def_colrs[cindx].blue;
	}
	else
	{
		/*
		 * Keep a count of how many times we have allocated the
		 * same color, so we can properly free them later.
		 */
		allocation_index[colr->pixel]++;

		/*
		 * If this is a new color, we've actually changed the default
		 * colormap, and may have to re-query it later.
		 */
		if (allocation_index[colr->pixel] == 1)
		{
			have_colors = 0;
		}
	}
}


static int
highbit(ul)
unsigned long ul;
{
	/*
	 * returns position of highest set bit in 'ul' as an integer (0-31),
	 * or -1 if none.
	 */

	int i;
	for (i=31; ((ul&0x80000000) == 0) && i>=0;  i--, ul<<=1);
	return i;
}


#ifdef VMS  /* PGE, changed from #ifndef Not_Stellan */
extern int bits_per_pixel(int dpy, int depth); /*this is  pixmaps.c*/
#endif /* Stellan */

/*
 * Make am image of appropriate depth for display from image data.
 */
XImage *
MakeImage(dsp, data, width, height, depth, img_info, clip)
	Display *dsp;
	unsigned char *data;
	int width, height;
	int depth;
	ImageInfo *img_info;
	int clip;
{
	int linepad, shiftnum;
	int shiftstart, shiftstop, shiftinc;
#ifndef VMS  /* PGE, changed from #ifdef Not_Stellan */
	int bytesperline;
#else
	int bytesperline,bpp;
#endif /* Stellan */
	int temp;
	int w, h;
	XImage *newimage;
	unsigned char *bit_data, *bitp, *datap;
	Visual *theVisual;
	int bmap_order;
	unsigned long c;
	int rshift, gshift, bshift;

#ifndef VMS  /* PGE, changed from #ifdef Not_Stellan */
	switch(depth)
#else
	/* The NCSA code contain these assumptions:
	6 and 8 bit deep screens are always 8 bits per pixel,
	1, 2 and 4 bit deep screens are always compactly packed,
	and 24 bit deep screens are 32 bits per pixel. 
        Unfortunately, one must consider more than the depth.
	The bits are NOT always compactly packed. So: */
	switch(bpp=bits_per_pixel(dsp,depth))

#endif /* Stellan */
	{
	    case 6:
	    case 8:
		bit_data = (unsigned char *)malloc(width * height);
/*
		bcopy(data, bit_data, (width * height));
*/
		memcpy(bit_data, data, (width * height));
		bytesperline = width;
		if (clip) {
			depth = 1;
		}
		newimage = XCreateImage(dsp,
			DefaultVisual(dsp, DefaultScreen(dsp)),
			depth, ZPixmap, 0, (char *)bit_data,
			width, height, 8, bytesperline);
		break;
	    case 1:
	    case 2:
	    case 4:
		if (BitmapBitOrder(dsp) == LSBFirst)
		{
			shiftstart = 0;
			shiftstop = 8;
#ifndef VMS  /* PGE, changed from #ifdef Not_Stellan */
			shiftinc = depth;
#else
			shiftinc = bpp;
#endif /* Stellan */
		}
		else
		{
#ifndef VMS  /* PGE, changed from #ifdef Not_Stellan */
			shiftstart = 8 - depth;
			shiftstop = -depth;
			shiftinc = -depth;
#else
			shiftstart = 8 - bpp;
			shiftstop = -bpp;
			shiftinc = -bpp;
#endif /* Stellan */
		}
		linepad = 8 - (width % 8);
		bit_data = (unsigned char *)malloc(((width + linepad) * height)
				+ 1);
		bitp = bit_data;
		datap = data;
		*bitp = 0;
		shiftnum = shiftstart;
		for (h=0; h<height; h++)
		{
			for (w=0; w<width; w++)
			{
				temp = *datap++ << shiftnum;
				*bitp = *bitp | temp;
				shiftnum = shiftnum + shiftinc;
				if (shiftnum == shiftstop)
				{
					shiftnum = shiftstart;
					bitp++;
					*bitp = 0;
				}
			}
			for (w=0; w<linepad; w++)
			{
				shiftnum = shiftnum + shiftinc;
				if (shiftnum == shiftstop)
				{
					shiftnum = shiftstart;
					bitp++;
					*bitp = 0;
				}
			}
		}
#ifndef VMS  /* PGE, changed from #ifdef Not_Stellan */
		bytesperline = (width + linepad) * depth / 8;
#else
		bytesperline = (width + linepad) * bpp / 8;
#endif /* Stellan */
		newimage = XCreateImage(dsp,
			DefaultVisual(dsp, DefaultScreen(dsp)),
			depth, ZPixmap, 0, (char *)bit_data,
			(width + linepad), height, 8, bytesperline);
		break;
	    /*
	     * WARNING:  This depth 16 code is donated code for 16 but
	     * TrueColor displays.  I have no access to such displays, so I
	     * can't really test it.
	     * Donated by - nosmo@ximage.com
	     */
	    case 16:
		bit_data = (unsigned char *)malloc(width * height * 2);
		bitp = bit_data;
		datap = data;

		theVisual = DefaultVisual(dsp, DefaultScreen(dsp));
		rshift = 15 - highbit(theVisual->red_mask);
		gshift = 15 - highbit(theVisual->green_mask);
		bshift = 15 - highbit(theVisual->blue_mask);
		bmap_order = BitmapBitOrder(dsp);
     
		for (w = (width * height); w > 0; w--)
		{
			temp = (((img_info->reds[(int)*datap] >> rshift) &
				 theVisual->red_mask) |
				((img_info->greens[(int)*datap] >> gshift) &
				 theVisual->green_mask) |
				((img_info->blues[(int)*datap] >> bshift) &
				 theVisual->blue_mask));
			if (bmap_order == MSBFirst)
			{
				*bitp++ = (temp >> 8) & 0xff;
				*bitp++ = temp & 0xff;
			}
			else
			{
				*bitp++ = temp & 0xff;
				*bitp++ = (temp >> 8) & 0xff;
			}

			datap++;
		}

		newimage = XCreateImage(dsp,
			DefaultVisual(dsp, DefaultScreen(dsp)),
			depth, ZPixmap, 0, (char *)bit_data,
			width, height, 16, 0);
		break;
	    case 24:
#ifdef VMS  /* PGE, changed from #ifndef Not_Stellan */
	    case 32:
#endif /* Stellan */
		bit_data = (unsigned char *)malloc(width * height * 4);

		theVisual = DefaultVisual(dsp, DefaultScreen(dsp));
		rshift = highbit(theVisual->red_mask) - 7;
		gshift = highbit(theVisual->green_mask) - 7;
		bshift = highbit(theVisual->blue_mask) - 7;
		bmap_order = BitmapBitOrder(dsp);

		bitp = bit_data;
		datap = data;
		for (w = (width * height); w > 0; w--)
		{
		    c =
		     (((img_info->reds[(int)*datap] >> 8) & 0xff) << rshift) |
		     (((img_info->greens[(int)*datap] >> 8) & 0xff) << gshift) |
		     (((img_info->blues[(int)*datap] >> 8) & 0xff) << bshift);

			datap++;

			if (bmap_order == MSBFirst)
			{
				*bitp++ = (unsigned char)((c >> 24) & 0xff);
				*bitp++ = (unsigned char)((c >> 16) & 0xff);
				*bitp++ = (unsigned char)((c >> 8) & 0xff);
				*bitp++ = (unsigned char)(c & 0xff);
			}
			else
			{
				*bitp++ = (unsigned char)(c & 0xff);
				*bitp++ = (unsigned char)((c >> 8) & 0xff);
				*bitp++ = (unsigned char)((c >> 16) & 0xff);
				*bitp++ = (unsigned char)((c >> 24) & 0xff);
			}
		}

		newimage = XCreateImage(dsp,
			DefaultVisual(dsp, DefaultScreen(dsp)),
			depth, ZPixmap, 0, (char *)bit_data,
			width, height, 32, 0);
		break;
	    default:
#ifndef DISABLE_TRACE
		if (htmlwTrace) {
			fprintf(stderr, "Don't know how to format image for display of depth %d\n", depth);
		}
#endif

		return(NULL);
	}

	return(newimage);
}


int
AnchoredHeight(hw)
	HTMLWidget hw;
{
	return((int)(AnchoredImage_height + IMAGE_DEFAULT_BORDER));
}


char *
IsMapForm(hw)
	HTMLWidget hw;
{
	char *str;

	str = (char *)malloc(strlen("ISMAP Form") + 1);
	if (str != NULL)
	{
		strcpy(str, "ISMAP Form");
	}
	return(str);
}


int
IsIsMapForm(hw, href)
	HTMLWidget hw;
	char *href;
{
	if ((href != NULL)&&(strcmp(href, "ISMAP Form") == 0))
	{
		return(1);
	}
	else
	{
		return(0);
	}
}


char *
DelayedHRef(hw)
	HTMLWidget hw;
{
	char *str;

	str = (char *)malloc(strlen("Delayed Image") + 1);
	if (str != NULL)
	{
		strcpy(str, "Delayed Image");
	}
	return(str);
}


int
IsDelayedHRef(hw, href)
	HTMLWidget hw;
	char *href;
{
	if ((href != NULL)&&(strcmp(href, "Delayed Image") == 0))
	{
		return(1);
	}
	else
	{
		return(0);
	}
}


Pixmap
DelayedImage(hw, anchored)
	HTMLWidget hw;
	Boolean anchored;
{
        if (delayed_image.image == (Pixmap)NULL)
        {
		delayed_image.transparent=0;
                delayed_image.image = XCreatePixmapFromBitmapData(
                        XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), DelayedImage_bits,
                        DelayedImage_width, DelayedImage_height,
/*
 * Without motif we use our own foreground resource instead of
 * using the manager's
 */
#ifdef MOTIF
                        hw->manager.foreground,
#else
                        hw->html.foreground,
#endif /* MOTIF */
                        hw->core.background_pixel,
                        DefaultDepthOfScreen(XtScreen(hw)));
	}

        if ((anchored == True)&&(anchored_image.image == (Pixmap)NULL))
        {
                Pixmap pix;

		anchored_image.transparent=0;
                anchored_image.image = XCreatePixmapFromBitmapData(
                        XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), AnchoredImage_bits,
                        AnchoredImage_width, AnchoredImage_height,
/*
 * Without motif we use our own foreground resource instead of
 * using the manager's
 */
#ifdef MOTIF
                        hw->manager.foreground,
#else
                        hw->html.foreground,
#endif /* MOTIF */
                        hw->core.background_pixel,
                        DefaultDepthOfScreen(XtScreen(hw)));
                pix = XCreatePixmap(
                        XtDisplay(hw->html.view),
                        XtWindow(hw->html.view),
                        DelayedImage_width,
                        (DelayedImage_height + AnchoredImage_height +
                                IMAGE_DEFAULT_BORDER),
                        DefaultDepthOfScreen(XtScreen(hw)));
                XSetForeground(XtDisplay(hw), hw->html.drawGC,
                        hw->core.background_pixel);
                XFillRectangle(XtDisplay(hw->html.view), pix,
                        hw->html.drawGC, 0, 0,
                        DelayedImage_width,
                        (DelayedImage_height + AnchoredImage_height +
                                IMAGE_DEFAULT_BORDER));
                XCopyArea(XtDisplay(hw->html.view),
                        anchored_image.image, pix, hw->html.drawGC,
                        0, 0, AnchoredImage_width, AnchoredImage_height,
                        0, 0);
                XCopyArea(XtDisplay(hw->html.view),
                        delayed_image.image, pix, hw->html.drawGC,
                        0, 0, DelayedImage_width, DelayedImage_height,
                        0, (AnchoredImage_height + IMAGE_DEFAULT_BORDER));
                XFreePixmap(XtDisplay(hw->html.view), anchored_image.image);
                anchored_image.image = pix;

                return(anchored_image.image);
	}

        return(delayed_image.image);
}


ImageInfo *
DelayedImageData(hw, anchored)
	HTMLWidget hw;
	Boolean anchored;
{
	delayed_image.delayed = 1;
	delayed_image.internal = 0;
	delayed_image.fetched = 0;
	delayed_image.width = DelayedImage_width;
	delayed_image.height = DelayedImage_height;
	delayed_image.num_colors = 0;
	delayed_image.reds = NULL;
	delayed_image.greens = NULL;
	delayed_image.blues = NULL;
	delayed_image.image_data = NULL;
	delayed_image.clip_data = NULL;
	delayed_image.image = None;
	delayed_image.clip = None;
	delayed_image.transparent = 0;

	if (anchored == True)
	{
		anchored_image.delayed = 0;
		anchored_image.internal = 0;
		anchored_image.fetched = 0;
		anchored_image.width = DelayedImage_width;
		anchored_image.height = DelayedImage_height +
			AnchoredImage_height + IMAGE_DEFAULT_BORDER;
		anchored_image.num_colors = 0;
		anchored_image.reds = NULL;
		anchored_image.greens = NULL;
		anchored_image.blues = NULL;
		anchored_image.image_data = NULL;
		anchored_image.image = None;
		anchored_image.clip_data = NULL;
		anchored_image.clip = None;
		anchored_image.transparent = 0;

		return(&anchored_image);
	}

	return(&delayed_image);
}


Pixmap
NoImage(hw)
	HTMLWidget hw;
{
	if (no_image.image == (Pixmap)NULL)
	{
		no_image.transparent=0;
		no_image.image = XCreatePixmapFromBitmapData(
			XtDisplay(hw),
			XtWindow(hw->html.view), NoImage_bits,
			NoImage_width, NoImage_height,
/*
 * Without motif we use our own foreground resource instead of
 * using the manager's
 */
#ifdef MOTIF
                        hw->manager.foreground,
#else
                        hw->html.foreground,
#endif /* MOTIF */
			hw->core.background_pixel,
			DefaultDepthOfScreen(XtScreen(hw)));
	}
	return(no_image.image);
}


ImageInfo *
NoImageData(hw)
	HTMLWidget hw;
{
	no_image.delayed = 0;
	no_image.internal = 0;
	no_image.fetched = 0;
	no_image.width = NoImage_width;
	no_image.height = NoImage_height;
	no_image.num_colors = 0;
	no_image.reds = NULL;
	no_image.greens = NULL;
	no_image.blues = NULL;
	no_image.image_data = NULL;
	no_image.clip_data = NULL;
	no_image.image = None;
	no_image.clip = None;
	no_image.transparent=0;

	return(&no_image);
}


Pixmap
InfoToImage(hw, img_info, clip)
	HTMLWidget hw;
	ImageInfo *img_info;
	int clip;
{
	int i, size;
	int delta, not_right_col, not_last_row;
	Pixmap Img;
	XImage *tmpimage;
	XColor tmpcolr;
	int *Mapping;
	unsigned char *tmpdata;
	unsigned char *ptr;
	unsigned char *ptr2;
	int Vclass;
	XVisualInfo vinfo, *vptr;
	Boolean need_to_dither;
	unsigned long black_pixel;
	unsigned long white_pixel;
	int depth;

	/* find the visual class. */
	vinfo.visualid = XVisualIDFromVisual(DefaultVisual(XtDisplay(hw),
		DefaultScreen(XtDisplay(hw))));
	vptr = XGetVisualInfo(XtDisplay(hw), VisualIDMask, &vinfo, &i);
	Vclass = vptr->class;
	depth=vptr->depth;
	if (clip) {
		need_to_dither = False;
	}
	else if (vptr->depth == 1)
	{
		need_to_dither = True;
		black_pixel = BlackPixel(XtDisplay(hw),
				DefaultScreen(XtDisplay(hw)));
		white_pixel = WhitePixel(XtDisplay(hw),
				DefaultScreen(XtDisplay(hw)));
	}
	else
	{
		need_to_dither = False;
	}
	XFree((char *)vptr);

	Mapping = (int *)malloc(img_info->num_colors * sizeof(int));

	if (!clip) {
		for (i=0; i < img_info->num_colors; i++)
		{
			tmpcolr.red = img_info->reds[i];
			tmpcolr.green = img_info->greens[i];
			tmpcolr.blue = img_info->blues[i];
			tmpcolr.flags = DoRed|DoGreen|DoBlue;
			if ((Vclass == TrueColor) || (Vclass == DirectColor))
			{
				Mapping[i] = i;
			}
			else if (need_to_dither == True)
			{
				Mapping[i] = ((tmpcolr.red>>5)*11 +
					      (tmpcolr.green>>5)*16 +
					      (tmpcolr.blue>>5)*5) / (65504/64);
			}
			else
			{
				FindColor(XtDisplay(hw),
					  (installed_colormap ?
					   installed_cmap :
					   DefaultColormapOfScreen(XtScreen(hw))),
					  &tmpcolr);
				Mapping[i] = tmpcolr.pixel;
			}
		}
	}

	/*
	 * Special case:  For 2 color non-black&white images, instead
	 * of 2 dither patterns, we will always drop them to be
	 * black on white.
	 */
	if ((need_to_dither == True)&&(img_info->num_colors == 2))
	{
		if (Mapping[0] < Mapping[1])
		{
			Mapping[0] = 0;
			Mapping[1] = 64;
		}
		else
		{
			Mapping[0] = 64;
			Mapping[1] = 0;
		}
	}

	size = img_info->width * img_info->height;
	if (size == 0)
	{
		tmpdata = NULL;
	}
	else
	{
		tmpdata = (unsigned char *)malloc(size);
	}
	if (tmpdata == NULL)
	{
		tmpimage = NULL;
		Img = (Pixmap)NULL;
	}
	else
	{
		if (clip) {
			ptr = img_info->clip_data;
		}
		else {
			ptr = img_info->image_data;
		}
		ptr2 = tmpdata;

		if (need_to_dither == True)
		{
			int cx, cy;

			if (clip) {
				for (ptr2 = tmpdata, ptr = img_info->clip_data;
				     ptr2 < tmpdata+(size-1); ptr2++, ptr++) {
					*ptr2 = Mapping[(int)*ptr];
				}
			}
			else {
				for (ptr2 = tmpdata, ptr = img_info->image_data;
				     ptr2 < tmpdata+(size-1); ptr2++, ptr++) {
					*ptr2 = Mapping[(int)*ptr];
				}
			}

			ptr2 = tmpdata;
			for (cy=0; cy < img_info->height; cy++)
			{
				for (cx=0; cx < img_info->width; cx++)
				{
					/*
					 * Assume high numbers are
					 * really negative.
					 */
					if (*ptr2 > 128)
					{
						*ptr2 = 0;
					}
					if (*ptr2 > 64)
					{
						*ptr2 = 64;
					}

					/*
					 * Traditional Floyd-Steinberg
					 */
					if (*ptr2 < 32)
					{
						delta = *ptr2;
						*ptr2 = black_pixel;
					}
					else
					{
						delta = *ptr2 - 64;
						*ptr2 = white_pixel;
					}
					if (not_right_col =
						(cx < (img_info->width-1)))
					{
						*(ptr2+1) += delta*7 >> 4;
					}

					if (not_last_row =
						(cy < (img_info->height-1)))
					{
						(*(ptr2+img_info->width)) +=
							delta*5 >> 4;
					}

					if (not_right_col && not_last_row)
					{
						(*(ptr2+img_info->width+1)) +=
							delta >> 4;
					}

					if (cx && not_last_row)
					{
						(*(ptr2+img_info->width-1)) +=
							delta*3 >> 4;
					}
					ptr2++;
				}
			}
		} /* end if (need_to_dither==True) */
		else
		{

			for (i=0; i < size; i++)
			{
				if (clip) {
					*ptr2++ = *ptr;
				}
				else {
					*ptr2++ = (unsigned char)Mapping[(int)*ptr];
				}
				ptr++;
			}
		}

		depth=DefaultDepthOfScreen(XtScreen(hw));
		tmpimage = MakeImage(XtDisplay(hw), tmpdata,
			img_info->width, img_info->height,
			depth, img_info, clip);

                /* Caught by Purify; should be OK. */
                free (tmpdata);

		Img = XCreatePixmap(XtDisplay(hw),
			XtWindow(hw->html.view),
			img_info->width, img_info->height,
			depth);
	}

	if ((tmpimage == NULL)||(Img == (Pixmap)NULL))
	{
		if (tmpimage != NULL)
		{
			XDestroyImage(tmpimage);
		}
		if (Img != (Pixmap)NULL)
		{
			XFreePixmap(XtDisplay(hw), Img);
		}
		img_info->width = NoImage_width;
		img_info->height = NoImage_height;
		Img = NoImage(hw);
	}
	else
	{
		XPutImage(XtDisplay(hw), Img, hw->html.drawGC, tmpimage, 0, 0,
			0, 0, img_info->width, img_info->height);
		XDestroyImage(tmpimage);
	}

        /* Caught by Purify; should be OK. */
        free((char *)Mapping);

	return(Img);
}

