 /*
  * Khoros: $Id: run_zoom.c,v 1.4 1992/03/20 22:39:53 dkhoros Exp $
  */

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

 /*
  * $Log: run_zoom.c,v $
 * Revision 1.4  1992/03/20  22:39:53  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability 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 OTHER DAMAGES WHAT-
 * SOEVER 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 PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

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

static  int    zoom_xpos = 0, zoom_ypos = 0;


/********************************************************
*
*  Routine Name:  run_zoom
*
*       Purpose:  drives the pane 'zoom'
*
*         Input:  form - pointer to the form tree 
*		  zoom_info  - information structure for pane 'zoom'
*        Output:  action of the application program
*
*     Called By:  run_zoom_subform()
*
*   Automatically Generated By:  conductor
*
********************************************************/


run_zoom(form, zoom_info)

xvf_form *form;
zoom_subform_zoom *zoom_info;
{

	_xvf_get_zoom(form, zoom_info);

	/*
	 * user clicked on 'live' string selection 'zoomfactor'
	 */
	if (zoom_info->zoomfactor_selected)
	 {
	    zoom->zoomfactor = zoom_info->zoomfactor;

	    XClearWindow(XtDisplay(zoom->image), XtWindow(zoom->image));
	    refresh_zoom();
	}
	else if (zoom_info->update_mode_selected)
	{
	    zoom->mode = zoom_info->update_mode;
	    update_zoom_mode();
	}
	else if (zoom_info->cursor_selected)
	{
	    zoom->cursor = zoom_info->cursor;
	    refresh_zoom();
	}



}

/************************************************************
*
*  MODULE NAME: update_zoom_mode
*
*      PURPOSE: changes the update zoom method to either
*		ButtonPress_Mode or Continuous_Mode.  If the mode
*		is button press then we update the zoom window
*		every time the user button presses in the image
*		window.  If the mode is continous then we update
*		the zoom window as the mouse moves thru the image
*		window.
*
*		A event handler is installed for the desired type
*		and the previous handler is deleted.
*
*        INPUT: none
*
*       OUTPUT: none
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


update_zoom_mode()
{
	unsigned long mask;


	if (zoom->mode == ButtonPress_Mode)
	{
	   mask = PointerMotionHintMask | PointerMotionMask;
	   XtRemoveEventHandler(xvdisplay->raster, mask, FALSE, update_zoom,
				NULL);

	   mask = ButtonPressMask;
	   XtAddEventHandler(xvdisplay->raster, mask, FALSE, update_zoom, NULL);
	}
	else if (zoom->mode == Continuous_Mode)
	{
	   mask = ButtonPressMask;
	   XtRemoveEventHandler(xvdisplay->raster, mask, FALSE, update_zoom,
				NULL);

	   mask = PointerMotionHintMask | PointerMotionMask;
	   XtAddEventHandler(xvdisplay->raster, mask, FALSE, update_zoom, NULL);
	}

        if (xvd_check_visibility(zoom->image) == True)
	   refresh_zoom();
}



/************************************************************
*
*  MODULE NAME: redisplay_zoom
*
*      PURPOSE: redisplay_zoom is the event handler that
*		is used to refresh the zoomed image to the
*		zoom window.  calls refresh_zoom() to do the actual
*		displaying.
*
*
*	 INPUT: widget     -  the widget for the event
*		clientData -  not used
*		event      -  the event
*
*       OUTPUT: none - refresh_zoom() does the actual refreshing
*
*   WRITTEN BY:
*
************************************************************/


void redisplay_zoom(widget, clientData, event)

Widget	widget;
caddr_t clientData;
XEvent  *event;
{
        ZoomStructure  *zoom = (ZoomStructure *) clientData;

	Window	    root;
	int	    x, y;
	unsigned    long mask;
	unsigned    int width, height, border_width, depth;


	/*
	 *  Make sure that there is a current image to zoom with.
	 */
	if (xvdisplay->image == NULL || zoom->ximage == NULL)
	   return;

	if (event->type == Expose && zoom->image == widget)
	{
	   /*
	    *  refresh the zoom display.
	    */
	   refresh_zoom();
	}
	else if (event->type == ConfigureNotify)
	{
           if ( !XGetGeometry(XtDisplay(zoom->image), XtWindow(zoom->image),
	            &root, &x, &y, &width, &height,  &border_width, &depth))
           {
	      xvf_error_wait("Window does not exist.","redisplay_zoom", NULL);
	      return;
           }

	   if (zoom->ximage->width != width || zoom->ximage->height != height)
	   {
	      XtFree(zoom->ximage->data);
	      zoom->ximage->width = width;
	      zoom->ximage->height = height;
	      zoom->ximage->bytes_per_line = width;
	      zoom->ximage->data = (char *) XtMalloc(width * height);
	      refresh_zoom();
	   }
	}
	else
	{
	   mask = ButtonPressMask;
	   XtRemoveEventHandler(xvdisplay->raster, mask, FALSE, update_zoom,
				   NULL);
	   mask = PointerMotionHintMask | PointerMotionMask;
	   XtRemoveEventHandler(xvdisplay->raster, mask, FALSE, update_zoom,
				   NULL);
	   if (xvd_check_visibility(widget) == False)
	      return;
	   else
	      update_zoom_mode();

	   refresh_zoom();
	}
}



