home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xpaint-247 / fillop.c < prev    next >
C/C++ Source or Header  |  1997-01-03  |  19KB  |  757 lines

  1. /* +-------------------------------------------------------------------+ */
  2. /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
  3. /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
  4. /* |                                                                   | */
  5. /* | Permission to use, copy, modify, and to distribute this software  | */
  6. /* | and its documentation for any purpose is hereby granted without   | */
  7. /* | fee, provided that the above copyright notice appear in all       | */
  8. /* | copies and that both that copyright notice and this permission    | */
  9. /* | notice appear in supporting documentation.  There is no           | */
  10. /* | representations about the suitability of this software for        | */
  11. /* | any purpose.  this software is provided "as is" without express   | */
  12. /* | or implied warranty.                                              | */
  13. /* |                                                                   | */
  14. /* +-------------------------------------------------------------------+ */
  15.  
  16. /* $Id: fillOp.c,v 1.9 1996/04/19 07:58:52 torsten Exp $ */
  17.  
  18. #include <X11/Intrinsic.h>
  19. #include <X11/StringDefs.h>
  20. #include <X11/cursorfont.h>
  21. #ifndef NOSTDHDRS
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <math.h>
  25. #endif
  26. #include "Paint.h"
  27. #include "protocol.h"
  28. #include "xpaint.h"
  29. #include "ops.h"
  30. #include "image.h"
  31. #include "misc.h"
  32. #include "palette.h"
  33. #include "color.h"
  34.  
  35. #ifndef M_PI
  36. #define M_PI            3.14159265358979323846
  37. #endif
  38.  
  39. typedef struct {
  40.     Boolean done;
  41.     int type;            /* 0 = plain fill, 1 = gradient fill, 2 = fractal fill */
  42. } LocalInfo;
  43.  
  44. #define ZINDEX8(x, y, img)  ((y) * img->bytes_per_line) + (x)
  45.  
  46. #define CLAMP(low, value, high) \
  47.     if (value < low) value = low; else if (value > high) value = high
  48. #define DEG2RAD    (M_PI/180.0)
  49.  
  50. static Pixmap buildGradientPixmap(Widget w, OpInfo * info,
  51.                  int width, int height, int depth, int mode);
  52. #if FEATURE_FRACTAL
  53. static Pixmap buildFractalPixmap(Widget w, OpInfo * info,
  54.                  int width, int height, int depth, int mode);
  55. #endif
  56.  
  57. static Drawable drawable;
  58. static Display *dpy;
  59. static XImage *source;
  60. static Boolean *mask;
  61. static int maskW, maskH;
  62. static int xMin, yMin, xMax, yMax;
  63. static int timeCount = 0;
  64. static int fillMode = 0, tfillMode = TFILL_RADIAL;
  65. #if FEATURE_FRACTAL
  66. static int ffillMode = 0;
  67. #endif
  68. static int deltaR, deltaG, deltaB;
  69. static int deltaRV, deltaGV, deltaBV;
  70. static Palette *deltaPal;
  71.  
  72. /*
  73.  * A Seed Fill Algorithm
  74.  * by Paul Heckbert
  75.  * from "Graphics Gems", Academic Press, 1990
  76.  */
  77.  
  78. /*
  79.  * fill.c : simple seed fill program
  80.  * Calls pixelread() to read pixels, pixelwrite() to write pixels.
  81.  *
  82.  * Paul Heckbert        13 Sept 1982, 28 Jan 1987
  83.  */
  84.  
  85. #define pixelwrite(x,y)    do {    \
  86.   mask[y * maskW + x] = True;    \
  87.   xMin = MIN(xMin, x);        \
  88.   yMin = MIN(yMin, y);        \
  89.   xMax = MAX(xMax, x);        \
  90.   yMax = MAX(yMax, y);        \
  91. } while (0)
  92.  
  93. #define STEP    32
  94.  
  95. static Pixel
  96. pixelread(int x, int y, Boolean first)
  97. {
  98.     static Pixel ov;
  99.     static int xMin, yMin, xMax, yMax;
  100.     Pixel p;
  101.     int n;
  102.  
  103.     if (first) {
  104.     xMin = MAX(x - STEP, 0);
  105.     yMin = MAX(y - STEP, 0);
  106.     xMax = MIN(x + STEP, maskW);
  107.     yMax = MIN(y + STEP, maskH);
  108.  
  109.     XGetSubImage(dpy, drawable, xMin, yMin,
  110.              xMax - xMin, yMax - yMin,
  111.              AllPlanes, ZPixmap, source,
  112.              xMin, yMin);
  113.  
  114.     ov = XGetPixel(source, x, y);
  115.  
  116.     return ov;
  117.     } else if (mask[y * maskW + x]) {
  118.     return (Pixel) - 1;
  119.     }
  120.     if (x < xMin) {
  121.     n = MAX(x - STEP, 0);
  122.     XGetSubImage(dpy, drawable, n, yMin,
  123.              xMin - n, yMax - yMin,
  124.              AllPlanes, ZPixmap, source,
  125.              n, yMin);
  126.     xMin = n;
  127.     }
  128.     if (y < yMin) {
  129.     n = MAX(y - STEP, 0);
  130.     XGetSubImage(dpy, drawable, xMin, n,
  131.              xMax - xMin, yMin - n,
  132.              AllPlanes, ZPixmap, source,
  133.              xMin, n);
  134.     yMin = n;
  135.     }
  136.     if (x >= xMax) {
  137.     n = MIN(x + STEP, maskW);
  138.     XGetSubImage(dpy, drawable, xMax, yMin,
  139.              n - xMax, yMax - yMin,
  140.              AllPlanes, ZPixmap, source,
  141.              xMax, yMin);
  142.     xMax = n;
  143.     }
  144.     if (y >= yMax) {
  145.     n = MIN(y + STEP, maskH);
  146.     XGetSubImage(dpy, drawable, xMin, yMax,
  147.              xMax - xMin, n - yMax,
  148.              AllPlanes, ZPixmap, source,
  149.              xMin, yMax);
  150.     yMax = n;
  151.     }
  152.     /*
  153.     **  Do it fast for those 8 bit displays...
  154.      */
  155.     if (source->bits_per_pixel == 8)
  156.     /*
  157.      * This is disgusting. 'data' is declared as _signed_ char,
  158.      * so values above 127 become negative if you're not careful.
  159.      */
  160.     p = (unsigned char) source->data[ZINDEX8(x, y, source)];
  161.     else
  162.     p = XGetPixel(source, x, y);
  163.  
  164.     return p;
  165. }
  166.  
  167. /*
  168. **  Exchange pixel 1 for filled pixel value 2
  169.  */
  170. static void 
  171. change(int sx, int sy, int width, int height)
  172. {
  173.     int x, y;
  174.     Pixel pix = XGetPixel(source, sx, sy);
  175.  
  176.     if (source->bits_per_pixel == 8) {
  177.     for (y = 0; y < height; y++) {
  178.         for (x = 0; x < width; x++)
  179.         if (((unsigned char) source->data[ZINDEX8(x, y, source)]) == pix)
  180.             pixelwrite(x, y);
  181.         if (y % 100 == 0)
  182.         StateTimeStep();
  183.     }
  184.     } else {
  185.     for (y = 0; y < height; y++) {
  186.         for (x = 0; x < width; x++)
  187.         if (XGetPixel(source, x, y) == pix)
  188.             pixelwrite(x, y);
  189.         if (y % 64 == 0)
  190.         StateTimeStep();
  191.     }
  192.     }
  193. }
  194.  
  195. typedef struct {
  196.     short y, xl, xr, dy;
  197. } Segment;
  198.  
  199. /*
  200.  * Filled horizontal segment of scanline y for xl<=x<=xr.
  201.  * Parent segment was on line y-dy.  dy=1 or -1
  202.  */
  203.  
  204. #define STACKSIZE 20000        /* max depth of stack */
  205.  
  206. #define PUSH(Y, XL, XR, DY)  /* push new segment on stack */ \
  207.     if (sp<stack+STACKSIZE && Y+(DY)>=0 && Y+(DY)<=height) \
  208.     {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
  209.  
  210. #define POP(Y, XL, XR, DY)    /* pop segment off stack */ \
  211.     {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
  212.  
  213. /*
  214.  * fill: set the pixel at (x,y) and all of its 4-connected neighbors
  215.  * with the same pixel value to the new pixel value nv.
  216.  * A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
  217.  */
  218.  
  219. static void 
  220. fill(int x, int y, int width, int height)
  221. {
  222.     int start, x1, x2, dy = 0;
  223.     Pixel ov;            /* old pixel value */
  224.     Segment stack[STACKSIZE], *sp = stack;    /* stack of filled segments */
  225.  
  226.     ov = pixelread(x, y, True);    /* read pv at seed point */
  227.     PUSH(y, x, x, 1);        /* needed in some cases */
  228.     PUSH(y + 1, x, x, -1);    /* seed segment (popped 1st) */
  229.  
  230.     while (sp > stack) {
  231.     /* pop segment off stack and fill a neighboring scan line */
  232.     POP(y, x1, x2, dy);
  233.     /*
  234.      * segment of scan line y-dy for x1<=x<=x2 was previously filled,
  235.      */
  236.     for (x = x1; x >= 0 && pixelread(x, y, False) == ov; x--)
  237.         pixelwrite(x, y);
  238.     if (x >= x1) {
  239.         for (x++; x <= x2 && pixelread(x, y, False) != ov; x++);
  240.         start = x;
  241.         if (x > x2)
  242.         continue;
  243.     } else {
  244.         start = x + 1;
  245.         if (start < x1)
  246.         PUSH(y, start, x1 - 1, -dy);    /* leak on left? */
  247.         x = x1 + 1;
  248.     }
  249.     do {
  250.         for (; x <= width && pixelread(x, y, False) == ov; x++)
  251.         pixelwrite(x, y);
  252.         PUSH(y, start, x - 1, dy);
  253.         if (x > x2 + 1)
  254.         PUSH(y, x2 + 1, x - 1, -dy);    /* leak on right? */
  255.         for (x++; x <= x2 && pixelread(x, y, False) != ov; x++);
  256.         start = x;
  257.     }
  258.     while (x <= x2);
  259.  
  260.     if (++timeCount % 32 == 0)
  261.         StateTimeStep();
  262.     }
  263. }
  264.  
  265. static int 
  266. similar(Pixel p)
  267. {
  268.     XColor *col;
  269.     int r, g, b;
  270.  
  271.     if (p == ((Pixel) - 1))
  272.     return 0;
  273.     col = PaletteLookup(deltaPal, p);
  274.     r = col->red / 256;
  275.     g = col->green / 256;
  276.     b = col->blue / 256;
  277.  
  278.     return (deltaR - deltaRV <= r) && (r <= deltaR + deltaRV)
  279.     && (deltaG - deltaGV <= g) && (g <= deltaG + deltaGV)
  280.     && (deltaB - deltaBV <= b) && (b <= deltaB + deltaBV);
  281. }
  282.  
  283. /*
  284.  * deltafill: set the pixel at (x,y) and all of its 4-connected neighbors
  285.  * with similar pixel values to the new pixel value nv.
  286.  * A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
  287.  */
  288.  
  289. static void 
  290. deltafill(int x, int y, int width, int height)
  291. {
  292.     int start, x1, x2, dy = 0;
  293.     Pixel ov;            /* old pixel value */
  294.     Segment stack[STACKSIZE], *sp = stack;    /* stack of filled segments */
  295.     XColor *col;
  296.  
  297.     ov = pixelread(x, y, True);    /* read pv at seed point */
  298.     col = PaletteLookup(deltaPal, ov);
  299.     deltaR = col->red / 256;
  300.     deltaG = col->green / 256;
  301.     deltaB = col->blue / 256;
  302.     GetChromaDelta(&deltaRV, &deltaGV, &deltaBV);
  303.     PUSH(y, x, x, 1);        /* needed in some cases */
  304.     PUSH(y + 1, x, x, -1);    /* seed segment (popped 1st) */
  305.  
  306.     while (sp > stack) {
  307.     /* pop segment off stack and fill a neighboring scan line */
  308.     POP(y, x1, x2, dy);
  309.     /*
  310.      * segment of scan line y-dy for x1<=x<=x2 was previously filled,
  311.      */
  312.     for (x = x1; x >= 0 && similar(pixelread(x, y, False)); x--)
  313.         pixelwrite(x, y);
  314.     if (x >= x1) {
  315.         for (x++; x <= x2 && !similar(pixelread(x, y, False)); x++);
  316.         start = x;
  317.         if (x > x2)
  318.         continue;
  319.     } else {
  320.         start = x + 1;
  321.         if (start < x1)
  322.         PUSH(y, start, x1 - 1, -dy);    /* leak on left? */
  323.         x = x1 + 1;
  324.     }
  325.     do {
  326.         for (; x <= width && similar(pixelread(x, y, False)); x++)
  327.         pixelwrite(x, y);
  328.         PUSH(y, start, x - 1, dy);
  329.         if (x > x2 + 1)
  330.         PUSH(y, x2 + 1, x - 1, -dy);    /* leak on right? */
  331.         for (x++; x <= x2 && !similar(pixelread(x, y, False)); x++);
  332.         start = x;
  333.     }
  334.     while (x <= x2);
  335.  
  336.     if (++timeCount % 32 == 0)
  337.         StateTimeStep();
  338.     }
  339. }
  340.  
  341. static void 
  342. press(Widget w, LocalInfo * l, XButtonEvent * event, OpInfo * info)
  343. {
  344.     XRectangle undo, rect;
  345.     int width, height;
  346.     GC dgc;
  347.     int isBusy, depth;
  348.     Pixmap pix, gradient;
  349.     Colormap cmap;
  350.  
  351.  
  352.     if (event->button == Button1)
  353.     dgc = info->first_gc;
  354.     else if (event->button == Button2)
  355.     dgc = info->second_gc;
  356.     else
  357.     return;
  358.  
  359.     dpy = XtDisplay(w);
  360.     drawable = info->drawable;
  361.  
  362.     UndoStart(w, info);
  363.  
  364.     XtVaGetValues(w, XtNdrawWidth, &width,
  365.           XtNdrawHeight, &height,
  366.           XtNdepth, &depth,
  367.           NULL);
  368.     if ((isBusy = (width * height > 6400)) != 0)
  369.     StateSetBusy(True);
  370.  
  371.     if ((mask = (Boolean *) XtCalloc(sizeof(Boolean), width * height)) == NULL)
  372.     return;
  373.     maskW = width;
  374.     maskH = height;
  375.  
  376.     memset(mask, False, width * height * sizeof(Boolean));
  377.  
  378.     if (fillMode != 1) {
  379.     source = NewXImage(dpy, NULL, depth, width, height);
  380.     } else {
  381.     source = PwGetImage(w, NULL);
  382.     }
  383.  
  384.     xMin = xMax = info->realX;
  385.     yMin = yMax = info->realY;
  386.  
  387.     switch (fillMode) {
  388.     case 0:
  389.     fill(info->realX, info->realY, width - 1, height - 1);
  390.     break;
  391.     case 1:
  392.     change(info->realX, info->realY, width, height);
  393.     break;
  394.     default:            /* 2 */
  395.     XtVaGetValues(w, XtNcolormap, &cmap, NULL);
  396.     deltaPal = PaletteFind(w, cmap);
  397.     deltafill(info->realX, info->realY, width - 1, height - 1);
  398.     break;
  399.     }
  400.  
  401.     rect.x = xMin;
  402.     rect.y = yMin;
  403.     rect.width = (xMax - xMin) + 1;
  404.     rect.height = (yMax - yMin) + 1;
  405.  
  406.     pix = MaskDataToPixmap(w, width, height, mask, &rect);
  407.  
  408.     XSetClipOrigin(dpy, dgc, rect.x, rect.y);
  409.     XSetClipMask(dpy, dgc, pix);
  410.  
  411.     switch (l->type) {
  412.     case 0:
  413.     XFillRectangles(dpy, info->drawable, dgc, &rect, 1);
  414.     if (!info->isFat)
  415.         XFillRectangles(dpy, XtWindow(w), dgc, &rect, 1);
  416.     break;
  417.  
  418.     case 1:
  419.     case 2:
  420.     /*
  421.      * Gradient fill: Build a pixmap of the required size and
  422.      * copy that to the region to be filled
  423.      */
  424.     if (l->type == 1)
  425.         gradient = buildGradientPixmap(w, info, rect.width, rect.height,
  426.                        depth, tfillMode);
  427. #ifdef FEATURE_FRACTAL
  428.     else
  429.         gradient = buildFractalPixmap(w, info, rect.width, rect.height,
  430.                       depth, ffillMode);
  431. #endif
  432.     XCopyArea(dpy, gradient, info->drawable, dgc,
  433.           0, 0, rect.width, rect.height, xMin, yMin);
  434.     if (!info->isFat)
  435.         XCopyArea(dpy, gradient, XtWindow(w), dgc,
  436.               0, 0, rect.width, rect.height, xMin, yMin);
  437.     XFreePixmap(dpy, gradient);
  438.     break;
  439.     }
  440.  
  441.  
  442.     XSetClipMask(dpy, dgc, None);
  443.     XFreePixmap(dpy, pix);
  444.  
  445.     XYtoRECT(xMin, yMin, xMax + 1, yMax + 1, &undo);
  446.     UndoSetRectangle(w, &undo);
  447.     PwUpdate(w, &undo, False);
  448.  
  449.     if (fillMode != 1)
  450.     XDestroyImage(source);
  451.  
  452.     if (isBusy)
  453.     StateSetBusy(False);
  454. }
  455.  
  456. void *
  457. FillAdd(Widget w)
  458. {
  459.     LocalInfo *l = (LocalInfo *) XtMalloc(sizeof(LocalInfo));
  460.     l->done = False;
  461.     l->type = 0;
  462.     OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  463.               (OpEventProc) press, l);
  464.     SetCrossHairCursor(w);
  465.     return l;
  466. }
  467.  
  468. void 
  469. FillRemove(Widget w, void *l)
  470. {
  471.     OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  472.              (OpEventProc) press, l);
  473.     XtFree((XtPointer) l);
  474. }
  475.  
  476. void 
  477. FillSetMode(int value)
  478. {
  479.     fillMode = value;
  480. }
  481.  
  482. /*
  483.  * Gradient fill
  484.  */
  485. static float TFillQuant = 50.0;
  486. static double TFillVoffset = 0.0;
  487. static double TFillHoffset = 0.0;
  488. static double TFillPad = 0.0;
  489. static int TFillTilt = 0;
  490.  
  491. void *
  492. TFillAdd(Widget w)
  493. {
  494.     LocalInfo *l = (LocalInfo *) XtMalloc(sizeof(LocalInfo));
  495.     l->done = False;
  496.     l->type = 1;
  497.     OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  498.               (OpEventProc) press, l);
  499.     SetCrossHairCursor(w);
  500.     return l;
  501. }
  502.  
  503. void 
  504. TFillRemove(Widget w, void *l)
  505. {
  506.     OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  507.              (OpEventProc) press, l);
  508.     XtFree((XtPointer) l);
  509. }
  510.  
  511. void 
  512. TFillSetMode(int value)
  513. {
  514.     tfillMode = value;
  515. }
  516.  
  517. /*
  518.  * voffset, hoffset, pad range from -1.0 to 1.0
  519.  * angle is in degrees
  520.  */
  521. void 
  522. TfillSetParameters(float voffset, float hoffset, float pad, int angle, int steps)
  523. {
  524.     TFillVoffset = voffset;
  525.     TFillHoffset = hoffset;
  526.     TFillPad = pad;
  527.     TFillTilt = angle;
  528.     TFillQuant = steps;
  529. }
  530.  
  531. /*
  532.  * Fractal fills
  533.  */
  534. #ifdef FEATURE_FRACTAL
  535. void *
  536. FFillAdd(Widget w)
  537. {
  538.     LocalInfo *l = (LocalInfo *) XtMalloc(sizeof(LocalInfo));
  539.     l->done = False;
  540.     l->type = 2;
  541.     OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  542.               (OpEventProc) press, l);
  543.     SetCrossHairCursor(w);
  544.     return l;
  545. }
  546.  
  547. void 
  548. FFillRemove(Widget w, void *l)
  549. {
  550.     OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE,
  551.              (OpEventProc) press, l);
  552.     XtFree((XtPointer) l);
  553. }
  554.  
  555. void 
  556. FFillSetMode(int value)
  557. {
  558.     ffillMode = value;
  559. }
  560.  
  561. #endif
  562.  
  563. #define SWAP(a, b)    { int t = (a); (a) = (b); (b) = t; }
  564. #define    SWAP_COLORS    { SWAP(rp, rs); SWAP(gp, gs); SWAP(bp, bs); }
  565.  
  566. /*
  567.  * Builds gradient pixmaps.
  568.  */
  569. static Pixmap
  570. buildGradientPixmap(Widget w, OpInfo * info,
  571.             int width, int height, int depth, int mode)
  572. {
  573.     Pixmap pixmap;
  574.     Image *image;
  575.     Colormap cmap;
  576.     Palette *pal;
  577.     unsigned char *op;
  578.     XGCValues value;
  579.     XColor *col;
  580.     int dx, dy, maxdist, ox, oy, x, y, xx, yy, rp, gp, bp, rs, gs, bs,
  581.      s;
  582.     float fraction, a, cs, sn;
  583.  
  584.  
  585.     image = ImageNew(width, height);
  586.     op = image->data;
  587.     XtVaGetValues(w, XtNcolormap, &cmap, NULL);
  588.     pal = PaletteFind(w, cmap);
  589.  
  590.     /* get primary colour */
  591.     XGetGCValues(XtDisplay(w), info->first_gc, GCForeground, &value);
  592.     col = PaletteLookup(pal, value.foreground);
  593.     rp = col->red / 256;
  594.     gp = col->green / 256;
  595.     bp = col->blue / 256;
  596.  
  597.     /* get secondary colour */
  598.     XGetGCValues(XtDisplay(w), info->second_gc, GCForeground, &value);
  599.     col = PaletteLookup(pal, value.foreground);
  600.     rs = col->red / 256;
  601.     gs = col->green / 256;
  602.     bs = col->blue / 256;
  603.  
  604.     if (TFillTilt < 0)
  605.     TFillTilt += 360;
  606.  
  607.     /* Some handy values */
  608.     ox = width * (TFillHoffset + 0.5);    /* Centre point */
  609.     oy = height * (0.5 - TFillVoffset);
  610.     cs = cos(TFillTilt * DEG2RAD);    /* For rotation of coords */
  611.     sn = sin(TFillTilt * DEG2RAD);
  612.     maxdist = MAX(ox, width - ox);    /* Max distance to any point in pixmap */
  613.     maxdist = MAX(maxdist, oy);
  614.     maxdist = MAX(maxdist, height - oy);
  615.  
  616.     switch (mode) {
  617.     case TFILL_RADIAL:
  618.     /*
  619.      * This mixes colours according to distance from the point
  620.      * in question to a centre point.
  621.      */
  622.     for (y = 0; y < height; y++) {
  623.         for (x = 0; x < width; x++) {
  624.         dx = x - ox;
  625.         dy = y - oy;
  626.         fraction = (sqrt(dx * dx + dy * dy) / maxdist - TFillPad) /
  627.             (1.0 - 2 * TFillPad);
  628.         CLAMP(0.0, fraction, 1.0);
  629.         fraction = ((int) (fraction * TFillQuant)) / TFillQuant;
  630.         *op++ = (1 - fraction) * rp + fraction * rs;
  631.         *op++ = (1 - fraction) * gp + fraction * gs;
  632.         *op++ = (1 - fraction) * bp + fraction * bs;
  633.         }
  634.     }
  635.     break;
  636.  
  637.     case TFILL_LINEAR:
  638.     /*
  639.      * This mixes colours according to distance from the point
  640.      * in question to the x axis (in the transformed system).
  641.      */
  642.     /*
  643.      * s is the maximum distance of any point
  644.      * (distance from the line y = ax+h to (w,0) or
  645.      * from y = ax to (w,h), whichever is greater)
  646.      */
  647.     if (TFillTilt == 90)    /* tan(90) goes towards infinity */
  648.         s = width;
  649.     else {
  650.         a = tan(TFillTilt * DEG2RAD);
  651.         s = abs(a * width + (a < 0 ? -height : height)) / sqrt(a * a + 1);
  652.     }
  653.     for (y = 0; y < height; y++)
  654.         for (x = 0; x < width; x++) {
  655.         yy = x * sn + y * cs;    /* only transformed y coord is needed */
  656.         fraction = (((float) yy) / s - TFillPad) / (1.0 - 2 * TFillPad);
  657.         fraction = ((int) (fraction * TFillQuant)) / TFillQuant;
  658.         CLAMP(0.0, fraction, 1.0);
  659.         *op++ = (1 - fraction) * rp + fraction * rs;
  660.         *op++ = (1 - fraction) * gp + fraction * gs;
  661.         *op++ = (1 - fraction) * bp + fraction * bs;
  662.         }
  663.     break;
  664.  
  665.     case TFILL_SQUARE:
  666.     /*
  667.      * This mixes colours according to distance from the point
  668.      * in question to the axis farthest away (in the transformed system).
  669.      */
  670.     for (y = 0; y < height; y++)
  671.         for (x = 0; x < width; x++) {
  672.         /* transform coords and take absolute value */
  673.         dx = x - ox;
  674.         dy = y - oy;
  675.         xx = abs(dx * cs - dy * sn);
  676.         yy = abs(dx * sn + dy * cs);
  677.         if (xx < yy)
  678.             s = yy;
  679.         else
  680.             s = xx;
  681.         fraction = (((float) s) / maxdist - TFillPad) / (1.0 - 2 * TFillPad);
  682.         fraction = ((int) (fraction * TFillQuant)) / TFillQuant;
  683.         CLAMP(0.0, fraction, 1.0);
  684.         *op++ = (1 - fraction) * rp + fraction * rs;
  685.         *op++ = (1 - fraction) * gp + fraction * gs;
  686.         *op++ = (1 - fraction) * bp + fraction * bs;
  687.         }
  688.     break;
  689.  
  690.     case TFILL_CONE:
  691.     /*
  692.      * Convert (x,y) to polar coordinates (r,a) and mix
  693.      * colours according to the fraction a/360.
  694.      */
  695.     for (y = 0; y < height; y++)
  696.         for (x = 0; x < width; x++) {
  697.         dx = x - ox;
  698.         dy = y - oy;
  699.         if (dx == 0)
  700.             a = dy > 0 ? 270 : 90;
  701.         else {
  702.             a = atan(((float) dy) / ((float) dx));
  703.             a *= 1.0 / DEG2RAD;
  704.             if (dx > 0)    /* atan() returns principal value -pi/2..pi/2 */
  705.             a = a + 180.0;
  706.             while (a < 0)    /* normalize */
  707.             a += 360.0;
  708.         }
  709.         a += TFillTilt;
  710.         while (a > 360.0)
  711.             a -= 360.0;
  712.         if (a > 180.0)    /* map (180,360) to (180,0) */
  713.             a = 360.0 - a;
  714.         fraction = (a / 180.0 - TFillPad) / (1.0 - 2 * TFillPad);
  715.         fraction = ((int) (fraction * TFillQuant)) / TFillQuant;
  716.         CLAMP(0.0, fraction, 1.0);
  717.         *op++ = (1 - fraction) * rp + fraction * rs;
  718.         *op++ = (1 - fraction) * gp + fraction * gs;
  719.         *op++ = (1 - fraction) * bp + fraction * bs;
  720.         }
  721.     break;
  722.     }
  723.  
  724.     pixmap = None;        /* force creation of new pixmap */
  725.     ImageToPixmapCmap(image, w, &pixmap, cmap);
  726.     return pixmap;
  727. }
  728.  
  729. #ifdef FEATURE_FRACTAL
  730. static Pixmap
  731. buildFractalPixmap(Widget w, OpInfo * info,
  732.            int width, int height, int depth, int mode)
  733. {
  734.     Image *image;
  735.     Pixmap pixmap;
  736.     Colormap cmap;
  737.  
  738.     switch (ffillMode) {
  739.     case 0:
  740.     image = draw_plasma(width, height);
  741.     break;
  742.     case 1:
  743.     image = draw_landscape(width, height, 1);
  744.     break;
  745.     case 2:
  746.     default:
  747.     image = draw_landscape(width, height, 0);
  748.     break;
  749.     }
  750.  
  751.     XtVaGetValues(w, XtNcolormap, &cmap, NULL);
  752.     pixmap = None;        /* force creation of new pixmap */
  753.     ImageToPixmapCmap(image, w, &pixmap, cmap);
  754.     return pixmap;
  755. }
  756. #endif
  757.