 /*
  * Khoros: $Id: run_lut.c,v 1.1 1991/05/10 15:58:30 khoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: run_lut.c,v 1.1 1991/05/10 15:58:30 khoros Exp $";
#endif

 /*
  * $Log: run_lut.c,v $
 * Revision 1.1  1991/05/10  15:58:30  khoros
 * Initial revision
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * 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"


/********************************************************
*
*  Routine Name:  run_lut
*
*       Purpose:  drives the pane 'lut'
*
*         Input:  form - pointer to the form tree 
*		  lut_info  - information structure for pane 'lut'
*        Output:  action of the application program
*
*     Called By:  run_lut_subform()
*
*   Automatically Generated By:  conductor -i editimage.form -l 3 -b
*
********************************************************/


run_lut(form, lut_info)

xvf_form *form;
lut_subform_lut *lut_info;
{
	Widget back;
	int    range;

	_xvf_get_lut(form, lut_info);

	/*
	 * user clicked on 'live' logical selection 'histogram'
	 */
	range = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;
	if (lut_info->histogram_selected)
	{
	   lut->histogram = lut_info->histogram;
	   refresh_lut(xvdisplay->pixelmin, range);
	}

	/*
	 * user clicked on 'live' logical selection 'hist_type'
	 */
	if (lut_info->hist_type_selected)
	{
	   lut->hist_type = lut_info->hist_type;
	   refresh_lut(xvdisplay->pixelmin, range);
	}

	/*
	 * user clicked on 'live' toggle selection 'color_model'
	 */
	if (lut_info->color_model_selected)
	{
	   lut->model = lut_info->color_model_val;
	   switch (lut_info->color_model_val)
	   {
	      case RGB:
		   back	  = lut->rgb_back;
		   lut->current_lut = lut->rgb;
	           break;

	      case CMY:
		   back = lut->cmy_back;
		   lut->current_lut = lut->cmy;
	           break;

	      case HSV:
		   back = lut->hsv_back;
		   lut->current_lut = lut->hsv;
	           break;

	      case HLS:
		   back = lut->hls_back;
		   lut->current_lut = lut->hls;
	           break;

	      case YIQ:
		   back = lut->yiq_back;
		   lut->current_lut = lut->yiq;
	           break;

	      case XYZ:
		   back = lut->xyz_back;
		   lut->current_lut = lut->xyz;
	           break;

	      case UVW:
		   back = lut->uvw_back;
		   lut->current_lut = lut->uvw;
	           break;

	      case GREY:
		   back = lut->grey_back;
		   lut->current_lut = lut->grey;
	           break;
	   }
	   XtMapWidget(back);
	   XRaiseWindow(display, XtWindow(back));
	}
}



/********************************************************
*
*  Routine Name:  redisplay_lut
*
*       Purpose:  callback that displays the lookup table 
*
*        Input: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*        Output:  none
*
*     Called By:  run_lut()
*
*    Written By:  Mark Young
*
********************************************************/

void  redisplay_lut(widget, clientData, event)

Widget  widget;
caddr_t clientData;
XEvent  *event;
{
        LutCanvasStruct *canvas = (LutCanvasStruct *) clientData;

        Window      root;
	double	    xfact;
        int         x, y, x1, x2, num;
        unsigned    int width, height, border_width, depth;


	num = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;
        if (event->type == Expose)
        {
           /*
            *  Compute the x position for values between min & max pixel
            *  ie) (xvdisplay->pixelmax - xvdisplay->pixelmax)
            */
	   xfact = lut_width/((double) num);
           x1 = event->xexpose.x/xfact;
           x2 = (event->xexpose.x + event->xexpose.width)/xfact;
           num = abs(x2 - x1) + 1;
        }
        else if (event->type == ConfigureNotify)
        {
           if ( !XGetGeometry(XtDisplay(widget), XtWindow(widget), &root,&x, &y,
                              &width, &height,  &border_width, &depth))
           {
              xvf_error_wait("Window does not exist.","redisplay_lut", NULL);
              return;
           }

           lut_xpos = lut_ypos = -1;
           lut_width = width;
           lut_height = height;
           x1 = xvdisplay->pixelmin;
        }
        else
        {
           return;
        }
        refresh_lut(x1, num);

        /*
         *  If the event was an expose event and the crosshairs were exposed
         *  then we need to redraw them, but only the exposed part.
         */
        if (event->type == Expose)
        {
           if (lut_xpos > event->xexpose.x && lut_xpos <
               (event->xexpose.x + event->xexpose.width))
           {
              XDrawLine(XtDisplay(widget), XtWindow(widget), gc_xor,
                        event->xexpose.x, lut_ypos, (event->xexpose.x +
                        event->xexpose.width), lut_ypos);
           }

           if (lut_ypos > event->xexpose.y && lut_ypos <
               (event->xexpose.y + event->xexpose.height))
           {
              XDrawLine(XtDisplay(widget), XtWindow(widget), gc_xor,
                        lut_xpos, event->xexpose.y, lut_xpos,
                        (event->xexpose.y + event->xexpose.height));
           }
        }
}



