 /*
  * Khoros: $Id: ximage.c,v 1.3 1991/12/18 09:15:26 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: ximage.c,v 1.3 1991/12/18 09:15:26 dkhoros Exp $";
#endif

 /*
  * $Log: ximage.c,v $
 * Revision 1.3  1991/12/18  09:15:26  dkhoros
 * HellPatch3
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 *            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"
#include "vrev.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>			ximage Routines				<<<<
   >>>>								<<<<
   >>>>	 Public Routines:					<<<<
   >>>>			xvd_create_ximage()			<<<<
   >>>>			xvd_create_pixmap()			<<<<
   >>>>			xvd_shrink_ximage()			<<<<
   >>>>								<<<<
   >>>>	 Private Routines:					<<<<
   >>>>			_xvd_load_ximage()			<<<<
   >>>>			_xvd_update_ximage()			<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


/************************************************************
*
*  MODULE NAME: _xvd_load_ximage
*
*      PURPOSE: This routine loads the pixmap
*
*	 INPUT: xvdisplay - pointer to a DisplayStructure
*		image	  - viff image
*		ximage	  - XImage
*		pixamp    - pixmap to be loaded
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int _xvd_load_ximage(xvdisplay, image, ximage, pixmap)

DisplayStructure *xvdisplay;
struct	 xvimage *image;
XImage	 **ximage;
Pixmap	 *pixmap;
{
	Pixmap	  newpixmap;
	XImage	  *newximage;


	if (!(newximage = xvd_create_ximage(xvdisplay, image, NULL)))
	   return(False);

	if (pixmap != NULL)
	   newpixmap = xvd_create_pixmap(xvdisplay, newximage, NULL);

	/*
	 *  Either assign or free the temporary Ximage and Pixmap resources.
	 */
	if (ximage != NULL)
	   *ximage = newximage;
	else
	   XDestroyImage(newximage);

	if (pixmap != NULL)
	   *pixmap = newpixmap;

	return(True);
}



/************************************************************
*
*  MODULE NAME: _xvd_update_ximage
*
*      PURPOSE: This routine updates the ximage and pixmap
*		from the viff image.  The xvdisplay struture
*		supplies information about the image we are
*		trying to update.
*
*	 INPUT: xvdisplay - pointer to a DisplayStructure
*		image	  - viff image
*		ximage	  - XImage
*		pixamp    - pixmap to be loaded
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int _xvd_update_ximage(xvdisplay, image, ximage, pixmap)

DisplayStructure *xvdisplay;
struct	 xvimage *image;
XImage	 **ximage;
Pixmap	 *pixmap;
{
	unsigned int width, height, depth, dummy;

	Pixmap	 newpixmap;
	XImage   *newximage;
	Display  *display = xvdisplay->display;


	/*
	 * We need to update the ximage and pixmap for the supplied
	 * image, but we need to make sure that the supplied has not
	 * changed such that we will need to rebuild it.  We would
	 * need to rebuild if the image size has changed or if it's
	 * depth has changed.
	 */
	newximage = NULL;
	if (ximage != NULL)
	{
	   if (*ximage != NULL)
	      newximage = *ximage;
	}

	newpixmap = None;
	if (pixmap != NULL)
	{
	   if (*pixmap != None)
	      newpixmap = *pixmap;
	}


	/*
	 *  check to see if the image is NULL, if so then free
	 *  the ximage and pixmap resources.
	 */
	if (image == NULL)
	{
	   if (newximage != NULL)
	   {
	      XDestroyImage(newximage);
	      *ximage = NULL;
	   }
	   if (newpixmap != None)
	   {
	      XFreePixmap(display, newpixmap);
	      *pixmap = None;
	   }
	   return(True);
	}

	/*
	 *  since the image is not NULL then let's get it's geometry so that
	 *  we can see how to update it.
	 */
	if (newximage != NULL)
	{
	   width  =  newximage->width;
	   height =  newximage->height;
	   depth  =  newximage->depth;
	}
	else if (newpixmap != None)
	{
	   if (!XGetGeometry(display, *pixmap, (Window *) &dummy,(int *) &dummy,
		 (int *) &dummy, &width, &height, &dummy, &depth))
	   {
	      return(False);
	   }
	}
	else
	{
	   /*
	    *  Since both the ximage and pixmap are not being used they
	    *  must want to create new ones.
	    */
	   if (ximage == NULL && pixmap == NULL)
	      return(False);
	   else
	      return(_xvd_load_ximage(xvdisplay, image, ximage, pixmap));
	}

	if ((width != image->row_size || height != image->col_size) ||
	         (image->data_storage_type == VFF_TYP_BIT && depth != 1) ||
	         (image->data_storage_type == VFF_TYP_1_BYTE && depth == 1))
	{
	   /*
	    *  Either the size or the type of image has changed so we better
	    *  reload everything.
	    */
	   if (newximage != NULL) XDestroyImage(newximage);
	   if (newpixmap != NULL) XFreePixmap(display, newpixmap);
	   return(_xvd_load_ximage(xvdisplay, image, ximage, pixmap));
	}

	/*
	 *  Otherwise we need to update the current ximage and pixmap with
	 *  the new image.
	 */
	if (!(newximage = xvd_create_ximage(xvdisplay, image, newximage)))
	   return(False);

	if (newpixmap != NULL)
	   (void) xvd_create_pixmap(xvdisplay, newximage, newpixmap);

	/*
	 *  If the ximage is NULL then the newximage was created by us and
	 *  should be freed.
	 */
	if (ximage == NULL)
	   XDestroyImage(newximage);

	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_create_ximage
