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

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

 /*
  * $Log: image.c,v $
 * Revision 1.4  1992/03/20  22:48:56  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"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>            Image Display Utility Routines             <<<<
   >>>>                                                       <<<<
   >>>>		    xvd_init_image()			      <<<<
   >>>>		    xvd_delete_image()			      <<<<
   >>>>		    xvd_convert_image()			      <<<<
   >>>>		    xvd_dither_image()		      	      <<<<
   >>>>		    xvd_compress_image()		      <<<<
   >>>>		    xvd_compress_grey_image()		      <<<<
   >>>>		    xvd_compress_color_image()		      <<<<
   >>>>		    xvd_seperate_images()		      <<<<
   >>>>		    xvd_update_position()		      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



/************************************************************
*
*  MODULE NAME: xvd_init_image
*
*      PURPOSE: takes a viff image, and prepares it to be displayed
*		as an XImage under X Windows, version 11 R4  (Obsolete:
*		Please use xvd_init_xvdisplay()).
*
*	 INPUT: display - the display for which we will be initializing
*		image   - the viff display image
*		shape   - the viff shape bitmap
*		clip    - the viff clip bitmap
*		overlay - the viff overlay image
*		private_cmap   - create a private colormap (if needed)
*		read_only - create read only color cells
*		shape_parent - whether the shape mask should be expanded
*			       so as not to shape it's parent
*
*       OUTPUT: the DisplayStructure
*
*    CALLED BY: the application program *
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


DisplayStructure *xvd_init_image(display, image, shape, clip, overlay,
			 private_cmap, read_only, shape_parent, colormap)

Display	       *display;
struct xvimage *image, *shape, *clip, *overlay;
int	       private_cmap, read_only;
Colormap       colormap;
{
	DisplayStructure *xvdisplay;


	xvdisplay = xvd_init_xvdisplay(display, image, shape, clip, overlay,
                         private_cmap, read_only, shape_parent, colormap);
	if (xvdisplay == NULL)
	   return(NULL);

	if (xvd_build_xvdisplay(xvdisplay) == False)
	{
	   (void) xvd_delete_xvdisplay_list(xvdisplay);
	   XtFree(xvdisplay);
	   return(NULL);
	}
	return(xvdisplay);
}



/************************************************************
*
*  MODULE NAME: xvd_delete_image
*
*      PURPOSE: deletes and free's all associated resources and
*		memory of and xvdisplay structure.  (Obsolete:
*		Please use xvd_delete_xvdisplay()).
*
*	 INPUT: xvdisplay - the xvdisplay structure to be freed.
*
*       OUTPUT: none
*
*    CALLED BY: the application program *
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

xvd_delete_image(xvdisplay)

DisplayStructure *xvdisplay;
{
	xvd_delete_xvdisplay(xvdisplay);

}  /* end xvd_delete_image */



/************************************************************
*
*  MODULE NAME:  xvd_convert_image
*
*      PURPOSE:  This routine is used to convert a non byte
*		 or bit image.  The routine automatically
*		 converts the image to byte.
*
*       OUTPUT:  the converted image
*
*    CALLED BY:  internal routine
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


int xvd_convert_image(xvdisplay)

DisplayStructure *xvdisplay;
{
	char   mesg[MAXBUF];
	struct xvimage *image;
	static int first = True;


	/*
	 *  Need to inform the user that the image is not in a displayable
	 *  format and see if they wish to continue.  We also need to prompt
	 *  them for the scale and normalization factor.
	 *
	 *  also need to convert known grey or rgb images to rgb.
	 */
	if (first)
	{
           (void) sprintf(mesg, "Can only display BYTE images.  Do you wish \
to convert the image?... the image will be normalized to 255.\n\nnote: if you \
answer yes then images will be automatically converted from now on");

	   if (!xvf_warn_wait(mesg, "xvd_convert_image", "Yes", "No"))
	      return(FALSE);
	   else
	      first = False;
	}

	/*
	 *  Call lvconvert() to convert the image to byte format and
	 *  normalization factor of 255.
	 */
	if ((image = copyimage(xvdisplay->image)) == NULL)
	   image = xvdisplay->image;
	else
	   xvdisplay->disp_image = image;

	if (!lvconvert(image, VFF_TYP_1_BYTE, True, False, 255.0, 1.0, False))
	{
	   xvf_error_wait("lvconvert failed!  Unable to convert image.",
			  "xvd_convert_image", NULL);
	   return(FALSE);
	}

	/*
	 *  If the converted image is the same as the original image then
	 *  we were forced to convert the original.  We need to inform the
	 *  user that the original image was modified.
	 */
	if (xvdisplay->image == image)
	{
	   xvf_error_wait("Warning!  Unable to make a copy of the original \
image.  The original image has been converted instead, which will be reflected \
when examining the image data.", "xvd_convert_image", NULL);
	}
	return(TRUE);

} /* end xvd_convert_image */