/********************************************************
*
*  Routine Name:  update_lut
*
*       Purpose:  callback that updates the lookup table display; 
*		  it updates the location of the crosshairs if necessary; 
*		  it may also draw the lut line and update the values in
*		  the lookup table accordingly.
*
*        Input: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*        Output:  none
*
*     Called By:  
*
*    Written By:  Mark Young
*
********************************************************/

/* The user is allowed a +/- tolerance of 5 pixels to stop the lut update */
#define   TOLERANCE 5
static	  XColor oldcolors[MAX_PIXELS];


void  update_lut(widget, clientData, event)

Widget  widget;
caddr_t clientData;
XEvent  *event;
{
        LutCanvasStruct *canvas = (LutCanvasStruct *) clientData;

        int       i, x, y, index, num, range, starty;
        double	  xfact, yfact, slope, value, val1, val2, val3;

	XColor	  color, *xcolors = xvdisplay->xcolors;
        static    int prevx, xpos, ypos;
	static	  int size = sizeof(XColor) * MAX_PIXELS;


	if (canvas != lut->active_canvas && lut->active_canvas != NULL)
	{
	   return;
	}
        else if (event->type == MotionNotify)
        {
	   if (lut->active_canvas == NULL)
	      return;
           else if (xvd_query_position(widget, &x, &y, True))
              return;
/*
	   else if (event->xmotion.is_hint == False)
	   {
	      x = event->xmotion.x;
	      y = event->xmotion.y;
	   }
 */
        }
	else if (event->type == ButtonPress)
        {
           x = event->xbutton.x;
           y = event->xbutton.y;
        }

	/*
	 *  Get the range of pixels that will be shown
	 */
	range = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;

	/*
	 *  Get the corresponding index.
	 */
	yfact = ((double) MAX_PIXELS - 1.0)/lut_height;
	xfact = lut_width/((double) range);
	index = x/xfact + xvdisplay->pixelmin;

	if (lut->active_canvas == NULL)
	{
	   xpos  = x; ypos  = y;
	   index1 = index; prevx  = -1;
	   lut->active_canvas = canvas;
	   bcopy(xcolors, oldcolors, size);
	   draw_crosshairs(canvas, lut_xpos, lut_ypos);
	   return;
	}
	else
	{
	   index2 = index;
	   if (prevx == -1) prevx = index;
	}

	/*
	 *  Check to see if we are retreating over any area that we
	 *  previous covered.  If so then we want to restore the xcolors
	 *  from the saved xcolor array.
	 */
	if (((index1 < prevx) && (index2 < prevx)) ||
	    ((prevx < index1) && (prevx < index2)))
	{
	   i = MIN(prevx, index2);
	   num = abs(prevx - index2) +1;
	   bcopy(&oldcolors[i], &xcolors[i], num*sizeof(XColor));
	   store_colors(xcolors, i, num);
#ifdef DEBUG
fprintf(stderr,"restoring %d - %d num %d \n", i, (i+num-1), num);
#endif
	   prevx = index2;
	   return;
	}
#ifdef DEBUG
num   = abs(index1 - index2) +1;
fprintf(stderr,"advancing %d - %d num %d \n", index1, index2, num);
#endif

	if (index1 != index2)
	{
	   if (index1 < index2)
	      starty = ypos;
	   else
	      starty = y;
	   slope = ((double) y - ypos)/(index2 - index1);
	}
	else
	{
	   slope = 1.0;
	   starty = y;
	}

	index = MIN(index1,index2);
	num   = abs(index1 - index2) +1;
	for (i = index; i < index+num; i++)
	{
	   if (lut->model == RGB || lut->model == CMY || lut->model == GREY)
	      value = MAX_INTEN - (slope*(i-index) + starty)*yfact*MAX_PIXELS;
	   else
	      value = 1.0 - (slope*(i-index) + starty)*yfact/MAX_PIXELS;

#ifdef DEBUG
fprintf(stderr,"slope %f num %d value %f\n", slope, (i-index), value);
#endif

	   switch(lut->model)
	   {
	       case RGB:
		    assign_color(canvas->num, value, &xcolors[i]);
		    break;

	       case CMY:
		    RGB_to_CMY(xcolors[i], color);
		    assign_color(canvas->num, value, &color);
		    CMY_to_RGB(color, xcolors[i]);
		    break;

	       case HSV:
		    RGB_to_HSV(xcolors[i], val1, val2, val3);
		    assign_value(canvas->num, value, &val1, &val2, &val3);
		    HSV_to_RGB(val1, val2, val3, xcolors[i]);
		    break;

	       case HLS:
		    RGB_to_HLS(xcolors[i], val1, val2, val3);
		    assign_value(canvas->num, value, &val1, &val2, &val3);
		    HLS_to_RGB(val1, val2, val3, xcolors[i]);
		    break;

	       case YIQ:
		    RGB_to_YIQ(xcolors[i], val1, val2, val3);
		    assign_value(canvas->num, value, &val1, &val2, &val3);
		    YIQ_to_RGB(val1, val2, val3, xcolors[i]);
		    break;

	       case UVW:
		    RGB_to_UVW(xcolors[i], val1, val2, val3);
		    assign_value(canvas->num, value, &val1, &val2, &val3);
		    UVW_to_RGB(val1, val2, val3, xcolors[i]);
		    break;

	       case XYZ:
		    RGB_to_XYZ(xcolors[i], val1, val2, val3);
		    assign_value(canvas->num, value, &val1, &val2, &val3);
		    XYZ_to_RGB(val1, val2, val3, xcolors[i]);
		    break;

	       case GREY:
		    xcolors[i].red   = (short) value;
		    xcolors[i].green = (short) value;
		    xcolors[i].blue  = (short) value;
		    break;
	   }

	   if (xvdisplay->active[i] > 0)
	      xcolors[i].flags = DoRed | DoGreen | DoBlue;
	   else
	      xcolors[i].flags = 0;
	}
	store_colors(xcolors, index, num);

	/*
	 *  Now that we have updated the color for this range we need to see if
	 *  we should the user  
	 */
	prevx = index2;
	if (event->type == ButtonPress)
	{ 
	   if ((abs(xpos - x) < TOLERANCE) && (abs(ypos - y) < TOLERANCE))
	   {
	      draw_crosshairs(canvas, lut_xpos, lut_ypos);
	      lut->active_canvas = NULL;
	   }
	   else
	   {
	      push_cstack(oldcolors, index1, index2);
	      bcopy(xcolors, oldcolors, size);
	      index1 = index2; prevx  = -1;
	      xpos  = x; ypos  = y;
	   }
	} 
}



