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