 /*
  * Khoros: $Id: utils.c,v 1.4 1992/03/20 22:49:17 dkhoros Exp $
  */

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

 /*
  * $Log: utils.c,v $
 * Revision 1.4  1992/03/20  22:49:17  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"
#include "cursors/cross"
#include "cursors/cross_mask"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                         <<<<
   >>>>         Image Display Utilities 			<<<<
   >>>>                                                         <<<<
   >>>>		xvd_initialize					<<<<
   >>>>		xvd_create_cursor				<<<<
   >>>>		xvd_query_position				<<<<
   >>>>		xvd_check_visibility				<<<<
   >>>>		xvd_resize_shape				<<<<
   >>>>		xvd_query_value					<<<<
   >>>>		xvd_query_rgbvalue				<<<<
   >>>>		xvd_image_value					<<<<
   >>>>		xvd_truecolor_image				<<<<
   >>>>                                                         <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  */



/************************************************************
*
*  MODULE NAME: xvd_initialize
*
*      PURPOSE: initializes global variables for use of the xvdisplay
*		library.  This routine creates the raster display cursor,
*		and creates the roi gc used when interactively extracting
*		and inserting a region into the display image.
*
*        INPUT:  display  - the X windows display variable 
*
*       OUTPUT:  none
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

/* display kludge */
extern int MaxCells;
extern int CurrentCells;
extern GC gc_inv, gc_xor;

xvd_initialize(display)

Display *display;
{
	XGCValues	values;
	unsigned long   mask;
	Colormap 	colormap;
	XColor          color, exact;

	Cursor	     xvd_create_cursor();
    	int screen = XDefaultScreen(display);


	/*
	 *  Add error handlers.  We need to add an X error handler,
	 *  so that we can recover from certain X protocol or toolkit
	 *  errors.
	 */
	XSetErrorHandler(xvd_error_handler);
	XSetIOErrorHandler(xvd_io_error_handler);
	XtSetErrorHandler(xvd_xt_error_handler);
	XtSetWarningHandler(xvd_xt_warning_handler);

	/*
	 *  set assorted global display variables
	 */
        ncolors = XDisplayCells(display, screen);

	/* display kludge */
	MaxCells = ncolors - 15;
	CurrentCells = 0;

	/*
	 *  Get the black and white pixel
	 */
	colormap = XDefaultColormap(display, screen);
	if (XAllocNamedColor(display, colormap, "black", &color, &exact))
	   xvd_black = color.pixel;
	else
	   xvd_black = XBlackPixel(display, screen);

	if (XAllocNamedColor(display, colormap, "white", &color, &exact))
	   xvd_white = color.pixel;
	else
	   xvd_white = XWhitePixel(display, screen);

	/*
	 *  create cursor that will be used inside raster window
	 */
	display_cursor = xvd_create_cursor(display, XDefaultRootWindow(display),
		cross_bits, cross_mask_bits, cross_width, cross_height,
		cross_mask_width, cross_mask_height,
		(unsigned int) cross_x_hot, (unsigned int) cross_y_hot,
		xvd_black, xvd_white);

	mask = GCLineWidth | GCFunction | GCSubwindowMode;
	values.line_width = 0;
	values.function = GXinvert;
	values.subwindow_mode = IncludeInferiors;
	gc_inv = XCreateGC(display, XDefaultRootWindow(display), mask, &values);

	mask = GCLineWidth | GCFunction | GCSubwindowMode;
	values.line_width = 0;
	values.function = GXxor;
	values.subwindow_mode = IncludeInferiors;
	gc_xor = XCreateGC(display, XDefaultRootWindow(display), mask, &values);

	mask = GCLineWidth | GCFunction;
	values.line_width = 0;
	values.function = GXcopy;
	xvd_gc = XCreateGC(display, XDefaultRootWindow(display), mask, &values);
	roi_gc = gc_inv;

}  /* end xvd_initialize */



