 /*
  * Khoros: $Id: color.c,v 1.4 1992/03/20 22:48:51 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: color.c,v 1.4 1992/03/20 22:48:51 dkhoros Exp $";
#endif

 /*
  * $Log: color.c,v $
 * Revision 1.4  1992/03/20  22:48:51  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 *            Copyright 1990 University of New Mexico
 *  
 *  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 docu-
 *  mentation, and that the name of UNM not be used in advertis-
 *  ing or publicity pertaining to distribution of the software
 *  without specific, written prior permission.  UNM makes no
 *  representations about the suitability of this software for
 *  any purpose.  It is provided "as is" without express or
 *  implied warranty.
 *  
 *  UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 *  INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 *  NESS, IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 *  INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 *  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 *  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 *  OF THIS SOFTWARE.
 *  
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "xvdisplay.h"

/* display kludge */
int MaxCells;
int CurrentCells;

typedef struct _xvdisplay_list
{
	DisplayStructure *xvdisplay;
	struct _xvdisplay_list *next;
} DisplayList;

static DisplayList *xvdisplay_list = NULL;

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>		Image Display Color Routines			<<<<
   >>>>								<<<<
   >>>>		xvd_initialize_colormap()			<<<<
   >>>>		xvd_build_histogram()				<<<<
   >>>>		xvd_set_colormap()				<<<<
   >>>>		xvd_load_xcolors()				<<<<
   >>>>		xvd_create_colormap()				<<<<
   >>>>		xvd_update_colormap()				<<<<
   >>>>		xvd_allocate_color()				<<<<
   >>>>		xvd_closest_color()				<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


/***********************************************************************
*
*  Routine Name: xvd_initialize_colormap
*        
*       Purpose: initializes and checks to see if a valid colormap
*		 can be built.
*
*         Input: xvdisplay - the xvdisplay struture.
*
*        Output: return true if the xvdisplay structure can be successfully
*		 initializes the colormap.
*
*    CALLED BY: internal routine
*
*    Written By:   Mark Young
*
***********************************************************************/


int xvd_initialize_colormap(xvdisplay)

DisplayStructure *xvdisplay;
{
	struct   xvimage *image = xvdisplay->disp_image;
	char     mesg[MaxLength];



	if (image->map_scheme != VFF_MS_NONE &&
	    image->map_storage_type != VFF_MAPTYP_1_BYTE)
	{
	   xvf_error_wait("Error!  Can only display images with a map storage\
 type of byte\n", "xvd_initialize_colormap", NULL);
	   return(FALSE);
	}

	/*
	 *  If the number of colors in the image is greater than the
	 *  maximum colors that can be displayed, then color compress
	 *  the image.
	 */
	xvd_build_histogram(xvdisplay);

	if (xvd_truecolor_image(image) == TRUE && xvdisplay->truecolor == TRUE)
	   return(TRUE);

	if (xvd_truecolor_image(image) == TRUE)
	{
	   /*
	    *  Need to inform the user that the image is an RGB, but we can
	    *  only display BYTE images.
 	    */
           (void) sprintf(mesg, "This is an RGB image.  That is, a 24 bit image made of of 3 8-bit bands; however, we can only display one band at a time.  Do you wish to compress the RGB image into a single band image (with an internal call to vgamut)?");
	   if (xvf_warn_wait(mesg, "xvd_initialize_colormap", NULL, NULL))
	   {
	       /* if they say "yes", compress the image,  & build histogram */
	       if (!(xvd_compress_image(xvdisplay, ncolors)))
	           return(FALSE);
	      /*
	       *  rebuild histogram.  We use this when building the colormap;
	       *  only colormap entries with data indexes get allocated.
	       */
	       xvd_build_histogram(xvdisplay);
	       return(TRUE);
	   }
	   
	}

	if (ncolors < xvdisplay->pixelnum)
	{
	   /*
	    *  Need to inform the user that the image has too many colors
	    *  to be displayed.  The user will be prompted to see if they
	    *  wish to continue.
 	    */
           (void) sprintf(mesg, "Image needs %d colors, but this screen can \
only display %d colors.  Do you wish to color compress the image? (compressing \
image will alter the data)\n", xvdisplay->pixelnum, ncolors);
	   if (!xvf_warn_wait(mesg, "xvd_initialize_colormap", NULL, NULL))
	   {
	      return(FALSE);
	   }

	   if (!(xvd_compress_image(xvdisplay, ncolors)))
	      return(FALSE);

	   /*
	    *  rebuild histogram.  We use this when building the colormap;
	    *  only colormap entries with data indexes get allocated.
	    */
	   xvd_build_histogram(xvdisplay);
	}
	return(TRUE);
}