/********************************************************
*
*  Routine Name:  update_canvas
*
*       Purpose:  routine updates the canvas indicating where
*		  the user is.
*
*        Input: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*        Output:  none
*
*    Written By:  Mark Young
*
********************************************************/


void update_canvas(widget, clientData, event)

Widget  widget;
caddr_t clientData;
XEvent  *event;
{
        LutCanvasStruct *canvas = (LutCanvasStruct *) clientData;

	double	xfact;
	char	buf[MAXBUF];
        Arg     args[MaxArgs];
	int	xval, yval, range, x, y;


        if (event->type == MotionNotify)
        {
           if (xvd_query_position(widget, &x, &y, True))
              return;
/*
	   else if (event->xmotion.is_hint == False)
	   {
	      x = event->xmotion.x;
	      y = event->xmotion.y;
	   }
 */
        }
        else if(event->type == LeaveNotify)
        {
           if (event->xcrossing.mode == NotifyNormal &&
	       lut->active_canvas == NULL)
           {
              draw_crosshairs(canvas, lut_xpos, lut_ypos);
              lut_xpos = lut_ypos = -1;
           }
           return;
        }

        /*
         *  Update the lut cross hairs that show the users position. We also
         *  need to update the x & y position widgets to show the current
	 *  position.
         */
	if (lut->active_canvas == NULL)
           draw_crosshairs(canvas, lut_xpos, lut_ypos);

        /*
         *  Compute the x & y position.  The x value will be between
	 *  xvdisplay->pixelmin & xvdisplay->pixelmax.  The y value will
	 *  be between 0 & 255.
         */
	range = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;
	xfact = lut_width/((double) range);
        xval = x/xfact  + xvdisplay->pixelmin;
        yval = (MAX_PIXELS - 1) - (y*((MAX_PIXELS - 1.0)/lut_height)) + 0.5;

        /*
         *  Update x & y position widget to reflect the current position
         */
        (void) sprintf(buf, "%3d", xval);
        XtSetArg(args[0], XtNlabel, buf);
        XtSetValues(canvas->xpos, args, 1);

        (void) sprintf(buf, "%3d", yval);
        XtSetArg(args[0], XtNlabel, buf);
        XtSetValues(canvas->ypos, args, 1); 

	lut_xpos = x;
	lut_ypos = y;
	if (lut->active_canvas == NULL)
           draw_crosshairs(canvas, lut_xpos, lut_ypos);
}



