home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xv310a / xvgraf.c < prev    next >
C/C++ Source or Header  |  1995-06-12  |  24KB  |  921 lines

  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. /***************************************************/
  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.      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. /***************************************************/
  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. /***************************************************/
  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. /***************************************************/
  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.     char *str1 = "Enter gamma";
  172.     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. /***************************************************/
  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.  
  478. static void drawHandPos(gp, hnum)
  479.      GRAF *gp;
  480.      int   hnum;
  481. {
  482.   int w;
  483.   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(str,"%3d,%3d",gp->hands[hnum].x,gp->hands[hnum].y);
  491.             else sprintf(str,"       ");
  492.  
  493.   XSetForeground(theDisp, theGC, gp->fg);
  494.   XSetBackground(theDisp, theGC, gp->bg);
  495.   XDrawImageString(theDisp, gp->win, theGC, 130-w, 1+ASCENT, 
  496.            str, (int) strlen(str));
  497.  
  498.   XSetFont(theDisp, theGC, mfont);
  499. }
  500.   
  501.  
  502. /***************************************************/
  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. /*********************/
  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. /*********************/
  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. /*********************/
  731. int Str2Graf(gp, str)
  732. GRAF_STATE *gp;
  733. 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.   XPoint coords[MAX_GHANDS];
  748.   int    spline, nhands, i, x, y;
  749.  
  750.   if (!str) return 1;  /* NULL strings don't parse well! */
  751.  
  752.   /* first, strip all pesky whitespace from str */
  753.   for (sp=str, dp=tstr; *sp; sp++) 
  754.     if (*sp > ' ') { *dp = *sp;  dp++; }
  755.   *dp = '\0';
  756.  
  757.   /* check for 'gamma'-style str */
  758.   if (*tstr == 'G' || *tstr == 'g') {
  759.     if (sscanf(tstr+1, "%lf", &(gp->gamma)) == 1 &&
  760.     gp->gamma >= -1000.0 && gp->gamma <= 1000.0) {
  761.       gp->gammamode = 1;
  762.       sprintf(gp->gvstr, "%.5g", gp->gamma);
  763.       return 0;
  764.       }
  765.     else return 1;
  766.   }
  767.     
  768.   /* read Spline, or Line (S/L) character */
  769.   sp = tstr;
  770.   if      (*sp == 'S' || *sp == 's') spline = 1;
  771.   else if (*sp == 'L' || *sp == 'l') spline = 0;
  772.   else return 1;
  773.  
  774.   /* read 'nhands' */
  775.   sp++;  dp = tstr1;
  776.   while (*sp && *sp != ':') { *dp = *sp;  dp++;  sp++; }
  777.   *dp++ = '\0';
  778.   nhands = atoi(tstr1);
  779.   if (nhands>MAX_GHANDS || nhands<2) return 1;
  780.  
  781.   /* read nhands coordinate pairs */
  782.   for (i=0; i<nhands && *sp; i++) {
  783.     sp++;  dp = tstr1;
  784.     while (*sp && *sp != ':') {*dp = *sp;  dp++;  sp++; }
  785.     *dp++ = '\0';
  786.     if (sscanf(tstr1,"%d,%d",&x, &y) != 2) return 1;
  787.     if (x < 0 || x > 255 || 
  788.     y < 0 || y > 255) return 1;  /* out of range */
  789.     coords[i].x = x;  coords[i].y = y;
  790.   }
  791.  
  792.   if (i<nhands) return 1;  /* string terminated early */
  793.  
  794.   gp->nhands = nhands;  gp->spline = spline;
  795.   for (i=0; i<nhands; i++) {
  796.     gp->hands[i].x = coords[i].x;
  797.     gp->hands[i].y = coords[i].y;
  798.   }
  799.  
  800.   return 0;
  801. }
  802.  
  803.  
  804.  
  805. /*********************/
  806. void GetGrafState(gp, gsp)
  807. GRAF *gp;
  808. GRAF_STATE *gsp;
  809. {
  810.   int i;
  811.  
  812.   gsp->spline = gp->spline;
  813.   gsp->entergamma= gp->entergamma;
  814.   gsp->gammamode = gp->gammamode;
  815.   gsp->gamma = gp->gamma;
  816.   gsp->nhands = gp->nhands;
  817.   strcpy(gsp->gvstr, gp->gvstr);
  818.   for (i=0; i<MAX_GHANDS; i++) {
  819.     gsp->hands[i].x = gp->hands[i].x;
  820.     gsp->hands[i].y = gp->hands[i].y;
  821.   }
  822. }
  823.  
  824.  
  825. /*********************/
  826. int SetGrafState(gp, gsp)
  827. GRAF *gp;
  828. GRAF_STATE *gsp;
  829. {
  830. #define IFSET(a,b) if ((a) != (b)) { a = b;  rv++; }
  831.   int i;
  832.   int rv = 0;
  833.  
  834.   IFSET(gp->spline,     gsp->spline);
  835.   IFSET(gp->entergamma, gsp->entergamma);
  836.   IFSET(gp->gammamode,  gsp->gammamode);
  837.   IFSET(gp->gamma,      gsp->gamma);
  838.   IFSET(gp->nhands,     gsp->nhands);
  839.  
  840.   if (strcmp(gp->gvstr, gsp->gvstr)) 
  841.     { strcpy(gp->gvstr, gsp->gvstr);  rv++; }
  842.  
  843.   for (i=0; i<gp->nhands; i++) {
  844.     IFSET(gp->hands[i].x, gsp->hands[i].x);
  845.     IFSET(gp->hands[i].y, gsp->hands[i].y);
  846.   }
  847.  
  848.   gp->butts[GFB_DELH].active = (gp->nhands > 2);
  849.   gp->butts[GFB_ADDH].active = (gp->nhands < MAX_GHANDS);
  850.  
  851.   gp->butts[GFB_SPLINE].lit =  gp->spline;
  852.   gp->butts[GFB_LINE].lit   = !gp->spline;
  853.  
  854.   if (rv) {
  855.     XClearWindow(theDisp,gp->gwin);
  856.     GenerateGrafFunc(gp,0);
  857.     RedrawGraf(gp,0);
  858.     RedrawGraf(gp,1);
  859.   }
  860.  
  861.   return rv;
  862. }
  863.  
  864.  
  865. /*********************/
  866. void InitSpline(x,y,n,y2)
  867.      int *x, *y, n;
  868.      double *y2;
  869. {
  870.   /* given arrays of data points x[0..n-1] and y[0..n-1], computes the
  871.      values of the second derivative at each of the data points
  872.      y2[0..n-1] for use in the splint function */
  873.  
  874.   int i,k;
  875.   double p,qn,sig,un,u[MAX_GHANDS];
  876.  
  877.   y2[0] = u[0] = 0.0;
  878.  
  879.   for (i=1; i<n-1; i++) {
  880.     sig = ((double) x[i]-x[i-1]) / ((double) x[i+1] - x[i-1]);
  881.     p = sig * y2[i-1] + 2.0;
  882.     y2[i] = (sig-1.0) / p;
  883.     u[i] = (((double) y[i+1]-y[i]) / (x[i+1]-x[i])) - 
  884.            (((double) y[i]-y[i-1]) / (x[i]-x[i-1]));
  885.     u[i] = (6.0 * u[i]/(x[i+1]-x[i-1]) - sig*u[i-1]) / p;
  886.   }
  887.   qn = un = 0.0;
  888.  
  889.   y2[n-1] = (un-qn*u[n-2]) / (qn*y2[n-2]+1.0);
  890.   for (k=n-2; k>=0; k--)
  891.     y2[k] = y2[k]*y2[k+1]+u[k];
  892. }
  893.  
  894.  
  895.  
  896. /*********************/
  897. double EvalSpline(xa,ya,y2a,n,x)
  898. double y2a[],x;
  899. int n,xa[],ya[];
  900. {
  901.   int klo,khi,k;
  902.   double h,b,a;
  903.  
  904.   klo = 0;
  905.   khi = n-1;
  906.   while (khi-klo > 1) {
  907.     k = (khi+klo) >> 1;
  908.     if (xa[k] > x) khi = k;
  909.     else klo = k;
  910.   }
  911.   h = xa[khi] - xa[klo];
  912.   if (h==0.0) FatalError("bad xvalues in splint\n");
  913.   a = (xa[khi]-x)/h;
  914.   b = (x-xa[klo])/h;
  915.   return (a*ya[klo] + b*ya[khi] + ((a*a*a-a)*y2a[klo] +(b*b*b-b)*y2a[khi]) 
  916.       * (h*h) / 6.0);
  917. }
  918.     
  919.  
  920.  
  921.