/***********************************************************************
*
*  Routine Name: xvd_build_histogram
*        
*       Purpose: find the histogram for the image in the xvdisplay structure.
*		 We also find the number of active pixels, as well as the
*		 starting and ending point of the active pixels.  Giving
*		 information about the active part.
*
*         Input: xvdisplay - the xvdisplay struture.
*
*        Output: none
*
*    CALLED BY: internal routine
*
*    Written By: Mark Young
*
***********************************************************************/


xvd_build_histogram(xvdisplay)

DisplayStructure *xvdisplay;
{
	int	 i, j, k;
	unsigned char *data;
	unsigned long size;

	struct   xvimage *image = xvdisplay->disp_image;
	Pixel    *histogram = xvdisplay->histogram;


	/*
	 *  build histogram.  We use this when building the colormap;
	 *  only colormap entries with data indexes get allocated.
	 *  We also use the histogram with the lookup table (lut)
	 *  widget.
	 */
	bzero((char *) histogram, sizeof(unsigned long) * MAX_PIXELS);
	data = (unsigned char *) image->imagedata;

	if (image->data_storage_type == VFF_TYP_BIT)
	{
	   for (k = 0; k < image->num_data_bands; k++)
	   {
	      for (j = 0;  j < image->col_size; j++)
	      {
		 for (i = 0; i < image->row_size; i++)
		 {
		     if (*data & (1 << (i % 8)))
			histogram[1]++;
		     else
			histogram[0]++;

		     if ((i % 8) == 7)
			data++;
		 }
		 if ((i % 8) != 0)
		    data++;
	      }
	   }
	}
	else
	{
	   size = image->row_size * image->col_size * image->num_data_bands;
	   for (i = 0; i < size; i++)
	   {
	      histogram[(int) *data] += 1;
	      data++;
	   }
	}

	xvdisplay->histmin  = xvdisplay->histmax = histogram[0];
	for (i = 1; i < MAX_PIXELS; i++)
	{
	    /*
	     *  find the min and max histogram values.
	     */
	   if (xvdisplay->histmin > histogram[i] && histogram[i] != 0)
	      xvdisplay->histmin = histogram[i];

	   if (xvdisplay->histmax < histogram[i])
	      xvdisplay->histmax = histogram[i];
	}

	xvdisplay->pixelmin = 
	xvdisplay->pixelmax = -1;
	xvdisplay->pixelnum = 0;
	for (i = 0; i < MAX_PIXELS; i++)
	{
	    /*
	     *  Find the number of active pixels
	     */
	    if (histogram[i] != 0)
	       xvdisplay->pixelnum++;

	   /*
	    *  Find the minimum and maximum pixel values.  This is used
	    *  by certain programs to show the range of pixels that have
	    *  a value.
	    */
	   if (xvdisplay->pixelmin == -1 && histogram[i] != 0)
	   {
	      xvdisplay->pixelmin = i;
	      xvdisplay->pixelmax = i;
	   }
	   else if (histogram[i] != 0)
	      xvdisplay->pixelmax = i;
	} 
}



/***********************************************************************
*
*  Routine Name: xvd_set_colormap
*        
*       Purpose: Sets a colormap for the given widget.
*
*         Input: xvdisplay - the xvdisplay struture.
*
*        Output: return true if the xvdisplay structure can be successfully
*		 initializes the colormap.
*
*    CALLED BY: internal routine
*
*    Written By: Mark Young
*
***********************************************************************/

xvd_set_colormap(widget, colormap)

