home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xtici.zip / xtici / widgets / Hueleaf.c < prev    next >
C/C++ Source or Header  |  1991-08-28  |  25KB  |  809 lines

  1. /*
  2.  * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
  3.  *     All Rights Reserved
  4.  * 
  5.  * This file is a component of an X Window System client which uses the Xcms 
  6.  * Color Management System.  TekColor is a trademark of Tektronix, Inc.  The
  7.  * TekColor Editor is the subject of U.S. and foreign patents pending.  The
  8.  * term "TekHVC" designates a particular color space that is the subject of
  9.  * U.S. Patent No. 4,985,853 (equivalent foreign patents pending).
  10.  * Permission is hereby granted to use, copy, modify, sell, and otherwise
  11.  * distribute this software and its documentation for the X Window System
  12.  * environment, for any purpose and without fee, provided that:
  13.  * 
  14.  * 1.    The code and documentation are only used to implement a 
  15.  *      TekColor Editor in an X Window System environment; and
  16.  * 2.    This copyright and permission notice is reproduced in all copies
  17.  *     of the code and in supporting documentation.
  18.  * 
  19.  * Permission is granted to modify this code as required to allow it to
  20.  * be compiled on any host computer, provided that the functionality of
  21.  * the TekColor Editor is not modified in any way.  A description of any 
  22.  * modifications must be sent to Tektronix, Inc.  Contact 
  23.  * Tektronix Inc., P.O. Box 1000, Mail Station 60-850, 
  24.  * Network Displays Division Engineering, Wilsonville, OR 97070.
  25.  *
  26.  * Tektronix makes no representation about the suitability of this software
  27.  * for any purpose.  It is provided "as is" and with all faults.
  28.  * 
  29.  * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
  30.  * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  31.  * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
  32.  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  33.  * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
  34.  * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  35.  * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
  36.  *  
  37.  *  
  38.  *    NAME
  39.  *        Hueleaf.c -- the HueleafWidget a drawing area for HvcleafWidget
  40.  *
  41.  *    DESCRIPTION
  42.  *        
  43.  *
  44.  *    HISTORY
  45.  *
  46.  *    HISTORY END
  47.  *
  48.  */
  49.  
  50. #ifndef LINT
  51. static char *copy_notice = "Copyright 1991 Tektronix, Inc.";
  52. #ifdef RCS_ID
  53. static char *rcsid=  "$Header: Hueleaf.c,v 1.3 91/08/28 10:09:55 adamsc Exp $";
  54. #endif /* RCS_ID */
  55. #endif /* LINT */
  56.  
  57. #include <stdio.h>
  58. #include <X11/IntrinsicP.h>
  59. #include <X11/StringDefs.h>
  60.  
  61. #include "HueleafP.h"
  62. #include "VShell.h"
  63.  
  64. #include <X11/Xcms.h>
  65.  
  66. static XtResource resources[] = {
  67. #define offset(field) XtOffset(HueleafWidget, hueleaf.field)
  68.     /* {name, class, type, size, offset, default_type, default_addr}, */
  69.     { XtNhvc, XtCHvc, XtRHvc, sizeof(XcmsColor *),
  70.       offset(pHvc), XtRImmediate, (XtPointer)NULL },
  71.     { XtNbase, XtCBase, XtRPixel, sizeof(Pixel),
  72.       offset(base), XtRImmediate, (XtPointer)-1 },
  73.     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  74.           offset(fgpixel), XtRImmediate, (XtPointer)XtDefaultForeground },
  75.     { XtNactivateCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
  76.       offset(activate), XtRImmediate, (XtPointer)NULL },
  77. #undef offset
  78. };
  79.  
  80. /* translation table and action list */
  81. static void Arm();
  82. static void Disarm();
  83. static void Drag();
  84.  
  85. static char defaultTranslations[] = 
  86.    "<Btn1Down>:        Arm()        \n\
  87.     <Btn1Up>:        Disarm()    \n\
  88.     Button1<PtrMoved>:    Drag()";
  89.  
  90. static XtActionsRec actionsList[] =
  91. {
  92.     { "Arm",        (XtActionProc) Arm},
  93.     { "Disarm",        (XtActionProc) Disarm},
  94.     { "Drag",        (XtActionProc) Drag}
  95. };
  96.  
  97. /* forward declarations for the class struct */
  98. static void Initialize();
  99. static void Destroy();
  100. static void Exposed();
  101. static void Resized();
  102. static Boolean SetValues();
  103. static XtGeometryResult QueryGeometry();
  104.  
  105. HueleafClassRec hueleafClassRec = {
  106.     { /* Core fields */
  107.     /* superclass        */    (WidgetClass) &simpleClassRec,
  108.     /* class_name        */    "Hueleaf",
  109.     /* widget_size        */    sizeof(HueleafRec),
  110.     /* class_initialize        */    NULL,
  111.     /* class_part_initialize    */    NULL,
  112.     /* class_inited        */    FALSE,
  113.     /* initialize        */    Initialize,
  114.     /* initialize_hook        */    NULL,
  115.     /* realize            */    XtInheritRealize,
  116.     /* actions            */    actionsList,
  117.     /* num_actions        */    XtNumber(actionsList),
  118.     /* resources        */    resources,
  119.     /* num_resources        */    XtNumber(resources),
  120.     /* xrm_class        */    NULLQUARK,
  121.     /* compress_motion        */    TRUE,
  122.     /* compress_exposure    */    TRUE,
  123.     /* compress_enterleave    */    TRUE,
  124.     /* visible_interest        */    FALSE,
  125.     /* destroy            */    Destroy,
  126.     /* resize            */    Resized,
  127.     /* expose            */    Exposed,
  128.     /* set_values        */    SetValues,
  129.     /* set_values_hook        */    NULL,
  130.     /* set_values_almost    */    XtInheritSetValuesAlmost,
  131.     /* get_values_hook        */    NULL,
  132.     /* accept_focus        */    NULL,
  133.     /* version            */    XtVersion,
  134.     /* callback_private        */    NULL,
  135.     /* tm_table            */    defaultTranslations,
  136.     /* query_geometry        */    QueryGeometry,
  137.     /* display_accelerator    */    NULL,
  138.     /* extension        */    NULL
  139.   },
  140.   { /* simple fields */
  141.     /* change_sensitive        */    XtInheritChangeSensitive,
  142.   },
  143.   { /* hueleaf fields */
  144.     /* extension        */    NULL
  145.   }
  146. };
  147.  
  148. WidgetClass hueleafWidgetClass = (WidgetClass)&hueleafClassRec;
  149.  
  150. /* forward declarations for utility functions */
  151. static void DoCallback();
  152. static void Draw();
  153. static void GetGC();
  154. static XcmsFloat InterpolateVC();
  155.  
  156. /*
  157.  * Controls what happens on draw.
  158.  * Ordering is important, going from least work to most.
  159.  */
  160. #define DrawMark    0
  161. #define DrawLeaf    1
  162. #define DrawAll        2
  163.  
  164. /* this is the slop in the calculations. */
  165. #define EPS        0.001
  166.  
  167. /* Some minimum size for this leaf */
  168. #define MIN_WIDTH       200
  169.  
  170. static void Arm(leaf, e)
  171. HueleafWidget leaf;
  172. XEvent *e;
  173. {
  174.     DoCallback(leaf, (XButtonEvent *)e, CR_ARM);
  175. }
  176.  
  177. static void Disarm(leaf, e)
  178. HueleafWidget leaf;
  179. XEvent *e;
  180. {
  181.     DoCallback(leaf, (XButtonEvent *)e, CR_DISARM);
  182. }
  183.  
  184. static void Drag(leaf, e)
  185. HueleafWidget leaf;
  186. XEvent *e;
  187. {
  188.     DoCallback(leaf, (XButtonEvent *)e, CR_DRAG);
  189. }
  190.  
  191.  
  192.  
  193. static char *warning1 = "hue must be non-negative and less than 360.0";
  194. static char *warning2 = "value must be between 0.0 and 100.0, inclusive";
  195. static char *warning3 = "chroma must be non-negative";
  196. static char *warning4 = "gamut of display device in this hue is too large";
  197. static char *warning5 = "XcmsTekHVCClipC failure from SampleMaxChroma";
  198. static char *warning6 = "XcmsConvertColors failure from SampleMaxChroma";
  199.  
  200. static void Warning (widget, message)
  201. Widget widget;
  202. char *message;
  203. {
  204.     /*  R4 says use XtAppWarningMsg but no information on how to set
  205.      *  the parameters.
  206.      */
  207.     if (!widget)
  208.     XtWarning(message);
  209.     else
  210.     XtAppWarning(XtWidgetToApplicationContext(widget), message);
  211. }
  212. /*
  213.  * Sample the maximum chroma at different values for this hue.
  214.  * Values are evenly spaced between max (100.0) and the value which 
  215.  * yields the max chroma of this hue.
  216.  */
  217. static void SampleMaxChroma(leaf)
  218. HueleafWidget leaf;
  219. {
  220.     int i, j;
  221.     XcmsColor max[ChromaSamples];
  222.     XcmsFloat inc, Hue;
  223.  
  224.     Hue = leaf->hueleaf.curhvc.spec.TekHVC.H;
  225.     /*
  226.      * Max value samples does not give enough discrimination around the point
  227.      * of max chromaticity for blue hues; so weight some extra samples here.
  228.      */
  229.     for (i = 0; i < ChromaSamples-2; i++) {
  230.     max[i].spec.TekHVC.H = Hue;
  231.     max[i].format = XcmsTekHVCFormat;
  232.     }
  233.     XcmsTekHVCQueryMaxVSamples(leaf->hueleaf.pXcmsCCC, Hue,
  234.                    max, (unsigned short)(ChromaSamples-2));
  235.     for (i = 0, j = ChromaSamples - 3; i < ChromaSamples-2; i++, j--) {
  236.     leaf->hueleaf.cValue[i] = max[j].spec.TekHVC.V;
  237.     leaf->hueleaf.maxChroma[i] = max[j].spec.TekHVC.C;
  238.     }
  239.     leaf->hueleaf.cValue[ChromaSamples-1] = max[0].spec.TekHVC.V;
  240.     leaf->hueleaf.maxChroma[ChromaSamples-1] = max[0].spec.TekHVC.C;
  241.     inc = (max[0].spec.TekHVC.V - max[1].spec.TekHVC.V) / 3.0;
  242.     max[0].spec.TekHVC.H = Hue;
  243.     max[0].spec.TekHVC.V -= inc;
  244.     for (i = 0, j = ChromaSamples-2; i < 2; i++, j--) {
  245.     if (XcmsTekHVCClipC(leaf->hueleaf.pXcmsCCC, max, 1, 0, NULL) == XcmsFailure) {
  246.         Warning(leaf, warning5);
  247.         return;
  248.     }
  249.     if ((XcmsConvertColors(leaf->hueleaf.pXcmsCCC, max, 1, 
  250.                    XcmsTekHVCFormat, NULL) == XcmsFailure) &&
  251.         max[0].format != XcmsTekHVCFormat) {
  252.         Warning(leaf, warning6);
  253.         return;
  254.     }
  255.     leaf->hueleaf.cValue[j] = max[0].spec.TekHVC.V;
  256.     leaf->hueleaf.maxChroma[j] = max[0].spec.TekHVC.C;
  257.     max[0].spec.TekHVC.V -= inc;
  258.     max[0].spec.TekHVC.H = Hue;
  259.     }
  260.  
  261. }
  262.  
  263. static void Destroy(leaf)
  264. HueleafWidget leaf;
  265. {
  266.     if (leaf->hueleaf.leafGC)
  267.     XFreeGC(XtDisplay(leaf), leaf->hueleaf.leafGC);
  268. }
  269.  
  270. /*ARGSUSED*/
  271. static void Exposed(leaf, e, region)
  272. HueleafWidget leaf;
  273. XEvent *e;
  274. Region region;
  275. {
  276.     Draw(leaf, DrawAll);
  277. }
  278.  
  279. static void Initialize(req, new)
  280. HueleafWidget req, new;
  281. {
  282.     /* For now use the Default Color Conversion Context */
  283.     new->hueleaf.pXcmsCCC = XcmsDefaultCCC(XtDisplay(new), 
  284.                        DefaultScreen (XtDisplay(new)));
  285.     if (req->core.width == 0)
  286.     new->core.width = MIN_WIDTH;
  287.     if (req->core.height == 0)
  288.     new->core.height = MIN_WIDTH;
  289.  
  290.     /* Building the GC is deferred until we know we have a window, */
  291.     /* ie, when the widget is first expose */
  292.     new->hueleaf.leafGC = 0;
  293.  
  294.     if (new->hueleaf.pHvc) {
  295.     XcmsColor *phvc = new->hueleaf.pHvc;
  296.  
  297.     bcopy ((char *)phvc, (char *)&new->hueleaf.curhvc, sizeof(XcmsColor));
  298.     /*
  299.      * validate hvc values:
  300.      * hue must be in range [0-360)
  301.      * value must be [0-100]
  302.      * chroma is open ended, but must be non-negative
  303.      */
  304.     if ( (phvc->spec.TekHVC.H < 0.0) || (phvc->spec.TekHVC.H >= 360.0) ) {
  305.         Warning(new, warning1);
  306.         while (phvc->spec.TekHVC.H < 0.0) {
  307.         phvc->spec.TekHVC.H += 360.0;
  308.         }
  309.         while (phvc->spec.TekHVC.H >= 360.0) {
  310.         phvc->spec.TekHVC.H -= 360.0;
  311.         }
  312.         new->hueleaf.curhvc.spec.TekHVC.H = phvc->spec.TekHVC.H;
  313.  
  314.     }
  315.     if ( (phvc->spec.TekHVC.V < 0.0) || (phvc->spec.TekHVC.V > 100.0) ) {
  316.         Warning(new, warning2);
  317.         if (phvc->spec.TekHVC.V < 0.0) {
  318.         new->hueleaf.curhvc.spec.TekHVC.V = EPS;
  319.         }
  320.         if (phvc->spec.TekHVC.V > 100.0) {
  321.         new->hueleaf.curhvc.spec.TekHVC.V = 100.0 - EPS;
  322.         }
  323.     }
  324.     if (phvc->spec.TekHVC.C < 0.0) {
  325.         Warning(new, warning3);
  326.         new->hueleaf.curhvc.spec.TekHVC.C = 0.0;
  327.     } else if (phvc->spec.TekHVC.C > LeafMaxChroma) {
  328.         /* Not an error, so no warning, but restrict anyway */
  329.         new->hueleaf.curhvc.spec.TekHVC.C = LeafMaxChroma;
  330.     }
  331.     } else {
  332.     new->hueleaf.curhvc.spec.TekHVC.H = 0.0;
  333.     new->hueleaf.curhvc.spec.TekHVC.V = 0.0;
  334.     new->hueleaf.curhvc.spec.TekHVC.C = 0.0;
  335.     new->hueleaf.curhvc.format = XcmsTekHVCFormat;
  336.     new->hueleaf.curhvc.pixel = -1;
  337.     }
  338.  
  339.     SampleMaxChroma(new);
  340.  
  341.     new->hueleaf.dragging = False;
  342.     new->hueleaf.lastx = new->hueleaf.lasty = -1;
  343. }
  344.  
  345. /* Only allow the leaf widget to be a square.              */
  346. /* The HVC leaf is assumed to have an aspect ratio of x / y = 1.0 */
  347. /* This is not used because geometry is determined in Manageleaf.c */
  348. static XtGeometryResult QueryGeometry (leaf, proposed, result)
  349.     HueleafWidget leaf;
  350.     XtWidgetGeometry *proposed, *result;
  351. {
  352.     XtGeometryMask requestMode = proposed->request_mode & (CWWidth | CWHeight);
  353.  
  354.     if (!((requestMode & CWHeight) || (requestMode & CWWidth))) {
  355.     result->request_mode = NULL;
  356.     return (XtGeometryYes);
  357.     }
  358.  
  359.     if (leaf->core.width == proposed->width && 
  360.     leaf->core.height == proposed->height && 
  361.     proposed->width == proposed->height && 
  362.     proposed->width > MIN_WIDTH) {
  363.         return (XtGeometryNo);
  364.     }
  365.  
  366.     result->request_mode = (CWWidth | CWHeight);
  367.     if (proposed->height != proposed->width) {
  368.     if (proposed->height > proposed->width) {
  369.         result->height = proposed->width;
  370.         result->width = proposed->width;
  371.     } else {
  372.         result->width = proposed->height;
  373.         result->height = proposed->height;        
  374.     }
  375.     } else {
  376.     result->height = proposed->height;
  377.     result->width = proposed->width;
  378.     if (result->height < MIN_WIDTH) {
  379.         result->height = result->width = MIN_WIDTH;
  380.         return (XtGeometryAlmost);
  381.     } else {
  382.         return (XtGeometryYes);
  383.     }
  384.     }
  385.     if (result->height < 100) {
  386.     result->height = result->width = MIN_WIDTH;
  387.     }
  388.     return (XtGeometryAlmost);
  389. }
  390.  
  391. /* Could cache leaf outline points for current size */
  392. static void Resized(leaf)
  393. HueleafWidget leaf;
  394. {
  395.     XRectangle rect;
  396.  
  397.     /* adjust the clipping rectangle to fit new geometry */
  398.     /* if there is not a context yet, simply return */
  399.     if (!leaf->hueleaf.leafGC)
  400.     return;
  401.  
  402.     rect.x = 0;
  403.     rect.y = 0;
  404.     rect.width = leaf->core.width;
  405.     rect.height = leaf->core.height;
  406.     /* only one rectanlge, so it must be banded */
  407.     XSetClipRectangles(XtDisplay(leaf), leaf->hueleaf.leafGC, 0, 0,
  408.         &rect, 1, YXBanded);
  409. }
  410.  
  411. /*
  412.  * Always draw ourselves if needed, so we can avoid redrawing shadows.
  413.  * This means we always return false, of course.
  414.  */
  415. /*ARGSUSED*/
  416. static Boolean SetValues(cur, req, new)
  417. HueleafWidget cur, req, new;
  418. {
  419.     int flag = -1;
  420.  
  421.     if (!new->hueleaf.pHvc)
  422.     return(False);
  423.  
  424.     if ( (new->hueleaf.pHvc->spec.TekHVC.H < 0.0) || (new->hueleaf.pHvc->spec.TekHVC.H >= 360.0) ) {
  425.     Warning(new, warning1);
  426.     new->hueleaf.curhvc.spec.TekHVC.H = cur->hueleaf.curhvc.spec.TekHVC.H;
  427.     } else
  428.     new->hueleaf.curhvc.spec.TekHVC.H = new->hueleaf.pHvc->spec.TekHVC.H;
  429.     if ( (new->hueleaf.pHvc->spec.TekHVC.V < 0.0) || (new->hueleaf.pHvc->spec.TekHVC.V > 100.0) ) {
  430.     Warning(new, warning2);
  431.     new->hueleaf.curhvc.spec.TekHVC.V = cur->hueleaf.curhvc.spec.TekHVC.V;
  432.     } else
  433.     new->hueleaf.curhvc.spec.TekHVC.V = new->hueleaf.pHvc->spec.TekHVC.V;
  434.     if (new->hueleaf.pHvc->spec.TekHVC.C < 0.0) {
  435.         Warning(new, warning3);
  436.     new->hueleaf.curhvc.spec.TekHVC.C = cur->hueleaf.curhvc.spec.TekHVC.C;
  437.     } else
  438.     new->hueleaf.curhvc.spec.TekHVC.C = new->hueleaf.pHvc->spec.TekHVC.C;
  439.     if (new->hueleaf.pHvc->spec.TekHVC.C > LeafMaxChroma) {
  440.         /* Not an error, so no warning, but restrict anyway */
  441.         new->hueleaf.pHvc->spec.TekHVC.C = LeafMaxChroma;
  442.     }
  443.  
  444.     if (new->hueleaf.curhvc.spec.TekHVC.H != cur->hueleaf.curhvc.spec.TekHVC.H) {
  445.     SampleMaxChroma(new);
  446.     if (flag < DrawLeaf)
  447.         flag = DrawLeaf;
  448.     }
  449.     if ( (new->hueleaf.curhvc.spec.TekHVC.V != cur->hueleaf.curhvc.spec.TekHVC.V) 
  450.         || (new->hueleaf.curhvc.spec.TekHVC.C != cur->hueleaf.curhvc.spec.TekHVC.C) ) {
  451.     if (flag < DrawMark)
  452.         flag = DrawMark;
  453.     }
  454.     if (new->hueleaf.base != cur->hueleaf.base) {
  455.     if (flag < DrawLeaf)
  456.         flag = DrawLeaf;
  457.     }
  458.  
  459.     if (flag >= 0 && XtIsRealized((Widget)new))
  460.     Draw(new, flag);
  461.  
  462.     return(False);
  463. }
  464.  
  465. #define XtoC(ex)  (LeafMaxChroma * \
  466.               (XcmsFloat)((ex) / (XcmsFloat)(width - 1)))
  467. #define YtoV(ey)  (100.0 - \
  468.               (((XcmsFloat)(ey) / (XcmsFloat)(height - 1)) * 100.0))
  469. /*
  470.  * This event type is a bit of a hack.
  471.  * This assumes that only button and mouse motion events will be passed
  472.  * this routine, and that the fields of interest in the structures (x,y)
  473.  * are identical.  As it turns out, the structures are exactly identical -
  474.  * at least when this was written.
  475.  */
  476. static void DoCallback(leaf, e, reason)
  477. HueleafWidget leaf;
  478. XButtonEvent *e;
  479. int reason;
  480. {
  481.     int width = leaf->core.width;
  482.     int height = leaf->core.height;
  483.     XcmsFloat v, c;
  484.     XcmsFloat diffv, diffc;
  485.     XcmsColor tmp;
  486.     HvcCallbackStruct data;
  487.  
  488.     v = YtoV(e->y);
  489.     c = XtoC(e->x);
  490.  
  491.     /* apply our known constraints */
  492.     if (v < 0.0)
  493.     v = 0.0;
  494.     else if (v > 100.0)
  495.     v = 100.0;
  496.     if (c < 0.0)
  497.     c = 0.0;
  498.     else if (c > LeafMaxChroma)
  499.     c = LeafMaxChroma;
  500.  
  501.     /* Don't exercise callback on drags if there is no change */
  502.     diffv = v - leaf->hueleaf.curhvc.spec.TekHVC.V;
  503.     if (diffv < 0.0)
  504.     diffv *= -1.0;        /* avoid call to abs */
  505.     diffc = c - leaf->hueleaf.curhvc.spec.TekHVC.C;
  506.     if (diffc < 0.0)
  507.     diffc *= -1.0;        /* avoid call to abs */
  508.     /* 0.1 is probably better than the device can handle, anyway */
  509.     if ( (diffv < 0.1) && (diffc < 0.1) && (reason == CR_DRAG) )
  510.     return;
  511.  
  512.     data.reason = reason;
  513.     data.event = (XEvent *)e;
  514.     data.hvc = &tmp;
  515.     tmp.spec.TekHVC.H = leaf->hueleaf.curhvc.spec.TekHVC.H;    /* hue never changes */
  516.     tmp.spec.TekHVC.V = v;
  517.     tmp.spec.TekHVC.C = c;
  518.     XtCallCallbacks((Widget)leaf, XtNactivateCallback, &data);
  519.  
  520.     /* could check to see if application trashed return */
  521.     leaf->hueleaf.curhvc.spec.TekHVC.V = tmp.spec.TekHVC.V;
  522.     leaf->hueleaf.curhvc.spec.TekHVC.C = tmp.spec.TekHVC.C;
  523.     /* Could check to see if there still is a change, but it's not worth it */
  524.     Draw(leaf, DrawMark);
  525. }
  526.  
  527. /* There is a point for each sample plus value 100 and value 0 */
  528. #define PointCt (ChromaSamples+2)
  529. /*  this (MarkSize) could be a widget attribute */
  530. #define MarkSize 5
  531. #define CtoX(c) ((int)((XcmsFloat)(c) * (XcmsFloat)(width-1) / \
  532.           LeafMaxChroma + 0.5))
  533. #define VtoY(v) (height - \
  534.          ((int)((XcmsFloat)(v) * (XcmsFloat)(height-1) / 100.0 + 0.5) \
  535.           + 1))
  536.  
  537. #define MarkLevels  12
  538. static XcmsFloat markValues[MarkLevels+1] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 
  539.     60.0, 70.0, 80.0, 87.5, 92.5, 97.5, 100.0};
  540. /* back off leaf fill this much from the edge of the leaf. Could be attribute*/
  541. #define FillPad    2
  542. /* It should not take any more than this colors to fill the leaf */
  543. #define MaxLeafColors    56
  544.  
  545. #define MIN(a,b)        ( ((a)<(b)) ? (a) : (b) )
  546.  
  547. #define fillPtsCopy(a,b) \
  548.     a[0] = b[0]; \
  549.     a[1] = b[1]; \
  550.     a[2] = b[2]; \
  551.     a[3] = b[3];
  552.  
  553. static void Draw(leaf, howmuch)
  554. HueleafWidget leaf;
  555. int howmuch;
  556. {
  557.     XPoint leafPts[PointCt];
  558.     Display *dpy = XtDisplay(leaf);
  559.     Window win = XtWindow(leaf);
  560.     int i;
  561.     int visualclass;
  562.     int x = 0;
  563.     int y = 0;
  564.     int width = leaf->core.width;
  565.     int height = leaf->core.height;
  566.     int mx, my;
  567.  
  568.     if (!XtIsRealized((Widget)leaf))
  569.     return;
  570.  
  571.     if (!leaf->hueleaf.leafGC)
  572.     GetGC(leaf);
  573.  
  574.     if (howmuch == DrawAll)
  575.     XClearWindow(dpy, win);
  576.     else if (howmuch == DrawLeaf)
  577.     XClearArea(dpy, win, x, y, width, height, False);
  578.     else
  579.     /* clear all around the mark, just in case */
  580.     XClearArea(dpy, win, 
  581.         leaf->hueleaf.lastx - 2 * MarkSize, 
  582.         leaf->hueleaf.lasty - 2 * MarkSize, 
  583.         4 * MarkSize, 4 * MarkSize, False);
  584.  
  585.     /* if erasing the mark damaged the frame, redraw everything */
  586.     if ( (leaf->hueleaf.lastx <= 2 * MarkSize)
  587.         || (leaf->hueleaf.lasty <= 2 * MarkSize)
  588.         || (leaf->hueleaf.lastx >= width - 2 * MarkSize)
  589.         || (leaf->hueleaf.lasty >= height - 2 * MarkSize) )
  590.     howmuch = DrawAll;
  591.  
  592.     /* always draw the outline of the leaf */
  593.     leafPts[0].x = x;
  594.     leafPts[0].y = y;
  595.     for (i = 1; i < PointCt - 1; i++) {
  596.     leafPts[i].x = CtoX(leaf->hueleaf.maxChroma[i-1]);
  597.     leafPts[i].y = VtoY(leaf->hueleaf.cValue[i-1]);
  598.     }
  599.     leafPts[PointCt-1].x = x;
  600.     leafPts[PointCt-1].y = y + height - 1;
  601.     XDrawLines(dpy, win, leaf->hueleaf.leafGC, 
  602.             leafPts, PointCt, CoordModeOrigin);
  603.  
  604.     /* if there is a base color, always fill the leaf */
  605.     if ((int)leaf->hueleaf.base >= 0) {
  606.     XPoint maxPoint, fillPts[4], fillPtsSave[2*MaxLeafColors][4];
  607.     int markX[MarkLevels+1];
  608.     int j, k, n;
  609.     XcmsFloat tx, ty;
  610.     XcmsColor cell[MaxLeafColors+1];
  611.     Pixel index = leaf->hueleaf.base;
  612.     /* unsigned char clamped[MaxLeafColors+1]; */
  613.  
  614.     maxPoint = leafPts[PointCt-2];
  615.     /* find points of max chroma at value .9, .8, ..., .1 */
  616.     j = 0;
  617.     for (i = MarkLevels - 1; i > 0; i--) {
  618.         ty = markValues[i];
  619.         while ( (j < ChromaSamples) && (leaf->hueleaf.cValue[j] > ty) ) {
  620.         j++;
  621.         }
  622.         if (j == 0)
  623.         tx = InterpolateVC(ty, 100.0, leaf->hueleaf.cValue[0],
  624.             0.0, leaf->hueleaf.maxChroma[0]);
  625.         else if (j == ChromaSamples)
  626.         tx = InterpolateVC(ty, leaf->hueleaf.cValue[j-1], 0.0,
  627.             leaf->hueleaf.maxChroma[j-1], 0.0);
  628.         else
  629.         tx = InterpolateVC(ty, 
  630.             leaf->hueleaf.cValue[j-1], leaf->hueleaf.cValue[j],
  631.             leaf->hueleaf.maxChroma[j-1], leaf->hueleaf.maxChroma[j]);
  632.  
  633.         markX[i] = CtoX(tx);
  634.     }
  635.     markX[0] = CtoX(0.0);
  636.     markX[MarkLevels] = markX[0];
  637.  
  638.     k = n = 0;
  639.     fillPts[2].y = VtoY(100.0);
  640.     for (i = MarkLevels; i > 0; i--) {
  641.         /* set value corresponding to middle of square */
  642.         fillPts[0].y = fillPts[2].y;    /* copy from previous edge */
  643.         fillPts[1].y = fillPts[0].y;
  644.         fillPts[2].y = VtoY(markValues[i-1]);
  645.         fillPts[3].y = fillPts[2].y;
  646.         fillPts[1].x = CtoX(0.0) - FillPad;
  647.         for (j = 0; j < 10; j++) {
  648.         fillPts[0].x = fillPts[1].x;    /* copy from previous edge */
  649.         fillPts[3].x = fillPts[0].x;
  650.         /* but set the chroma to right edge value */
  651.         cell[k].pixel = index;
  652.         cell[k].format = XcmsTekHVCFormat;
  653.         cell[k].spec.TekHVC.H = leaf->hueleaf.curhvc.spec.TekHVC.H;
  654.         cell[k].spec.TekHVC.V = 
  655.                 (markValues[i] + markValues[i-1]) / 2.0;
  656.         cell[k].spec.TekHVC.C = (XcmsFloat)(j + 1) * 10.0;
  657.         fillPts[1].x = CtoX((XcmsFloat)(j+1) * 10.0) - FillPad;
  658.         fillPts[2].x = fillPts[1].x;
  659.         if ( (fillPts[1].x > markX[i]) 
  660.             || (fillPts[2].x > markX[i-1]) ) {
  661.             fillPts[1].x = markX[i] - FillPad;
  662.             fillPts[2].x = markX[i-1] - FillPad;
  663.             j = 11;    /* force a break */
  664.         }
  665.         fillPtsCopy(fillPtsSave[n],fillPts);
  666.         n++;
  667.         /* Don't allow the cell range to overflow */
  668.         index++;
  669.         if (++k >= MaxLeafColors) {
  670.             --k;    --index;
  671.             Warning(leaf, warning4);
  672.         }
  673.         }
  674.     }
  675.  
  676.     /* fill the tip triangle with most vivid color */
  677.     fillPts[1] = maxPoint;
  678.     fillPts[1].x -= FillPad;
  679.     fillPts[2].x = CtoX(0.0) - FillPad;
  680.     fillPts[2].y = VtoY(100.0);
  681.     for (i = MarkLevels-1; i > 0; i--) {
  682.         fillPts[0] = fillPts[2];
  683.         fillPts[2].x = markX[i] - FillPad;
  684.         fillPts[2].y = VtoY(markValues[i]);
  685.         if (fillPts[2].y > maxPoint.y)
  686.         break;
  687.     }
  688.         fillPts[3] = fillPts[0];        /* assign 4th point for consistency */
  689.         fillPtsCopy(fillPtsSave[n],fillPts);
  690.         n++;
  691.  
  692.     cell[k].pixel = index;
  693.     cell[k].format = XcmsTekHVCFormat;
  694.     cell[k].spec.TekHVC.H = leaf->hueleaf.curhvc.spec.TekHVC.H;
  695.     cell[k].spec.TekHVC.V = leaf->hueleaf.cValue[ChromaSamples-1];
  696.     cell[k].spec.TekHVC.C = leaf->hueleaf.maxChroma[ChromaSamples-1];
  697.     k++;
  698.  
  699.     /* 
  700.      * Store colors cached in array while filling the leaf.
  701.      * Colors on edge of fill will have chromas decreased.
  702.      */
  703.     visualclass = GetVisualClass ((Widget)leaf);
  704.     if (visualclass == PseudoColor || visualclass == DirectColor ||
  705.         visualclass == GrayScale) 
  706.     {
  707.         if (XcmsStoreColors(XtDisplay(leaf), leaf->core.colormap, 
  708.                 cell, k, NULL) == XcmsFailure) {
  709. #ifdef XDEBUG
  710.         printf ("Warning: XcmsStoreColors failed in Hueleaf.c\n");
  711. #endif
  712.         }
  713.     } else {
  714.         for (i = 0; i < k; i++) {
  715.         if (XcmsAllocColor(XtDisplay(leaf), leaf->core.colormap,
  716.                    &cell[i], NULL)  == XcmsFailure) {
  717. #ifdef XDEBUG
  718.             printf ("Warning: XcmsAllocColors failed in Hueleaf.c\n");
  719. #endif        
  720.         }
  721.         }
  722.     }
  723.  
  724.         /*
  725.          * Fill all the saved polygons at once.
  726.          * There is a remote chance that we may have more polygons than
  727.          * the max color indeces that we could use.  This is handled by
  728.          * reusing the last index on the overflow polygons.
  729.          */
  730.         for(i = 0; i < n; i++) {
  731.         XSetForeground(dpy,leaf->hueleaf.leafGC,cell[MIN(i,k-1)].pixel);
  732.         XFillPolygon(dpy,win,leaf->hueleaf.leafGC,fillPtsSave[i],4,Convex,CoordModeOrigin);
  733.         }
  734.      
  735.     /* restore foreground to default */
  736.     XSetForeground(dpy, leaf->hueleaf.leafGC, leaf->hueleaf.fgpixel);
  737.     }
  738.  
  739.     /* always draw the marker */
  740.     mx = CtoX(leaf->hueleaf.curhvc.spec.TekHVC.C) - MarkSize / 2;
  741.     my = VtoY(leaf->hueleaf.curhvc.spec.TekHVC.V) - MarkSize / 2;
  742.     XFillRectangle(dpy, win, leaf->hueleaf.leafGC,
  743.             mx, my, MarkSize, MarkSize);
  744.  
  745.     /* remember position so it can be erased next time */
  746.     leaf->hueleaf.lastx = mx;
  747.     leaf->hueleaf.lasty = my;
  748. }
  749.  
  750. static void GetGC(leaf)
  751. HueleafWidget leaf;
  752. {
  753.     XtGCMask mask = GCForeground | GCBackground | GCFillStyle;
  754.     XGCValues gcv;
  755.     XRectangle rect;
  756.  
  757.     gcv.foreground = leaf->hueleaf.fgpixel;
  758.     gcv.background = leaf->core.background_pixel;
  759.     gcv.fill_style = FillSolid;
  760.     leaf->hueleaf.leafGC=XCreateGC(XtDisplay(leaf), XtWindow(leaf), mask,&gcv);
  761.  
  762.     mask |= GCLineStyle;
  763.     gcv.line_style = LineDoubleDash;
  764.  
  765.     rect.x = 0;
  766.     rect.y = 0;
  767.     rect.width = leaf->core.width;
  768.     rect.height = leaf->core.height;
  769.     /* only one rectanlge, so it must be banded */
  770.     XSetClipRectangles(XtDisplay(leaf), leaf->hueleaf.leafGC, 0, 0,
  771.         &rect, 1, YXBanded);
  772. }
  773.  
  774. /*
  775.  * Simple linear interpolation between points.
  776.  */
  777. static XcmsFloat InterpolateVC(midy, hiy, loy, hix, lox)
  778. XcmsFloat midy, hiy, loy, hix, lox;
  779. {
  780.     return(lox + (hix - lox) * (midy - loy) / (hiy - loy));
  781. }
  782.  
  783. /*
  784.  * Externally visible convenience function to update value, chroma
  785.  */
  786. void HueleafSetBase(leaf, base)
  787. HueleafWidget leaf;
  788. Pixel base;
  789. {
  790.     if ( base == leaf->hueleaf.base )
  791.     return;
  792.  
  793.     leaf->hueleaf.base = base;
  794.     Draw(leaf, DrawLeaf);
  795. }
  796.  
  797. void HueleafSetVC(leaf, v, c)
  798. HueleafWidget leaf;
  799. XcmsFloat v, c;
  800. {
  801.     if ((v == leaf->hueleaf.curhvc.spec.TekHVC.V) && 
  802.         (c == leaf->hueleaf.curhvc.spec.TekHVC.C) )
  803.     return;
  804.  
  805.     leaf->hueleaf.curhvc.spec.TekHVC.V = v;
  806.     leaf->hueleaf.curhvc.spec.TekHVC.C = c;
  807.     Draw(leaf, DrawMark);
  808. }
  809.