 /*
  * Khoros: $Id: roi.c,v 1.3 1992/03/20 22:49:07 dkhoros Exp $
  */

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

 /*
  * $Log: roi.c,v $
 * Revision 1.3  1992/03/20  22:49:07  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 *            Copyright 1992 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 1992 by UNM */
#include "xvdisplay.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>		Region of Interest Utilities			<<<<
   >>>>								<<<<
   >>>>			xvd_extract_roi()			<<<<
   >>>>			xvd_display_roi_bars()			<<<<
   >>>>			xvd_refresh_roi_bars()			<<<<
   >>>>								<<<<
   >>>>			xvd_insert_roi()			<<<<
   >>>>			xvd_display_roi_box()			<<<<
   >>>>			xvd_refresh_roi_box()			<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  */

/* used for extracting subimage with interactive bars */
static int roi_x1 = 20,
	   roi_y1 = 20,
	   roi_x2 = 100,
	   roi_y2 = 100;


GC gc_inv, gc_xor;

/* used for inserting subimage with interactive box */
static int roi_x, roi_y, roi_width, roi_height, roi_x_diff, roi_y_diff;


/* The user is allowed a +/- tolerance of 5 pixels to select ROI bars */
#define TOLERANCE 5



/************************************************************
*
*  MODULE NAME: xvd_extract_roi
*
*      PURPOSE: extracts a region of interest from the 
*		displayed image for output to a file
*
*	 INPUT: xvdisplay: pointer to the DisplayStructure
*	        mouse: flag telling if the region of interest will be
*		       extracted using the mouse (mouse = true) or with
*		       position parameters  if (mouse is true, then  
*		       x1, y1, x2, and y2 will be unused)
*
*		x1:    x position of first box coordinate
*		y1:    y position of first box coordinate
*		x2:    x position of second box coordinate
*		y2:    y position of second box coordinate
*		
*
*       OUTPUT: the xvimage structure representing the region of interest
*
*    CALLED BY: the application program
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

struct xvimage *xvd_extract_roi(xvdisplay, mouse, x1, y1, x2, y2)
DisplayStructure *xvdisplay;
int	mouse, x1, y1, x2, y2;
{
	Arg	 arg[MaxArgs];
	int	 status, i, x, y, width, height;
	unsigned long     mask;
	unsigned char     *maps;
	Dimension xoffset, yoffset;
	struct   xvimage  *image;
	XColor   *xcolors = xvdisplay->xcolors;
	void xvd_display_roi_bars();


	if (mouse)
	{
	   xvd_refresh_roi_bars(xvdisplay->raster);
	   mask = ButtonPressMask | ButtonReleaseMask | ExposureMask |
		  ButtonMotionMask;
	   XtInsertEventHandler(xvdisplay->raster, mask, False, 
			     xvd_display_roi_bars, NULL, XtListHead);

	   status = xvf_warn_wait("Please select the region to extracted by\
 dragging the roi bars to their desired positions and selecting the\
 \"Extract\" button.  Selecting \"Cancel\" will abort this operation.",
		   "xvd_extract_roi", "Extract", "Cancel");

	   xvd_refresh_roi_bars(xvdisplay->raster);
	   XtRemoveEventHandler(xvdisplay->raster, XtAllEvents, True, 
			        xvd_display_roi_bars, NULL); 

	   if (status == False)
	   {
	      return(NULL);
	   }
	}
	else
	{
	   roi_x1 = x1;
	   roi_x2 = x2;
	   roi_y1 = y1;
	   roi_y2 = y2;
	}

	/*
	 *  Get the x & y offset for the image.
	 */
	i = 0;
        XtSetArg(arg[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(arg[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(xvdisplay->raster, arg, i);

	x = MIN(roi_x1, roi_x2) + xoffset;
	y = MIN(roi_y1, roi_y2) + yoffset;

	width  = abs(roi_x1 - roi_x2);
	height = abs(roi_y1 - roi_y2);

	if (!lvextract(xvdisplay->image, &image, width, height, x, y, True))
	{
	   xvf_error_wait("lvextract failed!  Unable to extract region.",
			  "xvd_extract_roi", NULL);
	   return(NULL);
	}

	maps  = (unsigned char *) XtMalloc(3 * MAX_PIXELS);
	image->maps = (char *) maps;
	for (i = 0; i < MAX_PIXELS; i++)
	{
	    if (xvdisplay->histogram[i] > 0)
	    {
	       maps[i] = xcolors[i].red/256 + 0.5;
	       maps[i + MAX_PIXELS]     = xcolors[i].green/256 + 0.5;
	       maps[i + MAX_PIXELS * 2] = xcolors[i].blue/256 + 0.5;
	    }
	    else
	    {
	       maps[i] =
	       maps[i + MAX_PIXELS] =
	       maps[i + MAX_PIXELS * 2] = i;
	    }
	}
	image->map_row_size      = 3;
	image->map_col_size      = MAX_PIXELS;
	image->map_scheme        = VFF_MS_SHARED;
	image->map_storage_type  = VFF_MAPTYP_1_BYTE;
	image->color_space_model = VFF_CM_genericRGB;
	return(image);
}



/************************************************************
*
*  MODULE NAME:  xvd_display_roi_bars
*
*      PURPOSE:  this routine displays the interactive bars with which
*		 the user may select the region of interest.
*
*        INPUT:  widget     - the widget in which the image is contained
*                clientdata - should be originally set to the xvdisplay struct
*                event      - the event that initiated the callback
*
*       OUTPUT:  none
*
*    CALLED BY:  xvd_extract_roi()
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

void xvd_display_roi_bars(widget, clientData, event, dispatch)

Widget    widget;
XtPointer clientData;
XEvent    *event;
Boolean	  *dispatch;
{
	int	x, y;
	static int x1, x2, y1, y2;


	if (event->type == MotionNotify)
	{
	   if (!x1 && !x2 && !y1 && !y2)
	      return;

	   if (xvd_query_position(widget, &x, &y, True))
	      return;
	}
	else if (event->type == Expose)
	{
	   /*
	    *  refresh the roi position bars.
	    */
	   xvd_refresh_roi_bars(widget);
	   return;
	}
	else if (event->type == ButtonPress)
	{
	   x = event->xbutton.x;
	   y = event->xbutton.y;

	   /*
	    *  Find out which roi bar they are trying to position (if any).
	    */
	   if (x >= (roi_x1 - TOLERANCE) && x <= (roi_x1 + TOLERANCE))
	      x1 = True;
	   else
	      x1 = False;

	   if (x >= (roi_x2 - TOLERANCE) && x <= (roi_x2 + TOLERANCE))
	      x2 = True;
	   else
	      x2 = False;

	   if (y >= (roi_y1 - TOLERANCE) && y <= (roi_y1 + TOLERANCE))
	      y1 = True;
	   else
	      y1 = False;

	   if (y >= (roi_y2 - TOLERANCE) && y <= (roi_y2 + TOLERANCE))
	      y2 = True;
	   else
	      y2 = False;

	   *dispatch = False;
	   return;
	}
	else if (event->type == ButtonRelease)
	{
	   x = event->xbutton.x;
	   y = event->xbutton.y;

	   *dispatch = False;
	   x1 = x2 = y1 = y2 = False;
	}

	/*
	 *  Erase old position before drawing the new roi positions.
	 */
	xvd_refresh_roi_bars(widget);

	if (x1)
	   roi_x1 = x;
	else if (x2)
	   roi_x2 = x;

	if (y1)
	   roi_y1 = y;
	else if (y2)
	   roi_y2 = y;

	xvd_refresh_roi_bars(widget);
}



/************************************************************
*
*  MODULE NAME: xvd_refresh_roi_bars
*
*      PURPOSE: refreshes the interactive bars with which the user
*		selects the region of interest
*
*        INPUT: widget     - the widget in which the image is contained
*
*       OUTPUT:  none
*
*    CALLED BY:  xvd_display_roi_bars()
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

xvd_refresh_roi_bars(widget)

Widget widget;
{
	Display	   *display;
	Window	   root, window;
	int	   x, y;
	unsigned   int width, height, border_width, depth;

	display = XtDisplay(widget);
	window  = XtWindow(widget);

	if (!XGetGeometry(display, window, &root, &x, &y, &width, &height,
			  &border_width, &depth))
	{
	   xvf_error_wait("Window does not exist.","xvd_refresh_roi_bars",NULL);
	   return;
	}
	XDrawLine(display, window, gc_inv, roi_x1, 0, roi_x1, height);
	XDrawLine(display, window, gc_inv, 0, roi_y1, width, roi_y1);
	XDrawLine(display, window, gc_inv, roi_x2, 0, roi_x2, height);
	XDrawLine(display, window, gc_inv, 0, roi_y2, width, roi_y2);

	XDrawLine(display, window, gc_xor, roi_x1+1, 0, roi_x1+1, height);
	XDrawLine(display, window, gc_xor, 0, roi_y1+1, width, roi_y1+1);
	XDrawLine(display, window, gc_xor, roi_x2+1, 0, roi_x2+1, height);
	XDrawLine(display, window, gc_xor, 0, roi_y2+1, width, roi_y2+1);
}



/************************************************************
*
*  MODULE NAME: xvd_insert_roi
*
*      PURPOSE: inserts a subimage (region of interest) into the
*		displayed image 
*
*	 INPUT: xvdisplay: pointer to the DisplayStructure
*		subimage:  the subimage to be inserted
*		mouse: flag telling if the subimage will be
*		       placed using the mouse (mouse = true) or with
*		       position parameters  if (mouse is true, then  
*		       x and y will be unused)
*
*		x:    x position of the upper left hand corner
*		y:    y position of the upper left hand corner
*		
*
*       OUTPUT: the image is inserted into the xvdisplay image
*		and updated for the raster widget.  If the saveimage
*		pointer is not NULL then the contents below the 
*		inserted image is returned in the "saveimage" pointer.
*
*    CALLED BY: application program
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

int xvd_insert_roi(xvdisplay, subimage, mouse, x, y, saveimage)

DisplayStructure *xvdisplay;
struct xvimage *subimage, **saveimage;
int mouse;
int x, y;
{
	Arg	  arg[MaxArgs];
	unsigned  long     mask;
	Dimension xoffset, yoffset;
	void	  xvd_display_roi_box();
	int	  i, startx, starty, status;
	struct	  xvimage *image = xvdisplay->image;

	/*
	 *  Get the x & y offset for the image.
	 */
	i = 0;
        XtSetArg(arg[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(arg[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(xvdisplay->raster, arg, i);

	roi_width  = subimage->row_size;
	roi_height = subimage->col_size;
	if (subimage->startx != VFF_NOTSUB) roi_x = subimage->startx - xoffset;
	if (subimage->starty != VFF_NOTSUB) roi_y = subimage->starty - yoffset;

	if (mouse)
	{
	   xvd_refresh_roi_box(xvdisplay->raster);
           mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
		  ExposureMask;
           XtInsertEventHandler(xvdisplay->raster, mask, False,
                             xvd_display_roi_box, NULL, XtListHead);

           status = xvf_warn_wait("Please select the insert position by\
 dragging the roi box to its desired position and selecting the\
 \"Insert\" button.  Selecting \"Cancel\" will abort this operation.",
                   "xvd_insert_roi", "Insert", "Cancel");

           xvd_refresh_roi_box(xvdisplay->raster);
           XtRemoveEventHandler(xvdisplay->raster, XtAllEvents, True,
                                xvd_display_roi_box, NULL);

           if (status == False)
              return(False);

           startx = roi_x + xoffset;
           starty = roi_y + yoffset;
	}
	else
	{
	   startx = x;
	   starty = y;
	}

	if (image->data_storage_type != subimage->data_storage_type)
	{
	   /*
	    *  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.
	    */
	   if (!xvf_warn_wait("ROI image is of different data type. Do you \
wish to convert the image?", "xvd_insert_roi", NULL, NULL))
	   {
              return(False);
           }


           /*
            *  Call lvconvert() to convert the subimage to the same data format
	    *  and normalization factor of 255.
            */
           if (!lvconvert(subimage, image->data_storage_type, True, False,
			  255.0, 1.0, False))
           {
              xvf_error_wait("lvconvert failed!  Unable to insert image.",
                             "xvd_convert_image", NULL);
              return(False);
           }
	}

	/*
	 *  If the saveimage is not NULL then we need to extract the orig-
	 *  inal subimage before inserting the new one.  This way the calling
	 *  application can always restore the subimage if they choose to do so.
	 */
	if (saveimage != NULL)
	{
	   if (!lvextract(image, &saveimage, subimage->row_size,
			subimage->col_size, startx, starty, True))
	   {
	      xvf_error_wait("lvextract failed!  Unable to extract region for \
saving.  But will continue with inserting new roi region.", "xvd_extract_roi",
			     NULL);
	      *saveimage = NULL;
	      saveimage = NULL;
	   }
	}

	if (!lvinsert(image, subimage, startx, starty, 0.0, 0.0))
	{
	   xvf_error_wait("lvinsert failed!  Unable to insert region.",
			  "xvd_insert_roi", NULL);
	   if (saveimage != NULL)
	      freeimage(*saveimage);
	   return(False);
	}

	if (!xvd_update_region(xvdisplay, startx, starty,  subimage->row_size,
			subimage->col_size))
	{
	   xvf_error_wait("xvd_update_image failed! Unable to insert region.",
			  "xvd_insert_roi", NULL);
	   if (saveimage != NULL)
	      freeimage(*saveimage);
	   return(False);
	}
	return(True);
}



/************************************************************
*
*  MODULE NAME:  xvd_display_roi_box
*
*      PURPOSE:  this routine displays the interactive box with which
*		 the user may select the position for inserting a subimage.
*
*        INPUT:  widget     - the widget in which the image is contained
*                clientdata - should be originally set to the xvdisplay struct
*                event      - the event that initiated the callback
*
*       OUTPUT:  none
*
*    CALLED BY:  xvd_insert_roi()
*
*   WRITTEN BY:  Mark Young & Danielle Argiro
*
*
*************************************************************/

void xvd_display_roi_box(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	int	x, y;
	static int selected = False;


	if (event->type == MotionNotify)
	{
	   if (!selected)
	      return;

	   *dispatch = False;
	   if (xvd_query_position(widget, &x, &y, True))
	      return;

	}
	else if (event->type == Expose)
	{
	   /*
	    *  refresh the roi position bars.
	    */
	   xvd_refresh_roi_box(widget);
	   return;
	}
	else if (event->type == ButtonPress)
	{
	   x = event->xbutton.x;
	   y = event->xbutton.y;

	  /*
           *  check if they clicked on or in the box
	   */
	  if ((x + TOLERANCE >= roi_x) &&(x - TOLERANCE <= roi_x + roi_width) &&
	      (y + TOLERANCE >= roi_y) && (y - TOLERANCE <= roi_y + roi_height))
	  {
	    selected = True;
	    *dispatch = False;
	  }
	  else
	  {
	    selected = False;
	    return;
	  }

	  /*
           *
	   */
	  roi_x_diff = x - roi_x;
	  roi_y_diff = y - roi_y;
	  return;
	}
	else if (event->type == ButtonRelease)
	{
	   x = event->xbutton.x;
	   y = event->xbutton.y;
	   selected = False;
	   *dispatch = False;
	}

	/*
	 *  Erase box in old position 
	 */
	xvd_refresh_roi_box(widget);

	/*
	 *  Draw the new roi box
	 */
	if (selected)
	{
	   roi_x = x - roi_x_diff;
	   roi_y = y - roi_y_diff;
	}
	xvd_refresh_roi_box(widget);
}



/************************************************************
*
*  MODULE NAME: xvd_refresh_roi_box
*
*      PURPOSE: refreshes the interactive box with which the user
*		selects the position for inserting subimage
*
*        INPUT: widget     - the widget in which the image is contained
*
*       OUTPUT:  none
*
*    CALLED BY:  xvd_display_roi_box()
*
*   WRITTEN BY:  Mark Young & Danielle Argiro
*
*
*************************************************************/


xvd_refresh_roi_box(widget)

Widget widget;
{
	XDrawRectangle(XtDisplay(widget), XtWindow(widget), gc_inv, roi_x+1,
		       roi_y+1, roi_width, roi_height);
	XDrawRectangle(XtDisplay(widget), XtWindow(widget), gc_xor, roi_x+1,
		       roi_y+1, roi_width, roi_height);
}