Widget   widget;
Colormap colormap;
{
	int	 i;
	Arg	 arg[1];
	DisplayList	 *list;
	DisplayStructure *xvdisplay = NULL;

	Widget	 parent;
	Window	 windows[2];
	XWindowAttributes xwinattr;
	Display  *display = XtDisplay(widget);


	/*
	 *  Get the toplevel workspace widget.
	 */
	parent = widget;
	while (XtParent(parent) != NULL)
	   parent = XtParent(parent);

	/*
	 *  Now get the corresponding xvdisplay structure that the colormap
	 *  is associated with.  This is used latter to indicate if it is
	 *  even fesible to set the colormap with the associated parent or
	 *  display window.
	 */
	list = xvdisplay_list;
	while (list != NULL)
	{
	   if (list->xvdisplay->colormap == colormap)
	   {
	      xvdisplay = list->xvdisplay;
	      break;
	   }
	   list = list->next;
	}


	/*
	 *  Make sure that the two windows support the same visual.  If not
	 *  then we will not want to set the colormap on this window as it
	 *  will produce a colormap error.  Rather we will simply indicate that
	 *  an alternate colormap should be used using the XSet.
	 */
	XGetWindowAttributes(display, XtWindow(parent), &xwinattr);
	if (xvdisplay != NULL && xwinattr.visual != xvdisplay->visual)
	{
	   windows[0] = XtWindow(parent);
	   windows[1] = XtWindow(xvdisplay->raster);
	   (void) XSetWMColormapWindows(display, XtWindow(parent), windows, 2);
	}
	else
	{
	   i = 0;
	   XtSetArg(arg[i], XtNcolormap, colormap);    i++;
	   XtSetValues(parent, arg, i);
	   XSetWindowColormap(display, XtWindow(parent), colormap);
	}

	/*
	 *  Now that is settled we need to know if we can do the same for
	 *  the child.  But we do not need to actually use the set colormap
	 *  window manager call XSetWMColormapWindows(), since this attributes
	 *  is only set for toplevel windows.
	 */
	XGetWindowAttributes(display, XtWindow(widget), &xwinattr);
	if (xvdisplay != NULL && xwinattr.visual == xvdisplay->visual)
	{
	   i = 0;
	   XtSetArg(arg[i], XtNcolormap, colormap);    i++;
	   XtSetValues(widget, arg, i);
	   XSetWindowColormap(display, XtWindow(widget), colormap);
	}
}



