66ee7c172964fe9665479973fa7c7536afe03fa2
kate
  Tue Apr 10 22:05:33 2018 -0700
Add Postscript for bezier curve.  Fix ellipse. refs #21109

diff --git src/lib/memgfx.c src/lib/memgfx.c
index f7df336..1a48dca 100644
--- src/lib/memgfx.c
+++ src/lib/memgfx.c
@@ -996,30 +996,31 @@
 vg->getDot = (vg_getDot)mgSlowGetDot;
 vg->box = (vg_box)mgDrawBox;
 vg->line = (vg_line)mgDrawLine;
 vg->text = (vg_text)mgText;
 vg->textRight = (vg_textRight)mgTextRight;
 vg->textCentered = (vg_textCentered)mgTextCentered;
 vg->findColorIx = (vg_findColorIx)mgFindColor;
 vg->colorIxToRgb = (vg_colorIxToRgb)mgColorIxToRgb;
 vg->setWriteMode = (vg_setWriteMode)mgSetWriteMode;
 vg->setClip = (vg_setClip)mgSetClip;
 vg->unclip = (vg_unclip)mgUnclip;
 vg->verticalSmear = (vg_verticalSmear)mgVerticalSmear;
 vg->fillUnder = (vg_fillUnder)mgFillUnder;
 vg->drawPoly = (vg_drawPoly)mgDrawPoly;
 vg->ellipse = (vg_ellipse)mgEllipse;
