1 /*
2 * xvgraf.c - GRAF window handling functions
3 *
4 * callable functions:
5 *
6 * CreateGraf() - creates a GRAF window
7 * InitGraf() - inits GRAF handles to reasonable defaults
8 * RedrawGraf() - called by 'expose' events
9 * ClickGraf() - called when B1 clicked in GRAF window
10 * GrafKey() - called when keypress in GRAF window
11 * Graf2Str() - copies current GRAF settings to a string
12 * Str2Graf() - parses an xrdb string into GRAF settings
13 * GetGrafState() - copies GRAF data into GRAF_STATE structure
14 * SetGrafState() - sets GRAF data based on GRAF_STATE
15 * InitSpline() - called to generate y' table for EvalSpline
16 * EvalSpline() - evalutes spline function at given point
17 */
18
19 #include "copyright.h"
20
21 #include "xv.h"
22
23 #include "bits/gf1_addh"
24 #include "bits/gf1_delh"
25 #include "bits/gf1_line"
26 #include "bits/gf1_spln"
27 #include "bits/gf1_rst"
28 #include "bits/gf1_gamma"
29
30
31 #define GHIGH 147
32 #define GBHIGH 23
33 #define GBWIDE 30
34 #define GWIDE (128 + 3 + 4 + GBWIDE)
35
36 #define PW gf1_addh_width
37 #define PH gf1_addh_height
38
39
40 static int pixmaps_built = 0;
41 static Pixmap gfbpix[N_GFB];
42
43
44 static void drawGraf PARM((GRAF *, int));
45 static void drawHandPos PARM((GRAF *, int));
46
47
48
49 /***************************************************/
CreateGraf(gp,parent,x,y,fg,bg,title)50 void CreateGraf(gp, parent, x, y, fg, bg, title)
51 GRAF *gp;
52 Window parent;
53 int x,y;
54 unsigned long fg,bg;
55 const char *title;
56 {
57 /* NOTE: CreateGraf does not initialize hands[], nhands, or spline,
58 as these could be initialized by X resources (or whatever),
59 which takes place long before we can create the windows.
60
61 InitGraf() sets those fields of a GRAF to likely enough values */
62
63 int i;
64
65 if (!pixmaps_built) {
66 gfbpix[GFB_ADDH] = MakePix1(parent, gf1_addh_bits, PW, PH);
67 gfbpix[GFB_DELH] = MakePix1(parent, gf1_delh_bits, PW, PH);
68 gfbpix[GFB_LINE] = MakePix1(parent, gf1_line_bits, PW, PH);
69 gfbpix[GFB_SPLINE] = MakePix1(parent, gf1_spln_bits, PW, PH);
70 gfbpix[GFB_RESET] = MakePix1(parent, gf1_rst_bits, PW, PH);
71 gfbpix[GFB_GAMMA] = MakePix1(parent, gf1_gamma_bits,
72 gf1_gamma_width, gf1_gamma_height);
73
74 for (i=0; i<N_GFB && gfbpix[i] != (Pixmap) NULL; i++);
75 if (i<N_GFB) FatalError("can't create graph pixmaps");
76
77 pixmaps_built = 1;
78 }
79
80 gp->fg = fg;
81 gp->bg = bg;
82 gp->str = title;
83 gp->entergamma = 0;
84 gp->drawobj = NULL;
85
86 sprintf(gp->gvstr, "%.5g", gp->gamma);
87
88 gp->win = XCreateSimpleWindow(theDisp, parent, x,y, GWIDE, GHIGH, 1, fg,bg);
89 if (!gp->win) FatalError("can't create graph (main) window");
90
91 gp->gwin = XCreateSimpleWindow(theDisp, gp->win, 2, GHIGH-132,
92 128, 128, 1, fg,bg);
93 if (!gp->gwin) FatalError("can't create graph (sub) window");
94
95 for (i=0; i<N_GFB; i++) {
96 BTCreate(&gp->butts[i], gp->win, GWIDE-GBWIDE-2, 1+i * (GBHIGH + 1),
97 GBWIDE, GBHIGH, (char *) NULL, fg, bg, hicol, locol);
98 gp->butts[i].pix = gfbpix[i];
99 gp->butts[i].pw = PW;
100 gp->butts[i].ph = PH;
101 }
102
103 gp->butts[GFB_SPLINE].toggle = 1;
104 gp->butts[GFB_LINE ].toggle = 1;
105
106 gp->butts[GFB_SPLINE].lit = gp->spline;
107 gp->butts[GFB_LINE ].lit = !gp->spline;
108
109 if (gp->nhands == 2) gp->butts[GFB_DELH].active = 0;
110 if (gp->nhands == MAX_GHANDS) gp->butts[GFB_ADDH].active = 0;
111
112 GenerateGrafFunc(gp,0);
113 XSelectInput(theDisp, gp->win, ExposureMask | ButtonPressMask |
114 KeyPressMask);
115 XSelectInput(theDisp, gp->gwin, ExposureMask);
116
117 XMapSubwindows(theDisp, gp->win);
118
119 }
120
121
122 /***************************************************/
InitGraf(gp)123 void InitGraf(gp)
124 GRAF *gp;
125 {
126 gp->nhands = 4;
127 gp->spline = 1;
128 gp->hands[0].x = 0; gp->hands[0].y = 0;
129 gp->hands[1].x = 64; gp->hands[1].y = 64;
130 gp->hands[2].x = 192; gp->hands[2].y = 192;
131 gp->hands[3].x = 255; gp->hands[3].y = 255;
132
133 gp->gammamode = 0; gp->gamma = 1.0;
134 }
135
136
137 /***************************************************/
RedrawGraf(gp,gwin)138 void RedrawGraf(gp, gwin)
139 GRAF *gp;
140 int gwin;
141 {
142 int i;
143
144 /* if gwin, only redraw the graf window, otherwise, only redraw the
145 title and buttons */
146
147 if (gwin) drawGraf(gp,0);
148 else {
149 Draw3dRect(gp->win, 0,0, GWIDE-1, GHIGH-1, R3D_OUT, 1, hicol, locol,
150 gp->bg);
151
152 XSetForeground(theDisp, theGC, gp->fg);
153 XSetBackground(theDisp, theGC, gp->bg);
154 DrawString(gp->win, 2, 1+ASCENT, gp->str);
155
156 for (i=0; i<N_GFB; i++) BTRedraw(&gp->butts[i]);
157 }
158 }
159
160
161 /***************************************************/
drawGraf(gp,erase)162 static void drawGraf(gp,erase)
163 GRAF *gp;
164 int erase;
165 {
166 int i,x,y;
167 XPoint pts[129], *pt;
168
169
170 if (gp->entergamma) {
171 const char *str1 = "Enter gamma";
172 const char *str2 = "value: ";
173
174 XSetForeground(theDisp, theGC, gp->fg);
175 XSetBackground(theDisp, theGC, gp->bg);
176
177 XClearWindow(theDisp,gp->gwin);
178 DrawString(gp->gwin, 10, 30+ASCENT, str1);
179 DrawString(gp->gwin, 10, 30+ASCENT+CHIGH+3, str2);
180
181 x = 10 + StringWidth(str2) + 8;
182 y = 30 + ASCENT + CHIGH + 3;
183 i = StringWidth(gp->gvstr);
184 if (gp->entergamma < 0 && strlen(gp->gvstr)) {
185 /* show string highlited */
186 XFillRectangle(theDisp, gp->gwin, theGC, x-1, y-ASCENT-1,
187 (u_int) i+2, (u_int) CHIGH+2);
188 XSetForeground(theDisp, theGC, gp->bg);
189 }
190 else
191 XDrawLine(theDisp, gp->gwin, theGC, x+i, y-ASCENT, x+i, y+DESCENT);
192
193 DrawString(gp->gwin, x,y, gp->gvstr);
194
195 return;
196 }
197
198 if (erase) XSetForeground(theDisp, theGC, gp->bg);
199 else XSetForeground(theDisp, theGC, gp->fg);
200
201 for (i=0, pt=pts; i<256; i+=2,pt++) {
202 pt->x = i/2; pt->y = 127 - (gp->func[i]/2);
203 if (i==0) i = -1; /* kludge to get sequence 0,1,3,5, ... 253,255 */
204 }
205 XDrawLines(theDisp, gp->gwin, theGC, pts, 129, CoordModeOrigin);
206
207 if (erase) return; /* don't erase handles */
208
209
210 /* redraw handles */
211
212 XSetForeground(theDisp, theGC, gp->bg);
213
214 for (i=0; i<gp->nhands; i++) { /* clear inside rectangles */
215 x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2;
216 XFillRectangle(theDisp, gp->gwin, theGC, x-2, y-2, 5,5);
217 }
218
219 XSetForeground(theDisp,theGC,gp->fg);
220
221 for (i=0; i<gp->nhands; i++) { /* draw center dots */
222 x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2;
223 XDrawPoint(theDisp, gp->gwin, theGC, x, y);
224 }
225
226 for (i=0; i<gp->nhands; i++) { /* draw rectangles */
227 x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2;
228 XDrawRectangle(theDisp, gp->gwin, theGC, x-3, y-3, 6,6);
229 }
230
231 }
232
233
234
235 /***************************************************/
ClickGraf(gp,child,mx,my)236 int ClickGraf(gp,child,mx,my)
237 GRAF *gp;
238 Window child;
239 int mx,my;
240 {
241 /* returns '1' if GrafFunc was changed, '0' otherwise */
242
243 int i, j, rv;
244 byte oldfunc[256];
245 BUTT *bp;
246 Window rW, cW;
247 int x, y, rx, ry, firsttime=1;
248 unsigned int mask;
249
250 rv = 0;
251
252 while (1) { /* loop until Button1 up and ShiftKey up */
253 if (!XQueryPointer(theDisp,gp->win,&rW,&cW,&rx,&ry,
254 &mx,&my,&mask)) continue;
255 if (!firsttime && !(mask & (Button1Mask | ShiftMask))) break;
256
257 /* if it's not the first time, wait for Button1 to be pressed */
258 if (!firsttime && !(mask & Button1Mask)) continue;
259
260 firsttime = 0;
261
262 for (i=0; i<N_GFB; i++) {
263 bp = &gp->butts[i];
264 if (PTINRECT(mx, my, bp->x, bp->y, bp->w, bp->h)) break;
265 }
266
267 if (i<N_GFB) { /* found one */
268 if (BTTrack(bp)) { /* it was selected */
269 switch (i) {
270 case GFB_SPLINE:
271 case GFB_LINE:
272 gp->gammamode = 0;
273
274 if ((i==GFB_SPLINE && !gp->spline) ||
275 (i==GFB_LINE && gp->spline)) {
276 gp->spline = !gp->spline;
277 gp->butts[GFB_SPLINE].lit = gp->spline;
278 gp->butts[GFB_LINE].lit = !gp->spline;
279 BTRedraw(&gp->butts[GFB_SPLINE]);
280 BTRedraw(&gp->butts[GFB_LINE]);
281
282 for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
283 GenerateGrafFunc(gp,1);
284 for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
285 if (i<256) rv = 1;
286 }
287 else {
288 gp->butts[i].lit = 1;
289 BTRedraw(&gp->butts[i]);
290 }
291 break;
292
293 case GFB_RESET:
294 for (j=0; j<gp->nhands; j++) {
295 gp->hands[j].y = gp->hands[j].x;
296 }
297 gp->gammamode = 0;
298
299 for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
300 GenerateGrafFunc(gp,1);
301 for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
302 if (i<256) rv = 1;
303
304 break;
305
306 case GFB_GAMMA:
307 gp->entergamma = -1;
308 drawGraf(gp,1);
309 break;
310
311 case GFB_ADDH:
312 if (gp->nhands < MAX_GHANDS) {
313 /* find largest x-gap in handles, put new handle in mid */
314 int lgap, lpos, x, y;
315
316 lgap = gp->hands[1].x - gp->hands[0].x;
317 lpos = 1;
318 for (j=1; j<gp->nhands-1; j++)
319 if ((gp->hands[j+1].x - gp->hands[j].x) > lgap) {
320 lgap = gp->hands[j+1].x - gp->hands[j].x;
321 lpos = j+1;
322 }
323
324 /* open up position in hands[] array */
325 xvbcopy((char *) &gp->hands[lpos], (char *) &gp->hands[lpos+1],
326 (gp->nhands - lpos) * sizeof(XPoint));
327
328 x = gp->hands[lpos-1].x + lgap/2;
329 y = gp->func[x];
330 gp->hands[lpos].x = x;
331 gp->hands[lpos].y = y;
332 gp->nhands++;
333
334 for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
335 GenerateGrafFunc(gp,1);
336 for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
337 if (i<256) rv = 1;
338
339 if (gp->nhands==MAX_GHANDS) /* turn off 'add' button */
340 BTSetActive(&gp->butts[GFB_ADDH], 0);
341
342 if (gp->nhands==3) /* turn on 'del' button */
343 BTSetActive(&gp->butts[GFB_DELH], 1);
344 }
345 break;
346
347 case GFB_DELH:
348 if (gp->nhands > 2) {
349 /* find (middle) point whose x-distance to previous
350 and next points is minimal. Delete that point */
351 int dist, mdist, mpos;
352
353 mdist = (gp->hands[1].x - gp->hands[0].x) +
354 (gp->hands[2].x - gp->hands[1].x);
355 mpos = 1;
356
357 for (j=2; j<gp->nhands-1; j++) {
358 dist = (gp->hands[j ].x - gp->hands[j-1].x) +
359 (gp->hands[j+1].x - gp->hands[j].x);
360 if (dist < mdist) {
361 mdist = dist; mpos = j;
362 }
363 }
364
365 /* delete position 'mpos' in hands[] array */
366 xvbcopy((char *) &gp->hands[mpos+1], (char *) &gp->hands[mpos],
367 (gp->nhands-mpos-1) * sizeof(XPoint));
368
369 gp->nhands--;
370 for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
371 GenerateGrafFunc(gp,1);
372 for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
373 if (i<256) rv = 1;
374
375 if (gp->nhands==MAX_GHANDS-1) /* turn on 'add' button */
376 BTSetActive(&gp->butts[GFB_ADDH], 1);
377
378 if (gp->nhands==2) /* turn off 'del' button */
379 BTSetActive(&gp->butts[GFB_DELH], 0);
380 }
381 break;
382 }
383 }
384 }
385
386
387 else if (cW == gp->gwin) { /* clicked in graph */
388 int h, vertonly, offx, offy;
389
390 XTranslateCoordinates(theDisp, gp->win, gp->gwin,mx,my,&mx,&my,&cW);
391
392 /* see if x,y is within any of the handles */
393 for (h=0; h<gp->nhands; h++) {
394 if (PTINRECT(mx*2,(127-my)*2,
395 gp->hands[h].x-5,gp->hands[h].y-5,11,11)) break;
396 }
397
398 if (h==gp->nhands) { /* not found. wait 'til mouseup */
399 while (XQueryPointer(theDisp,gp->gwin,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
400 if (!(mask & Button1Mask)) break; /* button released */
401 }
402 }
403
404 else { /* track handle */
405 int origx, origy, orighx, orighy, dx, dy, olddx, olddy, grab;
406
407 drawHandPos(gp, h);
408
409 /* keep original mouse position in 'mx,my', and warp mouse to center
410 of screen */
411 grab = !XGrabPointer(theDisp, gp->gwin, False, 0, GrabModeAsync,
412 GrabModeAsync, None, inviso, (Time) CurrentTime);
413 XWarpPointer(theDisp, None, rootW, 0,0,0,0,
414 (int) dispWIDE/2, (int) dispHIGH/2);
415
416 origx = dispWIDE/2; origy = dispHIGH/2;
417 orighx = gp->hands[h].x; orighy = gp->hands[h].y;
418
419 gp->gammamode = 0;
420 offx = gp->hands[h].x - origx;
421 offy = gp->hands[h].y - origy;
422
423 vertonly = (h==0 || h==(gp->nhands-1));
424
425 olddx = 0; olddy = 0;
426
427 while (XQueryPointer(theDisp,rootW,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
428 int newx, newy;
429
430 if (!(mask & Button1Mask)) break; /* button released */
431
432 /* x,y are in screen coordinates */
433 if (vertonly) x = origx; /* no sidewards motion */
434
435 dx = x - origx; dy = origy - y; /* flip y axis */
436
437 /* new (virt) position of handle is (desired)
438 orighx + dx, orighy + dy */
439
440 if (!vertonly) { /* keep this handle between its neighbors */
441 if (dx+orighx <= gp->hands[h-1].x) dx=(gp->hands[h-1].x+1)-orighx;
442 if (dx+orighx >= gp->hands[h+1].x) dx=(gp->hands[h+1].x-1)-orighx;
443 }
444
445 newx = dx + orighx; newy = dy + orighy;
446 RANGE(newy, 0, 255);
447
448 if (newx != gp->hands[h].x || newy != gp->hands[h].y) {
449 /* this handle has moved... */
450 XSetForeground(theDisp, theGC, gp->bg);
451 XFillRectangle(theDisp, gp->gwin, theGC,
452 (gp->hands[h].x/2)-3, ((255-gp->hands[h].y)/2)-3, 7,7);
453
454 gp->hands[h].x = newx; gp->hands[h].y = newy;
455 drawGraf(gp,1); /* erase old trace */
456 GenerateGrafFunc(gp,0);
457 drawGraf(gp,0);
458 drawHandPos(gp, h);
459 rv = 1;
460 olddx = dx; olddy = dy;
461
462 if (gp->drawobj) (gp->drawobj)();
463 }
464 }
465
466 drawHandPos(gp, -1);
467 XWarpPointer(theDisp, None, gp->gwin, 0,0,0,0,
468 gp->hands[h].x/2, (255-gp->hands[h].y)/2);
469 if (grab) XUngrabPointer(theDisp, (Time) CurrentTime);
470 }
471 }
472 }
473
474 return rv;
475 }
476
477
drawHandPos(gp,hnum)478 static void drawHandPos(gp, hnum)
479 GRAF *gp;
480 int hnum;
481 {
482 int w;
483 const char *tstr = "888,888";
484
485 /* if hnum < 0, clears the text area */
486
487 XSetFont(theDisp, theGC, monofont);
488 w = XTextWidth(monofinfo, tstr, (int) strlen(tstr));
489
490 if (hnum >= 0) sprintf(dummystr,"%3d,%3d",gp->hands[hnum].x,gp->hands[hnum].y);
491 else sprintf(dummystr," ");
492
493 XSetForeground(theDisp, theGC, gp->fg);
494 XSetBackground(theDisp, theGC, gp->bg);
495 XDrawImageString(theDisp, gp->win, theGC, 130-w, 1+ASCENT,
496 dummystr, (int) strlen(dummystr));
497
498 XSetFont(theDisp, theGC, mfont);
499 }
500
501
502 /***************************************************/
GrafKey(gp,str)503 int GrafKey(gp,str)
504 GRAF *gp;
505 char *str;
506 {
507 int len, ok;
508
509 /* returns '1' if str was 'eaten', '2' if CR was hit, '0' otherwise. */
510
511 if (!gp->entergamma) { /* not entering a value yet */
512 if (*str == 'g') {
513 gp->entergamma = -1; /* special 'show old value highlited' */
514 drawGraf(gp,1);
515 str++;
516 }
517 else return 0;
518 }
519
520 while (*str) {
521 if (gp->entergamma == -1 &&
522 (*str != '\012' && *str != '\015' && *str != '\033')) {
523 gp->entergamma = 1;
524 gp->gvstr[0] = '\0';
525 drawGraf(gp,1);
526 }
527
528 ok = 0;
529 len = strlen(gp->gvstr);
530
531 if (*str>= '0' && *str <= '9') {
532 if (len < GVMAX) {
533 gp->gvstr[len++] = *str;
534 gp->gvstr[len] = '\0';
535 ok = 1;
536 }
537 }
538
539 else if (*str == '.') {
540 if (len < GVMAX && ((char *) index(gp->gvstr,'.'))==NULL) {
541 gp->gvstr[len++] = *str;
542 gp->gvstr[len] = '\0';
543 ok = 1;
544 }
545 }
546
547 else if (*str == '-') { /* only allowed as first char */
548 if (len==0) {
549 gp->gvstr[len++] = *str;
550 gp->gvstr[len] = '\0';
551 ok = 1;
552 }
553 }
554
555 else if (*str == '\010' || *str == '\177') { /* BS or DEL */
556 if (len > 0) gp->gvstr[--len] = '\0';
557 ok = 1;
558 }
559
560 else if (*str == '\025' || *str == '\013') { /* ^U or ^K clear line */
561 gp->gvstr[0] = '\0'; len = 0; ok = 1;
562 }
563
564 else if (*str == '\012' || *str == '\015' || *str == '\033') {
565 /* CR, LF or ESC*/
566 if (len>0 && *str != '\033') { /* 'Ok' processing */
567 if (sscanf(gp->gvstr, "%lf", &(gp->gamma))==1) {
568 if (gp->gamma >= 1000.0) gp->gamma = 1000.0;
569 if (gp->gamma <= -1000.0) gp->gamma = -1000.0;
570 gp->gammamode = 1;
571 gp->entergamma = 0;
572 GenerateGrafFunc(gp,1);
573 return 2;
574 }
575 }
576
577 else {
578 gp->entergamma = 0;
579 GenerateGrafFunc(gp,1);
580 }
581 break; /* out of *str loop */
582 }
583
584 if (!ok) XBell(theDisp, 0);
585 else {
586 XClearWindow(theDisp,gp->gwin);
587 drawGraf(gp,1);
588 }
589
590 str++;
591 }
592
593 return 1;
594 }
595
596
597
598 /*********************/
GenerateGrafFunc(gp,redraw)599 void GenerateGrafFunc(gp,redraw)
600 GRAF *gp;
601 int redraw;
602 {
603 /* generate new gp->func data (ie, handles have moved, or line/spline
604 setting has changed) and redraw the entire graph area */
605
606 int i,j,k;
607
608 /* do sanity check. (x-coords must be sorted (strictly increasing)) */
609
610 for (i=0; i<gp->nhands; i++) {
611 RANGE(gp->hands[i].x, 0, 255);
612 RANGE(gp->hands[i].y, 0, 255);
613 }
614
615 gp->hands[0].x = 0; gp->hands[gp->nhands-1].x = 255;
616 for (i=1; i<gp->nhands-1; i++) {
617 if (gp->hands[i].x < i) gp->hands[i].x = i;
618 if (gp->hands[i].x > 256-gp->nhands+i)
619 gp->hands[i].x = 256-gp->nhands+i;
620
621 if (gp->hands[i].x <= gp->hands[i-1].x)
622 gp->hands[i].x = gp->hands[i-1].x + 1;
623 }
624
625 /* recompute the function */
626
627 if (gp->gammamode) { /* do gamma function instead of interpolation */
628 double y, invgam;
629 if (gp->gamma < 0.0) {
630 invgam = 1.0 / -gp->gamma; /* invgram is now positive */
631 for (i=0; i<256; i++) {
632 y = pow( ((double) i / 255.0), invgam) * 255.0;
633 j = (int) floor(y + 0.5);
634 RANGE(j,0,255);
635 gp->func[255-i] = j; /* store the entries in reverse order */
636 }
637 }
638 else if (gp->gamma > 0.0) {
639 invgam = 1.0 / gp->gamma;
640 for (i=0; i<256; i++) {
641 y = pow( ((double) i / 255.0), invgam) * 255.0;
642 j = (int) floor(y + 0.5);
643 RANGE(j,0,255);
644 gp->func[i] = j;
645 }
646 }
647 else { /* gp->gamma == 0.0 */
648 for (i=0; i<256; i++) gp->func[i] = 0;
649 }
650
651
652 for (i=0; i<gp->nhands; i++) {
653 gp->hands[i].y = gp->func[gp->hands[i].x];
654 }
655 }
656
657 else if (!gp->spline) { /* do linear interpolation */
658 int y,x1,y1,x2,y2;
659 double yd;
660
661 for (i=0; i<gp->nhands-1; i++) {
662 x1 = gp->hands[ i ].x; y1 = gp->hands[ i ].y;
663 x2 = gp->hands[i+1].x; y2 = gp->hands[i+1].y;
664
665 for (j=x1,k=0; j<=x2; j++,k++) { /* x2 <= 255 */
666 yd = ((double) k * (y2 - y1)) / (x2 - x1);
667 y = y1 + (int) floor(yd + 0.5);
668 RANGE(y,0,255);
669 gp->func[j] = y;
670 }
671 }
672 }
673
674 else { /* splinear interpolation */
675 static int x[MAX_GHANDS], y[MAX_GHANDS];
676 double yf[MAX_GHANDS];
677 double yd;
678
679 for (i=0; i<gp->nhands; i++) {
680 x[i] = gp->hands[i].x; y[i] = gp->hands[i].y;
681 }
682
683 InitSpline(x, y, gp->nhands, yf);
684
685 for (i=0; i<256; i++) {
686 yd = EvalSpline(x, y, yf, gp->nhands, (double) i);
687 j = (int) floor(yd + 0.5);
688 RANGE(j,0,255);
689 gp->func[i] = j;
690 }
691 }
692
693
694 if (redraw) { /* redraw graph */
695 XClearWindow(theDisp, gp->gwin);
696 drawGraf(gp,0);
697 }
698 }
699
700
701 /*********************/
Graf2Str(gp,str)702 void Graf2Str(gp, str)
703 GRAF_STATE *gp;
704 char *str;
705 {
706 /* generates strings of the form: "S 3 : 0,0 : 63,63 : 255,255",
707 (meaning SPLINE, 3 points, and the 3 sets of handle coordinates)
708 This is the string that you'd put in the 'xv.preset1.vgraph:' resource,
709 ferinstance */
710
711 /* maximum length of string generated is 164 characters. str better be
712 able to hold it... */
713
714 int i;
715 char cstr[16];
716
717 if (gp->gammamode) {
718 sprintf(str,"G %g", gp->gamma);
719 }
720 else {
721 sprintf(str, "%c %d", gp->spline ? 'S' : 'L', gp->nhands);
722 for (i=0; i<gp->nhands; i++) {
723 sprintf(cstr," : %d,%d", gp->hands[i].x, gp->hands[i].y);
724 strcat(str, cstr);
725 }
726 }
727 }
728
729
730 /*********************/
Str2Graf(gp,str)731 int Str2Graf(gp, str)
732 GRAF_STATE *gp;
733 const char *str;
734 {
735 /* parses strings of the form: "S 3 : 0,0 : 63,63 : 255,255",
736 (meaning SPLINE, 3 points, and the 3 sets of handle coordinates)
737 This is the string that you'd put in the 'xv.preset1.igraf:' resource,
738 ferinstance */
739
740 /* returns '1' if unable to parse. Note: does NOT redraw the graf, as
741 it may be called before the graph window has even been created */
742
743 /* NOTE: I deliberately avoid using '*dp++ = *sp++', as this sort of
744 thing tends to break optimizers */
745
746 char tstr[256], tstr1[256], *sp, *dp;
747 const char *csp;
748 XPoint coords[MAX_GHANDS];
749 int spline, nhands, i, x, y;
750
751 if (!str) return 1; /* NULL strings don't parse well! */
752
753 /* first, strip all pesky whitespace from str */
754 for (csp=str, dp=tstr; *csp; csp++)
755 if (*csp > ' ') { *dp = *csp; dp++; }
756 *dp = '\0';
757
758 /* check for 'gamma'-style str */
759 if (*tstr == 'G' || *tstr == 'g') {
760 if (sscanf(tstr+1, "%lf", &(gp->gamma)) == 1 &&
761 gp->gamma >= -1000.0 && gp->gamma <= 1000.0) {
762 gp->gammamode = 1;
763 sprintf(gp->gvstr, "%.5g", gp->gamma);
764 return 0;
765 }
766 else return 1;
767 }
768
769 /* read Spline, or Line (S/L) character */
770 sp = tstr;
771 if (*sp == 'S' || *sp == 's') spline = 1;
772 else if (*sp == 'L' || *sp == 'l') spline = 0;
773 else return 1;
774
775 /* read 'nhands' */
776 sp++; dp = tstr1;
777 while (*sp && *sp != ':') { *dp = *sp; dp++; sp++; }
778 *dp++ = '\0';
779 nhands = atoi(tstr1);
780 if (nhands>MAX_GHANDS || nhands<2) return 1;
781
782 /* read nhands coordinate pairs */
783 for (i=0; i<nhands && *sp; i++) {
784 sp++; dp = tstr1;
785 while (*sp && *sp != ':') {*dp = *sp; dp++; sp++; }
786 *dp++ = '\0';
787 if (sscanf(tstr1,"%d,%d",&x, &y) != 2) return 1;
788 if (x < 0 || x > 255 ||
789 y < 0 || y > 255) return 1; /* out of range */
790 coords[i].x = x; coords[i].y = y;
791 }
792
793 if (i<nhands) return 1; /* string terminated early */
794
795 gp->nhands = nhands; gp->spline = spline;
796 for (i=0; i<nhands; i++) {
797 gp->hands[i].x = coords[i].x;
798 gp->hands[i].y = coords[i].y;
799 }
800
801 return 0;
802 }
803
804
805
806 /*********************/
GetGrafState(gp,gsp)807 void GetGrafState(gp, gsp)
808 GRAF *gp;
809 GRAF_STATE *gsp;
810 {
811 int i;
812
813 gsp->spline = gp->spline;
814 gsp->entergamma= gp->entergamma;
815 gsp->gammamode = gp->gammamode;
816 gsp->gamma = gp->gamma;
817 gsp->nhands = gp->nhands;
818 strcpy(gsp->gvstr, gp->gvstr);
819 for (i=0; i<MAX_GHANDS; i++) {
820 gsp->hands[i].x = gp->hands[i].x;
821 gsp->hands[i].y = gp->hands[i].y;
822 }
823 }
824
825
826 /*********************/
SetGrafState(gp,gsp)827 int SetGrafState(gp, gsp)
828 GRAF *gp;
829 GRAF_STATE *gsp;
830 {
831 #define IFSET(a,b) if ((a) != (b)) { a = b; rv++; }
832 int i;
833 int rv = 0;
834
835 IFSET(gp->spline, gsp->spline);
836 IFSET(gp->entergamma, gsp->entergamma);
837 IFSET(gp->gammamode, gsp->gammamode);
838 IFSET(gp->gamma, gsp->gamma);
839 IFSET(gp->nhands, gsp->nhands);
840
841 if (strcmp(gp->gvstr, gsp->gvstr))
842 { strcpy(gp->gvstr, gsp->gvstr); rv++; }
843
844 for (i=0; i<gp->nhands; i++) {
845 IFSET(gp->hands[i].x, gsp->hands[i].x);
846 IFSET(gp->hands[i].y, gsp->hands[i].y);
847 }
848
849 gp->butts[GFB_DELH].active = (gp->nhands > 2);
850 gp->butts[GFB_ADDH].active = (gp->nhands < MAX_GHANDS);
851
852 gp->butts[GFB_SPLINE].lit = gp->spline;
853 gp->butts[GFB_LINE].lit = !gp->spline;
854
855 if (rv) {
856 XClearWindow(theDisp,gp->gwin);
857 GenerateGrafFunc(gp,0);
858 RedrawGraf(gp,0);
859 RedrawGraf(gp,1);
860 }
861
862 return rv;
863 }
864
865
866 /*********************/
InitSpline(x,y,n,y2)867 void InitSpline(x,y,n,y2)
868 int *x, *y, n;
869 double *y2;
870 {
871 /* given arrays of data points x[0..n-1] and y[0..n-1], computes the
872 values of the second derivative at each of the data points
873 y2[0..n-1] for use in the splint function */
874
875 int i,k;
876 double p,qn,sig,un,u[MAX_GHANDS];
877
878 y2[0] = u[0] = 0.0;
879
880 for (i=1; i<n-1; i++) {
881 sig = ((double) x[i]-x[i-1]) / ((double) x[i+1] - x[i-1]);
882 p = sig * y2[i-1] + 2.0;
883 y2[i] = (sig-1.0) / p;
884 u[i] = (((double) y[i+1]-y[i]) / (x[i+1]-x[i])) -
885 (((double) y[i]-y[i-1]) / (x[i]-x[i-1]));
886 u[i] = (6.0 * u[i]/(x[i+1]-x[i-1]) - sig*u[i-1]) / p;
887 }
888 qn = un = 0.0;
889
890 y2[n-1] = (un-qn*u[n-2]) / (qn*y2[n-2]+1.0);
891 for (k=n-2; k>=0; k--)
892 y2[k] = y2[k]*y2[k+1]+u[k];
893 }
894
895
896
897 /*********************/
EvalSpline(xa,ya,y2a,n,x)898 double EvalSpline(xa,ya,y2a,n,x)
899 double y2a[],x;
900 int n,xa[],ya[];
901 {
902 int klo,khi,k;
903 double h,b,a;
904
905 klo = 0;
906 khi = n-1;
907 while (khi-klo > 1) {
908 k = (khi+klo) >> 1;
909 if (xa[k] > x) khi = k;
910 else klo = k;
911 }
912 h = xa[khi] - xa[klo];
913 if (h==0.0) FatalError("bad xvalues in splint\n");
914 a = (xa[khi]-x)/h;
915 b = (x-xa[klo])/h;
916 return (a*ya[klo] + b*ya[khi] + ((a*a*a-a)*y2a[klo] +(b*b*b-b)*y2a[khi])
917 * (h*h) / 6.0);
918 }
919
920
921
922