/********************************************************
*
*  Routine Name:  refresh_lut
*
*       Purpose:  routine that refreshes the lookup table display
*
*        Input:   index - the index into the color array
*		  num   - the number of colors to refresh from index
*
*        Output:  none
*
*    Written By:  Mark Young
*
********************************************************/

refresh_lut(index, num)

int	index, num;
{
	XColor   *xcolors = xvdisplay->xcolors;
	LutCanvasStruct *luts = lut->current_lut;

	Widget   canvas;
	XColor   color;
	XPoint	 points[3][MAX_PIXELS];
	register int i, j, x, width, height, range;
	register double pt0, pt1, pt2, yfact, xfact;


	if (index > xvdisplay->pixelmin)
	{
	   index--; num++;
	}

	if ((index + num) < xvdisplay->pixelmax)
	{
	   num++;
	}
	range = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;

	/*
	 *  Compute the area of the screen so that we can clear it before
	 *  we start drawing it.
	 */
	width  = lut_width -1;
	height = lut_height -1;
	xfact = width/((double) range);
	yfact = height/((double) MAX_INTEN);

	for (i = index, j = 0; i < index+num; i++)
	{
	   if (xvdisplay->active[i] > 0)
	   {
	      switch (lut->model)
	      {
	         case RGB:
		      points[0][j].y = height - yfact*xcolors[i].red;
		      points[1][j].y = height - yfact*xcolors[i].green;
		      points[2][j].y = height - yfact*xcolors[i].blue;
		      break;

	         case CMY:
		      RGB_to_CMY(xcolors[i], color);
		      points[0][j].y = height - yfact*color.red;
		      points[1][j].y = height - yfact*color.green;
		      points[2][j].y = height - yfact*color.blue;
		      break;

	         case HSV:
		      RGB_to_HSV(xcolors[i], pt0, pt1, pt2);
		      points[0][j].y = height * (1.0 - pt0);
		      points[1][j].y = height * (1.0 - pt1);
		      points[2][j].y = height * (1.0 - pt2);
		      break;

	         case HLS:
		      RGB_to_HLS(xcolors[i],pt0, pt1, pt2);
		      points[0][j].y = height * (1.0 - pt0);
		      points[1][j].y = height * (1.0 - pt1);
		      points[2][j].y = height * (1.0 - pt2);
		      break;

	         case YIQ:
		      RGB_to_YIQ(xcolors[i], pt0, pt1, pt2);
		      points[0][j].y = height * (1.0 - pt0);
		      points[1][j].y = height * (1.0 - pt1);
		      points[2][j].y = height * (1.0 - pt2);
		      break;

	         case UVW:
		      RGB_to_UVW(xcolors[i], pt0, pt1, pt2);
		      points[0][j].y = height * (1.0 - pt0);
		      points[1][j].y = height * (1.0 - pt1);
		      points[2][j].y = height * (1.0 - pt2);
		      break;

	         case XYZ:
		      RGB_to_XYZ(xcolors[i], pt0, pt1, pt2);
		      points[0][j].y = height * (1.0 - pt0);
		      points[1][j].y = height * (1.0 - pt1);
		      points[2][j].y = height * (1.0 - pt2);
		      break;

	         case GREY:
		      points[0][j].y = height - yfact*(xcolors[i].red +
				 xcolors[i].green + xcolors[i].blue)/3;
		      break;
	      }
	      points[0][j].x =
	      points[1][j].x =
	      points[2][j].x = (i - xvdisplay->pixelmin) * xfact;
	      j++;
	   }
	}

	x     = xfact * (index - xvdisplay->pixelmin);
	width = xfact * num + 0.5;
	if (lut->model == GREY && j > 0)
	{
	   canvas = luts[0].canvas;
	   XClearArea(display,XtWindow(canvas),x,0,width,lut_height,False);

	   if (lut->histogram)
	      draw_histogram(luts[0].canvas, grey, index, num);

	   XSetForeground(display, gc_set, luts[0].pixel);
	   XDrawLines(display, XtWindow(canvas), gc_set, points[0], j,
		      CoordModeOrigin);
	}
	else if (j > 0)
	{
	   for (i = 0; i < 3; i++)
	   {
	      canvas = luts[i].canvas;
	      XClearArea(display,XtWindow(canvas),x,0,width,lut_height,False);

	      if (lut->histogram)
	         draw_histogram(luts[i].canvas, grey, index, num);

	      XSetForeground(display, gc_set, luts[i].pixel);
	      XDrawLines(display, XtWindow(canvas), gc_set, points[i], j,
		         CoordModeOrigin);
	   }
	}
}