*
*      PURPOSE: This routine creates an ximage structure
*		according to a display structure and viff
*		image.
*
*	 INPUT: xvdisplay - pointer to a DisplayStructure
*		image	  - viff image
*
*	OUTPUT: return the new ximage structure or NULL upon
*		failure.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


XImage *xvd_create_ximage(xvdisplay, image, ximage)

DisplayStructure *xvdisplay;
struct   xvimage *image;
XImage   *ximage;
{
	register int x, y;
	register unsigned long pixel, *ldata;
	register unsigned char *red, *green, *blue, *data, *orig;

	double   value;
	char	 error[MaxLength];
	unsigned int  width, height;
	int	 i, src, size, bit_order, image_type;

	Display *display = xvdisplay->display;
	Visual  *visual  = xvdisplay->visual;
	int	depth    = xvdisplay->depth;
	XColor  *xcolors = xvdisplay->xcolors;


	/*
	 *  Make sure there is a image to create an ximage from.
	 */
	if (image == NULL)
	   return(NULL);

	if (ximage != NULL)
	{
	   if (ximage->depth != xvdisplay->depth)
	   {
	      XDestroyImage(ximage);
	      ximage = NULL;
	   }
	}
	width  = image->row_size;
	height = image->col_size;
	image_type = image->data_storage_type;

	/*
	 *  Check to see if the image is already allocated
	 */
	if (ximage == NULL)
	{
	   if (image_type == VFF_TYP_BIT)
	      depth = 1;

	   if (!(ximage = XCreateImage(display, visual, depth, ZPixmap,
			0, NULL, width, height, 8, 0)))
	   {
	      return(NULL);
	   }
	   size = ximage->bytes_per_line * height;
	   ximage->data = (char *) XtMalloc((unsigned) size);
	}

	if (xvdisplay->truecolor == True && xvd_truecolor_image(image) == True)
	{
	   red   = (unsigned char *) &image->imagedata[0];
	   green = (unsigned char *) &image->imagedata[width*height];
	   blue  = (unsigned char *) &image->imagedata[width*height*2];

	   if (ximage->bits_per_pixel == 32 && visual->bits_per_rgb == 8)
	   {
	      ldata = (unsigned long *) ximage->data;

	      for (i = 0; i < width*height; i++)
	      {
		  *ldata++ = (xcolors[*red++].pixel & visual->red_mask) |
			     (xcolors[*green++].pixel & visual->green_mask) |
			     (xcolors[*blue++].pixel & visual->blue_mask);
	      }

	      src = getmachorder(machtype(NULL));
	      if ((src == VFF_DEP_BIGENDIAN && ximage->byte_order == LSBFirst)||
		  (src == VFF_DEP_LITENDIAN && ximage->byte_order == MSBFirst))
	      {
	         convert_order(ximage->data,VFF_DEP_BIGENDIAN,VFF_DEP_LITENDIAN,
			width*height, ximage->bits_per_pixel >> 3);
	      }
	   }
	   else
	   {
	      for (y = 0; y < height; y++)
	      {
	         for (x = 0; x < width; x++)
	         {
		    pixel = (xcolors[*red++].pixel & visual->red_mask) |
			    (xcolors[*green++].pixel & visual->green_mask) |
			    (xcolors[*blue++].pixel & visual->blue_mask);
		    XPutPixel(ximage, x, y, pixel);
	         }
	      }
	   }
	}
	else if (xvdisplay->truecolor == True && visual->bits_per_rgb == 8 &&
		 image_type == VFF_TYP_1_BYTE && ximage->bits_per_pixel == 32)
	{
	   ldata = (unsigned long *) ximage->data;
	   orig  = (unsigned char *) image->imagedata;

	   for (i = 0; i < width*height; i++)
	      *ldata++ = (unsigned long) xcolors[*orig++].pixel;

	   src = getmachorder(machtype(NULL));
	   if ((src == VFF_DEP_BIGENDIAN && ximage->byte_order == LSBFirst) ||
	       (src == VFF_DEP_LITENDIAN && ximage->byte_order == MSBFirst))
	   {
	      convert_order(ximage->data, VFF_DEP_BIGENDIAN, VFF_DEP_LITENDIAN,
			width*height, ximage->bits_per_pixel >> 3);
	   }
	}
	else if (image_type == VFF_TYP_1_BYTE && ximage->bits_per_pixel == 8)
	{
	   data = (unsigned char *) ximage->data;
	   orig = (unsigned char *) image->imagedata;
	   for (i = 0; i < width*height; i++)
	      *data++ = (unsigned char) xcolors[*orig++].pixel;
	}
	else if (image_type == VFF_TYP_BIT)
	{
	   ximage->bitmap_unit = 8;
	   ximage->bitmap_bit_order = LSBFirst;
	   size = ximage->bytes_per_line * height;

	   /*
	    *  Byte copy the image data into ximage data.
	    */
	   bcopy(image->imagedata, ximage->data, size);
	}
	else
	{
	   for (y = 0; y < height; y++)
	   {
	      for (x = 0; x < width; x++)
	      {
	         if (xvd_image_value(image, x, y, 0, &value))
	            XPutPixel(ximage, x, y, xcolors[(int) value].pixel);
	      }
	   }
	}
	return(ximage);
}