/************************************************************
*
*  MODULE NAME:  xvd_dither_image
*
*      PURPOSE:  This routine is used to dither a byte image.
*		 This is done when trying to display a non-
*		 dithered image on a monochrome display.		 
*
*       OUTPUT:  the dithered image
*
*    CALLED BY:  internal routine
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

int xvd_dither_image(xvdisplay)

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


	if (image->data_storage_type == VFF_TYP_BIT)
	{
	   return(TRUE);
	}
	else if (image->data_storage_type != VFF_TYP_1_BYTE)
	{
	   /* error has occured */
	   return(FALSE);
	}

	/*
	 *  Need to inform the user that only dithered images can be
	 *  displayed.  The user is  then prompted if they wish to
	 *  continue, and if so to choose the dither technique to use.
	 */
        (void) sprintf(mesg, "Image is a byte image, but this terminal can only\
 display monochrome images.  Do you wish to dither the image?\n");
	if (!xvf_warn_wait(mesg, "xvd_dither_image", NULL, NULL))
	{
	   return(FALSE);
	}

	/*
	 *  Make sure that the display image is not the same as the original
	 *  image.  If so then we need to make a copy before dithering.
	 */
	if (image == xvdisplay->image)
	{
	   if ((image = copyimage(xvdisplay->image)) == NULL)
	      image = xvdisplay->image;
	   else
	       xvdisplay->disp_image = image;
	}

	if (image->map_row_size > 1)
	{
	   if (!lvmsquish(image, 1, TRUE))
	   {
	      xvf_error_wait("lvmsquish failed!  Unable to prepare image for\
 dithering.", "xvd_dither_image", NULL);
	      return(FALSE);
	   }
	}
	else if (image->map_scheme != VFF_MS_NONE)
        {
	   if (!lvmapdata(image))
	   {
	      xvf_error_wait("lvmapdata failed!  Unable to prepare image for\
 dithering.", "xvd_dither_image", NULL);
	      return(FALSE);
	   }
	}

	if (!lverrdif(image))
	{
	   xvf_error_wait("lverrdif failed!  Unable to dither image.",
			  "xvd_dither_image", NULL);
	   return(FALSE);
	}

	/*
	 *  If the dithered image is the same as the original image then
	 *  we were forced to dither the original.  We need to inform the
	 *  user that the original image was modified.
	 */
	if (xvdisplay->image == image)
	{
	   xvf_error_wait("Warning!  Unable to make a copy of the original \
image.  The original image has been converted instead, which will be reflected \
when examining the image data.", "xvd_convert_image", NULL);
	}
	return(TRUE);

} /* end xvd_dither_image */



/************************************************************
*
*  MODULE NAME:  xvd_compress_image
*
*      PURPOSE:  This routine is used to compress grey or color an
*		 image.  This is done when the number of colors
*		 in an image is more than the screen can display.
*
*	 INPUT:  xvdisplay - pointer to the display structure
*		 ncolors   - the maximum number of colors that can
*			     be displayed
*       OUTPUT:  the compressed image
*
*    CALLED BY:  internal routine
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


int xvd_compress_image(xvdisplay, ncolors)

DisplayStructure *xvdisplay;
int		 ncolors;
{
	struct  xvimage *image = xvdisplay->disp_image;


	/*
	 *  Make sure that the display image is not the same as the original
	 *  image.  If so then we need to make a copy before compressing.
	 */
	if (image == xvdisplay->image && image->map_scheme != VFF_MS_NONE)
	{
	   if ((image = copyimage(xvdisplay->image)) == NULL)
	      image = xvdisplay->image;
	   else
	       xvdisplay->disp_image = image;
	}

	if ((image->map_row_size == 1 || image->map_scheme == VFF_MS_NONE) &&
	    (image->num_data_bands != 3 && !(image->color_space_model ==
	     VFF_CM_ntscRGB || image->color_space_model == VFF_CM_genericRGB)))
        {
	   if (!xvd_compress_grey_image(xvdisplay, ncolors))
	      return(FALSE);
	   else
	      return(TRUE);
	}
	else
	{
	   if (!xvd_compress_color_image(xvdisplay, ncolors))
	      return(FALSE);
	   else
	      return(TRUE);
	}

} /* end xvd_compress_image */