/************************************************************
*
*  Module Name:  draw_histogram
*
*      Purpose:  draws a histogram onto the specified canvas
*		 widget.
*
*        Input:  widget - the lut canvas widget
*		 pixel  - the color to be drawn
*		 index  - begin index into the histogram
*		 num    - num of colors to draw
*
*       Output:  Draws the histogram
*
*   Written By:  Mark Young
*
*
*************************************************************/


draw_histogram(widget, pixel, index, num)

Widget  widget;
unsigned long pixel;
int	index, num;
{
	XPoint   points[MAX_PIXELS+2];
	XSegment segments[MAX_PIXELS];

	register int i, j, range;
	register double height, width, xfact, hfact;


	range  = (xvdisplay->pixelmax - xvdisplay->pixelmin) + 1;
	width  = lut_width - 1;
	height = lut_height - 1;
	xfact  = width/range;
	hfact  = height/xvdisplay->histmax;

	/*
	 *  Compute histogram
	 */
	j = 0;
	if (lut->hist_type == Continuous || lut->hist_type == Filled)
	{
	   if (lut->hist_type == Filled)
	   {
	      if ((height - xvdisplay->histogram[index] * hfact) > 0)
	      {
		 points[j].x = 0;
		 points[j].y = height;
		 j++;
	      }
	   }

	   for (i = index; i < index+num; i++)
	   {
	      points[j].x = (i - xvdisplay->pixelmin) * xfact;
	      points[j].y = height - xvdisplay->histogram[i] * hfact;
	      j++;
	   }

	   if (lut->hist_type == Filled)
	   {
	      if ((height - xvdisplay->histogram[index+num-1] * hfact) > 0)
	      {
		 points[j].x = (index + num - xvdisplay->pixelmin) * xfact;
		 points[j].x = 0;
		 points[j].y = height;
		 j++;
	      }
	   }
	}
	else if (lut->hist_type == Discrete)
	{
	   for (i = index; i < index+num; i++)
	   {
	      segments[j].x1 =
	      segments[j].x2 = (i - xvdisplay->pixelmin) * xfact;
	      segments[j].y1 = height;
	      segments[j].y2 = height - xvdisplay->histogram[i] * hfact;
	      j++;
	   }
	}

	/*
	 *  Draw histogram
	 */
	XSetForeground(display, gc_set, pixel);
	if (j > 0)
	{
	   if (lut->hist_type == Continuous)
	   {
	      XDrawLines(display, XtWindow(widget), gc_set, points, j,
			CoordModeOrigin);
	   }
	   else if (lut->hist_type == Discrete)
	   {
	      XDrawSegments(display, XtWindow(widget), gc_set, segments, j);
	   }
	   else if (lut->hist_type == Filled)
	   {
	      XFillPolygon(display, XtWindow(widget), gc_set, points, j,
			Nonconvex, CoordModeOrigin);
	   }
	}
}



/************************************************************
*
*  Module Name: draw_crosshairs
*
*      Purpose: Draws the cross hairs in each of the individual
*		lut canvases.
*
*        Input: canvas  - the canvas to draw the crosshairs
*		xpos	- the x center position of the crosshairs
*		ypos	- the y center position of the crosshairs
*
*       Output: draws the cross hairs
*
*   Written By: Mark Young
*
*
*************************************************************/


draw_crosshairs(canvas, xpos, ypos)

LutCanvasStruct *canvas;
int	xpos, ypos;
{
	Window window = XtWindow(canvas->canvas);

	/*
	 *  If the x or y postition is either -1 then return.
	 */
	if (xpos == -1 || ypos == -1)
	   return;

	XDrawLine(display, window, gc_xor, xpos, 0, xpos, lut_height);
	XDrawLine(display, window, gc_xor, 0, ypos, lut_width, ypos);
}
