home *** CD-ROM | disk | FTP | other *** search
/ Fractal Frenzy 1 / WalnutCreekFractalFrenzy-1.iso / pc / viewers / x11 / xv221.tz / xv221 / xv-2.21 / xvgam.c < prev    next >
C/C++ Source or Header  |  1992-04-28  |  75KB  |  2,816 lines

  1. /* 
  2.  * xvgam.c
  3.  *
  4.  * callable functions:
  5.  *   <many>
  6.  */
  7.  
  8. /*
  9.  * Copyright 1989, 1990, 1991, 1992 by John Bradley and
  10.  *                       The University of Pennsylvania
  11.  *
  12.  * Permission to use, copy, and distribute for non-commercial purposes,
  13.  * is hereby granted without fee, providing that the above copyright
  14.  * notice appear in all copies and that both the copyright notice and this
  15.  * permission notice appear in supporting documentation. 
  16.  *
  17.  * The software may be modified for your own purposes, but modified versions
  18.  * may not be distributed.
  19.  *
  20.  * This software is provided "as is" without any expressed or implied warranty.
  21.  *
  22.  * The author may be contacted via:
  23.  *    US Mail:   John Bradley
  24.  *               GRASP Lab, Room 301C
  25.  *               3401 Walnut St.  
  26.  *               Philadelphia, PA  19104
  27.  *
  28.  *    Phone:     (215) 898-8813
  29.  *    EMail:     bradley@cis.upenn.edu       
  30.  */
  31.  
  32.  
  33. #include "xv.h"
  34. #include "bitmaps.h"
  35.  
  36. #define GAMW 664
  37. #define GAMH 518
  38.  
  39. #define GAMbutF 386
  40.  
  41. #define CMAPX  10
  42. #define CMAPY  17
  43. #define CMAPCW 12
  44. #define CMAPCH 10
  45. #define CMAPW  (CMAPCW * 16)
  46. #define CMAPH  (CMAPCH * 16)
  47.  
  48. #define MAXUNDO 32
  49.  
  50. #define BUTTH   23
  51. #define N_HMAP   6     /* # of Hue modification remappings */
  52.  
  53. #define N_HDBUTT 5
  54. #define HDB_ROTL  0
  55. #define HDB_ROTR  1
  56. #define HDB_EXPND 2
  57. #define HDB_SHRNK 3
  58. #define HDB_FLIP  4
  59.  
  60. #define N_HDBUTT2 4
  61. #define HDB_DESAT 2
  62. #define HDB_SAT   3
  63.  
  64.  
  65. #define HD_CLEAR  0x01   /* clears inside of hue dial */
  66. #define HD_FRAME  0x02
  67. #define HD_HANDS  0x04
  68. #define HD_DIR    0x08
  69. #define HD_VALS   0x10
  70. #define HD_TITLE  0x20
  71. #define HD_CLHNDS 0x40
  72. #define HD_BUTTS  0x80
  73. #define HD_ALL   (HD_FRAME | HD_HANDS | HD_DIR | HD_VALS | HD_TITLE | HD_BUTTS)
  74.  
  75. #define HD_RADIUS 30   /* radius of bounding circle of HDIALs */
  76.  
  77. #define DEG2RAD (3.14159 / 180.0)
  78. #define RAD2DEG (180.0 / 3.14159)
  79.  
  80.  
  81.  
  82. /* stuff for colormap Undo button */
  83. struct cmapstate { byte  r[256], g[256], b[256];
  84.            int cellgroup[256];
  85.            int curgroup;
  86.            int maxgroup;
  87.            int editColor;
  88.          } prevcmap, tmpcmap;
  89.  
  90. struct hmap {    int src_st;
  91.          int src_en;
  92.          int src_ccw;
  93.          int dst_st;
  94.          int dst_en;
  95.          int dst_ccw;
  96.            };
  97.  
  98. struct gamstate { struct hmap hmap[N_HMAP];
  99.           int hueRBnum;                          /* 1 - 6 */
  100.           int wht_stval, wht_satval, wht_enab;
  101.           int satval;
  102.           GRAF_STATE istate, rstate, gstate, bstate;
  103.         } undo[MAXUNDO], preset[4], defstate; 
  104. static int uptr, uhead, utail;
  105.  
  106. typedef struct huedial {
  107.          Window win;      /* window that dial exists in */
  108.          int    x,y;      /* coordinates of center of dial */
  109.                  int    range;    /* 0 = single value, 1 = range */
  110.          int    stval;    /* start of range (ONLY val ifnot range) */
  111.          int    enval;    /* end of range */
  112.          int    ccwise;   /* 1 if range goes ccwise, 0 if cwise */
  113.          char  *str;      /* title string */
  114.          u_long fg,bg;    /* colors */
  115.          int    satval;   /* saturation value on non-range dial */
  116.          BUTT   hdbutt[N_HDBUTT];
  117.          CBUTT  enabCB;
  118.            } HDIAL;
  119.  
  120.  
  121.  
  122. static BUTT   hueclrB;
  123. static CBUTT  enabCB, autoCB, resetCB;
  124. static HDIAL  srcHD, dstHD, whtHD;
  125. static DIAL   satDial, rhDial, gsDial, bvDial;
  126. static GRAF   intGraf, rGraf, gGraf, bGraf;
  127. static RBUTT *hueRB;
  128. static Window cmapF, hsvF, rgbF, modF, butF;
  129. static struct hmap hmap[N_HMAP];
  130. static int    hremap[360];
  131.  
  132.  
  133.  
  134. #ifdef __STDC__
  135. static void changedGam();
  136. static void drawGam(int,int,int,int);
  137. static void drawBut(int,int,int,int);
  138. static void drawArrow(int, int);
  139. static void drawCmap(void);
  140. static void clickCmap(int,int,int);
  141. static void clickGam(int,int);
  142. static void selectCell(int,int);
  143. static int  deladdCell(int, int);
  144. static void doCmd(int);
  145. static void SetHSVmode(void);
  146. static void applyGamma();
  147. static void calcHistEQ(int *);
  148. static void saveGamState(void);
  149. static void gamUndo(void);
  150. static void gamRedo(void);
  151. static void save_gstate(struct gamstate *);
  152. static void restore_gstate(struct gamstate *);
  153. static void rndCols(void);
  154. static void saveCMap(struct cmapstate *);
  155. static void restoreCMap(struct cmapstate *);
  156. static void parseResources(void);
  157. static void makeResources(void);
  158.  
  159. static void HDCreate(HDIAL *, Window, int, int, int, int, int, int, char *,
  160.               u_long, u_long);
  161. static void HDRedraw(HDIAL *, int);
  162. static int  HDClick(HDIAL *, int, int);
  163. static int  HDTrack(HDIAL *, int, int);
  164. static int  hdg2xdg(int);
  165. static void pol2xy(int, int, double, int, int *, int *);
  166. static int  computeHDval(HDIAL *, int, int);
  167. static void initHmap(void);
  168. static void init1hmap(int);
  169. static void dials2hmap(void);
  170. static void hmap2dials(void);
  171. static void build_hremap(void);
  172.  
  173. #else
  174. static void changedGam(), drawGam(), drawBut(), drawArrow(), drawCmap();
  175. static void clickCmap(), clickGam(), selectCell();
  176. static void doCmd(), applyGamma(), calcHistEQ();
  177. static int  deladdCell();
  178. static void SetHSVmode(), saveGamState(), gamUndo(), gamRedo();
  179. static void save_gstate(), restore_gstate();
  180. static void rndCols(), saveCMap(), restoreCMap(), parseResources();
  181. static void makeResources();
  182.  
  183. static void HDCreate(), HDRedraw(), pol2xy();
  184. static int  HDClick(), HDTrack(), hdg2xdg(), computeHDval();
  185. static void initHmap(), init1hmap(), dials2hmap(), hmap2dials();
  186. static void build_hremap();
  187. #endif
  188.  
  189.  
  190.  
  191.  
  192.  
  193. /***************************************************/
  194. void CreateGam(geom)
  195. char *geom;
  196. {
  197.   XClassHint classh;
  198.  
  199.   gamW = CreateWindow("xv color editor", geom, GAMW,GAMH, infofg,infobg);
  200.   if (!gamW) FatalError("can't create cedit window!");
  201.  
  202.   classh.res_name = "xv";
  203.   classh.res_class = "XVcedit";
  204.   XSetClassHint(theDisp, gamW, &classh);
  205.   StoreDeleteWindowProp(gamW);
  206.  
  207.   cmapF = XCreateSimpleWindow(theDisp,gamW, 10,   8,212,339, 1,infofg,infobg);
  208.   butF  = XCreateSimpleWindow(theDisp,gamW, 10, 353,212, 96, 1,infofg,infobg);
  209.   modF  = XCreateSimpleWindow(theDisp,gamW, 10, 455,212, 53, 1,infofg,infobg);
  210.   hsvF  = XCreateSimpleWindow(theDisp,gamW, 242,  8,205,500, 1,infofg,infobg);
  211.   rgbF  = XCreateSimpleWindow(theDisp,gamW, 467,  8,185,500, 1,infofg,infobg);
  212.  
  213.   if (!cmapF || !butF || !modF || !hsvF || !rgbF) 
  214.     FatalError("couldn't create frame windows");
  215.  
  216.   XSelectInput(theDisp, gamW,  ExposureMask);
  217.   XSelectInput(theDisp, cmapF, ExposureMask | ButtonPressMask);
  218.   XSelectInput(theDisp, butF,  ExposureMask | ButtonPressMask);
  219.   XSelectInput(theDisp, modF,  ExposureMask | ButtonPressMask);
  220.   XSelectInput(theDisp, hsvF,  ExposureMask | ButtonPressMask);
  221.   XSelectInput(theDisp, rgbF,  ExposureMask);
  222.  
  223.   if (ctrlColor) XSetWindowBackground(theDisp, gamW, locol);
  224.             else XSetWindowBackgroundPixmap(theDisp, gamW, grayTile);
  225.  
  226.   /********** COLORMAP editing doo-wahs ***********/
  227.  
  228.  
  229.   BTCreate(&gbut[G_BCOLUNDO], cmapF, 5, 182, 66, BUTTH, 
  230.        "ColUndo", infofg, infobg);
  231.   BTCreate(&gbut[G_BCOLREV], cmapF,  5 + 66 + 1, 182, 67, BUTTH, 
  232.        "Revert", infofg, infobg);
  233.   BTCreate(&gbut[G_BHSVRGB], cmapF,  5+66+67+2,  182, 66, BUTTH, 
  234.        "RGB/HSV", infofg, infobg);
  235.  
  236.   BTCreate(&gbut[G_BMONO], cmapF,    5, 206, 66, BUTTH, 
  237.        "Grey", infofg, infobg);
  238.   BTCreate(&gbut[G_BRV],   cmapF,    5 + 66 + 1, 206, 67, BUTTH, 
  239.        "RevVid", infofg, infobg);
  240.   BTCreate(&gbut[G_BRNDCOL], cmapF,  5 + 66 + 67 + 2, 206, 66, BUTTH, 
  241.        "Random", infofg, infobg);
  242.  
  243.   DCreate(&rhDial, cmapF, 5, 232, 66, 100,   0,360,180, 5, 
  244.       infofg, infobg, "Hue", NULL);
  245.   DCreate(&gsDial, cmapF, 72, 232, 66, 100,  0,360,180, 5, 
  246.       infofg, infobg, "Sat.", NULL);
  247.   DCreate(&bvDial, cmapF, 139, 232, 66, 100,   0,360,180, 5, 
  248.       infofg, infobg, "Value", NULL);
  249.  
  250.  
  251.   /*********** CONTROL BUTTONS ***********/
  252.  
  253. /* positioning constants for buttons.  (arranged as 4x4 grid...) */
  254. #define BXSPACE 53
  255. #define BYSPACE (BUTTH+1)
  256.  
  257. #define BX0 0
  258. #define BX1 (BX0 + BXSPACE)
  259. #define BX2 (BX0 + BXSPACE*2)
  260. #define BX3 (BX0 + BXSPACE*3)
  261.  
  262. #define BY0 0
  263. #define BY1 (BY0 + BYSPACE)
  264. #define BY2 (BY0 + BYSPACE*2)
  265. #define BY3 (BY0 + BYSPACE*3)
  266.  
  267.   BTCreate(&gbut[G_BAPPLY],  butF, BX0,BY0, 52,BUTTH,"Apply", infofg,infobg);
  268.   BTCreate(&gbut[G_BNOGAM],  butF, BX0,BY1, 52,BUTTH,"NoMod", infofg,infobg);
  269.   BTCreate(&gbut[G_BMAXCONT],butF, BX0,BY2, 52,BUTTH,"Norm",  infofg,infobg);
  270.   BTCreate(&gbut[G_BHISTEQ], butF, BX0,BY3, 52,BUTTH,"HistEq",infofg,infobg);
  271.  
  272.   BTCreate(&gbut[G_BUP_BR],butF, BX1,BY0, 52,BUTTH,"Brite",infofg,infobg);
  273.   BTCreate(&gbut[G_BDN_BR],butF, BX1,BY1, 52,BUTTH,"Dim",  infofg,infobg);
  274.   BTCreate(&gbut[G_BUP_CN],butF, BX1,BY2, 52,BUTTH,"Sharp",infofg,infobg);
  275.   BTCreate(&gbut[G_BDN_CN],butF, BX1,BY3, 52,BUTTH,"Dull", infofg,infobg);
  276.  
  277.   BTCreate(&gbut[G_BRESET],butF, BX2,   BY0, 52,BUTTH,"Reset", infofg,infobg);
  278.   BTCreate(&gbut[G_B1],    butF, BX2,   BY1, 25,BUTTH,"1",    infofg,infobg);
  279.   BTCreate(&gbut[G_B2],    butF, BX2+26,BY1, 26,BUTTH,"2",    infofg,infobg);
  280.   BTCreate(&gbut[G_B3],    butF, BX2,   BY2, 25,BUTTH,"3",    infofg,infobg);
  281.   BTCreate(&gbut[G_B4],    butF, BX2+26,BY2, 26,BUTTH,"4",    infofg,infobg);
  282.   BTCreate(&gbut[G_BSET],  butF, BX2,   BY3, 52,BUTTH,"Set",  infofg,infobg);
  283.  
  284.   BTCreate(&gbut[G_BUNDO], butF, BX3, BY0, 52,BUTTH,"Undo",   infofg,infobg);
  285.   BTCreate(&gbut[G_BREDO], butF, BX3, BY1, 52,BUTTH,"Redo",   infofg,infobg);
  286.   BTCreate(&gbut[G_BGETRES],butF,BX3, BY2, 52,BUTTH,"CutRes", infofg,infobg);
  287.   BTCreate(&gbut[G_BCLOSE],butF, BX3, BY3, 52,BUTTH,"Close",  infofg,infobg);
  288.  
  289.  
  290.   gbut[G_BSET].toggle = 1;
  291.   gbut[G_BUNDO].active = 0;
  292.   gbut[G_BREDO].active = 0;
  293.  
  294.   CBCreate(&enabCB, modF,2,2,     "Display with HSV/RGB mods.",infofg,infobg);
  295.   CBCreate(&autoCB, modF,2,2+17,  "Auto-apply HSV/RGB mods.",  infofg,infobg);
  296.   CBCreate(&resetCB,modF,2,2+17*2,"Auto-reset on new image.",  infofg,infobg);
  297.   enabCB.val = autoCB.val = resetCB.val = 1;
  298.  
  299.  
  300.   /************ HSV editing doo-wahs **************/
  301.  
  302.  
  303.   HDCreate(&srcHD, hsvF,  51, 65, 1, 0, 30, 0, "From", infofg, infobg);
  304.   HDCreate(&dstHD, hsvF, 155, 65, 1, 0, 30, 0, "To",   infofg, infobg);
  305.  
  306.   HDCreate(&whtHD, hsvF,  50,243, 0, 0,  0, 0, "White",infofg, infobg);
  307.  
  308.   DCreate(&satDial, hsvF, 100, 199, 100, 121, -100, 100, 0, 5, 
  309.        infofg, infobg, "Saturation", "%");
  310.  
  311.   hueRB = RBCreate(NULL, hsvF,  7, 153, "1", infofg, infobg);
  312.   RBCreate        (hueRB,hsvF, 47, 153, "2", infofg, infobg);
  313.   RBCreate        (hueRB,hsvF, 87, 153, "3", infofg, infobg);
  314.   RBCreate        (hueRB,hsvF,  7, 170, "4", infofg, infobg);
  315.   RBCreate        (hueRB,hsvF, 47, 170, "5", infofg, infobg);
  316.   RBCreate        (hueRB,hsvF, 87, 170, "6", infofg, infobg);
  317.  
  318.   BTCreate(&hueclrB, hsvF, 127, 158, 70, BUTTH, "Reset", infofg, infobg);
  319.  
  320.   initHmap();
  321.   hmap2dials();
  322.   build_hremap();
  323.  
  324.   InitGraf(&intGraf);
  325.   CreateGraf(&intGraf, hsvF, 20, 339, infofg, infobg, "Intensity");
  326.  
  327.  
  328.   /********* RGB color correction doo-wahs ***********/
  329.  
  330.  
  331.   InitGraf(&rGraf);
  332.   CreateGraf(&rGraf, rgbF, 10, 20, infofg, infobg, "Red");
  333.  
  334.   InitGraf(&gGraf);
  335.   CreateGraf(&gGraf, rgbF, 10, 179, infofg, infobg, "Green");
  336.   
  337.   InitGraf(&bGraf);
  338.   CreateGraf(&bGraf, rgbF, 10, 338, infofg, infobg, "Blue");
  339.   
  340.   SetHSVmode();
  341.  
  342.   save_gstate(&defstate);
  343.   save_gstate(&preset[0]);
  344.   save_gstate(&preset[1]);
  345.   save_gstate(&preset[2]);
  346.   save_gstate(&preset[3]);
  347.   
  348.   parseResources();
  349.   restore_gstate(&defstate);   /* defstate may have changed due to resources */
  350.  
  351.   uptr = utail = uhead = 0;
  352.   save_gstate(&undo[0]);
  353.  
  354.   XMapSubwindows(theDisp, cmapF);
  355.   XMapSubwindows(theDisp, hsvF);
  356.   XMapSubwindows(theDisp, rgbF);
  357.   XMapSubwindows(theDisp, gamW);
  358. }
  359.   
  360.  
  361. /***************************************************/
  362. int GamCheckEvent(xev)
  363. XEvent *xev;
  364. {
  365.   /* check event to see if it's for one of our subwindows.  If it is,
  366.      deal accordingly, and return '1'.  Otherwise, return '0' */
  367.  
  368.   int rv;
  369.  
  370.   rv = 1;
  371.   
  372.   if (xev->type == Expose) {
  373.     int x,y,w,h;
  374.     XExposeEvent *e = (XExposeEvent *) xev;
  375.     x = e->x;  y = e->y;  w = e->width;  h = e->height;
  376.  
  377.     /* throw away excess redraws for 'dumb' windows */
  378.     if (e->count > 0 && 
  379.     (e->window == satDial.win || e->window == rhDial.win ||
  380.      e->window == gsDial.win  || e->window == bvDial.win ||
  381.      e->window == cmapF       || e->window == modF       ||
  382.      e->window == intGraf.win || e->window == rGraf.win  ||
  383.      e->window == gGraf.win   || e->window == bGraf.win  ||
  384.      e->window == rgbF)) {}
  385.  
  386.     else if (e->window == gamW)        drawGam(x, y, w, h);
  387.     else if (e->window == butF)        drawBut(x, y, w, h);
  388.     else if (e->window == satDial.win) DRedraw(&satDial);
  389.     else if (e->window == rhDial.win)  DRedraw(&rhDial);
  390.     else if (e->window == gsDial.win)  DRedraw(&gsDial);
  391.     else if (e->window == bvDial.win)  DRedraw(&bvDial);
  392.  
  393.     else if (e->window == intGraf.win) RedrawGraf(&intGraf,x,y,w,h);
  394.     else if (e->window == rGraf.win)   RedrawGraf(&rGraf,  x,y,w,h);
  395.     else if (e->window == gGraf.win)   RedrawGraf(&gGraf,  x,y,w,h);
  396.     else if (e->window == bGraf.win)   RedrawGraf(&bGraf,  x,y,w,h);
  397.  
  398.     else if (e->window == cmapF) drawCmap();
  399.  
  400.     else if (e->window == modF) {
  401.       CBRedraw(&enabCB);
  402.       CBRedraw(&autoCB);
  403.       CBRedraw(&resetCB);
  404.     }
  405.  
  406.     else if (e->window == hsvF) {
  407.       XRectangle xr;
  408.       xr.x = x;  xr.y = y;  xr.width = e->width;  xr.height = e->height;
  409.       XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
  410.  
  411.       XSetForeground(theDisp, theGC, infofg);
  412.       ULineString(hsvF, "HSV Modification", 2, 1+ASCENT);
  413.  
  414.       HDRedraw(&srcHD, HD_ALL);
  415.       HDRedraw(&dstHD, HD_ALL);
  416.       HDRedraw(&whtHD, HD_ALL);
  417.       RBRedraw(hueRB, -1);
  418.       BTRedraw(&hueclrB);
  419.  
  420.       XSetClipMask(theDisp, theGC, None);
  421.     }
  422.  
  423.     else if (e->window == rgbF) {
  424.       XSetForeground(theDisp, theGC, infofg);
  425.       ULineString(rgbF, "RGB Modification", 2, 1+ASCENT);
  426.     }
  427.  
  428.     else rv = 0;
  429.   }
  430.  
  431.   else if (xev->type == ButtonPress) {
  432.     XButtonEvent *e = (XButtonEvent *) xev;
  433.     int i,x,y;
  434.     x = e->x;  y = e->y;
  435.  
  436.     if (e->button == Button1) {
  437.       if      (e->window == butF)  clickGam(x,y);
  438.       else if (e->window == cmapF) clickCmap(x,y,1);
  439.  
  440.       else if (e->window == modF) {
  441.     if (CBClick(&enabCB,x,y)) {
  442.       if (CBTrack(&enabCB)) applyGamma();  /* enabCB changed, regen */
  443.     }
  444.  
  445.     else if (CBClick(&autoCB,x,y)) {
  446.       CBTrack(&autoCB);
  447.       if (autoCB.val) applyGamma();  /* auto-apply turned on, apply now */
  448.     }
  449.  
  450.     else if (CBClick(&resetCB,x,y)) CBTrack(&resetCB);
  451.       }
  452.  
  453.  
  454.       else if (e->window == hsvF) {
  455.     if (HDClick(&srcHD, x,y) || HDClick(&dstHD, x,y)) { 
  456.       dials2hmap();
  457.       build_hremap();
  458.       changedGam();
  459.     }
  460.     else if (HDClick(&whtHD, x,y)) changedGam();
  461.  
  462.     else if (PTINRECT(x,y,hueclrB.x, hueclrB.y, hueclrB.w, hueclrB.h)) {
  463.       if (BTTrack(&hueclrB)) {   /* RESET */
  464.         dstHD.stval  = srcHD.stval;
  465.         dstHD.enval  = srcHD.enval;
  466.         dstHD.ccwise = srcHD.ccwise;
  467.         HDRedraw(&dstHD, HD_ALL | HD_CLEAR);
  468.         dials2hmap();
  469.         build_hremap();
  470.         changedGam();
  471.       }
  472.     }
  473.  
  474.     else if ((i=RBClick(hueRB,x,y)) >= 0) {
  475.       dials2hmap();
  476.       if (RBTrack(hueRB, i)) hmap2dials();
  477.     }
  478.       }
  479.  
  480.       else if (e->window == intGraf.win) {
  481.     GRAF_STATE gs;
  482.     if (ClickGraf(&intGraf, e->subwindow, x,y)) {
  483.       GetGrafState(&intGraf, &gs);
  484.       changedGam();
  485.     }
  486.       }
  487.  
  488.       else if (e->window == rGraf.win) {
  489.     if (ClickGraf(&rGraf, e->subwindow, x,y)) changedGam();
  490.       }
  491.  
  492.       else if (e->window == gGraf.win) {
  493.     if (ClickGraf(&gGraf, e->subwindow, x,y)) changedGam();
  494.       }
  495.  
  496.       else if (e->window == bGraf.win) {
  497.     if (ClickGraf(&bGraf, e->subwindow, x,y)) changedGam();
  498.       }
  499.  
  500.  
  501.       else if (e->window == satDial.win) {
  502.     if (DTrack(&satDial, x, y)) changedGam();
  503.       }
  504.  
  505.       else if (e->window == rhDial.win ||
  506.            e->window == gsDial.win ||
  507.            e->window == bvDial.win) {
  508.  
  509.     if ((e->window == rhDial.win && DTrack(&rhDial, x,y)) || 
  510.         (e->window == gsDial.win && DTrack(&gsDial, x,y)) ||
  511.         (e->window == bvDial.win && DTrack(&bvDial, x,y))) {
  512.       saveCMap(&prevcmap);
  513.       BTSetActive(&gbut[G_BCOLUNDO],1);
  514.       ApplyEditColor(0);
  515.     }
  516.       }
  517.  
  518.       else rv = 0;
  519.     }
  520.  
  521.     else if (e->button == Button2) {
  522.       if (e->window == cmapF) clickCmap(x, y, 2);
  523.       else rv = 0;
  524.     }
  525.  
  526.     else if (e->button == Button3) {
  527.       if (e->window == cmapF) clickCmap(x, y, 3);
  528.       else rv = 0;
  529.     }
  530.     else rv = 0;
  531.   }
  532.  
  533.  
  534.   else if (xev->type == KeyPress) {
  535.     XKeyEvent *e = (XKeyEvent *) xev;
  536.     char buf[128];  KeySym ks;
  537.     int stlen;
  538.     
  539.     stlen = XLookupString(e,buf,128,&ks,(XComposeStatus *) NULL);
  540.     buf[stlen] = '\0';
  541.  
  542.     if (!stlen) return 0;
  543.  
  544.     else if (e->window == intGraf.win) rv = GrafKey(&intGraf,buf);
  545.     else if (e->window == rGraf.win  ) rv = GrafKey(&rGraf,buf);
  546.     else if (e->window == gGraf.win  ) rv = GrafKey(&gGraf,buf);
  547.     else if (e->window == bGraf.win  ) rv = GrafKey(&bGraf,buf);
  548.     else rv = 0;
  549.  
  550.     if (rv>1) changedGam();   /* hit 'enter' in one of Graf's */
  551.   }
  552.  
  553.   else rv = 0;
  554.  
  555.   return rv;
  556. }
  557.  
  558.  
  559.  
  560. /***************************************************/
  561. static void changedGam()
  562. {
  563.   /* called whenever an HSV/RGB gamma ctrl has changed
  564.      applies change to image if autoCB.val is set */
  565.  
  566.   saveGamState();
  567.   if (autoCB.val) applyGamma();
  568. }
  569.  
  570.  
  571. /***************************************************/
  572. void GamBox(vis)
  573. int vis;
  574. {
  575.   if (vis) XMapRaised(theDisp, gamW);
  576.   else     XUnmapWindow(theDisp, gamW);
  577.  
  578.   gamUp = vis;
  579. }
  580.  
  581.  
  582. /***************************************************/
  583. static void drawGam(x,y,w,h)
  584. int x,y,w,h;
  585. {
  586.   XRectangle xr;
  587.  
  588.   xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
  589.   XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
  590.  
  591.   drawArrow(232,178);
  592.   drawArrow(457,178);
  593.  
  594.   XSetClipMask(theDisp, theGC, None);
  595. }
  596.  
  597.  
  598. /***************************************************/
  599. static void drawBut(x,y,w,h)
  600. int x,y,w,h;
  601. {
  602.   int i;
  603.   XRectangle xr;
  604.  
  605.   xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
  606.   XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
  607.  
  608.   for (i=0; i<G_NBUTTS; i++) {
  609.     if (gbut[i].win == butF) BTRedraw(&gbut[i]);
  610.   }
  611.  
  612.   XSetClipMask(theDisp, theGC, None);
  613. }
  614.  
  615.  
  616. /***************************************************/
  617. static void drawArrow(x,y)
  618. int x,y;
  619. {
  620.   XPoint pts[8];
  621.   
  622.   pts[0].x = x+10;     pts[0].y = y;
  623.   pts[1].x = x-4;      pts[1].y = y-100;
  624.   pts[2].x = x-4;      pts[2].y = y-40;
  625.   pts[3].x = x-10;     pts[3].y = y-40;
  626.   pts[4].x = x-10;     pts[4].y = y+40;
  627.   pts[5].x = x-4;      pts[5].y = y+40;
  628.   pts[6].x = x-4;      pts[6].y = y+100;
  629.   pts[7].x = pts[0].x; pts[7].y = pts[0].y;
  630.  
  631.   XSetForeground(theDisp, theGC, infobg);
  632.   XFillPolygon(theDisp, gamW, theGC, pts, 8, Convex, CoordModeOrigin);
  633.   XSetForeground(theDisp, theGC, infofg);
  634.   XDrawLines(theDisp, gamW, theGC, pts, 8, CoordModeOrigin);
  635. }
  636.  
  637.  
  638. /***************************************************/
  639. static void drawCmap()
  640. {
  641.   int i;
  642.  
  643.   XSetForeground(theDisp, theGC, infofg);
  644.   ULineString(cmapF, "Colormap Editing", 2, 1+ASCENT);
  645.  
  646.   for (i=0; i<G_NBUTTS; i++) {
  647.     if (gbut[i].win == cmapF) BTRedraw(&gbut[i]);
  648.   }
  649.  
  650.   RedrawCMap();
  651. }
  652.  
  653.  
  654.  
  655. /***************************************************/
  656. void NewCMap()
  657. {
  658.   /* called when we've loaded a new picture */
  659.  
  660.   int i;
  661.  
  662.   XClearArea(theDisp, cmapF, CMAPX, CMAPY, CMAPW+1, CMAPH+1, False);
  663.   for (i=0; i<256; i++) cellgroup[i] = 0;
  664.   curgroup = maxgroup = 0;
  665.  
  666.   BTSetActive(&gbut[G_BCOLUNDO],0);
  667.  
  668.   if (resetCB.val) {            /* auto-reset gamma controls */
  669.     i = autoCB.val;
  670.     if (i) autoCB.val = 0;      /* must NOT apply changes! */
  671.     restore_gstate(&defstate);
  672.     autoCB.val = i;
  673.   }
  674. }
  675.  
  676.  
  677. /***************************************************/
  678. void RedrawCMap()
  679. {
  680.   int i;
  681.  
  682.   XSetLineAttributes(theDisp, theGC, 0, LineSolid, CapButt, JoinMiter);
  683.   XSetForeground(theDisp, theGC, infofg);
  684.  
  685.   for (i=0; i<numcols; i++) {
  686.     int x,y;
  687.     x = CMAPX + (i%16)*CMAPCW;
  688.     y = CMAPY + (i/16)*CMAPCH;
  689.     XDrawRectangle(theDisp, cmapF, theGC, x, y, CMAPCW,CMAPCH);
  690.   }
  691.  
  692.   for (i=0; i<numcols; i++) {
  693.     int x,y;
  694.     x = CMAPX + (i%16)*CMAPCW;
  695.     y = CMAPY + (i/16)*CMAPCH;
  696.  
  697.     XSetForeground(theDisp, theGC, cols[i]);
  698.     XFillRectangle(theDisp, cmapF, theGC, x+1, y+1, CMAPCW-1,CMAPCH-1);
  699.  
  700.     if (i == editColor || (curgroup && (cellgroup[i]==curgroup))) {
  701.       XSetForeground(theDisp, theGC, infobg);
  702.       XDrawRectangle(theDisp, cmapF, theGC, x+1, y+1, CMAPCW-2,CMAPCH-2);
  703.       XSetForeground(theDisp, theGC, infofg);
  704.       XDrawRectangle(theDisp, cmapF, theGC, x+2, y+2, CMAPCW-4,CMAPCH-4);
  705.     }
  706.   }
  707. }
  708.  
  709.  
  710. /***************************************************/
  711. static void selectCell(cellno, sel)
  712. int cellno, sel;
  713. {
  714.   int x,y;
  715.  
  716.   if (cellno >= numcols) return;
  717.  
  718.   x = CMAPX + (cellno%16)*CMAPCW;
  719.   y = CMAPY + (cellno/16)*CMAPCH;
  720.  
  721.   if (!sel) {   /* unhighlight a cell */
  722.     XSetForeground(theDisp, theGC, cols[cellno]);
  723.     XFillRectangle(theDisp, cmapF, theGC, x+1, y+1, CMAPCW-1,CMAPCH-1);
  724.   }
  725.   else {  /* highlight a cell */
  726.     XSetForeground(theDisp, theGC, infobg);
  727.     XDrawRectangle(theDisp, cmapF, theGC, x+1, y+1, CMAPCW-2,CMAPCH-2);
  728.     XSetForeground(theDisp, theGC, infofg);
  729.     XDrawRectangle(theDisp, cmapF, theGC, x+2, y+2, CMAPCW-4,CMAPCH-4);
  730.   }
  731. }
  732.  
  733.  
  734. /***************************************************/
  735. static void clickGam(x,y)
  736. int x,y;
  737. {
  738.   int i;
  739.   BUTT *bp;
  740.  
  741.   for (i=0; i<G_NBUTTS; i++) {
  742.     bp = &gbut[i];
  743.     if (bp->win == butF && PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break;
  744.   }
  745.  
  746.   /* if 'Set' is lit, and we didn't click 'set' or 'Reset' or '1'..'4', 
  747.      turn it off */
  748.   if (i!=G_BSET && i!=G_B1 && i!=G_B2 && i!=G_B3 && i!=G_B4 && i!=G_BRESET
  749.       && gbut[G_BSET].lit) {
  750.     gbut[G_BSET].lit = 0;  
  751.     BTRedraw(&gbut[G_BSET]);
  752.   }
  753.   
  754.  
  755.   if (i<G_NBUTTS) {  /* found one */
  756.     if (BTTrack(bp)) doCmd(i);
  757.   }
  758. }
  759.  
  760.  
  761. /***************************************************/
  762. static void clickCmap(x,y,but)
  763. int x,y,but;
  764. {
  765.   int i, recolor;
  766.   BUTT *bp;
  767.  
  768.   if (but==1) {   /* if left click, check the cmap controls */
  769.     for (i=0; i<G_NBUTTS; i++) {
  770.       bp = &gbut[i];
  771.       if (bp->win == cmapF && PTINRECT(x,y,bp->x, bp->y, bp->w, bp->h)) break;
  772.     }
  773.  
  774.     if (i<G_NBUTTS) {  /* found one */
  775.       if (BTTrack(bp)) doCmd(i);
  776.       return;
  777.     }
  778.   }
  779.  
  780.  
  781.   /* see if we're anywhere in the colormap area */
  782.  
  783.   if (PTINRECT(x,y,CMAPX,CMAPY,CMAPW,CMAPH)) {
  784.     if (but==1) {           /* select different cell/group for editing */
  785.       /* compute colorcell # */
  786.       i = ((x-CMAPX)/CMAPCW) + ((y-CMAPY)/CMAPCH)*16;
  787.       if (i<numcols) {    /* clicked in colormap.  track til mouseup */
  788.     if (i!=editColor) ChangeEC(i);
  789.  
  790.     while (1) {
  791.       Window       rW,cW;
  792.       int          rx,ry,x,y;
  793.       unsigned int mask;
  794.  
  795.       if (XQueryPointer(theDisp,cmapF,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  796.         if (!(mask & Button1Mask)) break;    /* button released */
  797.  
  798.         RANGE(x, CMAPX, CMAPX+CMAPW-1);
  799.         RANGE(y, CMAPY, CMAPY+CMAPH-1);
  800.  
  801.         i = ((x-CMAPX)/CMAPCW) + ((y-CMAPY)/CMAPCH)*16;
  802.         if (i<numcols && i != editColor) ChangeEC(i);
  803.       }
  804.     } /* while */
  805.       } /* if i<numcols */
  806.     } /* if but==1 */
  807.  
  808.     
  809.     else if (but==2) {   /* color smooth */
  810.       int cellnum, delc, col1, j, delr, delg, delb;
  811.  
  812.       /* compute colorcell # */
  813.       cellnum = ((x-CMAPX)/CMAPCW) + ((y-CMAPY)/CMAPCH)*16;
  814.       if (cellnum<numcols) {
  815.     delc = abs(cellnum - editColor);
  816.     if (delc) {  /* didn't click on same cell */
  817.       saveCMap(&tmpcmap);  /* save the current cmap state */
  818.  
  819.       if (cellnum < editColor) col1 = cellnum;  else col1 = editColor;
  820.  
  821.       delr = rcmap[col1 + delc] - rcmap[col1];
  822.       delg = gcmap[col1 + delc] - gcmap[col1];
  823.       delb = bcmap[col1 + delc] - bcmap[col1];
  824.  
  825.       for (i=0; i<delc; i++) {
  826.         rcmap[col1 + i] = rcmap[col1] + (delr * i) / delc;
  827.         gcmap[col1 + i] = gcmap[col1] + (delg * i) / delc;
  828.         bcmap[col1 + i] = bcmap[col1] + (delb * i) / delc;
  829.  
  830.         if (cellgroup[col1 + i]) { 
  831.           /* propogate new color to all members of this group */
  832.           for (j=0; j<numcols; j++) 
  833.         if (cellgroup[j] == cellgroup[col1 + i]) {
  834.           rcmap[j] = rcmap[col1 + i];
  835.           gcmap[j] = gcmap[col1 + i];
  836.           bcmap[j] = bcmap[col1 + i];
  837.         }
  838.         }
  839.       }
  840.  
  841.       for (i=0; i<numcols; i++) {
  842.         if (rcmap[i] != tmpcmap.r[i] ||
  843.         gcmap[i] != tmpcmap.g[i] ||
  844.         bcmap[i] != tmpcmap.b[i]) break;
  845.       }
  846.  
  847.       if (i<numcols) {  /* something changed */
  848.         bcopy(&tmpcmap, &prevcmap, sizeof(struct cmapstate));
  849.         BTSetActive(&gbut[G_BCOLUNDO],1);
  850.         applyGamma();
  851.       }
  852.     }
  853.       }
  854.     }
  855.  
  856.  
  857.     else if (but==3) { /* add/delete cell(s) from current group */
  858.       int lastcell,j,resetdel,curcell;
  859.  
  860.       /* better save the current cmap state, as it might change */
  861.       saveCMap(&tmpcmap);
  862.  
  863.       recolor = resetdel = 0;
  864.       /* compute colorcell # clicked in */
  865.       curcell = ((x-CMAPX)/CMAPCW) + ((y-CMAPY)/CMAPCH)*16;
  866.       if (curcell<numcols) {    /* clicked in colormap.  track til mouseup */
  867.     if (deladdCell(curcell, 1)) recolor=1;
  868.  
  869.     lastcell = curcell;
  870.  
  871.     j = XGrabPointer(theDisp, cmapF, False, 0, GrabModeAsync, 
  872.              GrabModeAsync, None, None, CurrentTime);
  873.     while (1) {
  874.       Window       rW,cW;
  875.       int          rx,ry,x,y;
  876.       unsigned int mask;
  877.  
  878.       if (XQueryPointer(theDisp,cmapF,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  879.         /* if button3 and shift released */
  880.         if (!(mask & (Button3Mask | ShiftMask))) break;  
  881.  
  882.         /* if user lets go of B3, reset addonly/delonly flag & lastcell */
  883.         if (!(mask & Button3Mask) && (mask & ShiftMask)) {
  884.           resetdel = 1;
  885.           lastcell = -1;
  886.         }
  887.  
  888.         if (mask & Button3Mask) {  /* select while b3 down */
  889.           RANGE(x, CMAPX, CMAPX+CMAPW-1);
  890.           RANGE(y, CMAPY, CMAPY+CMAPH-1);
  891.  
  892.           curcell = ((x-CMAPX)/CMAPCW) + ((y-CMAPY)/CMAPCH)*16;
  893.           if (curcell<numcols && curcell != lastcell) {
  894.         lastcell = curcell;
  895.         if (deladdCell(curcell, resetdel)) recolor=1;
  896.         resetdel = 0;
  897.           }
  898.         }
  899.       }
  900.     } /* while */
  901.  
  902.     XUngrabPointer(theDisp, CurrentTime);
  903.  
  904.     /* restore all cells that aren't in curgroup, unless curgroup=0,
  905.        in which case restore everything...  nasty */
  906.     for (i=0; i<numcols; i++) {
  907.       if (!curgroup || cellgroup[i]!=curgroup) {
  908.         rcmap[i] = tmpcmap.r[i];
  909.         gcmap[i] = tmpcmap.g[i];
  910.         bcmap[i] = tmpcmap.b[i];
  911.       }
  912.     }
  913.  
  914.     if (recolor) {
  915.       /* colors changed.  save to color undo area */
  916.       bcopy(&tmpcmap, &prevcmap, sizeof(struct cmapstate));
  917.       BTSetActive(&gbut[G_BCOLUNDO],1);
  918.       applyGamma();   /* have to regen entire image when groupings chg */
  919.     }
  920.  
  921.       } /* if i<numcols */
  922.     } /* if but==3 */
  923.  
  924.   } /* if PTINRECT */
  925. }
  926.  
  927.  
  928.  
  929.  
  930. /***************************************************/
  931. static int deladdCell(cnum, first)
  932. int cnum, first;
  933. {
  934.   int i,j,rv;
  935.   static int mode;
  936.  
  937. #define ADDORDEL 0
  938. #define DELONLY  1
  939. #define ADDONLY  2
  940.  
  941.   /* if 'first', we can add/delete cells as appropriate.  otherwise,
  942.      the (static) value of 'mode' determines whether we can
  943.      delete or add cells to groups.  The practical upshot is that it should
  944.      behave like the 'fat-bits pencil' in MacPaint */
  945.  
  946.   /* cases:  curgroup>0, clicked on something in same group
  947.                          remove target from group
  948.          curgroup>0, clicked on something in different group
  949.                      merge groups.  (target group gets 
  950.              set equal to current values)
  951.              curgroup>0, clicked on something in no group
  952.                      add target to curgroup
  953.              curgroup=0, clicked on something in a group
  954.                      add editColor to target group, 
  955.              set curgroup = target group
  956.              target group gets current values
  957.          curgroup=0, clicked on something in no group
  958.                      create a new group, add both cells to it
  959.    */
  960.  
  961.  
  962.   rv = 0;
  963.   if (first) mode = ADDORDEL;
  964.  
  965.   if (curgroup) {
  966.     if ((mode!=ADDONLY) && cellgroup[cnum] == curgroup) {
  967.       /* remove target from curgroup.  If it's the last one, delete group */
  968.  
  969.       for (i=0,j=0; i<numcols; i++) {     /* count #cells in this group */
  970.     if (cellgroup[i] == curgroup) j++;
  971.       }
  972.  
  973.       if (j>1) {  /* remove target cell from group */
  974.     cellgroup[cnum] = 0;
  975.     selectCell(cnum,0);
  976.     mode = DELONLY;
  977.     if (cnum==editColor) { /* set editColor to first cell in group */
  978.       for (i=0; i<numcols && cellgroup[i]!=curgroup; i++);
  979.       if (i<numcols) editColor = i;
  980.     }
  981.       }
  982.       else {  /* last cell in group.  set to group=0, but don't unhighlight */
  983.     cellgroup[cnum] = 0;
  984.     curgroup = 0;
  985.       }
  986.     }
  987.  
  988.     else if ((mode!=DELONLY) && cellgroup[cnum] != curgroup && 
  989.          cellgroup[cnum]>0) {
  990.       /* merge clicked-on group into curgroup */
  991.       mode = ADDONLY;
  992.       rv = 1;  j = cellgroup[cnum];
  993.       for (i=0; i<numcols; i++) {
  994.     if (cellgroup[i] == j) {
  995.       cellgroup[i] = curgroup;
  996.       selectCell(i,1);
  997.       rcmap[i] = rcmap[editColor];
  998.       gcmap[i] = gcmap[editColor];
  999.       bcmap[i] = bcmap[editColor]; 
  1000.     }
  1001.       }
  1002.     }
  1003.         
  1004.     else if ((mode!=DELONLY) && cellgroup[cnum] == 0) {
  1005.       /* merge clicked-on cell into curgroup */
  1006.       mode = ADDONLY;
  1007.       rv = 1;
  1008.       cellgroup[cnum] = curgroup;
  1009.       selectCell(cnum,1);
  1010.       rcmap[cnum] = rcmap[editColor];
  1011.       gcmap[cnum] = gcmap[editColor];
  1012.       bcmap[cnum] = bcmap[editColor]; 
  1013.     }
  1014.   }
  1015.  
  1016.   else {  /* !curgroup */
  1017.     if ((mode!=DELONLY) && cellgroup[cnum] != 0) {
  1018.       /* merge editColor into clicked-on group */
  1019.       /* clicked on group, however, takes values of editCell */
  1020.       mode = ADDONLY;
  1021.       rv = 1;  j = cellgroup[cnum];
  1022.       for (i=0; i<numcols; i++) {
  1023.     if (cellgroup[i] == j) {
  1024.       selectCell(i,1);
  1025.       rcmap[i] = rcmap[editColor];
  1026.       gcmap[i] = gcmap[editColor];
  1027.       bcmap[i] = bcmap[editColor]; 
  1028.     }
  1029.       }
  1030.       curgroup = cellgroup[cnum];
  1031.       cellgroup[editColor] = curgroup;
  1032.     }
  1033.         
  1034.     else if ((mode!=DELONLY) && (cellgroup[cnum] == 0) 
  1035.          && (cnum != editColor)) {
  1036.       /* create new group for these two cells (cnum and editColor) */
  1037.       mode = ADDONLY;
  1038.       rv = 1;
  1039.       maxgroup++;
  1040.       cellgroup[cnum] = cellgroup[editColor] = maxgroup;
  1041.       selectCell(cnum,1);
  1042.       curgroup = maxgroup;
  1043.       rcmap[cnum] = rcmap[editColor];
  1044.       gcmap[cnum] = gcmap[editColor];
  1045.       bcmap[cnum] = bcmap[editColor];
  1046.     }
  1047.   }
  1048.  
  1049.   return rv;
  1050. }        
  1051.         
  1052.  
  1053. /*********************/
  1054. void ChangeEC(num)
  1055. int num;
  1056. {
  1057.   /* given a color # that is to become the new editColor, do all 
  1058.      highlighting/unhighlighting, copy editColor's rgb values to
  1059.      the rgb/hsv dials */
  1060.  
  1061.   int i,oldgroup;
  1062.  
  1063.   oldgroup = curgroup;
  1064.  
  1065.   if (curgroup && cellgroup[num] != curgroup) {
  1066.     /* a group is currently selected, and we're picking a new cell that
  1067.        isn't in the group, have to unhighlight entire group */
  1068.  
  1069.     for (i=0; i<numcols; i++) {
  1070.       if (cellgroup[i] == curgroup) selectCell(i,0);
  1071.     }
  1072.   }
  1073.   else if (!curgroup) selectCell(editColor,0);
  1074.  
  1075.   editColor = num;
  1076.   curgroup = cellgroup[editColor];
  1077.  
  1078.   if (curgroup && curgroup != oldgroup) {
  1079.     /* if new cell is in a group, highlight that group */
  1080.     for (i=0; i<numcols; i++) {
  1081.       if (cellgroup[i] == curgroup) selectCell(i,1);
  1082.     }
  1083.   }
  1084.   else if (!curgroup) selectCell(editColor,1);
  1085.  
  1086.  
  1087.   if (hsvmode) {
  1088.     double h, s, v;
  1089.     rgb2hsv(rcmap[editColor], gcmap[editColor], bcmap[editColor], &h, &s, &v);
  1090.     if (h<0) h = 0;
  1091.  
  1092.     DSetVal(&rhDial, (int) h);
  1093.     DSetVal(&gsDial, (int) (s*100));
  1094.     DSetVal(&bvDial, (int) (v*100));
  1095.   }
  1096.   else {
  1097.     DSetVal(&rhDial, rcmap[editColor]);
  1098.     DSetVal(&gsDial, gcmap[editColor]);
  1099.     DSetVal(&bvDial, bcmap[editColor]);
  1100.   }
  1101. }
  1102.   
  1103.     
  1104. /*********************/
  1105. void ApplyECctrls()
  1106. {
  1107.   /* sets values of {r,g,b}cmap[editColor] based on dial settings */
  1108.  
  1109.   if (hsvmode) {
  1110.     int rv, gv, bv;
  1111.     hsv2rgb((double) rhDial.val, ((double) gsDial.val) / 100.0, 
  1112.         ((double) bvDial.val) / 100.0, &rv, &gv, &bv);
  1113.     rcmap[editColor] = rv;
  1114.     gcmap[editColor] = gv;
  1115.     bcmap[editColor] = bv;
  1116.   }
  1117.   else {
  1118.     rcmap[editColor] = rhDial.val;
  1119.     gcmap[editColor] = gsDial.val;
  1120.     bcmap[editColor] = bvDial.val;
  1121.   }
  1122. }
  1123.  
  1124.  
  1125.  
  1126. /*********************/
  1127. void GenerateFSGamma()
  1128. {
  1129.   /* this function generates the Floyd-Steinberg gamma curve (fsgamcr)
  1130.  
  1131.      This function generates a 4 point spline curve to be used as a 
  1132.      non-linear grey 'colormap'.  Two of the points are nailed down at 0,0
  1133.      and 255,255, and can't be changed.  You specify the other two.  If
  1134.      you specify points on the line (0,0 - 255,255), you'll get the normal
  1135.      linear reponse curve.  If you specify points of 50,0 and 200,255, you'll
  1136.      get grey values of 0-50 to map to black (0), and grey values of 200-255
  1137.      to map to white (255) (roughly).  Values between 50 and 200 will cover
  1138.      the output range 0-255.  The reponse curve will be slightly 's' shaped. */
  1139.  
  1140.   int i,j;
  1141.   static int x[4] = {0,32,224,255};
  1142.   static int y[4] = {0, 0,255,255};
  1143.   double yf[4];
  1144.  
  1145.   InitSpline(x, y, 4, yf);
  1146.   
  1147.   for (i=0; i<256; i++) {
  1148.     j = (int) EvalSpline(x, y, yf, 4, (double) i);
  1149.     if (j<0) j=0;
  1150.     else if (j>255) j=255;
  1151.     fsgamcr[i] = j;
  1152.   }
  1153. }
  1154.  
  1155.  
  1156. /*********************/
  1157. static void doCmd(cmd)
  1158. int cmd;
  1159. {
  1160.   int i;
  1161.   GRAF_STATE gs;
  1162.  
  1163.   switch (cmd) {
  1164.  
  1165.   case G_BAPPLY: 
  1166.     if (enabCB.val != 1) { enabCB.val = 1;  CBRedraw(&enabCB); }
  1167.     applyGamma();           
  1168.     break;
  1169.  
  1170.   case G_BNOGAM:
  1171.     if (enabCB.val != 0) { enabCB.val = 0;  CBRedraw(&enabCB); }
  1172.     applyGamma();           
  1173.     break;
  1174.  
  1175.   case G_BUNDO:  gamUndo();  break;
  1176.   case G_BREDO:  gamRedo();  break;
  1177.   case G_BCLOSE: GamBox(0);  break;
  1178.  
  1179.  
  1180.   case G_BHISTEQ: {
  1181.     int histeq[256];
  1182.  
  1183.     calcHistEQ(histeq);
  1184.     
  1185.     for (i=0; i<256; i++) 
  1186.       intGraf.func[i] = histeq[i];
  1187.     
  1188.     for (i=0; i< intGraf.nhands; i++)
  1189.       intGraf.hands[i].y = intGraf.func[intGraf.hands[i].x];
  1190.     
  1191.     intGraf.entergamma = 0;
  1192.     XClearWindow(theDisp, intGraf.gwin);
  1193.     RedrawGraf(&intGraf, 0, 0, 200, 200);
  1194.     changedGam();
  1195.   }
  1196.     break;
  1197.  
  1198.  
  1199.  
  1200.   case G_BDN_BR: 
  1201.   case G_BUP_BR: GetGrafState(&intGraf, &gs);
  1202.                  for (i=0; i < gs.nhands; i++) {
  1203.            if (cmd==G_BUP_BR) gs.hands[i].y += 10;
  1204.                          else gs.hands[i].y -= 10;
  1205.            RANGE(gs.hands[i].y, 0, 255);
  1206.          }
  1207.                  SetGrafState(&intGraf, &gs);
  1208.                  changedGam();
  1209.                  break;
  1210.  
  1211.  
  1212.   case G_BUP_CN: GetGrafState(&intGraf, &gs);
  1213.                  for (i=0; i < gs.nhands; i++) {
  1214.            if (gs.hands[i].x < 128) gs.hands[i].y -= 10;
  1215.                                else gs.hands[i].y += 10;
  1216.            RANGE(gs.hands[i].y, 0, 255);
  1217.          }
  1218.                  SetGrafState(&intGraf, &gs);
  1219.                  changedGam();
  1220.                  break;
  1221.  
  1222.   case G_BDN_CN: GetGrafState(&intGraf, &gs);
  1223.                  for (i=0; i < gs.nhands; i++) {
  1224.            if (gs.hands[i].y < 128) {
  1225.              gs.hands[i].y += 10;
  1226.              if (gs.hands[i].y > 128) gs.hands[i].y = 128;
  1227.            }
  1228.            else {
  1229.              gs.hands[i].y -= 10;
  1230.              if (gs.hands[i].y < 128) gs.hands[i].y = 128;
  1231.            }
  1232.          }
  1233.                  SetGrafState(&intGraf, &gs);
  1234.                  changedGam();
  1235.                  break;
  1236.  
  1237.  
  1238.   case G_BMAXCONT: { int minv, maxv, v;
  1239.              minv = 255;  maxv = 0;
  1240.              for (i=0; i<numcols; i++) {
  1241.                v = MONO(rcmap[i],gcmap[i],bcmap[i]);
  1242.                if (v<minv) minv = v;
  1243.                if (v>maxv) maxv = v;
  1244.              }
  1245.  
  1246.              gs.spline = 0;
  1247.              gs.entergamma = 0;
  1248.              gs.gammamode = 0;
  1249.              gs.nhands = 4;
  1250.              gs.hands[0].x = 0;       gs.hands[0].y = 0;
  1251.              gs.hands[1].x = minv;    gs.hands[1].y = 0;
  1252.              gs.hands[2].x = maxv;    gs.hands[2].y = 255;
  1253.              gs.hands[3].x = 255;     gs.hands[3].y = 255;
  1254.  
  1255.              if (minv<1)   { gs.hands[1].x = gs.hands[1].y = 1; }
  1256.              if (maxv>254) { gs.hands[2].x = gs.hands[2].y = 254; }
  1257.  
  1258.              SetGrafState(&intGraf, &gs);
  1259.              changedGam();
  1260.            }
  1261.                    break;
  1262.  
  1263.   case G_BRESET:
  1264.   case G_B1:
  1265.   case G_B2:
  1266.   case G_B3:
  1267.   case G_B4: { struct gamstate *ptr;
  1268.  
  1269.            if      (cmd==G_B1)     ptr = &preset[0];
  1270.            else if (cmd==G_B2)     ptr = &preset[1];
  1271.            else if (cmd==G_B3)     ptr = &preset[2];
  1272.            else if (cmd==G_B4)     ptr = &preset[3];
  1273.            else if (cmd==G_BRESET) ptr = &defstate;
  1274.                 
  1275.            if (gbut[G_BSET].lit) {
  1276.          save_gstate(ptr);
  1277.          gbut[G_BSET].lit = 0;
  1278.          BTRedraw(&gbut[G_BSET]);
  1279.            }
  1280.            else restore_gstate(ptr);
  1281.          }
  1282.              break;
  1283.  
  1284.   case G_BSET:  break;
  1285.  
  1286.  
  1287.   case G_BHSVRGB:
  1288.     hsvmode = !hsvmode;
  1289.     SetHSVmode();
  1290.     saveGamState();
  1291.     break;
  1292.  
  1293.  
  1294.   case G_BCOLREV: 
  1295.     {
  1296.       struct cmapstate tmp1cmap;
  1297.       int gchg;
  1298.  
  1299.       for (i=0; i<numcols && prevcmap.cellgroup[i]==0; i++);
  1300.       gchg = (i!=numcols);
  1301.  
  1302.       saveCMap(&tmpcmap);         /* buffer current cmapstate */
  1303.     
  1304.       for (i=0; i<numcols; i++) { /* do reversion */
  1305.     rcmap[i] = rorg[i];  
  1306.     gcmap[i] = gorg[i];
  1307.     bcmap[i] = borg[i];
  1308.     cellgroup[i] = 0;
  1309.       }
  1310.       curgroup = maxgroup = 0;
  1311.  
  1312.       saveCMap(&tmp1cmap);        /* buffer current cmapstate */
  1313.     
  1314.       /* prevent multiple 'Undo All's from filling Undo buffer */
  1315.       if (bcmp(&tmpcmap, &tmp1cmap, sizeof(struct cmapstate))) {
  1316.     /* the reversion changed the cmapstate */
  1317.     bcopy(&tmpcmap, &prevcmap, sizeof(struct cmapstate));
  1318.     BTSetActive(&gbut[G_BCOLUNDO],1);
  1319.  
  1320.     RedrawCMap();
  1321.     ChangeEC(editColor);
  1322.     applyGamma();
  1323.     if (gchg) ApplyEditColor(1);
  1324.       }
  1325.     }
  1326.     break;
  1327.  
  1328.  
  1329.   case G_BRNDCOL:
  1330.     saveCMap(&prevcmap);
  1331.     BTSetActive(&gbut[G_BCOLUNDO],1);
  1332.     rndCols();
  1333.     break;
  1334.   
  1335.   case G_BRV:
  1336.     saveCMap(&prevcmap);
  1337.     BTSetActive(&gbut[G_BCOLUNDO],1);
  1338.  
  1339.     if (hsvmode) {              /* reverse video in HSV space (flip V only) */
  1340.       double h,s,v;   int rr, gr, br;
  1341.       for (i=0; i<numcols; i++) {
  1342.     rgb2hsv(rcmap[i], gcmap[i], bcmap[i], &h, &s, &v);
  1343.  
  1344.     v = 1.0 - v;
  1345.     if (v <= .05) v = .05;
  1346.  
  1347.     hsv2rgb(h, s, v, &rr, &gr, &br);
  1348.     rcmap[i] = rr;  gcmap[i] = gr;  bcmap[i] = br;
  1349.       }
  1350.     }
  1351.     else {
  1352.       for (i=0; i<numcols; i++) {
  1353.     rcmap[i] = 255-rcmap[i];
  1354.     gcmap[i] = 255-gcmap[i];
  1355.     bcmap[i] = 255-bcmap[i];
  1356.       }
  1357.     }
  1358.     ChangeEC(editColor);
  1359.     applyGamma();
  1360.     break;
  1361.   
  1362.  
  1363.   case G_BMONO:
  1364.     saveCMap(&prevcmap);
  1365.     BTSetActive(&gbut[G_BCOLUNDO],1);
  1366.     for (i=0; i<numcols; i++) {
  1367.       rcmap[i] = gcmap[i] = bcmap[i] = MONO(rcmap[i],gcmap[i],bcmap[i]);
  1368.     }
  1369.     ChangeEC(editColor);
  1370.     applyGamma();
  1371.     break;
  1372.   
  1373.  
  1374.   case G_BCOLUNDO:
  1375.     for (i=0; i<numcols && cellgroup[i]==prevcmap.cellgroup[i]; i++);
  1376.  
  1377.     saveCMap(&tmpcmap);
  1378.     restoreCMap(&prevcmap);
  1379.     bcopy(&tmpcmap, &prevcmap, sizeof(struct cmapstate));
  1380.     RedrawCMap();
  1381.     ChangeEC(editColor);
  1382.     applyGamma();
  1383.     if (i!=numcols) ApplyEditColor(1);
  1384.     break;
  1385.  
  1386.   case G_BGETRES:   makeResources();  break;
  1387.   }
  1388. }
  1389.  
  1390.  
  1391. /*********************/
  1392. static void SetHSVmode()
  1393. {
  1394.   if (!hsvmode) {
  1395.     rhDial.title = "Red";
  1396.     gsDial.title = "Green";
  1397.     bvDial.title = "Blue";
  1398.            
  1399.     DSetRange(&rhDial, 0, 255, rcmap[editColor], 16);
  1400.     DSetRange(&gsDial, 0, 255, gcmap[editColor], 16);
  1401.     DSetRange(&bvDial, 0, 255, bcmap[editColor], 16);
  1402.  
  1403.     XClearWindow(theDisp, rhDial.win);    DRedraw(&rhDial);
  1404.     XClearWindow(theDisp, gsDial.win);    DRedraw(&gsDial);
  1405.     XClearWindow(theDisp, bvDial.win);    DRedraw(&bvDial);
  1406.   }
  1407.  
  1408.   else {
  1409.     double h,s,v;
  1410.  
  1411.     rhDial.title = "Hue";
  1412.     gsDial.title = "Sat.";
  1413.     bvDial.title = "Value";
  1414.  
  1415.     rgb2hsv(rcmap[editColor], gcmap[editColor], bcmap[editColor],
  1416.         &h, &s, &v);
  1417.  
  1418.     if (h<0.0) h = 0.0;
  1419.     DSetRange(&rhDial, 0, 360, (int) h, 5);
  1420.     DSetRange(&gsDial, 0, 100, (int) (s*100), 5);
  1421.     DSetRange(&bvDial, 0, 100, (int) (v*100), 5);
  1422.  
  1423.     XClearWindow(theDisp, rhDial.win);    DRedraw(&rhDial);
  1424.     XClearWindow(theDisp, gsDial.win);    DRedraw(&gsDial);
  1425.     XClearWindow(theDisp, bvDial.win);    DRedraw(&bvDial);
  1426.   }
  1427. }
  1428.  
  1429.  
  1430. /*********************/
  1431. static void applyGamma()
  1432. {
  1433.   /* called to regenerate the image based on r/g/bcmap or output of rgb/hsv
  1434.      filters.  Doesn't check autoCB.  */
  1435.  
  1436.   int i,j;
  1437.   byte oldr[256], oldg[256], oldb[256];
  1438.  
  1439.   /* save current 'desired' colormap */
  1440.   bcopy(r, oldr, numcols);
  1441.   bcopy(g, oldg, numcols);
  1442.   bcopy(b, oldb, numcols);
  1443.  
  1444.   GammifyColors();
  1445.  
  1446.   /* if current 'desired' colormap hasn't changed, don't DO anything */
  1447.   if (!bcmp(r, oldr, numcols) && 
  1448.       !bcmp(g, oldg, numcols) && 
  1449.       !bcmp(b, oldb, numcols)) return;
  1450.  
  1451.  
  1452.  
  1453.   /* special case: if using R/W color, just modify the colors and leave */
  1454.   if (rwcolor && rwthistime) {
  1455.     XColor ctab[256];
  1456.  
  1457.     for (i=0; i<nfcols; i++) {
  1458.       j = fc2pcol[i];
  1459.       if (mono) {
  1460.     int intens = MONO(r[j], g[j], b[j]);
  1461.     ctab[i].red = ctab[i].green = ctab[i].blue = intens<<8;
  1462.       }
  1463.       else {
  1464.     ctab[i].red   = r[j]<<8;
  1465.     ctab[i].green = g[j]<<8;
  1466.     ctab[i].blue  = b[j]<<8;
  1467.       }
  1468.  
  1469.       ctab[i].pixel = freecols[i];
  1470.       ctab[i].flags = DoRed | DoGreen | DoBlue;
  1471.       XStoreColor(theDisp, LocalCmap ? LocalCmap : theCmap, &ctab[i]);
  1472.     }
  1473.     XStoreColor(theDisp, LocalCmap ? LocalCmap : theCmap, &ctab[0]);
  1474.  
  1475.     for (i=0; i<numcols; i++) {
  1476.       rdisp[colAllocOrder[i]] = r[rwpc2pc[i]];
  1477.       gdisp[colAllocOrder[i]] = g[rwpc2pc[i]];
  1478.       bdisp[colAllocOrder[i]] = b[rwpc2pc[i]];
  1479.     }
  1480.  
  1481.     return;
  1482.   }
  1483.     
  1484.  
  1485.   FreeAllColors();
  1486. #ifdef FOO
  1487.   if (theVisual->class & 1 && ncols>0) XClearWindow(theDisp,mainW);
  1488. #endif
  1489.  
  1490.   SortColormap();
  1491.   AllocColors();
  1492.   CreateXImage();
  1493.  
  1494.   if (useroot) MakeRootPic();
  1495.           else DrawWindow(0,0,eWIDE,eHIGH);
  1496.   if (but[BCROP].active) InvCropRect();
  1497.   SetCursors(-1);
  1498. }
  1499.  
  1500.  
  1501. /*********************/
  1502. static void calcHistEQ(histeq)
  1503.      int *histeq;
  1504. {
  1505.   int i, maxv, topbin, hist[256], rgb[256];
  1506.   byte *ep;
  1507.   unsigned long total;
  1508.  
  1509.   for (i=0; i<256; i++) {
  1510.     hist[i] = 0;
  1511.     rgb[i] = MONO(rcmap[i],gcmap[i],bcmap[i]);
  1512.   }
  1513.  
  1514.   /* compute intensity histogram */
  1515.   for (i=eWIDE*eHIGH,ep=epic; i>0; i--, ep++) hist[rgb[*ep]]++;
  1516.  
  1517.   if (DEBUG) {
  1518.     fprintf(stderr,"intensity histogram:  ");
  1519.     for (i=0; i<256; i++) fprintf(stderr,"%d ", hist[i]);
  1520.     fprintf(stderr,"\n\n");
  1521.   }
  1522.  
  1523.   /* compute histeq curve */
  1524.   total = topbin = 0;
  1525.   for (i=0; i<256; i++) {
  1526.     histeq[i] = (total * 255) / ((unsigned long) eWIDE * eHIGH);
  1527.     if (hist[i]) topbin = i;
  1528.     total += hist[i];
  1529.   }
  1530.  
  1531.   /* stretch range, as histeq[255] is probably *not* equal to 255 */
  1532.   maxv = (histeq[topbin]) ? histeq[topbin] : 255;   /* avoid div by 0 */
  1533.   for (i=0; i<256; i++)
  1534.     histeq[i] = (histeq[i] * 255) / maxv;
  1535.  
  1536.   if (DEBUG) {
  1537.     fprintf(stderr,"intensity eq curve:  ");
  1538.     for (i=0; i<256; i++) fprintf(stderr,"%d ", histeq[i]);
  1539.     fprintf(stderr,"\n\n");
  1540.   }
  1541.  
  1542.   /* play it safe:  do a range check on histeq */
  1543.   for (i=0; i<256; i++) RANGE(histeq[i],0,255);
  1544. }
  1545.  
  1546.  
  1547. /*********************/
  1548. void GammifyColors()
  1549. {
  1550.   int i;
  1551.  
  1552.   if (enabCB.val) {
  1553.     for (i=0; i<numcols; i++) Gammify1(i);
  1554.   }
  1555.   else {
  1556.     for (i=0; i<numcols; i++) { 
  1557.       r[i] = rcmap[i];
  1558.       g[i] = gcmap[i];
  1559.       b[i] = bcmap[i];
  1560.     }
  1561.   }
  1562. }
  1563.  
  1564.  
  1565. #define NOHUE -1
  1566.  
  1567. /*********************/
  1568. void Gammify1(col)
  1569. int col;
  1570. {
  1571.   int rv, gv, bv, vi, hi;
  1572.   double h,s,v;
  1573.  
  1574.   rv = rcmap[col];  gv = gcmap[col];  bv = bcmap[col];
  1575.   if (DEBUG>1) fprintf(stderr,"Gammify:  %d,%d,%d",rv,gv,bv);
  1576.  
  1577.   rgb2hsv(rv, gv, bv, &h, &s, &v);
  1578.   if (DEBUG>1) fprintf(stderr," -> %f,%f,%f",h,s,v);
  1579.  
  1580.   /* map near-black to black to avoid weird effects */
  1581.   if (v <= .0625) s = 0.0;
  1582.  
  1583.   /* apply intGraf.func[] function to 'v' (the intensity) */
  1584.   vi = (int) floor((v * 255.0) + 0.5);
  1585.   if (DEBUG>1) fprintf(stderr," (vi=%d)",vi);
  1586.  
  1587.   v = intGraf.func[vi] / 255.0;
  1588.   if (DEBUG>1) fprintf(stderr," (v=%f)",v);
  1589.  
  1590.   if (h>=0) {
  1591.     hi = (int) h;  
  1592.     if (hi<0)    hi += 360;
  1593.     if (hi>=360) hi -= 360;
  1594.     h = (double) hremap[hi];
  1595.   }
  1596.   else {
  1597.     if (whtHD.enabCB.val) {
  1598.       h = (double) whtHD.stval;
  1599.       s = (double) whtHD.satval / 100.0;
  1600.  
  1601.       /* special case:  if stval = satval = 0, set hue = -1 */
  1602.       if (whtHD.stval == 0 && whtHD.satval == 0) h = -1.0;
  1603.     }
  1604.   }
  1605.  
  1606.   /* apply satDial value to s */
  1607.   s = s + ((double) satDial.val) / 100.0;
  1608.   if (s<0.0) s = 0.0;
  1609.   if (s>1.0) s = 1.0;
  1610.  
  1611.   hsv2rgb(h,s,v,&rv, &gv, &bv);
  1612.   if (DEBUG>1) fprintf(stderr," -> %d,%d,%d",rv,gv,bv);
  1613.  
  1614.   r[col] = rGraf.func[rv];  
  1615.   g[col] = gGraf.func[gv];
  1616.   b[col] = bGraf.func[bv];
  1617.   if (DEBUG>1) fprintf(stderr," -> %d,%d,%d\n",r[col],g[col],b[col]);
  1618. }
  1619.  
  1620.  
  1621. /*********************/
  1622. void rgb2hsv(r,g,b, hr, sr, vr)
  1623. int r, g, b;
  1624. double *hr, *sr, *vr;
  1625. {
  1626.   double rd, gd, bd, h, s, v, max, min, del, rc, gc, bc;
  1627.  
  1628.   /* convert RGB to HSV */
  1629.   rd = r / 255.0;            /* rd,gd,bd range 0-1 instead of 0-255 */
  1630.   gd = g / 255.0;
  1631.   bd = b / 255.0;
  1632.  
  1633.   /* compute maximum of rd,gd,bd */
  1634.   if (rd>=gd) { if (rd>=bd) max = rd;  else max = bd; }
  1635.          else { if (gd>=bd) max = gd;  else max = bd; }
  1636.  
  1637.   /* compute minimum of rd,gd,bd */
  1638.   if (rd<=gd) { if (rd<=bd) min = rd;  else min = bd; }
  1639.          else { if (gd<=bd) min = gd;  else min = bd; }
  1640.  
  1641.   del = max - min;
  1642.   v = max;
  1643.   if (max != 0.0) s = (del) / max;
  1644.              else s = 0.0;
  1645.  
  1646.   h = NOHUE;
  1647.   if (s != 0.0) {
  1648.     rc = (max - rd) / del;
  1649.     gc = (max - gd) / del;
  1650.     bc = (max - bd) / del;
  1651.  
  1652.     if      (rd==max) h = bc - gc;
  1653.     else if (gd==max) h = 2 + rc - bc;
  1654.     else if (bd==max) h = 4 + gc - rc;
  1655.  
  1656.     h = h * 60;
  1657.     if (h<0) h += 360;
  1658.   }
  1659.  
  1660.   *hr = h;  *sr = s;  *vr = v;
  1661. }
  1662.  
  1663.  
  1664.  
  1665. /*********************/
  1666. void hsv2rgb(h, s, v, rr, gr, br)
  1667. double h, s, v;
  1668. int *rr, *gr, *br;
  1669. {
  1670.   int    j;
  1671.   double rd, gd, bd;
  1672.   double f, p, q, t;
  1673.  
  1674.   /* convert HSV back to RGB */
  1675.   if (h==NOHUE || s==0.0) { rd = v;  gd = v;  bd = v; }
  1676.   else {
  1677.     if (h==360.0) h = 0.0;
  1678.     h = h / 60.0;
  1679.     j = (int) floor(h);
  1680.     f = h - j;
  1681.     p = v * (1-s);
  1682.     q = v * (1 - (s*f));
  1683.     t = v * (1 - (s*(1 - f)));
  1684.  
  1685.     switch (j) {
  1686.     case 0:  rd = v;  gd = t;  bd = p;  break;
  1687.     case 1:  rd = q;  gd = v;  bd = p;  break;
  1688.     case 2:  rd = p;  gd = v;  bd = t;  break;
  1689.     case 3:  rd = p;  gd = q;  bd = v;  break;
  1690.     case 4:  rd = t;  gd = p;  bd = v;  break;
  1691.     case 5:  rd = v;  gd = p;  bd = q;  break;
  1692.     default: rd = v;  gd = t;  bd = p;  break;  /* never happen */
  1693.     }
  1694.   }
  1695.  
  1696.   *rr = (int) floor((rd * 255.0) + 0.5);
  1697.   *gr = (int) floor((gd * 255.0) + 0.5);
  1698.   *br = (int) floor((bd * 255.0) + 0.5);
  1699. }
  1700.  
  1701.  
  1702.  
  1703. /*********************/
  1704. static void save_gstate(gs)
  1705. struct gamstate *gs;
  1706. {
  1707.   bcopy(hmap, gs->hmap, sizeof(hmap));
  1708.  
  1709.   gs->wht_stval = whtHD.stval;  
  1710.   gs->wht_satval = whtHD.satval;  
  1711.   gs->wht_enab = whtHD.enabCB.val;
  1712.  
  1713.   gs->hueRBnum = RBWhich(hueRB);
  1714.  
  1715.   gs->satval = satDial.val;
  1716.   GetGrafState(&intGraf,&gs->istate);
  1717.   GetGrafState(&rGraf,  &gs->rstate);
  1718.   GetGrafState(&gGraf,  &gs->gstate);
  1719.   GetGrafState(&bGraf,  &gs->bstate);
  1720. }
  1721.  
  1722.  
  1723. /*********************/
  1724. static void restore_gstate(gs)
  1725. struct gamstate *gs;
  1726. {
  1727.   int changed = 0;
  1728.   struct hmap *hm;
  1729.  
  1730.   if (gs->hueRBnum != RBWhich(hueRB)) {
  1731.     RBSelect(hueRB, gs->hueRBnum);
  1732.     changed++;
  1733.   }
  1734.  
  1735.   if (bcmp(hmap, gs->hmap, sizeof(hmap))) {   /* hmap has changed */
  1736.     bcopy(gs->hmap, hmap, sizeof(hmap));
  1737.     build_hremap();
  1738.     changed++;
  1739.  
  1740.     hm = &(gs->hmap[gs->hueRBnum]);
  1741.  
  1742.     if (srcHD.stval  != hm->src_st ||
  1743.     srcHD.enval  != hm->src_en ||
  1744.     srcHD.ccwise != hm->src_ccw) {
  1745.       srcHD.stval  = hm->src_st;
  1746.       srcHD.enval  = hm->src_en;
  1747.       srcHD.ccwise = hm->src_ccw;
  1748.       HDRedraw(&srcHD, HD_ALL | HD_CLEAR);
  1749.     }
  1750.     
  1751.     if (dstHD.stval  != hm->dst_st ||
  1752.     dstHD.enval  != hm->dst_en ||
  1753.     dstHD.ccwise != hm->dst_ccw) {
  1754.       dstHD.stval  = hm->dst_st;
  1755.       dstHD.enval  = hm->dst_en;
  1756.       dstHD.ccwise = hm->dst_ccw;
  1757.       HDRedraw(&dstHD, HD_ALL | HD_CLEAR);
  1758.     }
  1759.   }    
  1760.  
  1761.  
  1762.   if (whtHD.stval != gs->wht_stval || whtHD.satval != gs->wht_satval ||
  1763.       whtHD.enabCB.val != gs->wht_enab) {
  1764.     whtHD.stval  = gs->wht_stval;
  1765.     whtHD.satval  = gs->wht_satval;
  1766.     whtHD.enabCB.val = gs->wht_enab;
  1767.     CBRedraw(&whtHD.enabCB);
  1768.     HDRedraw(&whtHD, HD_ALL | HD_CLEAR);
  1769.     changed++;
  1770.   }
  1771.     
  1772.   if (gs->satval != satDial.val) {
  1773.     DSetVal(&satDial,gs->satval);
  1774.     changed++;
  1775.   }
  1776.  
  1777.   if (SetGrafState(&intGraf, &gs->istate)) changed++;
  1778.   if (SetGrafState(&rGraf,   &gs->rstate)) changed++;
  1779.   if (SetGrafState(&gGraf,   &gs->gstate)) changed++;
  1780.   if (SetGrafState(&bGraf,   &gs->bstate)) changed++;
  1781.  
  1782.   if (changed) changedGam();
  1783. }
  1784.  
  1785.  
  1786. static int nosave_kludge = 0;
  1787.  
  1788. /*********************/
  1789. static void saveGamState()
  1790. {
  1791.   /* increment uptr, sticks current state into uptr
  1792.      set utail = uptr
  1793.      If utail==uhead, increment uhead (buffer full, throw away earliest)
  1794.    */
  1795.  
  1796.   if (nosave_kludge) return;
  1797.  
  1798.   uptr = (uptr+1) % MAXUNDO;
  1799.   save_gstate(&undo[uptr]);
  1800.   utail = uptr;
  1801.   if (utail == uhead) uhead = (uhead + 1) % MAXUNDO;
  1802.  
  1803.   BTSetActive(&gbut[G_BUNDO],1);
  1804.   BTSetActive(&gbut[G_BREDO],0);
  1805. }
  1806.  
  1807.  
  1808.  
  1809. /*********************/
  1810. static void gamUndo()
  1811. {
  1812.   /* if uptr!=uhead  decements uptr, restores state pointed to by uptr
  1813.                      if uptr now == uhead, turn off 'Undo' button
  1814.    */
  1815.  
  1816.   if (uptr != uhead) {   /* this should always be true when gamUndo called */
  1817.     uptr = (uptr + MAXUNDO - 1) % MAXUNDO;
  1818.     nosave_kludge = 1;
  1819.     restore_gstate(&undo[uptr]);
  1820.     nosave_kludge = 0;
  1821.     if (uptr == uhead) BTSetActive(&gbut[G_BUNDO],0);
  1822.     if (uptr != utail) BTSetActive(&gbut[G_BREDO],1);
  1823.   }
  1824. }
  1825.  
  1826.  
  1827.  
  1828. /*********************/
  1829. static void gamRedo()
  1830. {
  1831.   /* if uptr != utail   increments uptr, restores state pointed to by uptr
  1832.                         if uptr now == utail, turn off 'Redo' button */
  1833.  
  1834.   if (uptr != utail) {   /* this should always be true when gamRedo called */
  1835.     uptr = (uptr + 1) % MAXUNDO;
  1836.     nosave_kludge = 1;
  1837.     restore_gstate(&undo[uptr]);
  1838.     nosave_kludge = 0;
  1839.     if (uptr != uhead) BTSetActive(&gbut[G_BUNDO],1);
  1840.     if (uptr == utail) BTSetActive(&gbut[G_BREDO],0);
  1841.   }
  1842. }
  1843.  
  1844.  
  1845.  
  1846.  
  1847. /*********************/
  1848. static void rndCols()
  1849. {
  1850.   int i,j;
  1851.  
  1852.   for (i=0; i<numcols; i++) {
  1853.     if (cellgroup[i]) {
  1854.       /* determine if this group's already been given a random color */
  1855.       for (j=0; j<i && cellgroup[i] != cellgroup[j]; j++);
  1856.       if (j<i) {  /* it has */
  1857.     rcmap[i] = rcmap[j];  gcmap[i] = gcmap[j];  bcmap[i] = bcmap[j];
  1858.     continue;
  1859.       }
  1860.     }
  1861.  
  1862.     rcmap[i] = random()&0xff;
  1863.     gcmap[i] = random()&0xff;
  1864.     bcmap[i] = random()&0xff;
  1865.   }
  1866.  
  1867.   ChangeEC(editColor);
  1868.   applyGamma();
  1869. }
  1870.  
  1871.  
  1872.  
  1873. /*********************/
  1874. static void saveCMap(cst)
  1875. struct cmapstate *cst;
  1876. {
  1877.   int i;
  1878.  
  1879.   for (i=0; i<256; i++) {
  1880.     cst->r[i] = rcmap[i];
  1881.     cst->g[i] = gcmap[i];
  1882.     cst->b[i] = bcmap[i];
  1883.     cst->cellgroup[i] = cellgroup[i];
  1884.   }
  1885.  
  1886.   cst->curgroup  = curgroup;
  1887.   cst->maxgroup  = maxgroup;
  1888.   cst->editColor = editColor;
  1889. }
  1890.  
  1891.  
  1892. /*********************/
  1893. static void restoreCMap(cst)
  1894. struct cmapstate *cst;
  1895. {
  1896.   int i;
  1897.  
  1898.   for (i=0; i<256; i++) {
  1899.     rcmap[i] = cst->r[i];
  1900.     gcmap[i] = cst->g[i];
  1901.     bcmap[i] = cst->b[i];
  1902.     cellgroup[i] = cst->cellgroup[i];
  1903.   }
  1904.  
  1905.   curgroup = cst->curgroup;
  1906.   maxgroup = cst->maxgroup;
  1907.   editColor = cst->editColor;
  1908. }
  1909.  
  1910.  
  1911.     
  1912.  
  1913. /*********************/
  1914. static void parseResources()
  1915. {
  1916.   char gname[80], tmp[80], tmp1[256];
  1917.   int  i,j;
  1918.   struct gamstate *gsp, gs;
  1919.  
  1920.  
  1921.   /* look for the simple coledit-related resources */
  1922.   if (rd_flag("autoApply"))   autoCB.val = def_int;
  1923.   if (rd_flag("displayMods")) enabCB.val = def_int;
  1924.   if (rd_flag("autoReset"))   resetCB.val = def_int;
  1925.  
  1926.  
  1927.   /* look for preset/default resources */
  1928.   for (i=0; i<5; i++) {
  1929.     if (i) { sprintf(gname,"preset%d",i);  gsp = &preset[i-1]; }
  1930.       else { sprintf(gname,"default");     gsp = &defstate; }
  1931.  
  1932.     bcopy(gsp, &gs, sizeof(struct gamstate));   /* load 'gs' with defaults */
  1933.  
  1934.     for (j=0; j<6; j++) {                       /* xv.*.huemap resources */
  1935.       sprintf(tmp, "%s.huemap%d", gname, j+1);
  1936.       if (rd_str_cl(tmp, "Setting.Huemap")) {   /* got one */
  1937.     int fst, fen, tst, ten;
  1938.     char fcw[32], tcw[32];
  1939.  
  1940.     if (DEBUG) fprintf(stderr,"parseResource 'xv.%s: %s'\n",tmp, def_str);
  1941.     lower_str(def_str);
  1942.     if (sscanf(def_str,"%d %d %s %d %d %s",
  1943.            &fst, &fen, fcw, &tst, &ten, tcw) != 6) {
  1944.       fprintf(stderr,"%s: unable to parse resource 'xv.%s: %s'\n", 
  1945.           cmd, tmp, def_str);
  1946.     }
  1947.     else {
  1948.       RANGE(fst, 0, 359);   RANGE(fen, 0, 359);
  1949.       RANGE(tst, 0, 359);   RANGE(ten, 0, 359);
  1950.       gs.hmap[j].src_st  = fst;
  1951.       gs.hmap[j].src_en  = fen;
  1952.       gs.hmap[j].src_ccw = (strcmp(fcw,"ccw") == 0);
  1953.       gs.hmap[j].dst_st  = tst;
  1954.       gs.hmap[j].dst_en  = ten;
  1955.       gs.hmap[j].dst_ccw = (strcmp(tcw,"ccw") == 0);
  1956.     }
  1957.       }
  1958.     }
  1959.  
  1960.     sprintf(tmp, "%s.whtmap", gname);           /* xv.*.whtmap resource */
  1961.     if (rd_str_cl(tmp, "Setting.Whtmap")) {        /* got one */
  1962.       int wst, wsat, enab;
  1963.       if (DEBUG) fprintf(stderr,"parseResource 'xv.%s: %s'\n",tmp, def_str);
  1964.       if (sscanf(def_str,"%d %d %d", &wst, &wsat, &enab) != 3) {
  1965.     fprintf(stderr,"%s: unable to parse resource 'xv.%s: %s'\n", 
  1966.         cmd, tmp, def_str);
  1967.       }
  1968.       else {                                    /* successful parse */
  1969.     RANGE(wst, 0, 359);  RANGE(wsat, 0, 100);
  1970.     gs.wht_stval  = wst;
  1971.     gs.wht_satval = wsat;
  1972.     gs.wht_enab   = enab;
  1973.       }
  1974.     }
  1975.  
  1976.     sprintf(tmp, "%s.satval", gname);           /* xv.*.satval resource */
  1977.     if (rd_str_cl(tmp, "Setting.Satval")) {         /* got one */
  1978.       int sat;
  1979.       if (DEBUG) fprintf(stderr,"parseResource 'xv.%s: %s'\n",tmp, def_str);
  1980.       if (sscanf(def_str,"%d", &sat) != 1) {
  1981.     fprintf(stderr,"%s: unable to parse resource 'xv.%s: %s'\n", 
  1982.         cmd, tmp, def_str);
  1983.       }
  1984.       else {                                    /* successful parse */
  1985.     RANGE(sat, -100, 100);
  1986.     gs.satval = sat;
  1987.       }
  1988.     }
  1989.  
  1990.     for (j=0; j<4; j++) {                       /* xv.*.*graf resources */
  1991.       GRAF_STATE gstat, *gsgst;
  1992.       switch (j) {
  1993.       case 0: sprintf(tmp, "%s.igraf", gname);  gsgst = &gs.istate; break;
  1994.       case 1: sprintf(tmp, "%s.rgraf", gname);  gsgst = &gs.rstate; break;
  1995.       case 2: sprintf(tmp, "%s.ggraf", gname);  gsgst = &gs.gstate; break;
  1996.       case 3: sprintf(tmp, "%s.bgraf", gname);  gsgst = &gs.bstate; break;
  1997.       default: sprintf(tmp, "%s.bgraf", gname);  gsgst = &gs.bstate; break;
  1998.       }
  1999.  
  2000.       if (rd_str_cl(tmp, "Setting.Graf")) {       /* got one */
  2001.     strcpy(tmp1, def_str);
  2002.     bcopy(gsgst, &gstat, sizeof(GRAF_STATE));
  2003.     if (DEBUG) fprintf(stderr,"parseResource 'xv.%s: %s'\n",tmp, tmp1);
  2004.     if (!Str2Graf(&gstat, tmp1)) {            /* successful parse */
  2005.       bcopy(&gstat, gsgst, sizeof(GRAF_STATE));
  2006.     }
  2007.       }
  2008.     }
  2009.     
  2010.     /* copy (potentially) modified gs back to default/preset */
  2011.     bcopy(&gs, gsp, sizeof(struct gamstate));
  2012.   }
  2013. }
  2014.  
  2015.  
  2016. /*********************/
  2017. static void makeResources()
  2018. {
  2019.   char rsrc[2000];     /* wild over-estimation */
  2020.   char gname[40], rname[64], tmp[256], tmp1[256];
  2021.   struct gamstate gstate;
  2022.   int i;
  2023.  
  2024.   rsrc[0] = '\0';
  2025.  
  2026.   /* write out current state */
  2027.   save_gstate(&gstate);
  2028.   strcpy(gname, "xv.default");
  2029.   
  2030.   /* write out huemap resources */
  2031.   for (i=0; i<6; i++) {
  2032.     if (1 || gstate.hmap[i].src_st  != gstate.hmap[i].dst_st ||
  2033.     gstate.hmap[i].src_en  != gstate.hmap[i].dst_en ||
  2034.     gstate.hmap[i].src_ccw != gstate.hmap[i].dst_ccw) {
  2035.       sprintf(tmp, "%s.huemap%d: %3d %3d %3s %3d %3d %3s\n", gname, i+1, 
  2036.           gstate.hmap[i].src_st, gstate.hmap[i].src_en, 
  2037.           gstate.hmap[i].src_ccw ? "CCW" : "CW",
  2038.           gstate.hmap[i].dst_st, gstate.hmap[i].dst_en, 
  2039.           gstate.hmap[i].dst_ccw ? "CCW" : "CW");
  2040.       strcat(rsrc, tmp);
  2041.     }
  2042.   }
  2043.  
  2044.   /* write out whtmap resource */
  2045.   if (1 || gstate.wht_stval || gstate.wht_satval || gstate.wht_enab != 1) {
  2046.     sprintf(tmp, "%s.whtmap:  %d %d %d\n", gname, gstate.wht_stval, 
  2047.         gstate.wht_satval, gstate.wht_enab);
  2048.     strcat(rsrc, tmp);
  2049.   }
  2050.  
  2051.   /* write out satval resource */
  2052.   if (1 || gstate.satval) {
  2053.     sprintf(tmp, "%s.satval:  %d\n", gname, gstate.satval);
  2054.     strcat(rsrc, tmp);
  2055.   }
  2056.  
  2057.  
  2058.   /* write out graf resources */
  2059.   for (i=0; i<4; i++) {
  2060.     GRAF_STATE *gfstat;
  2061.     switch (i) {
  2062.     case 0: sprintf(rname, "%s.igraf", gname);  gfstat = &gstate.istate; break;
  2063.     case 1: sprintf(rname, "%s.rgraf", gname);  gfstat = &gstate.rstate; break;
  2064.     case 2: sprintf(rname, "%s.ggraf", gname);  gfstat = &gstate.gstate; break;
  2065.     case 3: sprintf(rname, "%s.bgraf", gname);  gfstat = &gstate.bstate; break;
  2066.     default:
  2067.       sprintf(rname, "%s.bgraf", gname);  gfstat = &gstate.bstate; break;
  2068.     }
  2069.  
  2070.     Graf2Str(gfstat, tmp1);
  2071.     sprintf(tmp, "%s: %s\n", rname, tmp1);
  2072.     strcat(rsrc, tmp);
  2073.   }
  2074.   XStoreBytes(theDisp, rsrc, strlen(rsrc));
  2075. }
  2076.     
  2077.     
  2078.     
  2079.         
  2080.  
  2081. /**********************************************/
  2082. /*************  HUE wheel functions ***********/
  2083. /**********************************************/
  2084.  
  2085.  
  2086. static int hdb_pixmaps_built = 0;
  2087. static Pixmap hdbpix1[N_HDBUTT];
  2088. static Pixmap hdbpix2[N_HDBUTT2];
  2089. #define PW 15
  2090. #define PH 15
  2091.  
  2092. /**************************************************/
  2093. static void HDCreate(hd, win, x, y, r, st, en, ccwise, str, fg, bg)
  2094. HDIAL *hd;
  2095. Window win;
  2096. int x,y,r,st,en,ccwise;
  2097. char *str;
  2098. u_long fg,bg;
  2099. {
  2100.   int i;
  2101.  
  2102.   hd->win    = win;
  2103.   hd->x      = x;
  2104.   hd->y      = y;
  2105.   hd->range  = r;
  2106.   hd->stval  = st;
  2107.   hd->enval  = en;
  2108.   hd->satval = 0;
  2109.   hd->ccwise = ccwise;
  2110.   hd->str    = str;
  2111.   hd->fg     = fg;
  2112.   hd->bg     = bg;
  2113.  
  2114.   if (!hdb_pixmaps_built) {
  2115.     hdbpix1[HDB_ROTL]   = XCreatePixmapFromBitmapData(theDisp, win,
  2116.                                     h_rotl_bits, PW,PH, 1, 0, 1);
  2117.     hdbpix1[HDB_ROTR]   = XCreatePixmapFromBitmapData(theDisp, win,
  2118.                                     h_rotr_bits, PW,PH, 1, 0, 1);
  2119.     hdbpix1[HDB_FLIP]   = XCreatePixmapFromBitmapData(theDisp, win,
  2120.                                     h_flip_bits, PW,PH, 1, 0, 1);
  2121.     hdbpix1[HDB_EXPND]  = XCreatePixmapFromBitmapData(theDisp, win,
  2122.                                     h_sinc_bits, PW,PH, 1, 0, 1);
  2123.     hdbpix1[HDB_SHRNK]  = XCreatePixmapFromBitmapData(theDisp, win,
  2124.                                     h_sdec_bits, PW,PH, 1, 0, 1);
  2125.  
  2126.     hdbpix2[HDB_SAT]    = XCreatePixmapFromBitmapData(theDisp, win,
  2127.                                     h_sat_bits, PW,PH, 1, 0, 1);
  2128.     hdbpix2[HDB_DESAT]  = XCreatePixmapFromBitmapData(theDisp, win,
  2129.                                     h_desat_bits, PW,PH, 1, 0, 1);
  2130.  
  2131.     hdbpix2[HDB_ROTL]  = hdbpix1[HDB_ROTL];
  2132.     hdbpix2[HDB_ROTR]  = hdbpix1[HDB_ROTR];
  2133.   }
  2134.  
  2135.     
  2136.   if (hd->range) {
  2137.     BTCreate(&hd->hdbutt[HDB_ROTL], win, x-50,y+60,18,18,NULL,fg,bg);
  2138.     BTCreate(&hd->hdbutt[HDB_ROTR], win, x-30,y+60,18,18,NULL,fg,bg);
  2139.     BTCreate(&hd->hdbutt[HDB_FLIP], win, x-10,y+60,18,18,NULL,fg,bg);
  2140.     BTCreate(&hd->hdbutt[HDB_EXPND],win, x+10,y+60,18,18,NULL,fg,bg);
  2141.     BTCreate(&hd->hdbutt[HDB_SHRNK],win, x+30,y+60,18,18,NULL,fg,bg);
  2142.  
  2143.     for (i=0; i<N_HDBUTT; i++) {
  2144.       hd->hdbutt[i].pix = hdbpix1[i];
  2145.       hd->hdbutt[i].pw = PW;
  2146.       hd->hdbutt[i].ph = PH;
  2147.     }
  2148.   }
  2149.  
  2150.   else {
  2151.     BTCreate(&hd->hdbutt[HDB_ROTL], win, x-39,y+60,18,18,NULL,fg,bg);
  2152.     BTCreate(&hd->hdbutt[HDB_ROTR], win, x-19,y+60,18,18,NULL,fg,bg);
  2153.     BTCreate(&hd->hdbutt[HDB_DESAT],win, x+1, y+60,18,18,NULL,fg,bg);
  2154.     BTCreate(&hd->hdbutt[HDB_SAT],  win, x+21,y+60,18,18,NULL,fg,bg);
  2155.     CBCreate(&hd->enabCB, win, x+23, y-44, "", fg, bg);
  2156.     hd->enabCB.val = 1;
  2157.  
  2158.     for (i=0; i<N_HDBUTT2; i++) {
  2159.       hd->hdbutt[i].pix = hdbpix2[i];
  2160.       hd->hdbutt[i].pw = PW;
  2161.       hd->hdbutt[i].ph = PH;
  2162.     }
  2163.   }
  2164. }
  2165.  
  2166.  
  2167. /**************************************************/
  2168. static void HDRedraw(hd, flags)
  2169. HDIAL *hd;
  2170. int flags;
  2171. {
  2172.   int    i, x, y, x1, y1;
  2173.   double a;
  2174.  
  2175.   if (flags & HD_CLEAR) {
  2176.     XSetForeground(theDisp, theGC, hd->bg);
  2177.     XFillArc(theDisp, hd->win, theGC, hd->x-(HD_RADIUS-1),hd->y-(HD_RADIUS-1),
  2178.          (HD_RADIUS-1)*2, (HD_RADIUS-1)*2, 0, 360*64);
  2179.   }
  2180.  
  2181.   if (flags & HD_FRAME) {
  2182.     static char *colstr = "RYGCBM";
  2183.     char tstr[2];
  2184.  
  2185.     XSetForeground(theDisp, theGC, hd->fg);
  2186.     XDrawArc(theDisp, hd->win, theGC, hd->x - HD_RADIUS, hd->y - HD_RADIUS,
  2187.          HD_RADIUS*2, HD_RADIUS*2, 0, 360*64);
  2188.     
  2189.     for (i=0; i<6; i++) {
  2190.       int kldg;
  2191.  
  2192.       if (i==2 || i==5) kldg = -1;  else kldg = 0;
  2193.       a = hdg2xdg(i*60) * DEG2RAD;
  2194.       pol2xy(hd->x, hd->y, a, HD_RADIUS+1,      &x,  &y);
  2195.       pol2xy(hd->x, hd->y, a, HD_RADIUS+4+kldg, &x1, &y1);
  2196.       XDrawLine(theDisp, hd->win, theGC, x,y,x1,y1);
  2197.  
  2198.       tstr[0] = colstr[i];  tstr[1] = '\0';
  2199.       pol2xy(hd->x, hd->y, a, HD_RADIUS+10, &x, &y);
  2200.       CenterString(hd->win, tstr, x,y);
  2201.     }
  2202.   }
  2203.  
  2204.   if (flags & HD_HANDS || flags & HD_CLHNDS) {
  2205.     if (flags & HD_CLHNDS) XSetForeground(theDisp, theGC, hd->bg);
  2206.                       else XSetForeground(theDisp, theGC, hd->fg);
  2207.  
  2208.     if (hd->range ) {
  2209.       if (flags & HD_HANDS)   /* draw center dot */
  2210.     XFillRectangle(theDisp, hd->win, theGC, hd->x-1, hd->y-1, 3,3);
  2211.  
  2212.       a = hdg2xdg(hd->stval) * DEG2RAD;
  2213.       pol2xy(hd->x, hd->y, a, HD_RADIUS - 4, &x, &y);
  2214.       XDrawLine(theDisp, hd->win, theGC, hd->x, hd->y, x,y);
  2215.       
  2216.       if (flags & HD_CLHNDS) 
  2217.     XFillRectangle(theDisp, hd->win, theGC, x-2,y-2, 5,5);
  2218.       else {
  2219.     XSetForeground(theDisp, theGC, hd->bg);
  2220.     XFillRectangle(theDisp, hd->win, theGC, x-1, y-1, 3,3);
  2221.     XSetForeground(theDisp, theGC, hd->fg);
  2222.     XDrawPoint(theDisp, hd->win, theGC, x, y);
  2223.     XDrawRectangle(theDisp, hd->win, theGC, x-2, y-2, 4,4);
  2224.       }
  2225.  
  2226.       a = hdg2xdg(hd->enval) * DEG2RAD;
  2227.       pol2xy(hd->x, hd->y, a, HD_RADIUS - 4, &x, &y);
  2228.       XDrawLine(theDisp, hd->win, theGC, hd->x, hd->y, x,y);
  2229.     
  2230.       if (flags & HD_CLHNDS) 
  2231.     XFillRectangle(theDisp, hd->win, theGC, x-2,y-2, 5,5);
  2232.       else {
  2233.     XSetForeground(theDisp, theGC, hd->bg);
  2234.     XFillRectangle(theDisp, hd->win, theGC, x-1, y-1, 3,3);
  2235.     XSetForeground(theDisp, theGC, hd->fg);
  2236.     XDrawPoint(theDisp, hd->win, theGC, x, y);
  2237.     XDrawRectangle(theDisp, hd->win, theGC, x-2, y-2, 4,4);
  2238.       }
  2239.     }
  2240.  
  2241.     else {  /* not a range;  hue/sat dial */
  2242.       int r;
  2243.  
  2244.       /* compute x,y position from stval/satval */
  2245.       a = hdg2xdg(hd->stval) * DEG2RAD;
  2246.       r = ((HD_RADIUS - 4) * hd->satval) / 100;
  2247.       pol2xy(hd->x, hd->y, a, r, &x, &y);
  2248.  
  2249.       if (flags & HD_CLHNDS) 
  2250.     XFillRectangle(theDisp, hd->win, theGC, x-2,y-2, 5,5);
  2251.       else {
  2252.     XFillRectangle(theDisp, hd->win, theGC, hd->x-1, hd->y-1, 3,3);
  2253.  
  2254.     XSetForeground(theDisp, theGC, hd->bg);
  2255.     XFillRectangle(theDisp, hd->win, theGC, x-1, y-1, 3,3);
  2256.     XSetForeground(theDisp, theGC, hd->fg);
  2257.     XDrawPoint(theDisp, hd->win, theGC, x, y);
  2258.     XDrawRectangle(theDisp, hd->win, theGC, x-2, y-2, 4,4);
  2259.       }
  2260.     }
  2261.   }
  2262.     
  2263.  
  2264.  
  2265.  
  2266.   if ((flags & HD_DIR || flags & HD_CLHNDS) && hd->range) {
  2267.     int xdg1, xdg2, xdlen;
  2268.  
  2269.     if (flags & HD_CLHNDS) XSetForeground(theDisp, theGC, hd->bg);
  2270.                       else XSetForeground(theDisp, theGC, hd->fg);
  2271.  
  2272.     if (hd->ccwise) {
  2273.       xdg1 = hdg2xdg(hd->stval);
  2274.       xdg2 = hdg2xdg(hd->enval);
  2275.     }
  2276.     else {
  2277.       xdg1 = hdg2xdg(hd->enval);
  2278.       xdg2 = hdg2xdg(hd->stval);
  2279.     }
  2280.  
  2281.     xdlen = xdg2 - xdg1;
  2282.     if (xdlen<0) xdlen += 360;   /* note: 0 len means no range */
  2283.  
  2284.     if (xdlen>1) {
  2285.       XDrawArc(theDisp, hd->win, theGC, hd->x - ((HD_RADIUS*3)/5),
  2286.            hd->y - ((HD_RADIUS*3)/5), (HD_RADIUS*6)/5, (HD_RADIUS*6)/5,
  2287.            xdg1 * 64, xdlen * 64);
  2288.     }
  2289.  
  2290.     if (xdlen > 16) {
  2291.       xdg1 = hdg2xdg(hd->enval);
  2292.       if (hd->ccwise) xdg2 = xdg1-10;
  2293.                  else xdg2 = xdg1+10;
  2294.  
  2295.       pol2xy(hd->x, hd->y, xdg2 * DEG2RAD, ((HD_RADIUS*3)/5) + 3, &x, &y);
  2296.       pol2xy(hd->x, hd->y, xdg1 * DEG2RAD, ((HD_RADIUS*3)/5),     &x1,&y1);
  2297.       XDrawLine(theDisp, hd->win, theGC, x, y, x1, y1);
  2298.  
  2299.       if (hd->ccwise) xdg2 = xdg1-16;
  2300.                  else xdg2 = xdg1+16;
  2301.       pol2xy(hd->x, hd->y, xdg2 * DEG2RAD, ((HD_RADIUS*3)/5) - 2, &x, &y);
  2302.       XDrawLine(theDisp, hd->win, theGC, x, y, x1, y1);
  2303.     }
  2304.   }
  2305.  
  2306.  
  2307.   if (flags & HD_VALS) {
  2308.     char vstr[32];
  2309.  
  2310.     XSetFont(theDisp, theGC, monofont);
  2311.     XSetForeground(theDisp, theGC, hd->fg);
  2312.     XSetBackground(theDisp, theGC, hd->bg);
  2313.  
  2314.     if (hd->range) {
  2315.       sprintf(vstr,"%3d\007,%3d\007 %s", hd->stval, hd->enval, 
  2316.           hd->ccwise ? "CCW" : " CW");
  2317.     }
  2318.     else {
  2319.       sprintf(vstr,"%3d\007 %3d%%", hd->stval, hd->satval);
  2320.     }
  2321.       
  2322.     XDrawImageString(theDisp, hd->win, theGC,
  2323.              hd->x - XTextWidth(monofinfo, vstr, strlen(vstr))/2,
  2324.              hd->y + HD_RADIUS + 24, vstr, strlen(vstr));
  2325.     XSetFont(theDisp, theGC, mfont);
  2326.   }
  2327.  
  2328.  
  2329.   if (flags & HD_TITLE) {
  2330.     XSetForeground(theDisp, theGC, hd->fg);
  2331.     ULineString(hd->win, hd->str, hd->x - HD_RADIUS - 15,
  2332.         hd->y - HD_RADIUS - 4);
  2333.   }
  2334.  
  2335.  
  2336.   if (flags & HD_BUTTS) {
  2337.     if (hd->range) {
  2338.       for (i=0; i<N_HDBUTT; i++) BTRedraw(&hd->hdbutt[i]);
  2339.     }
  2340.     else {
  2341.       for (i=0; i<N_HDBUTT2; i++) BTRedraw(&hd->hdbutt[i]);
  2342.       CBRedraw(&hd->enabCB);
  2343.     }
  2344.   }
  2345.  
  2346.   if (!hd->range && !hd->enabCB.val) {  /* draw dimmed */
  2347.     XSync(theDisp, False);
  2348.     DimRect(hd->win, hd->x-HD_RADIUS-15, hd->y-HD_RADIUS-4-ASCENT,
  2349.         2*HD_RADIUS+30,(2*HD_RADIUS+4+ASCENT+80), hd->bg);
  2350.     XSync(theDisp, False);
  2351.     CBRedraw(&hd->enabCB);
  2352.   }
  2353. }
  2354.  
  2355.  
  2356.     
  2357. /**************************************************/
  2358. static int HDClick(hd,mx,my)
  2359. HDIAL *hd;
  2360. int mx, my;
  2361. {
  2362.   /* called when a click received.  checks whether HDTrack needs to be
  2363.      called.  Returns '1' if click is in HD dial area, 0 otherwise */
  2364.  
  2365.   int bnum,maxb;
  2366.   BUTT *bp;
  2367.  
  2368.   if (CBClick(&hd->enabCB, mx, my)) {
  2369.     if (CBTrack(&hd->enabCB)) {
  2370.       HDRedraw(hd, HD_ALL);
  2371.       return 1;
  2372.     }
  2373.   }
  2374.  
  2375.   if (!hd->range && !hd->enabCB.val) return 0;    /* disabled */
  2376.  
  2377.  
  2378.   if ( ((mx - hd->x) * (mx - hd->x)  +  (my - hd->y) * (my - hd->y)) 
  2379.       < (HD_RADIUS * HD_RADIUS)) {
  2380.     return HDTrack(hd,mx,my);
  2381.   }
  2382.  
  2383.   if (hd->range) maxb = N_HDBUTT;  else maxb = N_HDBUTT2;
  2384.  
  2385.   for (bnum=0; bnum<maxb; bnum++) {
  2386.     bp = &hd->hdbutt[bnum];
  2387.     if (PTINRECT(mx,my, bp->x, bp->y, bp->w, bp->h)) break;
  2388.   }
  2389.   if (bnum==maxb) return 0;
  2390.  
  2391.   if (bnum==HDB_FLIP && hd->range) {
  2392.     if (BTTrack(bp)) {
  2393.       int t;
  2394.       HDRedraw(hd, HD_CLHNDS);
  2395.       t = hd->stval;  hd->stval = hd->enval;  hd->enval = t;
  2396.       hd->ccwise = !hd->ccwise;
  2397.       HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2398.       return 1;
  2399.     }
  2400.     return 0;
  2401.   }
  2402.  
  2403.   else {    /* track buttons til mouse up */
  2404.     Window rW, cW;
  2405.     int    rx,ry,x,y;
  2406.     unsigned int mask;
  2407.  
  2408.     bp->lit = 1;  BTRedraw(bp);  /* light it up */
  2409.  
  2410.     /* loop until mouse is released */
  2411.     while (XQueryPointer(theDisp,hd->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  2412.       if (!(mask & Button1Mask)) break;    /* button released */
  2413.  
  2414.       /* check to see if state needs toggling */
  2415.       if (( bp->lit && !PTINRECT(x,y,bp->x,bp->y,bp->w,bp->h)) ||
  2416.       (!bp->lit &&  PTINRECT(x,y,bp->x,bp->y,bp->w,bp->h))) {
  2417.     bp->lit = !bp->lit;
  2418.     BTRedraw(bp);
  2419.       }
  2420.  
  2421.       if (bp->lit) {
  2422.     switch (bnum) {
  2423.     case HDB_ROTL:
  2424.       HDRedraw(hd, HD_CLHNDS);
  2425.       hd->stval--;  if (hd->stval<0) hd->stval += 360;
  2426.       hd->enval--;  if (hd->enval<0) hd->enval += 360;
  2427.       HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2428.       break;
  2429.  
  2430.     case HDB_ROTR:
  2431.       HDRedraw(hd, HD_CLHNDS);
  2432.       hd->stval++;  if (hd->stval>=360) hd->stval -= 360;
  2433.       hd->enval++;  if (hd->enval>=360) hd->enval -= 360;
  2434.       HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2435.       break;
  2436.  
  2437.     /* case HDB_DESAT: */
  2438.     /* case HDB_SAT:   */
  2439.     case HDB_EXPND:
  2440.     case HDB_SHRNK:
  2441.       if (hd->range) {
  2442.         if ((bnum == HDB_EXPND) &&
  2443.         (( hd->ccwise && hd->enval == (hd->stval+1))  ||
  2444.          (!hd->ccwise && hd->enval == (hd->stval-1))))
  2445.         { /* max size:  can't grow */ }
  2446.  
  2447.         else if (bnum == HDB_SHRNK && hd->stval == hd->enval)
  2448.           { /* min size, can't shrink */ }
  2449.  
  2450.         else {   /* can shrink or grow */
  2451.           HDRedraw(hd, HD_CLHNDS);
  2452.           if ((bnum==HDB_EXPND &&  hd->ccwise) ||
  2453.           (bnum==HDB_SHRNK && !hd->ccwise)) {
  2454.         if ((hd->stval & 1) == (hd->enval & 1))
  2455.           hd->stval = (hd->stval + 1 + 360) % 360;
  2456.         else
  2457.           hd->enval = (hd->enval - 1 + 360) % 360;
  2458.           }
  2459.  
  2460.           else {
  2461.         if ((hd->stval & 1) == (hd->enval & 1))
  2462.           hd->stval = (hd->stval - 1 + 360) % 360;
  2463.         else
  2464.           hd->enval = (hd->enval + 1 + 360) % 360;
  2465.           }
  2466.           HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2467.         }
  2468.       }
  2469.  
  2470.       else {   /* hue/sat dial:  SAT/DESAT */
  2471.         if (bnum == HDB_DESAT && hd->satval>0) {
  2472.           HDRedraw(hd, HD_CLHNDS);
  2473.           hd->satval--;  if (hd->satval<0) hd->satval = 0;
  2474.           HDRedraw(hd, HD_HANDS | HD_VALS);
  2475.         }
  2476.           
  2477.         else if (bnum == HDB_SAT && hd->satval<100) {
  2478.           HDRedraw(hd, HD_CLHNDS);
  2479.           hd->satval++;  if (hd->satval>100) hd->satval = 100;
  2480.           HDRedraw(hd, HD_HANDS | HD_VALS);
  2481.         }
  2482.       }
  2483.           
  2484.       break;
  2485.       
  2486.     }
  2487.     Timer(150);
  2488.       }
  2489.     }
  2490.  
  2491.     XFlush(theDisp);
  2492.   }
  2493.  
  2494.   if (bp->lit) {  bp->lit = 0;  BTRedraw(bp); }
  2495.     
  2496.   return 1;
  2497. }
  2498.  
  2499.  
  2500.  
  2501. /**************************************************/
  2502. static int HDTrack(hd,mx,my)
  2503. HDIAL *hd;
  2504. int mx,my;
  2505. {
  2506.   /* called when clicked in dial area.  tracks dragging handles around...
  2507.      returns '1' if anything changed */
  2508.  
  2509.   Window       rW,cW;
  2510.   int          rx,ry, x,y, ival,j, rv;
  2511.   unsigned int mask;
  2512.  
  2513.   rv = 0;
  2514.  
  2515.   if (!hd->range) {     /* hue/sat dial */
  2516.     int ihue, isat, newhue, newsat;
  2517.     double dx,dy,dist;
  2518.  
  2519.     ihue = hd->stval;  isat = hd->satval;
  2520.  
  2521.     /* loop until mouse is released */
  2522.     while (XQueryPointer(theDisp,hd->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  2523.       if (!(mask & Button1Mask)) break;    /* button released */
  2524.  
  2525.       /* compute new value based on mouse pos */
  2526.       newhue = computeHDval(hd, x,y);
  2527.       if (newhue<0) newhue = hd->stval;  /* at 'spaz' point, keep hue const */
  2528.  
  2529.       dx = x - hd->x;  dy = y - hd->y;
  2530.       dist = sqrt(dx*dx + dy*dy);
  2531.       
  2532.       newsat = (int) (dist / ((double) (HD_RADIUS - 4)) * 100);
  2533.       RANGE(newsat,0,100);
  2534.  
  2535.       if (newhue != hd->stval || newsat != hd->satval) {
  2536.     HDRedraw(hd, HD_CLHNDS);
  2537.     hd->stval = newhue;  hd->satval = newsat;
  2538.     HDRedraw(hd, HD_HANDS | HD_VALS);
  2539.       }
  2540.     }
  2541.  
  2542.     rv = (hd->stval != ihue || hd->satval != isat);
  2543.   }
  2544.  
  2545.   else {   /* the hard case */
  2546.     double a;
  2547.     int    handle = 0, *valp;
  2548.  
  2549.     /* determine if we're in either of the handles */
  2550.     a = hdg2xdg(hd->stval) * DEG2RAD;
  2551.     pol2xy(hd->x, hd->y, a, HD_RADIUS-4, &x,&y);
  2552.     if (PTINRECT(mx,my,x-3,y-3,7,7)) handle = 1;
  2553.  
  2554.     a = hdg2xdg(hd->enval) * DEG2RAD;
  2555.     pol2xy(hd->x, hd->y, a, HD_RADIUS-4, &x,&y);
  2556.     if (PTINRECT(mx,my,x-3,y-3,7,7)) handle = 2;
  2557.     
  2558.  
  2559.  
  2560.     if (!handle) {  /* not in either, rotate both */
  2561.       int oldj, len, origj;
  2562.  
  2563.       if (!hd->ccwise) {
  2564.     len = ((hd->enval - hd->stval + 360) % 360);
  2565.     oldj = (hd->stval + len/2 + 360) % 360;
  2566.       }
  2567.       else {
  2568.     len = ((hd->stval - hd->enval + 360) % 360);
  2569.     oldj = (hd->enval + len/2 + 360) % 360;
  2570.       }
  2571.  
  2572.       origj = j = oldj;
  2573.  
  2574.       while (XQueryPointer(theDisp,hd->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  2575.     if (!(mask & Button1Mask)) break;    /* button released */
  2576.  
  2577.     /* compute new value based on mouse pos */
  2578.     j = computeHDval(hd, x,y);
  2579.     if (j>=0 && j != oldj) {
  2580.       oldj = j;
  2581.       HDRedraw(hd, HD_CLHNDS);
  2582.       if (!hd->ccwise) {
  2583.         hd->stval = (j - len/2 + 360) % 360;
  2584.         hd->enval = (j + len/2 + 360) % 360;
  2585.       }
  2586.       else {
  2587.         hd->stval = (j + len/2 + 360) % 360;
  2588.         hd->enval = (j - len/2 + 360) % 360;
  2589.       }
  2590.       HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2591.     }
  2592.       }
  2593.       rv = (origj != j);
  2594.     }
  2595.         
  2596.  
  2597.     else {  /* in one of the handles */
  2598.       if (handle==1) valp = &(hd->stval);  else valp = &(hd->enval);
  2599.       ival = *valp;
  2600.  
  2601.       /* loop until mouse is released */
  2602.       while (XQueryPointer(theDisp,hd->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  2603.     if (!(mask & Button1Mask)) break;    /* button released */
  2604.  
  2605.     /* compute new value based on mouse pos */
  2606.     j = computeHDval(hd, x,y);
  2607.     if (j>=0 && j != *valp) {
  2608.       int ndist, ddist;
  2609.  
  2610.       HDRedraw(hd, HD_CLHNDS);
  2611.  
  2612.       if (!hd->ccwise) {
  2613.         ddist = (hd->enval - hd->stval + 360) % 360;
  2614.         if (handle==1) 
  2615.           ndist = (hd->enval - j + 360) % 360;
  2616.         else
  2617.           ndist = (j - hd->stval + 360) % 360;
  2618.       }
  2619.       else {
  2620.         ddist = (hd->stval - hd->enval + 360) % 360;
  2621.         if (handle==1) 
  2622.           ndist = (j - hd->enval + 360) % 360;
  2623.         else
  2624.           ndist = (hd->stval - j + 360) % 360;
  2625.       }
  2626.  
  2627.       if (abs(ddist - ndist) >= 180 && ddist<180) 
  2628.         hd->ccwise = !hd->ccwise;
  2629.       
  2630.       *valp = j;
  2631.       HDRedraw(hd, HD_HANDS | HD_DIR | HD_VALS);
  2632.     }
  2633.       }
  2634.       rv = (*valp != ival);
  2635.     }
  2636.   }
  2637.  
  2638.   return rv;
  2639. }
  2640.     
  2641.       
  2642.  
  2643. /**************************************************/
  2644. static int hdg2xdg(hdg)
  2645. int hdg;
  2646. {
  2647.   int xdg;
  2648.  
  2649.   xdg = 270 - hdg;
  2650.   if (xdg < 0)   xdg += 360;
  2651.   if (xdg > 360) xdg -= 360;
  2652.  
  2653.   return xdg;
  2654. }
  2655.  
  2656.  
  2657. /**************************************************/
  2658. static void pol2xy(cx, cy, ang, rad, xp, yp)
  2659. int cx, cy, rad, *xp, *yp;
  2660. double ang;
  2661. {
  2662.   *xp = cx + (int) (cos(ang) * (double) rad);
  2663.   *yp = cy - (int) (sin(ang) * (double) rad);
  2664. }
  2665.  
  2666.   
  2667. /***************************************************/
  2668. static int computeHDval(hd, x, y)
  2669. HDIAL *hd;
  2670. int x, y;
  2671. {
  2672.   int dx, dy;
  2673.   double angle;
  2674.  
  2675.   /* compute dx, dy (distance from center).  Note: +dy is *up* */
  2676.   dx = x - hd->x;  dy = hd->y - y;
  2677.  
  2678.   /* if too close to center, return -1 (invalid) avoid 'spazzing' */
  2679.   if (abs(dx) < 3 && abs(dy) < 3) return -1;
  2680.  
  2681.   /* figure out angle of vector dx,dy */
  2682.   if (dx==0) {     /* special case */
  2683.     if (dy>0) angle =  90.0;
  2684.          else angle = -90.0;
  2685.   }
  2686.   else if (dx>0) angle = atan((double)  dy / (double)  dx) * RAD2DEG;
  2687.   else           angle = atan((double) -dy / (double) -dx) * RAD2DEG + 180.0;
  2688.  
  2689.   angle = 270 - angle;   /* map into h-degrees */
  2690.   if (angle >= 360.0) angle -= 360.0;
  2691.   if (angle <    0.0) angle += 360.0;
  2692.  
  2693.   return (int) angle;
  2694. }
  2695.  
  2696.  
  2697.  
  2698.     
  2699. /****************************************************/
  2700. static void initHmap()
  2701. {
  2702.   int i;
  2703.   for (i=0; i<N_HMAP; i++) init1hmap(i);
  2704. }
  2705.  
  2706.  
  2707. /****************************************************/
  2708. static void init1hmap(i)
  2709. int i;
  2710. {
  2711.   int cang, width;
  2712.  
  2713.   width = 360 / N_HMAP;
  2714.  
  2715.   cang = i * width;
  2716.   hmap[i].src_st  = hmap[i].dst_st = (cang - width/2 + 360) % 360;
  2717.   hmap[i].src_en  = hmap[i].dst_en = (cang - width/2 + width +360) % 360;
  2718.   hmap[i].src_ccw = hmap[i].dst_ccw = 0;
  2719. }
  2720.  
  2721.  
  2722. /****************************************************/
  2723. static void dials2hmap()
  2724. {
  2725.   int i;
  2726.   i = RBWhich(hueRB);
  2727.  
  2728.   hmap[i].src_st  = srcHD.stval;
  2729.   hmap[i].src_en  = srcHD.enval;
  2730.   hmap[i].src_ccw = srcHD.ccwise;
  2731.  
  2732.   hmap[i].dst_st  = dstHD.stval;
  2733.   hmap[i].dst_en  = dstHD.enval;
  2734.   hmap[i].dst_ccw = dstHD.ccwise;
  2735. }
  2736.  
  2737.  
  2738. /****************************************************/
  2739. static void hmap2dials()
  2740. {
  2741.   int i;
  2742.   i = RBWhich(hueRB);
  2743.  
  2744.   srcHD.stval  = hmap[i].src_st;
  2745.   srcHD.enval  = hmap[i].src_en;
  2746.   srcHD.ccwise = hmap[i].src_ccw;
  2747.  
  2748.   dstHD.stval  = hmap[i].dst_st;
  2749.   dstHD.enval  = hmap[i].dst_en;
  2750.   dstHD.ccwise = hmap[i].dst_ccw;
  2751.  
  2752.   HDRedraw(&srcHD, HD_ALL | HD_CLEAR);
  2753.   HDRedraw(&dstHD, HD_ALL | HD_CLEAR);
  2754. }
  2755.  
  2756.  
  2757. /****************************************************/
  2758. static void build_hremap()
  2759. {
  2760.   int i,j, st1, en1, inc1, len1, st2, en2, inc2, len2;
  2761.   int a1, a2;
  2762.  
  2763.   /* start with a 1:1 mapping */
  2764.   for (i=0; i<360; i++) hremap[i] = i;
  2765.  
  2766.   for (i=0; i<N_HMAP; i++) {
  2767.     if ((hmap[i].src_st  != hmap[i].dst_st) ||
  2768.     (hmap[i].src_en  != hmap[i].dst_en) ||
  2769.     (hmap[i].src_ccw != hmap[i].dst_ccw)) {   /* not a 1:1 mapping */
  2770.  
  2771.       st1  = hmap[i].src_st;  
  2772.       en1  = hmap[i].src_en;
  2773.       if (hmap[i].src_ccw) {
  2774.     inc1 = -1; 
  2775.     len1 = (st1 - en1 + 360) % 360;
  2776.       }
  2777.       else {
  2778.     inc1 = 1;
  2779.     len1 = (en1 - st1 + 360) % 360;
  2780.       }
  2781.  
  2782.       st2 = hmap[i].dst_st;
  2783.       en2 = hmap[i].dst_en;
  2784.       if (hmap[i].dst_ccw) {
  2785.     inc2 = -1; 
  2786.     len2 = (st2 - en2 + 360) % 360;
  2787.       }
  2788.       else {
  2789.     inc2 = 1;
  2790.     len2 = (en2 - st2 + 360) % 360;
  2791.       }
  2792.  
  2793.       if (len1==0) {
  2794.     a1 = st1;
  2795.     a2 = st2;
  2796.     hremap[a1] = a2;
  2797.       }
  2798.       else {
  2799.     if (DEBUG) fprintf(stderr,"mapping %d: %d,%d %s  %d,%d %s\n",
  2800.                i, hmap[i].src_st, hmap[i].src_en,
  2801.                hmap[i].src_ccw ? "ccw" : "cw",
  2802.                hmap[i].dst_st, hmap[i].dst_en,
  2803.                hmap[i].dst_ccw ? "ccw" : "cw");
  2804.  
  2805.     for (j=0, a1=st1; j<=len1; a1 = (a1 + inc1 + 360) % 360, j++) {
  2806.       a2 = (((inc2 * len2 * j) / len1 + st2) + 360) % 360;
  2807.       hremap[a1] = a2;
  2808.       if (DEBUG) fprintf(stderr,"(%d->%d)  ", a1, a2);
  2809.     }
  2810.     if (DEBUG) fprintf(stderr,"\n");
  2811.       }
  2812.     }
  2813.   }
  2814. }
  2815.  
  2816.