/************************************************************
*
*  MODULE NAME:  xvd_compress_grey_image
*
*      PURPOSE:  This routine is used to compress a grey
*		 image.  This is done when the number of colors
*		 in an image is more than the screen can display.
*
*		 The image is compressed by calling "vgamut" to
*		 compress down to the desired number of colors.
*
*       OUTPUT:  the compressed image
*
*    CALLED BY:  internal routine
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


int xvd_compress_grey_image(xvdisplay, ncolors)

DisplayStructure *xvdisplay;
int		 ncolors;
{
	char	command[MAXBUF], *input, *output;
	struct	xvimage *readimage();

	struct  xvimage *image = xvdisplay->disp_image;

	if (image->map_scheme != VFF_MS_NONE)
	{
	   if (!lvmapdata(image))
	   {
	      xvf_error_wait("lvmapdata failed!  Unable to compress colors.",
			     "xvd_compress_grey_image", NULL);
	      return(FALSE);
	   }
	}

	input  = vtempnam("imagein");
	output = vtempnam("imageout");
	(void) sprintf(command,"v1bgamut -i %s -o %s -n %d", input, output,
		       ncolors);

	if (!writeimage(input, image))
	{
	   xvf_error_wait("writeimage failed! Unable to write temporary image\
 file.", "xvd_compress_grey_image", NULL);
	   return(FALSE);
	}
	system(command);

	image = readimage(output);
	unlink(input);
	unlink(output);

	if (image == NULL)
	{
	   xvf_error_wait("readimage failed! Unable to read temporary image\
 file.", "xvd_compress_grey_image", NULL);
	   return(FALSE);
	}
	else
	{
	   if (xvdisplay->disp_image != xvdisplay->image &&
	       xvdisplay->disp_image != NULL)
	   {
	      freeimage(xvdisplay->disp_image);
	   }
	   xvdisplay->disp_image = image;
	}
	return(TRUE);

} /* end xvd_compress_grey_image */



/************************************************************
*
*  MODULE NAME:  xvd_compress_color_image
*
*      PURPOSE:  This routine is used to compress a color
*		 image.  This is done when the number of colors
*		 in an image is more than the screen can display.
*
*		 The image is compressed by calling "vgamut" to
*		 compress down to the desired number of colors.
*
*       OUTPUT:  the compressed image
*
*    CALLED BY:  internal routine
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


int xvd_compress_color_image(xvdisplay, ncolors)

DisplayStructure *xvdisplay;
int              ncolors;
{
	char	command[MAXBUF], *input, *output;
	struct	xvimage *images[3], *readimage();

	struct  xvimage *image = xvdisplay->disp_image;

	if (image->map_scheme != VFF_MS_NONE)
	{
	   if (!lvmapdata(image))
	   {
	      xvf_error_wait("lvmapdata failed!  Unable to compress colors.",
			     "xvd_compress_color_image", NULL);
	      return(FALSE);
	   }
	}

	if (image->num_data_bands == 1)
	{
	   images[0] = (struct xvimage *) image;
	   images[1] = (struct xvimage *) image;
	   images[2] = (struct xvimage *) image;

	   if (!lvbandcomb(&image, images, 3, 0, VFF_CM_NONE))
	   {
	      xvf_error_wait("lvbandcomb failed! Unable to compress colors.",
			     "xvd_compress_color_image", NULL);
	      return(FALSE);
	   }
	}
	input  = vtempnam("imagein");
	output = vtempnam("imageout");
	(void) sprintf(command,"vgamut -i %s -o %s -n %d -p 8", input, output,
		       ncolors);

	if (!writeimage(input, image))
	{
	   xvf_error_wait("writeimage failed! Unable to write temporary image\
 file.", "xvd_compress_color_image", NULL);
	   return(FALSE);
	}
	system(command);

	image = readimage(output);
	unlink(input);
	unlink(output);

	if (image == NULL)
	{
	   xvf_error_wait("readimage failed! Unable to read temporary image\
 file.", "xvd_compress_color_image", NULL);
	   return(FALSE);
	}
	else
	{
	   if (xvdisplay->disp_image != xvdisplay->image &&
	       xvdisplay->disp_image != NULL)
	   {
	      freeimage(xvdisplay->disp_image);
	   }
	   xvdisplay->disp_image = image;
	}
	return(TRUE);

} /* end xvd_compress_color_image */



/************************************************************
*
*  MODULE NAME:  xvd_seperate_images
*
*      PURPOSE:  This routine is used to create an array of
*		 images from a single multi-band image.  It
*		 is similar to the lvbandsprt, but it will create
*		 images instead of a single band per image.  This
*		 is what allows animate to distinguish when it
*		 should display multiple RGB images rather than
*		 animate each band of each image.  This is being
*		 to referred as the seperate display images, rather
*		 than each seperate band of the entire multi-band
*		 image.
*
*        INPUT:  image - the image to be seperated into seperate
*			 display images.
*
*       OUTPUT:  num_images - the number of images
*		 and returns an array of images or NULL upon failure
*
*    CALLED BY:  application programmer
*
*   WRITTEN BY:  Tom Sauer & Mark Young
*
*
*************************************************************/