/************************************************************
*
*  MODULE NAME: xvd_load_xcolors
*
*      PURPOSE: This routine is used to load the xcolor array
*		with the colors in the image.  These colors
*		are used in allocating the image's X colormap.
*
*       OUTPUT: loads the xcolor array from the given image
*		structure.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_load_xcolors(xvdisplay)

DisplayStructure *xvdisplay;
{
	int	  color_num, i;
	unsigned  char *maps;

	XColor	  *xcolors = 	    xvdisplay->xcolors;
	struct	  xvimage *image =  xvdisplay->disp_image;
	unsigned  long *histogram = xvdisplay->histogram;
	

	maps = (unsigned char *)  image->maps;
	if (image->map_scheme == VFF_MS_NONE || maps == NULL)
	{
	   /*
	    *  Since there is no mapping associated with the image,
	    *  the colormap is linear grey.
	    */
	   if (image->data_storage_type == VFF_TYP_BIT)
	   {
	      xcolors[0].red   = 
	      xcolors[0].green = 
	      xcolors[0].blue  = 0;
	      xcolors[0].flags = DoRed | DoGreen | DoBlue;

	      xcolors[1].red   = 
	      xcolors[1].green = 
	      xcolors[1].blue  = MAX_INTEN;
	      xcolors[1].flags = DoRed | DoGreen | DoBlue;
	   }
	   else
	   {
	      for (i = 0; i < MAX_PIXELS; i++)
	      {
	         xcolors[i].red   = 
	         xcolors[i].green = 
	         xcolors[i].blue  = i * 256;

	         if (histogram[i] != 0)
	            xcolors[i].flags = DoRed | DoGreen | DoBlue;
	         else
		    xcolors[i].flags = NULL;
	      }
	   }
	}
	else if (image->map_row_size == 1)
	{
	   /*
	    *  Since there is one map associated with the image,
	    *  the colormap is grey.
	    */
	   color_num = image->map_col_size;
	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	      if (i < color_num)
	      {
		 xcolors[i].red   = 
		 xcolors[i].green = 
		 xcolors[i].blue  = maps[i] * 256;
	      }
	      else
	      {
		 xcolors[i].red   = 
		 xcolors[i].green = 
		 xcolors[i].blue  = i * 256;
	      }
		
	      if (histogram[i] != 0)
		 xcolors[i].flags = DoRed | DoGreen | DoBlue;
	      else
		 xcolors[i].flags = NULL;
	   }
	}
	else if (image->map_row_size == 3 && ((image->color_space_model == 
	   VFF_CM_ntscRGB) || (image->color_space_model == VFF_CM_genericRGB))
	   || (image->color_space_model == VFF_CM_NONE))
	{
	   /*
	    *  Since there is three maps associated with the image and there
	    *  are of type RGB, the colormap is pseudo color.
	    */
	   color_num = image->map_col_size;
	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	      if (i < color_num)
	      {
		 xcolors[i].red   = maps[i]  * 256;
		 xcolors[i].green = maps[i + color_num] * 256;
		 xcolors[i].blue  = maps[i + 2 * color_num] * 256;
	      }
	      else
	      {
		 xcolors[i].red   = 
		 xcolors[i].green = 
		 xcolors[i].blue  = i * 256;
	      }
		
	      if (histogram[i] != 0)
		 xcolors[i].flags = DoRed | DoGreen | DoBlue;
	      else
		 xcolors[i].flags = NULL;
	   }
	}
	else
	{
	   xvf_error_wait("Error!  Can only display images with a map row size\
 of 1 or 3 and the following color space models.  (VFF_CM_ntscRGB,\
 VFF_CM_genericRGB, VFF_CM_NONE)\n",
			  "xvd_load_xcolors", NULL);
	   return(FALSE);
	}
	return(TRUE);

} /* end xvd_load_xcolors */



/************************************************************
*
*  MODULE NAME: xvd_create_colormap
*
*      PURPOSE: Create the colormap that  will be used by display
*		programs.  The colormap will be used in displaying
*		the display image as well other display utilities.
*		The colormap cells will be private if the xvdisplay
*		read_only is set to false.  This means we will
*		allocate read/write cells instead of read only ones.
*
*        INPUT: xvdisplay - the display structure from which we will derive
*			    a colormap.
*
*	OUTPUT: loads a new colormap into the xvdisplay structure.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

int xvd_create_colormap(xvdisplay)

DisplayStructure *xvdisplay;
{
	int	 i, status;
	unsigned long *histogram = xvdisplay->histogram;


	for (i = 0; i < MAX_PIXELS; i++)
	{
	    if (histogram[i] != 0)
	       (void) xvd_allocate_color(xvdisplay, i);
	    else
	       xvdisplay->active[i] = False;
	}

	if (xvdisplay->read_only == False)
	   xvd_init_nonxcolors(xvdisplay);

	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_update_colormap
*
*      PURPOSE: Updates the all the associated images with the
*		xvdisplay structure at once.
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*
*       OUTPUT: returns True if we are successful, otherwise False.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvd_update_colormap(xvdisplay)

DisplayStructure *xvdisplay;
{
	int	 i, j;
	Pixel	 pixels[256];

	Display  *display = xvdisplay->display;
	XColor   *xcolors = xvdisplay->xcolors;
	unsigned long *histogram = xvdisplay->histogram;


	/*
	 *  Free all the non-used colors cells in the colormap that may
	 *  have occured
	 */
	for (i = 0, j = 0; i < MAX_PIXELS; i++)
	{
	   if ((xvdisplay->read_only == True && xvdisplay->active[i]) ||
	       (histogram[i] == 0 && xvdisplay->active[i]))
	   {
	      pixels[j++] = xcolors[i].pixel;
	      xvdisplay->active[i] = False;

	      /* display kludge */
	      if (xvdisplay->read_only == False)
		 CurrentCells--;
	   }
	}

	if (j > 0)
	   XFreeColors(display, xvdisplay->colormap, pixels, j, 0);

	if (xvdisplay->read_only == True)
	{
	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	      if (histogram[i] > 0)
	         (void) xvd_allocate_color(xvdisplay, i);
	   }
	}
	else
	{
	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	      if (histogram[i] > 0 && xvdisplay->active[i])
	         XStoreColor(display, xvdisplay->colormap, &xcolors[i]);

	      else if (histogram[i] > 0 && !xvdisplay->active[i])
	         (void) xvd_allocate_color(xvdisplay, i);
	   }
	}

	if (xvdisplay->read_only == False)
	   xvd_init_nonxcolors(xvdisplay);
}