/************************************************************
*
*  MODULE NAME: xvd_query_position
*
*      PURPOSE: This routine is used to tell if the event is
*		of type MotionNotify.  The routine is used to
*		aid other routines in condensing motion events.
*
*	 INPUT: widget - the widget which had the event
*		clip   - whether to clip the mouse position to the window
*
*       OUTPUT: x -  returns the x position relative to the widget
*		y -  returns the y position relative to the widget
*
*		returns true if the next event is a MotionNotify
*		for the given widget.  Otherwise it queries the
*		mouse and returns the x and y position.
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int  xvd_query_position(widget, x, y, clip)

Widget	widget;
int	*x, *y;
int	clip;
{
	Display  *display = XtDisplay(widget);
	Window   window   = XtWindow(widget);

	XEvent   next;
	Window   root, child;
	int	 rx, ry, xt, yt, motion_queued = False;
	unsigned int width, height, dummy, mask;


	if (XEventsQueued(display, QueuedAfterReading))
	{
	   XPeekEvent(display, &next);
	   if (next.type == MotionNotify)
	   {
	      if (next.xmotion.window == window)
		 motion_queued = True;
	   }
	}
	XQueryPointer(display, window, &root, &child, &rx, &ry, x, y, &mask);
	XTranslateCoordinates(display, root, window, rx, ry, &xt, &yt, &child);

	/*
	 *  Clip the position to the window.
	 */
	if (clip)
	{
	   if (XGetGeometry(display, window, &root, (int *) &dummy, 
		(int *) &dummy, &width, &height, &dummy, &dummy))
	   {
	      if (*x < 0) *x = 0;
	      else if (*x >= width) *x = width -1;

	      if (*y < 0) *y = 0;
	      else if (*y >= height) *y = height -1;
	   }
	}
	return(motion_queued);
}



/************************************************************
*
*  MODULE NAME: xvd_check_visibility
*
*      PURPOSE: This routine determines if a widget is
*		currently visible.
*
*	 INPUT: widget - the widget to be checked
*
*       OUTPUT: returns True if the widget is currently visible
*		otherwise false is returned.
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

int  xvd_check_visibility(widget)

Widget widget;
{
	XWindowAttributes xwa;

	if (!XGetWindowAttributes(XtDisplay(widget), XtWindow(widget), &xwa))
	   return(False);

	if (xwa.map_state == IsViewable)
	   return(True);
	else
	   return(False);
}



/************************************************************
*
*  MODULE NAME: xvd_create_cursor
*
*      PURPOSE: create a display cursor for the raster widget.
*		Given the bit information a cursor will be created
*		using the supplied bitmap and mask bitmap and
*		foreground and background color.
*
*	 INPUT: display   - the X Window display variable
*		window    - the X window used to create the cursor with.
*		src_bits  - the cursor bitmap
*		mask_bits - the cursor mask bitmap
*		src_width   -
*		src_height  -
*		mask_width  -
*		mask_height - the width and height of the cursor
*			      and mask cursor bitmaps.
*		x & y	    - the x and y center position or "hot"
*			      point representing the "position"
*		fg & bg     - the foreground and background pixel.
*
*       OUTPUT: the desired cursor from bitmap data
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


Cursor xvd_create_cursor(display, window, src_bits, mask_bits,
		src_width, src_height, mask_width, mask_height,
		x, y, fg, bg)

Display	 *display;
Window	 window;
char	 *src_bits, *mask_bits;
unsigned int x, y;
unsigned int src_width, src_height, mask_width, mask_height;
unsigned long fg, bg;
{
	Cursor  cursor;
	Pixmap  source, mask;
    	int	screen = XDefaultScreen(display);
	XColor  foreground, background;


	source = XCreateBitmapFromData(display, window, src_bits, src_width,
				       src_height);
	mask   = XCreateBitmapFromData(display, window, mask_bits, mask_width,
				       mask_height);

	foreground.pixel = fg;
	foreground.flags = DoRed | DoBlue | DoGreen;
	background.pixel = bg;
	background.flags = DoRed | DoBlue | DoGreen;
	XQueryColor(display, XDefaultColormap(display, screen), &foreground);
	XQueryColor(display, XDefaultColormap(display, screen), &background);
	cursor = XCreatePixmapCursor(display, source, mask, &foreground,
				     &background, x, y);
	return(cursor);
}



/************************************************************
*
*  MODULE NAME: xvd_resize_shape
*
*      PURPOSE: This routine is a kludge to get the XImage widget
*		to shape the only the ximage widget, otherwise the
*		parent is shaped out.
*
*	 INPUT:  - the widget to be checked
*
*       OUTPUT: returns True if the widget is currently visible
*		otherwise false is returned.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


struct xvimage *xvd_resize_shape(xvdisplay, shape)

DisplayStructure *xvdisplay;
struct xvimage *shape;
{
	Widget   parent;
	Position xpos, ypos, x, y;

	int	 type;
	unsigned int w, h, dummy;
	Window   root, window;
	struct   xvimage *newshape;


	parent = xvdisplay->raster;
	while (XtParent(parent))
	   parent = XtParent(parent);

	window = XtWindow(parent);
	if (!XGetGeometry(xvdisplay->display, window, &root, (int *) &dummy, 
		(int *) &dummy, &w, &h, &dummy, &dummy))
	   return(shape);

        XtTranslateCoords(parent, 0, 0, &x, &y);
        XtTranslateCoords(xvdisplay->raster, 0, 0, &xpos, &ypos);
	xpos = xpos - x;
	ypos = ypos - y;

	type = shape->data_storage_type;
	if (!lvgconst(&newshape, (int) w, (int) h, type, 1.0, 0.0, 1))
	   return(shape);
	
	if (!lvinsert(newshape, shape, (int) xpos, (int) ypos, 1.0, 0.0))
	   return(shape);

	return(newshape);
}



/************************************************************
*
*  MODULE NAME: xvd_query_value
*
*      PURPOSE: This routine is used return the value of the
*	        image at the desired x & y position (the pixel).
*		xvd_query_value() will also return the index
*		into both the  image map and the X color array
*		sotred in the DisplayStructure.  This routine also
*		returns a flag indicating whether the currently
*		displayed pixel color is actually allocated or is
*		the closest available color to another allocated color.
*		You may pass in NULL for the value, index, or allocated
*		parameters, in which case the field will be ignored.
*
*	 INPUT:  xvdisplay - the display structure
*		 x  & y    - the x and y position
*		 value	   - the pixel value of the image (return)
*		 index	   - the index into the colormap (return)
*		 allocated - the whether the pixel at that position
*			     is currently allocated
*
*       OUTPUT: returns the value at the x & y position
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_query_value(xvdisplay, x, y, value, index, allocated)

DisplayStructure *xvdisplay;
int	 x, y, *index, *allocated;
double	 *value;
{
	double dtemp;
	int    j, temp;
	struct xvimage *disp_image;
	XColor *xcolors = xvdisplay->xcolors;


	/*
	 *  Get the image value at the x & y location.  If value is NULL then
	 *  this simply becomes a check to see if we are within the bounds of
	 *  the image.
	 */
	if (xvd_image_value(xvdisplay->image, x, y, 0, value) == False)
	   return(False);

	/*
	 *  If the index is not NULL then we also need to find the index
	 *  or pixel of the displayed image.  This gives us the index into
	 *  the colormap.
	 */
	if (index != NULL || allocated != NULL)
	{
	   disp_image = xvdisplay->disp_image;
	   if (xvd_image_value(disp_image, x, y, 0, &dtemp) == False)
	   {
	      return(False);
	   }
	   temp = (int) dtemp;

	   if (allocated != NULL)
	      *allocated = xvdisplay->active[temp];

	   if (index != NULL)
	   {
	      /*
	       *  If they want the lookup value then we need to make sure
	       *  that this pixel is not allocated to another index (ie.
	       *  closest color to some other pixel).
	       */
	      *index = temp;
	      if (xvdisplay->active[temp] == False)
	      {
		 for (j = 0; j < MAX_PIXELS; j++)
		 {
		    if (xcolors[temp].pixel == xcolors[j].pixel &&
			xvdisplay->active[j] == True)
		    {
		       *index = 1;
		       break;
		    }
		 }
	      }
	   }
	}
	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_query_rgbvalue