/************************************************************
*
*  MODULE NAME: update_zoom
*
*      PURPOSE: Updates the zoomed image on the screen.  This
*		routine does not do the actual refreshing of the
*		image, but rather queries the position of the mouse
*		in the screen, and then calls refresh_zoom().
*
*	 INPUT: widget     -  the widget for the event
*		clientData -  not used
*		event      -  the event
*
*       OUTPUT: calls refresh_zoom() to display zoomed image.
*
*   WRITTEN BY: Mark Young
*
*
************************************************************/


void update_zoom(widget, clientData, event)

Widget	widget;
caddr_t clientData;
XEvent  *event;
{
	int	  i, x, y;
	Arg	  args[MaxArgs];
	Dimension xoffset, yoffset;


	if (event->type == MotionNotify)
	{
	   if (xvd_query_position(widget, &x, &y, True))
	      return;
	}
	else if (event->type == ButtonPress)
	{
	   x = event->xbutton.x;
	   y = event->xbutton.y;
	}
	else
	{
	   return;
	}

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

	/*
	 *  Assign global zoom position for x & y.  This is used when
	 *  refreshing the pixel widget, but first we need to check to
	 *  make sure the x & y position are within the zoom window.
	 */
	zoom_xpos = x + xoffset;
	zoom_ypos = y + yoffset;
	refresh_zoom();
}



/************************************************************
*
*  MODULE NAME: refresh_zoom
*
*      PURPOSE: refreshes the zoom image to the zoom window.
*
*        INPUT: none - uses the global "zoom_xpos" & "zoom_ypos"
*		       as the x and y position to offset.
*
*	OUTPUT: displays a zoomed image to the pixel window
*
*   WRITTEN BY: Mark Young
*
************************************************************/