/************************************************************
*
*  MODULE NAME: xvd_init_nonxcolors
*
*      PURPOSE: If the colormap is private (meaning the user is
*		allowed to store new color values in the xcolors
*		array) set all unused xcolors pixels to the last
*		available read/write pixel.  We do this since X
*		Windows insists that when storing colors into a
*		X color array all pixels must be pointing to
*		read/write cells, even if you are have the flags
*		set to do nothing (ie flags=0).
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*
*       OUTPUT: none
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvd_init_nonxcolors(xvdisplay)

DisplayStructure *xvdisplay;
{
	int   i;
	Pixel pixel;


	if (xvdisplay->read_only == False)
	{
	   /*
	    *  find last available pixel.
	    */
	   pixel = MAX_PIXELS;
	   for (i = MAX_PIXELS-1; i >= 0; i--)
	   {
	       if (xvdisplay->active[i])
	       {
		  pixel = xvdisplay->xcolors[i].pixel;
	 	  break;
	       }
	   }

	   if (pixel == MAX_PIXELS)
	      return;

	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	       if (xvdisplay->histogram[i] == 0 && !xvdisplay->active[i])
		  xvdisplay->xcolors[i].pixel = pixel;
	   }
	}
}



/************************************************************
*
*  MODULE NAME: xvd_allocate_color
*
*      PURPOSE: Allocate the color specified in the color array.  The
*		index is used in conjunction with the xcolor array to
*		indicate which color needs to be allocated.  If the
*		display structure colormap is private then try to
*		allocate a private color cell, otherwise try to allocate
*		it using sharable colormaps.
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*		index     - the index into the xcolor array telling
*			    us what color we wish to allocate.
*
*       OUTPUT: returns True if we are successful, otherwise False.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

int xvd_allocate_color(xvdisplay, index)

DisplayStructure *xvdisplay;
int		 index;
{
	Pixel   planes[1], pixel, red, green, blue;
	int     status, colormap_change = False;

	XColor	*xcolors = xvdisplay->xcolors;
	Display	*display = xvdisplay->display;
	int	screen = XDefaultScreen(display);


	if (xvdisplay->read_only == True)
	{
	   /*
	    *  Request a read-only color since the private colormap is
	    *  false.
	    */
	   if (!XAllocColor(display, xvdisplay->colormap, &xcolors[index]))
	   {
	      /*
	       *  If no more colors are available in this colormap, check to
	       *  see if this colormap is the default colormap, if so then
	       *  create a private colormap with our already allocated entries
	       *  and try again.  If the colormap is not the default then call
	       *  xvd_closest_color() to find the closest available color. 
	       */
	      if (xvdisplay->colormap == XDefaultColormap(display, screen) &&
		  xvdisplay->private_colormap == True)
	      {
		 colormap_change = xvd_copy_colormap_and_free(xvdisplay);
		 xvd_allocate_color(xvdisplay, index);
	      }
	      else xvd_closest_color(xvdisplay, index);
	   }
	   else
	   {
	      xvdisplay->pixelalloc++;
	      xvdisplay->active[index] = True;
	   }
	}
	else
	{
	   /*
	    *  Request a read/write color since the private colormap is
	    *  false.
	    */
	   /* display kludge */
	   if (CurrentCells < MaxCells || xvdisplay->truecolor == True)
	   {
	      status = XAllocColorCells(display, xvdisplay->colormap, True,
				planes, 0, &pixel, 1);
	   }
	   else
	   {
	      status = False;
	   }
#ifdef DEBUG
if(status)
printf("Alloc passed color (index %d) (pixel %d)\n",index, pixel);
else
printf("Alloc failed color (index %d) (pixel %d)\n",index, pixel);
#endif
	   /* if (status == False || pixel >= MAX_PIXELS) */

	   if (status == False)
	   {
	      /*
	       *  If no more colors are available in this colormap, check to
	       *  see if this colormap is the default colormap, if so then
	       *  create a private colormap with our already allocated entries
	       *  and try again.  If the colormap is not the default then call
	       *  xvd_closest_color() to find the closest available color. 
	       */
	      if (xvdisplay->colormap == XDefaultColormap(display, screen) &&
		  xvdisplay->private_colormap == True)
	      {
		 colormap_change = xvd_copy_colormap_and_free(xvdisplay);
		 (void) xvd_allocate_color(xvdisplay, index);
	      }
	      else
		 (void) xvd_closest_color(xvdisplay, index);
	   }
	   else
	   {
	      /* display kludge */
	      CurrentCells++;

	      xvdisplay->pixelalloc++;
	      xvdisplay->active[index] = True;
	      xcolors[index].pixel = pixel;
	      xcolors[index].flags = DoRed | DoGreen | DoBlue;
	      XStoreColor(display, xvdisplay->colormap, &xcolors[index]);
	   }
	}
	return(colormap_change);
}