struct xvimage **xvd_seperate_images(image, num_images)

struct xvimage	 *image;
int              *num_images;
{
	struct  xvimage **images, **create_images_list();
	int     i, num, num_data_bands, data_size, dcnt, map_size,
		mcnt, lsize, lcnt;


	/*
	 *  Make sure there is an image to seperate
	 */
	if (image == NULL)
	   return(NULL);

        /*
	 *  go compute the image size and map size in bytes
	 */
        if (!imagesize(image, &data_size, &dcnt, &map_size, &mcnt, &lsize,
		       &lcnt))
        {
	   xvf_error_wait("imagesize failed!  Unable to seperate images.",
			  "xvd_seperate_images", NULL);
           return(NULL);
        }

	/*
	 *  If this is a true color image then we will need to divide the image
	 *  into bands of three.
	 */
	if (xvd_truecolor_image(image))
	   num_data_bands = 3;
	else
	   num_data_bands = 1;


        /*
	 * want the data_size for each band, not the total image data size
         * so divide by the number of image bands
	 */
	num = image->num_data_bands/num_data_bands;
        data_size /= num;

	/*
	 *  Create the array of images in which to store the images.
	 */
	if ((images = create_images_list(num)) == NULL)
	{
	   xvf_error_wait("create_images_list failed!  Unable to create \
seperate images in which to store the different images bands.",
			  "xvd_seperate_images", NULL);
	   return(NULL);
	}

        /*
	 * if the map scheme is ONEPERBAND then only want the size of each
         * map, not the total map size
	 */
        if (image->map_scheme == VFF_MS_ONEPERBAND) 
           map_size /= image->num_data_bands;

        /*
	 * loop through all data bands and separate them out into
         * one band images, process the maps and copy the image
         * header from the input image.
 	 */
        for (i = 0; i < num; i++)
	{
           switch (image->map_scheme)
	   {
              case VFF_MS_NONE:
		   images[i]->maps = NULL;
                   break;

              case VFF_MS_ONEPERBAND:
                   images[i]->maps = &image->maps[map_size*i];
                   break;

              case VFF_MS_SHARED:
                   images[i]->maps = &image->maps[0];
                   break;

	      default:
		   images[i]->maps = NULL;
		   break;
           }
           copyheader(image, images[i]);
           images[i]->num_data_bands = num_data_bands;

           images[i]->location = image->location;
           images[i]->imagedata = &image->imagedata[data_size*i];
        }

	if (num_images != NULL) *num_images = num;
        return(images);
} /* end xvd_seperate_images */



/************************************************************
*
*  MODULE NAME: xvd_update_position
*
*      PURPOSE: event handler which updates the information label 
*		on the position widget according to the position 
*		of the cursor in the raster widget
*
*	 INPUT: widget - the widget that had the event
*		clientdata - used for the xvdisplay struct
*		event - the event that evoked this event handler
*
*       OUTPUT: none
*
*    CALLED BY: internal routine (callback)
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

void xvd_update_position(widget, clientData, event)

Widget  widget;
caddr_t clientData;
XEvent  *event;
{
	DisplayStructure *xvdisplay = (DisplayStructure *) clientData;

        char	    buf[25];
        Arg	    args[2];
	Dimension   xoffset, yoffset;
	int	    i, x, y, allocated;
	double	    value, rgbvalue[3];


	if (xvd_query_position(widget, &x, &y, True))
	   return;

	i = 0;
	XtSetArg(args[i], XtNxoffset, &xoffset);  i++;
	XtSetArg(args[i], XtNyoffset, &yoffset);  i++;
	XtGetValues(xvdisplay->raster, args, i);

	x += xoffset;
	y += yoffset;
	if (xvdisplay->image == NULL)
	{
	   sprintf(buf,"%3d x %3d", x, y);
	}
	else if (xvd_truecolor_image(xvdisplay->image))
	{
	   if (xvd_query_rgbvalue(xvdisplay, x, y, rgbvalue, NULL, NULL))
	   {
	      sprintf(buf,"%3d x %3d  = (%3g %3g %3g) ", x, y, rgbvalue[0],
		      rgbvalue[1], rgbvalue[2]);
	   }
	   else
	      sprintf(buf,"%3d x %3d", x, y);
	}
	else
	{
	   if (xvd_query_value(xvdisplay, x, y, &value, NULL, &allocated))
	   {
	      if (allocated)
	         sprintf(buf,"%3d x %3d  = %3g ", x, y, value);
	      else
	         sprintf(buf,"%3d x %3d  = %3g *", x, y, value);
	   }
	   else
	      sprintf(buf,"%3d x %3d", x, y);
	}

	i = 0;
	XtSetArg(args[i], XtNlabel, buf);	  	i++;
        XtSetValues(xvdisplay->position, args, i);
}