refresh_zoom()
{
	register  float	  factor;
	unsigned  char	  *data, *line, *imgdata;
	register  int	  xpos, ypos, i, j, k, x, y, width, height;
	register  Pixel	  pixel;

	XColor	  color;
	float     hue, light, saturation;
	int	  temp, index, xcursor, ycursor, num_bytes, rgbindex[3];

	XImage  *ximage  = xvdisplay->ximage;
	Display *display = XtDisplay(zoom->image);
	Window  window   = XtWindow(zoom->image);


	/*
	 *  Make sure that there is a current image to zoom with.
	 */
	if (xvdisplay->image == NULL || zoom->ximage == NULL)
	   return;

	data     = (unsigned char *) zoom->ximage->data;
	imgdata  = (unsigned char *) ximage->data;
	width	 = zoom->ximage->width;
	height	 = zoom->ximage->height;
	factor	 = zoom->zoomfactor;

	x = zoom_xpos;
	y = zoom_ypos;

	temp = width/(2*factor);
	if ((x - temp) < 0)
	   x = temp;
	else if ((x + temp) >= ximage->width)
	   x = ximage->width - temp;
	x -= temp;

	temp = height/(2*factor);
	if ((y - temp) < 0)
	   y = temp;
	else if ((y + temp) >= ximage->height)
	   y = ximage->height - temp;
	y -= temp;

	xcursor = (zoom_xpos - x)*factor + factor/2;
	ycursor = (zoom_ypos - y)*factor + factor/2;

	if (factor == 1.0)
	{
	   XPutImage(display, window, gc_zoom, ximage, x, y, 0, 0,width,height);
	}
	else if (ximage->depth == 1)
	{
	   int pos, rowsize;

	   i = pos = 0; ypos = y;
	   rowsize = (ximage->width+7)/8;
	   while ((i < height) && (ypos < ximage->height))
	   {
	       ypos = y + i/factor;

	       /* save the start of the scan line */
	       line = data;
	       imgdata = (unsigned char *) (ximage->data + rowsize * ypos);

	       pos = xpos = 0;
	       for (j = 0; (j < width) && (xpos < ximage->width); j++)
	       {
		   xpos = x + j/factor;
		   if (imgdata[xpos/8] & (1 << (xpos%8)))
		      *data = (*data | (1 << pos));
		   else
		      *data = (*data & ~(1 << pos));

		   if (pos == 7)
		   {
		      data++;
		      pos = 0;
		   }
		   else pos++;
	       }
	       data = line + (width+7)/8;
	       i++;

	       for (k = 1; (k < factor) && (i < height); k++, i++)
	       {
		   bcopy(line, data, (j+7)/8);
		   data += (width+7)/8;
	       }
	   }
	   y = (height - i)/2;
	   x = (width  - j)/2;
	   XPutImage(display, window, gc_zoom, zoom->ximage, 0, 0, x, y, j, i);
	}
	else if (ximage->depth == 8)
	{
	   i = 0; ypos = y;
	   while ((i < height) && (ypos < ximage->height))
	   {
	       ypos = y + i/factor;

	       /* save the start of the scan line */
	       line = data;
	       imgdata = (unsigned char *)(ximage->data + ximage->width * ypos);

	       xpos = 0;
	       for (j = 0; (j < width) && (xpos < ximage->width); j++)
	       {
		   xpos = x + j/factor;
		   *(data++) = *(imgdata + xpos);
	       }
	       data = line + width;
	       i++;

	       /* duplicate the rest of the scan lines */
	       for (k = 1; (k < factor) && (i < height); k++, i++)
	       {
		   bcopy(line, data, j);
		   data += width;
	       }
	   }
	   y = (height - i)/2;
	   x = (width  - j)/2;
	   XPutImage(display, window, gc_zoom, zoom->ximage, 0, 0, x, y, j, i);
	}
	else
	{
	   i = 0; ypos = y;
	   num_bytes = ceil(((double) zoom->ximage->bits_per_pixel)/8);
	   while ((i < height) && (ypos < ximage->height))
	   {
	       ypos = y + i/factor;

	       xpos = 0;
	       for (j = 0; (j < width) && (xpos < ximage->width); j++)
	       {
		   xpos = x + j/factor;
		   pixel = XGetPixel(ximage, xpos, ypos);
		   XPutPixel(zoom->ximage, j, i, pixel);
	       }

	       /* duplicate the rest of the scan lines */
	       line = data + width*i*num_bytes;
	       i++;

	       for (k = 1; (k < factor) && (i < height); k++, i++)
	       {
		   bcopy(line, &line[j*num_bytes*k], j*num_bytes);
	       }
	   }
	   y = (height - i)/2;
	   x = (width  - j)/2;
	   XPutImage(display, window, gc_zoom, zoom->ximage, 0, 0, x, y, j, i);
	}

	x = xcursor + x;
	y = ycursor + y;
	if (zoom->cursor == Cross)
	{
	   XSetForeground(display, gc_set, white);
	   XDrawLine(display, window, gc_set, x-1, y-10, x-1, y+10);
	   XDrawLine(display, window, gc_set, x+1, y-10, x+1, y+10);
	   XDrawLine(display, window, gc_set, x-10, y-1, x+10, y-1);
	   XDrawLine(display, window, gc_set, x-10, y+1, x+10, y+1);
	   XSetForeground(display, gc_set, black);
	   XDrawLine(display, window, gc_set, x, y-10, x, y+10);
	   XDrawLine(display, window, gc_set, x-10, y, x+10, y);
	}
	else if (zoom->cursor == Box)
	{
	   XSetForeground(display, gc_set, white);
	   XDrawRectangle(display, window, gc_set, x-2, y-2, 4, 4);
	   XSetForeground(display, gc_set, black);
	   XDrawRectangle(display, window, gc_set, x-3, y-3, 6, 6);
	}
	else if (zoom->cursor == Dot)
	{
	   if (xvd_truecolor_image(xvdisplay->image) == False)
	   {
	      xvd_query_value(xvdisplay, zoom_xpos, zoom_ypos, NULL, &index,
			      NULL);
	      color = xvdisplay->xcolors[index];
	   }
	   else
	   {
	      xvd_query_rgbvalue(xvdisplay, zoom_xpos, zoom_ypos, NULL,
			         rgbindex, NULL);
	      color.red     = xvdisplay->xcolors[rgbindex[0]].red;
	      color.green   = xvdisplay->xcolors[rgbindex[1]].green;
	      color.blue    = xvdisplay->xcolors[rgbindex[2]].blue;
	   }

	   RGB_to_HLS(color, hue, light, saturation);
           if (light >= 0.5)
              XSetForeground(display, gc_set, black);
           else
              XSetForeground(display, gc_set, white);

	   XDrawPoint(display, window, gc_set, x, y);
	}
}