/************************************************************
*
*  MODULE NAME: xvd_copy_colormap_and_free
*
*      PURPOSE: Kludge number two.....  It turns out that OpenWindows
*		2.0 has a bug as well with the XCopyColormapAndFree()
*		routine.  But instead it core dumpes the server, which
*		makes it difficult to get around.  Instead of trying
*		back flips and hand stands i've decided to just penalize
*		only the users running OpenWindows.
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*
*       OUTPUT: none...
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_copy_colormap_and_free(xvdisplay)

DisplayStructure *xvdisplay;
{
	int	 i;
	Colormap new;
	Display  *display = xvdisplay->display;
	Visual   *visual  = xvdisplay->visual;
	XColor   *xcolors = xvdisplay->xcolors;
	Colormap colormap = xvdisplay->colormap;
	Window   rootwindow = XDefaultRootWindow(display);


#ifdef OW_OVERRIDE
	int OW_over = True;
#else
	int OW_over = False;
#endif

	/*
	 *  See if we are running OpenWindows 2.0...
	 */
	if (VendorRelease(display) == 2000 && OW_over == False &&
	    strcmp("X11/NeWS - Sun Microsystems Inc.",
	    ServerVendor(display)) == 0)
	{
	   /*
	    *  Oh crap!!! The users running OpenWindows 2.0 which means if
	    *  we call XCopyColormapAndFree() we run the risk of core dumping
	    *  the server....
	    */
	   new = XCreateColormap(display, rootwindow, visual, AllocNone);
	   xvdisplay->colormap = new;
	   for (i = 0; i < MAX_PIXELS; i++)
	   {
	      if (xvdisplay->active[i] == True)
	      {
		 CurrentCells--;
		 XFreeColors(display, colormap, &xcolors[i].pixel, 1, 0);
		 xvdisplay->active[i] = False;
		 (void) xvd_allocate_color(xvdisplay, i);
	      }
	   }
	   return(False);
	}
	else
	{
	   /*
	    *  No problem...  Just call XCopyColormapAndFree() since we won't
	    *  core dump the server.  All other server seem to be able to
	    *  XCopyColormapAndFree() pixels from the default colormap,
	    *  although they do get confused about the number of available
	    *  pixels, but that was what Kludge number one was all about :-).
	    */
	   new = XCopyColormapAndFree(display, xvdisplay->colormap);
	   xvdisplay->colormap = new;
	   return(True);
	}
}



