d47135580518b138a64b785eddbc65891e48be21 kate Fri Jan 12 10:13:52 2018 -0800 Integrate antialiased ellipse code. Awaits antialiasing fix from Braney. refs #17512 diff --git src/hg/lib/hvGfx.c src/hg/lib/hvGfx.c index 392e1d2..1ac59e9 100644 --- src/hg/lib/hvGfx.c +++ src/hg/lib/hvGfx.c @@ -253,66 +253,30 @@ struct memPng *png = (struct memPng *)hvg->vg->data; struct memGfx *img = (struct memGfx *)png; if ((x < img->clipMinX) || (x > img->clipMaxX) || (y < img->clipMinY) || (y > img->clipMaxY)) return; Color *pt = _mgPixAdr(img,x,y); float invFrac = 1 - frac; int r = COLOR_32_RED(*pt) * invFrac + COLOR_32_RED(col) * frac; int g = COLOR_32_GREEN(*pt) * invFrac + COLOR_32_GREEN(col) * frac; int b = COLOR_32_BLUE(*pt) * invFrac + COLOR_32_BLUE(col) * frac; hvGfxDot(hvg, x, y, MAKECOLOR_32(r,g,b)); hvGfxDot(hvg, x, y, MAKECOLOR_32(r,g,b)); } -void hvGfxCurveSeg(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, Color color) -/* Draw a segment of a curve within 3 points (quadratic Bezier) - * 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 sx = x2-x1, sy = y2-y1; - long xx = x0-x1, yy = y0-y1, xy; /* relative values for checks */ - double dx, dy, err, 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*cur*(x1-x0)+xx-xy; /* differences 1st degree */ - dy = 4.0*sx*cur*(y0-y1)+yy-xy; - xx += xx; yy += yy; err = dx+dy+xy; /* error 1st step */ - do { - hvGfxDot(hvg, x0, y0, color); /* plot curve */ - if (x0 == x2 && y0 == y2) return; /* last pixel -> curve finished */ - y1 = 2*err < dx; /* save value for test of y step */ - if (2*err > dy) { x0 += sx; dx -= xy; err += dy += yy; } /* x step */ - if ( y1 ) { y0 += sy; dy -= xy; err += dx += xx; } /* y step */ - } while (dy < 0 && dx > 0); /* gradient negates -> algorithm fails */ - } - hvGfxLine(hvg, x0,y0, x2,y2, color); /* plot remaining part to end */ -} - void hvGfxCurveSegAA(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, Color color) /* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier) * 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 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 */ } @@ -369,89 +333,114 @@ 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 */ hvGfxCurveSegAA(hvg,x0,y0, floor(r+0.5),y, x,y, color); 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 */ } hvGfxCurveSegAA(hvg,x0,y0, x1,y1, x2,y2, color); /* remaining part */ } -#ifdef PROBLEM +void hvGfxEllipseDrawAA(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color, int mode) // TODO: Tame anti-aliasing with Braney help, or drop -void hvGfxEllipseAA(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color) -/* Draw an anti-aliased ellipse specified by rectangle, using Bresenham algorithm. +/* Draw an anti-aliased ellipse or half (top or bottom) using Bresenham algorithm. * Point 0 is left, point 1 is top of rectangle * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html */ { long a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* diameter */ float dx = 4*(a-1.0)*b*b, dy = 4*(b1+1)*a*a; /* error increment */ float ed, i, err = b1*a*a-dx+dy; /* error of 1.step */ bool f; if (a == 0 || b == 0) return hvGfxLine(hvg, x0, y0, x1, y1, color); 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*a; b1 = 8*b*b; - int q = 0; - for (q=0; q<1000; q++) { /* approximate ed=sqrt(dx*dx+dy*dy) */ + // FIXME: end condition not triggered on this loop //for (;;) { /* approximate ed=sqrt(dx*dx+dy*dy) */ + int q = 0; + for (q=0; q<10000; q++) { /* approximate ed=sqrt(dx*dx+dy*dy) */ + // i = min(dx,dy); ed = max(dx,dy); if (y0 == y1+1 && err > dy && a > b1) ed = 255*4./a; /* x-tip */ else ed = 255/(ed+2*ed*i*i/(4*ed*ed+i*i)); /* approximation */ i = ed*fabs(err+dx-dy); /* get intensity value by pixel error */ - mixDot(hvg, x0,y0, i, color); mixDot(hvg, x0,y1, i, color); - mixDot(hvg, x1,y0, i, color); mixDot(hvg, x1,y1, i, color); + if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y0, i, color); + mixDot(hvg, x1,y0, i, color); + } + if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y1, i, color); + mixDot(hvg, x1,y1, i, color); + } //mixDot(hvg, x0,y0, 1-fabs(err-dx-dy-xy)/ed, color); /* plot curve */ if ((f = 2*err+dy) >= 0) { /* x step, remember condition */ if (x0 >= x1) break; i = ed*(err+dx); if (i < 255) { - mixDot(hvg, x0,y0+1, i, color); mixDot(hvg, x0,y1-1, i, color); - mixDot(hvg, x1,y0+1, i, color); mixDot(hvg, x1,y1-1, i, color); + if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y0+1, i, color); + mixDot(hvg, x1,y0+1, i, color); + } + if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y1-1, i, color); + mixDot(hvg, x1,y1-1, i, color); + } } /* do error increment later since values are still needed */ } if ((2*err) <= dx) { /* y step */ i = ed*(dy-err); if (i < 255) { - mixDot(hvg, x0+1,y0, i, color); mixDot(hvg, x1-1,y0, i, color); - mixDot(hvg, x0+1,y1, i, color); mixDot(hvg, x1-1,y1, i, color); + if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) { + mixDot(hvg, x0+1,y0, i, color); + mixDot(hvg, x1-1,y0, i, color); + } + if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) { + mixDot(hvg, x0+1,y1, i, color); + mixDot(hvg, x1-1,y1, i, color); + } } y0++; y1--; err += dy += a; } if (f) { x0++; x1--; err -= dx -= b1; } /* x error increment */ } if (--x0 == x1++) /* too early stop of flat ellipses */ while (y0-y1 < b) { i = 255*4*fabs(err+dx)/b1; /* -> finish tip of ellipse */ - mixDot(hvg, x0,++y0, i, color); mixDot(hvg, x1,y0, i, color); - mixDot(hvg, x0,--y1, i, color); mixDot(hvg, x1,y1, i, color); + if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y0+1, i, color); + mixDot(hvg, x1,y0+1, i, color); + } + if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) { + mixDot(hvg, x0,y1-1, i, color); + mixDot(hvg, x1,y1-1, i, color); + } + y0++; y1--; err += dy += a; } } -#endif void hvGfxEllipseDraw(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color, int mode) /* Draw an ellipse (or limit to top or bottom) specified by rectangle, using Bresenham algorithm. * Point 0 is left, point 1 is top of rectangle * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html */ { 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;