*
*      PURPOSE: This routine is used return the value of the
*	        image at the desired x & y position (the pixel).
*		xvd_query_value() will also return the index
*		into both the  image map and the X color array
*		sotred in the DisplayStructure.  This routine also
*		returns a flag indicating whether the currently
*		displayed pixel color is actually allocated or is
*		the closest available color to another allocated color.
*		You may pass in NULL for the value, index, or allocated
*		parameters, in which case the field will be ignored.
*
*	 INPUT:  xvdisplay - the display structure
*		 x  & y    - the x and y position
*		 value	   - the pixel value of the image (return)
*		 index	   - the index into the colormap (return)
*		 allocated - the whether the pixel at that position
*			     is currently allocated
*
*       OUTPUT: returns the value at the x & y position
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_query_rgbvalue(xvdisplay, x, y, value, index, allocated)

DisplayStructure *xvdisplay;
int	 x, y, index[], allocated[];
double	 value[];
{
	double dtemp;
	int    i, j, temp;
	struct xvimage *disp_image;
	XColor *xcolors = xvdisplay->xcolors;


	/*
	 *  Get the image value at the x & y location.  If value is NULL then
	 *  this simply becomes a check to see if we are within the bounds of
	 *  the image.
	 */
	if (value == NULL)
	{
	   if (xvd_image_value(xvdisplay->image, x, y, 0, NULL) == False)
	      return(False);
	}
	else
	{
	   if (xvd_image_value(xvdisplay->image, x, y, 0, &value[0]) == False)
	      return(False);
	   if (xvd_image_value(xvdisplay->image, x, y, 1, &value[1]) == False)
	      return(False);
	   if (xvd_image_value(xvdisplay->image, x, y, 2, &value[2]) == False)
	      return(False);
	}

	/*
	 *  If the index is not NULL then we also need to find the index
	 *  or pixel of the displayed image.  This gives us the index into
	 *  the colormap.
	 */
	if (index != NULL || allocated != NULL)
	{
	   disp_image = xvdisplay->disp_image;
	   for (i = 0; i < 3; i++)
	   {
	      if (xvd_image_value(disp_image, x, y, 0, &dtemp) == False)
	      {
	         return(False);
	      }
	      temp = (int) dtemp;

	      if (allocated != NULL)
	         allocated[i] = xvdisplay->active[temp];

	      if (index != NULL)
	      {
	         /*
	          *  If they want the lookup value then we need to make sure
	          *  that this pixel is not allocated to another index (ie.
	          *  closest color to some other pixel).
	          */
	         index[i] = temp;
	         if (xvdisplay->active[temp] == False)
	         {
		    for (j = 0; j < MAX_PIXELS; j++)
		    {
		       if (xcolors[temp].pixel == xcolors[j].pixel &&
			   xvdisplay->active[j] == True)
		       {
		          index[i] = 1;
		          break;
		       }
		    }
	         }
	      }
	   }
	}
	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_image_value
*
*      PURPOSE: This routine is used return the value of the
*	        image at the desired x & y position (the pixel).
*
*	 INPUT:  image   - the image structure
*		 x  & y  - the x and y position
*		 band    - the band number to be checked
*		 value	 - the pixel value of the image (return)
*
*       OUTPUT: returns the value at the x & y position
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvd_image_value(image, x, y, band, value)

struct xvimage *image;
int    x, y, band;
double *value;
{
	int      i, n;
	short    *sptr;
	int      *iptr;
	float    *fptr;
	double   *dptr;
	unsigned char *bptr;


	if (image == NULL)
	   return(False);
	else if (x < 0 || x >= image->row_size || y < 0 ||
		 y >= image->col_size || band > image->num_data_bands)
	   return(False);

	if (value != NULL)
	{
	   i = BPIXEL(band, x, y, image->col_size, image->row_size);
	   switch (image->data_storage_type)
	   {
	       case VFF_TYP_BIT:
		    bptr = (unsigned char *) image->imagedata;
		    n = (image->row_size+7)/8;
		    *value = ((bptr[x/8 + y*n] & (1 << (x % 8))) ? 1.0 : 0.0);
		    break;

	       case VFF_TYP_1_BYTE:
		    bptr = (unsigned char *) image->imagedata;
		    *value = (double) bptr[i];
		    break;

	       case VFF_TYP_2_BYTE:
		    sptr = (short *) image->imagedata;
		    *value = (double) sptr[i];
		    break;

	       case VFF_TYP_4_BYTE:
		    iptr = (int *) image->imagedata;
		    *value = (double) iptr[i];
		    break;

	       case VFF_TYP_FLOAT:
		    fptr = (float *) image->imagedata;
		    *value = (double) fptr[i];
		    break;

	       case VFF_TYP_DOUBLE:
		    dptr = (double *) image->imagedata;
		    *value = (double) dptr[i];
		    break;

	       default:
		    return(False);
		    break;
	   }
	}
	return(True);
}



/************************************************************
*
*  MODULE NAME: xvd_truecolor_image
*
*      PURPOSE: This routine is used to see if an image is
*		a true color image.  A true color image or 24
*		bit image is one that has an 8 bit image for
*		each RGB component.  And also has an RGB color
*		space with no maps.
*
*	 INPUT:  image - the image
*
*       OUTPUT: returns TRUE if it is a true color image otherwise
*		FALSE is returned.
*
*    CALLED BY: internal routine
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

int xvd_truecolor_image(image)

struct xvimage *image;
{
	if ((image->color_space_model == VFF_CM_ntscRGB ||
	     image->color_space_model == VFF_CM_genericRGB) &&
	    (image->map_scheme == VFF_MS_NONE) &&
	    (image->num_data_bands % 3 == 0))
	   return(TRUE);
	else
	   return(FALSE);
}