/************************************************************
*
*  MODULE NAME: xvd_closest_color
*
*      PURPOSE: Go thru the color array looking for the closest
*		color.  We do this by querying the current colormap
*		and doing a absolute color difference to find the
*		closest color.  If the colormap is private then
*		we find the closest color in the color arrays only.
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*		index     - the index of the xcolor to be found
*
*       OUTPUT: returns True if we are successful, otherwise False.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvd_closest_color(xvdisplay, index)

DisplayStructure *xvdisplay;
int	index;
{
	Pixel	pixel, j;
	XColor	temp[MAX_PIXELS];
	int     i, read_only, diff, tmpdiff, tmpixel;

	Display	*display = xvdisplay->display;
	XColor  *xcolors = xvdisplay->xcolors;


	/*
	 *  Get the current colors in the colormap.
	 */
	if (xvdisplay->read_only == True)
	{
	   for (i = 0; i < ncolors; i++)
	   {
	      temp[i].pixel = i;
	      temp[i].flags = DoRed | DoGreen | DoBlue;
	   }
	   XQueryColors(display, xvdisplay->colormap, temp, ncolors);
	   read_only = True;
	}
	else
	   read_only = False;

	/*
	 *  Now race thru the list to find the closest color.
	 */
	diff = MAX_INTEN*3+1;
	for (i = 0; i < ncolors; i++)
	{
	   if (read_only == True)
	   {
	      tmpdiff = abs((int) temp[i].red   - xcolors[index].red) +
	                abs((int) temp[i].green - xcolors[index].green) +
		        abs((int) temp[i].blue  - xcolors[index].blue);
	      tmpixel = temp[i].pixel;
	   }
	   else
	   {
	      if (xvdisplay->active[i] == True)
	      {
	         tmpdiff = abs((int) xcolors[i].red   - xcolors[index].red) +
	                abs((int) xcolors[i].green - xcolors[index].green) +
		        abs((int) xcolors[i].blue  - xcolors[index].blue);
		 tmpixel = xvdisplay->xcolors[i].pixel;
	      }
	      else
	      {
		 continue;
	      }
	   }

	   if (tmpdiff < diff)
	   {
	      diff  = tmpdiff;
	      pixel = tmpixel;
	   }
	}

	/*
	 *  Since we are making this xcolor point to an existing pixel, we
	 *  should go ahead and add this color's histogram to that of the
	 *  pixel's histogram.  We should also make this pixel not active
	 *  so that application program won't try and free or store colors
	 *  into a different xcolor entry.
	 */
	xcolors[index].pixel     = pixel;
	xvdisplay->active[index] = False;

#ifdef DEBUG
printf("pixel %d  total diff %d.  absolute red %d green %d blue %d\n", pixel, diff, abs((int) temp[pixel].red - xcolors[index].red), abs((int) temp[pixel].green - xcolors[index].green), abs((int) temp[pixel].blue - xcolors[index].blue));
#endif
}



/************************************************************
*
*  MODULE NAME: xvd_add_xvdisplay_list
*
*      PURPOSE: Add the xvdisplay the internal xvdisplay list.
*
*        INPUT: xvdisplay - the xvdisplay structure to be updated.
*
*       OUTPUT: returns True if we are successful, otherwise False.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_add_xvdisplay_list(xvdisplay)

DisplayStructure *xvdisplay;
{
	DisplayList *temp;


	if ((temp = (DisplayList *) calloc(1, sizeof(DisplayList))) == NULL)
	   return(False);

	temp->xvdisplay = xvdisplay;
	temp->next      = xvdisplay_list;
	xvdisplay_list  = temp;
	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_delete_xvdisplay_list
*
*      PURPOSE: Delete the xvdisplay the internal xvdisplay list.
*
*        INPUT: xvdisplay - the xvdisplay structure to be deleted.
*
*       OUTPUT: returns True if we are successful, otherwise False.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_delete_xvdisplay_list(xvdisplay)

DisplayStructure *xvdisplay;
{
	DisplayList *current, *last;


	last = current = xvdisplay_list;
	while (current != NULL)
	{
	   if (current->xvdisplay == xvdisplay)
	      break;

	   last = current;
	   current = current->next;
	}

	if (current == NULL)
	   return(False);

	if (xvdisplay_list == current)
	   xvdisplay_list = xvdisplay_list->next;
	else
	   last->next = current->next;

	free(current);
	return(True);
}