/************************************************************
*
*  MODULE NAME: xvd_create_pixmap
*
*      PURPOSE: This routine creates an pixmap structure
*		according to a display structure and ximage.
*
*	 INPUT: xvdisplay - pointer to a DisplayStructure
*		ximage	  - ximage structure
*
*	OUTPUT: return the new pixmap or None upon failure.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

Pixmap xvd_create_pixmap(xvdisplay, ximage, pixmap)

DisplayStructure *xvdisplay;
XImage		 *ximage;
Pixmap		 pixmap;
{

	GC	 gc;
	unsigned int dummy;

	Display  *display = xvdisplay->display;
        Window   rootwindow = XDefaultRootWindow(display);


	/*
	 *  Make sure there is a ximage to create a pixmap from.
	 */
	if (ximage == NULL)
	   return(NULL);

	/*
	 *  if the pixmap is None then we need to create a new one before
	 *  trying to store a new image into it.
	 */
	if (pixmap == None)
	{
	   pixmap = XCreatePixmap(display, rootwindow, ximage->width,
		ximage->height, ximage->depth);

	   if (!XGetGeometry(display, pixmap, (Window *) &dummy,(int *) &dummy, 
		 (int *) &dummy, &dummy, &dummy, &dummy, &dummy))
	   {
	      return(None);
	   }
	}

	gc = XCreateGC(display, pixmap, (unsigned long) 0, (XGCValues *) 0);
	XPutImage(display, pixmap, gc, ximage, 0, 0, 0, 0, ximage->width,
		ximage->height);
	XFreeGC(display, gc);
	return(pixmap);
}



/************************************************************
*
*  MODULE NAME: xvd_shrink_ximage
*
*      PURPOSE: Takes the ximage that is contained in the xvdisplay structure,
*		and returns a shrinked ximage of the desired width and 
*		height.  This is commonly used when creating an icon
*		or image to zoom in.
*
*        INPUT: display - pointer to the X display structure
*		ximage  - the ximage to subsampled
*		desired_width - desired width of the subsampled image
*		desired_height - desired height of the subsampled image
*		subximage - a pointer used to indicate whether we should
*			    use or create the subsampled ximage.
*
*       OUTPUT:	the subsampled ximage
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young 
*
*
*************************************************************/


XImage *xvd_shrink_ximage(display, ximage, desired_width, desired_height,
			  subximage)

Display *display;
XImage  *ximage, *subximage;
int	desired_width, desired_height;
{
	char	   error[MaxLength];
	double	   x, y, xstep, ystep;
	register   unsigned long pixel;
	int        i, j; 
        int 	   width, height;


	/*
	 *  Make sure there is a ximage to create a sub-sampled ximage from.
	 */
	if (ximage == NULL)
	   return(NULL);

	width  = MIN(desired_width,  ximage->width);
	height = MIN(desired_height, ximage->height);
	xstep = ximage->width/((double) width);
	ystep = ximage->height/((double) height);

	if (subximage != NULL)
	{
	   if (subximage->width != width || subximage->height != height)
	   {
	      XDestroyImage(subximage);
	      subximage = NULL;
	   }
	}

	if (subximage == NULL)
	{
	   if ((subximage = XSubImage(ximage, 0, 0, (unsigned int) width, 
				      (unsigned int) height)) == NULL)
	      return(NULL);
	}

	/*
	 *  Sub-sample the byte ximage
	 */
	y = 0.0;
	for (j = 0; j < height; j++)
	{
	   x = 0.0;
	   for (i = 0; i < width; i++)
	   {
	      pixel = XGetPixel(ximage, (int) x, (int) y);
	      XPutPixel(subximage, i, j, pixel);
	      x += xstep;
	   }
	   y += ystep;
	}
	return(subximage);
}
