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