+vg->curve = (vg_curve)mgCurve;
 vg->setHint = (vg_setHint)mgSetHint;
 vg->getHint = (vg_getHint)mgGetHint;
 vg->getFontPixelHeight = (vg_getFontPixelHeight)mgGetFontPixelHeight;
 vg->getFontStringWidth = (vg_getFontStringWidth)mgGetFontStringWidth;
 }
 
 
 struct hslColor mgRgbToHsl(struct rgbColor rgb)
 /* Convert RGB to HSL colorspace (see http://en.wikipedia.org/wiki/HSL_and_HSV)
  * In HSL, Hue is the color in the range [0,360) with 0=red 120=green 240=blue,
  * Saturation goes from a shade of grey (0) to fully saturated color (1000), and
  * Lightness goes from black (0) through the hue (500) to white (1000). */
 {
 unsigned char rgbMax = max3(rgb.r, rgb.g, rgb.b);
 unsigned char rgbMin = min3(rgb.r, rgb.g, rgb.b);
@@ -1244,15 +1245,179 @@
  * Returns the transformed rgb value 
  * Use H=0, S=V=1 for identity transformation
  */
 {
 struct hsvColor hsv = mgRgbToHsv(in);
 hsv.h += h;
 while (hsv.h < 0.0)
     hsv.h += 360.0;
 while (hsv.h > 360.0)
     hsv.h -= 360.0;
 hsv.s = min(max(hsv.s * s, 0), 1000);
 hsv.v = min(max(hsv.v * v, 0), 1000);
 return mgHsvToRgb(hsv);
 }
 
+void mgEllipse(struct memGfx *mg, int x0, int y0, int x1, int y1, Color color,
+                        int mode, boolean isDashed)
+/* Draw an ellipse (or limit to top or bottom) specified by rectangle, using Bresenham algorithm.
+ * Optionally, alternate dots.
+ * Point 0 is left, point 1 is top of rectangle
+ * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html
+ * Author: Zingl Alois, 8/22/2016
+ */
+{
+   int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* values of diameter */
+   long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */
+   long err = dx+dy+b1*a*a, e2; /* error of 1.step */
+
+   if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
+   if (y0 > y1) y0 = y1; /* .. exchange them */
+   y0 += (b+1)/2; y1 = y0-b1;   /* starting pixel */
+   a *= 8*a; b1 = 8*b*b;
+
+   int dots = 0;
+   do {
+       if (!isDashed || (++dots % 3))
+           {
+           if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) 
+               {
+               mgPutDot(mg, x1, y0, color); /*   I. Quadrant */
+               mgPutDot(mg, x0, y0, color); /*  II. Quadrant */
+               }
+           if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) 
+               {
+               mgPutDot(mg, x0, y1, color); /* III. Quadrant */
+               mgPutDot(mg, x1, y1, color); /*  IV. Quadrant */
+               }
+           }
+       e2 = 2*err;
+       if (e2 <= dy) { y0++; y1--; err += dy += a; }  /* y step */
+       if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */
+   } while (x0 <= x1);
+
+   while (y0-y1 < b) {  /* too early stop of flat ellipses a=1 */
+       if (!isDashed && (++dots % 3))
+           {
+           mgPutDot(mg, x0-1, y0, color); /* -> finish tip of ellipse */
+           mgPutDot(mg, x1+1, y0++, color);
+           mgPutDot(mg, x0-1, y1, color);
+           mgPutDot(mg, x1+1, y1--, color);
+           }
+   }
+}
+
+static int mgCurveSegAA(struct memGfx *mg, int x0, int y0, int x1, int y1, int x2, int y2, 
+                        Color color, boolean isDashed)
+/* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier)
+ * Return max y value. Optionally alternate dots.
+ * Adapted trivially from code posted on github and at http://members.chello.at/~easyfilter/bresenham.html */
+ /* Thanks to author  * @author Zingl Alois
+ * @date 22.08.2016 */
+{
+   int yMax = 0;
+   int sx = x2-x1, sy = y2-y1;
+   long xx = x0-x1, yy = y0-y1, xy;             /* relative values for checks */
+   double dx, dy, err, ed, cur = xx*sy-yy*sx;                    /* curvature */
+
+   assert(xx*sx <= 0 && yy*sy <= 0);      /* sign of gradient must not change */
+
+   if (sx*(long)sx+sy*(long)sy > xx*xx+yy*yy) {     /* begin with longer part */
+      x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur;     /* swap P0 P2 */
+   }
+   if (cur != 0)
+   {                                                      /* no straight line */
+      xx += sx; xx *= sx = x0 < x2 ? 1 : -1;              /* x step direction */
+      yy += sy; yy *= sy = y0 < y2 ? 1 : -1;              /* y step direction */
+      xy = 2*xx*yy; xx *= xx; yy *= yy;             /* differences 2nd degree */
+      if (cur*sx*sy < 0) {                              /* negated curvature? */
+         xx = -xx; yy = -yy; xy = -xy; cur = -cur;
+      }
+      dx = 4.0*sy*(x1-x0)*cur+xx-xy;                /* differences 1st degree */
+      dy = 4.0*sx*(y0-y1)*cur+yy-xy;
+      xx += xx; yy += yy; err = dx+dy+xy;                   /* error 1st step */
+      int dots = 0;
+      do {
+         cur = fmin(dx+xy,-xy-dy);
+         ed = fmax(dx+xy,-xy-dy);               /* approximate error distance */
+         ed += 2*ed*cur*cur/(4*ed*ed+cur*cur);
+         if (!isDashed || (++dots % 3))
+            {
+            mixDot(mg, x0,y0, 1-fabs(err-dx-dy-xy)/ed, color);          /* plot curve */
+            if (y0 > yMax)
+                yMax = y0;
+            }
+         if (x0 == x2 || y0 == y2) break;     /* last pixel -> curve finished */
+         x1 = x0; cur = dx-err; y1 = 2*err+dy < 0;
+         if (2*err+dx > 0) {                                        /* x step */
+            if (err-dy < ed) 
+                {
+                mixDot(mg, x0,y0+sy, 1-fabs(err-dy)/ed, color);
+                if (y0 > yMax)
+                    yMax = y0;
+                }
+            x0 += sx; dx -= xy; err += dy += yy;
+         }
+         if (y1) {                                                  /* y step */
+            if (cur < ed) 
+                {
+                mixDot(mg, x1+sx,y0, 1-fabs(cur)/ed, color);
+                if (y0 > yMax)
+                    yMax = y0;
+                }
+            y0 += sy; dy -= xy; err += dx += xx;
+         }
+      } while (dy < dx);                  /* gradient negates -> close curves */
+   }
+   mgDrawLine(mg, x0,y0, x2,y2, color);                  /* plot remaining needle to end */
+   if (y0 > yMax)
+       yMax = y0;
+   return yMax;
+}
+
+int mgCurve(struct memGfx *mg, int x0, int y0, int x1, int y1, int x2, int y2, Color color,
+                        boolean isDashed)
+/* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier)
+ * Return max y value. Optionally draw curve as dashed line.
+ * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html
+ * Author: Zingl Alois, 8/22/2016
+ */
+/* TODO: allow specifying a third point on the line
+ *  P(t) = (1-t)^2 * p0 + 2 * (1-t) * t * p1 + t^2 * p2
+ */
+{
+   int x = x0-x1, y = y0-y1;
+   double t = x0-2*x1+x2, r;
+   int yMax = 0, yMaxRet = 0;
+   if ((long)x*(x2-x1) > 0) {                        /* horizontal cut at P4? */
+      if ((long)y*(y2-y1) > 0)                     /* vertical cut at P6 too? */
+         if (fabs((y0-2*y1+y2)/t*x) > abs(y)) {               /* which first? */
+            x0 = x2; x2 = x+x1; y0 = y2; y2 = y+y1;            /* swap points */
+         }                            /* now horizontal cut at P4 comes first */
+      t = (x0-x1)/t;
+      r = (1-t)*((1-t)*y0+2.0*t*y1)+t*t*y2;                       /* By(t=P4) */
+      t = (x0*x2-x1*x1)*t/(x0-x1);                       /* gradient dP4/dx=0 */
+      x = floor(t+0.5); y = floor(r+0.5);
+      r = (y1-y0)*(t-x0)/(x1-x0)+y0;                  /* intersect P3 | P0 P1 */
+      yMax = mgCurveSegAA(mg,x0,y0, x,floor(r+0.5), x,y, color, isDashed);
+      r = (y1-y2)*(t-x2)/(x1-x2)+y2;                  /* intersect P4 | P1 P2 */
+      x0 = x1 = x; y0 = y; y1 = floor(r+0.5);             /* P0 = P4, P1 = P8 */
+   }
+   if ((long)(y0-y1)*(y2-y1) > 0) {                    /* vertical cut at P6? */
+      t = y0-2*y1+y2; t = (y0-y1)/t;
+      r = (1-t)*((1-t)*x0+2.0*t*x1)+t*t*x2;                       /* Bx(t=P6) */
+      t = (y0*y2-y1*y1)*t/(y0-y1);                       /* gradient dP6/dy=0 */
+      x = floor(r+0.5); y = floor(t+0.5);
+      r = (x1-x0)*(t-y0)/(y1-y0)+x0;                  /* intersect P6 | P0 P1 */
+      yMaxRet = mgCurveSegAA(mg,x0,y0, floor(r+0.5),y, x,y, color, isDashed);
+      if (yMaxRet > yMax)
+        yMax = yMaxRet;
+      r = (x1-x2)*(t-y2)/(y1-y2)+x2;                  /* intersect P7 | P1 P2 */
+      x0 = x; x1 = floor(r+0.5); y0 = y1 = y;             /* P0 = P6, P1 = P7 */
+   }
+   yMaxRet = mgCurveSegAA(mg,x0,y0, x1,y1, x2,y2, color, isDashed); /* remaining part */
+   if (yMaxRet > yMax)
+     yMax = yMaxRet;
+   return yMax;
+}
+
+