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