Bresenham 算法具有线宽的抗锯齿二次贝塞尔曲线
Bresenham's Algorithm Anti-aliased quadratic Bézier curve with line thickness
在 http://members.chello.at/easyfilter/bresenham.html 找到的文档中,它介绍了基于 Bresenham 算法创建曲线的过程,并以创建抗锯齿粗线的算法结束,但是如何将其应用于二次贝塞尔曲线(在同一站点上找到的抗锯齿版本)?
我尝试将 plotQuadBezierSegAA 函数的最后一行更改为使用粗线的算法,但显然它没有用,因为它正在一个一个地计算其他像素。 (我从plotLineAA(x0,y0, x2,y2);
改成了plotLineWidth(x0, y0, x2, y2, wd);
)
我还尝试绘制更多曲线,稍微移动直到它具有所需的厚度,但它会产生抗锯齿颜色的问题。 (按 x 和 y 步骤(xx 和 yy 变量)移动并递归调用 plotQuadBezierSegWidth 的 for 循环)。
None 的这种尝试实际上是有效的,所以请有人帮我完成这条曲线的厚度。 (目前的算法是来自该站点上的 plotQuadBezierSegAA 的算法)。
转移代码:
void plotQuadBezierSegAA(int x0, int y0, int x1, int y1, int x2, int y2, int wd)
{
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 */
// SHIFTING HERE
plotQuadBezierSegAA(x0 + xx, y0 + yy, x1 + xx, y1 + yy, x2 + xx, y2 + yy, wd - 1);
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 */
do {
cur = fmin(dx+xy,-xy-dy);
ed = fmax(dx+xy,-xy-dy); /* approximate error distance */
ed = 255/(ed+2*ed*cur*cur/(4.*ed*ed+cur*cur));
setPixelAA(x0,y0, ed*fabs(err-dx-dy-xy)); /* plot curve */
if (x0 == x2 && y0 == y2) return;/* 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) setPixelAA(x0,y0+sy, ed*fabs(err-dy));
x0 += sx; dx -= xy; err += dy += yy;
}
if (y1) { /* y step */
if (cur < ed) setPixelAA(x1+sx,y0, ed*fabs(cur));
y0 += sy; dy -= xy; err += dx += xx;
}
} while (dy < dx); /* gradient negates -> close curves */
}
plotLineAA(x0,y0, x2,y2); /* plot remaining needle to end */
}
在暂存器中(这里的 js 文件:http://members.chello.at/easyfilter/bresenham.js 第 909 到 1049 行)它使用 plotQuadRationalBezierWidth 并将 w 设置为 1 来计算具有指定宽度的正常贝塞尔曲线行(wd 参数)。
从文件移植的c代码:
void plotQuadRationalBezierWidthSeg(
int x0, int y0, int x1, int y1, int x2, int y2, int w, float th) { /* plot a limited rational Bezier segment
of thickness th, squared weight */
int sx = x2 - x1, sy = y2 - y1; /* relative values for checks */
int dx = x0 - x2, dy = y0 - y2, xx = x0 - x1, yy = y0 - y1;
double xy = xx * sy + yy * sx, cur = xx * sy - yy * sx, err, e2,
ed; /* curvature */
bool fullBreak = false;
if (cur != 0.0 && w > 0.0) { /* no straight line */
if (sx * sx + sy * sy >
xx * xx + yy * yy) { /* begin with longer part */
x2 = x0;
x0 -= dx;
y2 = y0;
y0 -= dy;
cur = -cur; /* swap P0 P2 */
}
xx = 2.0 * (4.0 * w * sx * xx + dx * dx); /* differences 2nd degree */
yy = 2.0 * (4.0 * w * sy * yy + dy * dy);
sx = x0 < x2 ? 1 : -1; /* x step direction */
sy = y0 < y2 ? 1 : -1; /* y step direction */
xy = -2.0 * sx * sy * (2.0 * w * xy + dx * dy);
if (cur * sx * sy < 0) { /* negated curvature? */
xx = -xx;
yy = -yy;
cur = -cur;
xy = -xy;
}
dx = 4.0 * w * (x1 - x0) * sy * cur +
xx / 2.0; /* differences 1st degree */
dy = 4.0 * w * (y0 - y1) * sx * cur + yy / 2.0;
if (w < 0.5 &&
(dx + xx <= 0 || dy + yy >= 0)) { /* flat ellipse, algo fails */
cur = (w + 1.0) / 2.0;
w = std::sqrtf(w);
xy = 1.0 / (w + 1.0);
sx = std::floor((x0 + 2.0 * w * x1 + x2) * xy / 2.0 +
0.5); /* subdivide curve */
sy = std::floor((y0 + 2.0 * w * y1 + y2) * xy / 2.0 +
0.5); /* plot separately */
dx = std::floor((w * x1 + x0) * xy + 0.5);
dy = std::floor((y1 * w + y0) * xy + 0.5);
plotQuadRationalBezierWidthSeg(x0, y0, dx, dy, sx, sy, cur, th);
dx = std::floor((w * x1 + x2) * xy + 0.5);
dy = std::floor((y1 * w + y2) * xy + 0.5);
return plotQuadRationalBezierWidthSeg(sx, sy, dx, dy, x2, y2, cur,
th);
}
for (err = 0;
dy + 2 * yy < 0 && dx + 2 * xx > 0;) /* loop of steep/flat curve */
if (dx + dy + xy < 0) { /* steep curve */
do {
ed = -dy -
2 * dy * dx * dx /
(4. * dy * dy + dx * dx); /* approximate sqrt */
w = (th - 1) * ed; /* scale line width */
x1 = std::floor((err - ed - w / 2) / dy); /* start offset */
e2 = err - x1 * dy - w / 2; /* error value at offset */
x1 = x0 - x1 * sx; /* start point */
setPixel(x1, y0, 255 * e2 / ed); /* aliasing pre-pixel */
for (e2 = -w - dy - e2; e2 - dy < ed; e2 -= dy)
setPixel(x1 += sx, y0); /* pixel on thick line */
setPixel(x1 + sx, y0,
255 * e2 / ed); /* aliasing post-pixel */
if (y0 == y2) return; /* last pixel -> curve finished */
y0 += sy;
dy += xy;
err += dx;
dx += xx; /* y step */
if (2 * err + dy > 0) { /* e_x+e_xy > 0 */
x0 += sx;
dx += xy;
err += dy;
dy += yy; /* x step */
}
if (x0 != x2 && (dx + 2 * xx <= 0 || dy + 2 * yy >= 0))
if (std::abs(y2 - y0) > std::abs(x2 - x0)) {
fullBreak = true;
break;
} else
break; /* other curve near */
} while (dx + dy + xy < 0); /* gradient still steep? */
if (fullBreak) break;
/* change from steep to flat curve */
for (cur = err - dy - w / 2, y1 = y0; cur < ed;
y1 += sy, cur += dx) {
for (e2 = cur, x1 = x0; e2 - dy < ed; e2 -= dy)
setPixel(x1 -= sx, y1); /* pixel on thick line */
setPixel(x1 - sx, y1,
255 * e2 / ed); /* aliasing post-pixel */
}
} else { /* flat curve */
do {
ed = dx +
2 * dx * dy * dy /
(4. * dx * dx + dy * dy); /* approximate sqrt */
w = (th - 1) * ed; /* scale line width */
y1 = std::floor((err + ed + w / 2) / dx); /* start offset */
e2 = y1 * dx - w / 2 - err; /* error value at offset */
y1 = y0 - y1 * sy; /* start point */
setPixel(x0, y1, 255 * e2 / ed); /* aliasing pre-pixel */
for (e2 = dx - e2 - w; e2 + dx < ed; e2 += dx)
setPixel(x0, y1 += sy); /* pixel on thick line */
setPixel(x0, y1 + sy,
255 * e2 / ed); /* aliasing post-pixel */
if (x0 == x2) return; /* last pixel -> curve finished */
x0 += sx;
dx += xy;
err += dy;
dy += yy; /* x step */
if (2 * err + dx < 0) { /* e_y+e_xy < 0 */
y0 += sy;
dy += xy;
err += dx;
dx += xx; /* y step */
}
if (y0 != y2 && (dx + 2 * xx <= 0 || dy + 2 * yy >= 0))
if (std::abs(y2 - y0) <= std::abs(x2 - x0)) {
fullBreak = true;
break;
} else
break; /* other curve near */
} while (dx + dy + xy >= 0); /* gradient still flat? */
if (fullBreak) break;
/* change from flat to steep curve */
for (cur = -err + dx - w / 2, x1 = x0; cur < ed;
x1 += sx, cur -= dy) {
for (e2 = cur, y1 = y0; e2 + dx < ed; e2 += dx)
setPixel(x1, y1 -= sy); /* pixel on thick line */
setPixel(x1, y1 - sy,
255 * e2 / ed); /* aliasing post-pixel */
}
}
}
setLine(x0, y0, x2, y2, th); /* confusing error values */
}
void plotQuadRationalBezierWidth(
int x0, int y0, int x1, int y1, int x2, int y2, int w,
float th) { /* plot any anti-aliased quadratic rational Bezier curve */
int x = x0 - 2 * x1 + x2, y = y0 - 2 * y1 + y2;
double xx = x0 - x1, yy = y0 - y1, ww, t, q;
if (xx * (x2 - x1) > 0) { /* horizontal cut at P4? */
if (yy * (y2 - y1) > 0) /* vertical cut at P6 too? */
if (std::abs(xx * y) > std::abs(yy * x)) { /* which first? */
x0 = x2;
x2 = xx + x1;
y0 = y2;
y2 = yy + y1; /* swap points */
} /* now horizontal cut at P4 comes first */
if (x0 == x2 || w == 1.0)
t = (x0 - x1) / x;
else { /* non-rational or rational case */
q = std::sqrt(4.0 * w * w * (x0 - x1) * (x2 - x1) +
(x2 - x0) * (x2 - x0));
if (x1 < x0) q = -q;
t = (2.0 * w * (x0 - x1) - x0 + x2 + q) /
(2.0 * (1.0 - w) * (x2 - x0)); /* t at P4 */
}
q = 1.0 / (2.0 * t * (1.0 - t) * (w - 1.0) + 1.0); /* sub-divide at t */
xx = (t * t * (x0 - 2.0 * w * x1 + x2) + 2.0 * t * (w * x1 - x0) + x0) *
q; /* = P4 */
yy = (t * t * (y0 - 2.0 * w * y1 + y2) + 2.0 * t * (w * y1 - y0) + y0) *
q;
ww = t * (w - 1.0) + 1.0;
ww *= ww * q; /* squared weight P3 */
w = ((1.0 - t) * (w - 1.0) + 1.0) * std::sqrt(q); /* weight P8 */
x = std::floor(xx + 0.5);
y = std::floor(yy + 0.5); /* P4 */
yy = (xx - x0) * (y1 - y0) / (x1 - x0) + y0; /* intersect P3 | P0 P1 */
plotQuadRationalBezierWidthSeg(x0, y0, x, std::floor(yy + 0.5), x, y,
ww, th);
yy = (xx - x2) * (y1 - y2) / (x1 - x2) + y2; /* intersect P4 | P1 P2 */
y1 = std::floor(yy + 0.5);
x0 = x1 = x;
y0 = y; /* P0 = P4, P1 = P8 */
}
if ((y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */
if (y0 == y2 || w == 1.0)
t = (y0 - y1) / (y0 - 2.0 * y1 + y2);
else { /* non-rational or rational case */
q = std::sqrt(4.0 * w * w * (y0 - y1) * (y2 - y1) +
(y2 - y0) * (y2 - y0));
if (y1 < y0) q = -q;
t = (2.0 * w * (y0 - y1) - y0 + y2 + q) /
(2.0 * (1.0 - w) * (y2 - y0)); /* t at P6 */
}
q = 1.0 / (2.0 * t * (1.0 - t) * (w - 1.0) + 1.0); /* sub-divide at t */
xx = (t * t * (x0 - 2.0 * w * x1 + x2) + 2.0 * t * (w * x1 - x0) + x0) *
q; /* = P6 */
yy = (t * t * (y0 - 2.0 * w * y1 + y2) + 2.0 * t * (w * y1 - y0) + y0) *
q;
ww = t * (w - 1.0) + 1.0;
ww *= ww * q; /* squared weight P5 */
w = ((1.0 - t) * (w - 1.0) + 1.0) * std::sqrt(q); /* weight P7 */
x = std::floor(xx + 0.5);
y = std::floor(yy + 0.5); /* P6 */
xx = (x1 - x0) * (yy - y0) / (y1 - y0) + x0; /* intersect P6 | P0 P1 */
plotQuadRationalBezierWidthSeg(x0, y0, std::floor(xx + 0.5), y, x, y,
ww, th);
xx = (x1 - x2) * (yy - y2) / (y1 - y2) + x2; /* intersect P7 | P1 P2 */
x1 = std::floor(xx + 0.5);
x0 = x;
y0 = y1 = y; /* P0 = P6, P1 = P7 */
}
plotQuadRationalBezierWidthSeg(x0, y0, x1, y1, x2, y2, w * w, th);
}
void plotQuadBezierWidth(int x0, int y0, int x1, int y1, int x2, int y2, int wd) {
plotQuadRationalBezierWidth(x0, y0, x1, y1, x2, y2, 1, wd);
}
示例(粗细 5px):
plotQuadBezierWidth(10, 10, 10, 300, 100, 500, 5);
示例(粗细 1px):
plotQuadBezierWidth(10, 10, 10, 300, 100, 500, 1);
在 http://members.chello.at/easyfilter/bresenham.html 找到的文档中,它介绍了基于 Bresenham 算法创建曲线的过程,并以创建抗锯齿粗线的算法结束,但是如何将其应用于二次贝塞尔曲线(在同一站点上找到的抗锯齿版本)?
我尝试将 plotQuadBezierSegAA 函数的最后一行更改为使用粗线的算法,但显然它没有用,因为它正在一个一个地计算其他像素。 (我从plotLineAA(x0,y0, x2,y2);
改成了plotLineWidth(x0, y0, x2, y2, wd);
)
我还尝试绘制更多曲线,稍微移动直到它具有所需的厚度,但它会产生抗锯齿颜色的问题。 (按 x 和 y 步骤(xx 和 yy 变量)移动并递归调用 plotQuadBezierSegWidth 的 for 循环)。
None 的这种尝试实际上是有效的,所以请有人帮我完成这条曲线的厚度。 (目前的算法是来自该站点上的 plotQuadBezierSegAA 的算法)。
转移代码:
void plotQuadBezierSegAA(int x0, int y0, int x1, int y1, int x2, int y2, int wd)
{
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 */
// SHIFTING HERE
plotQuadBezierSegAA(x0 + xx, y0 + yy, x1 + xx, y1 + yy, x2 + xx, y2 + yy, wd - 1);
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 */
do {
cur = fmin(dx+xy,-xy-dy);
ed = fmax(dx+xy,-xy-dy); /* approximate error distance */
ed = 255/(ed+2*ed*cur*cur/(4.*ed*ed+cur*cur));
setPixelAA(x0,y0, ed*fabs(err-dx-dy-xy)); /* plot curve */
if (x0 == x2 && y0 == y2) return;/* 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) setPixelAA(x0,y0+sy, ed*fabs(err-dy));
x0 += sx; dx -= xy; err += dy += yy;
}
if (y1) { /* y step */
if (cur < ed) setPixelAA(x1+sx,y0, ed*fabs(cur));
y0 += sy; dy -= xy; err += dx += xx;
}
} while (dy < dx); /* gradient negates -> close curves */
}
plotLineAA(x0,y0, x2,y2); /* plot remaining needle to end */
}
在暂存器中(这里的 js 文件:http://members.chello.at/easyfilter/bresenham.js 第 909 到 1049 行)它使用 plotQuadRationalBezierWidth 并将 w 设置为 1 来计算具有指定宽度的正常贝塞尔曲线行(wd 参数)。
从文件移植的c代码:
void plotQuadRationalBezierWidthSeg(
int x0, int y0, int x1, int y1, int x2, int y2, int w, float th) { /* plot a limited rational Bezier segment
of thickness th, squared weight */
int sx = x2 - x1, sy = y2 - y1; /* relative values for checks */
int dx = x0 - x2, dy = y0 - y2, xx = x0 - x1, yy = y0 - y1;
double xy = xx * sy + yy * sx, cur = xx * sy - yy * sx, err, e2,
ed; /* curvature */
bool fullBreak = false;
if (cur != 0.0 && w > 0.0) { /* no straight line */
if (sx * sx + sy * sy >
xx * xx + yy * yy) { /* begin with longer part */
x2 = x0;
x0 -= dx;
y2 = y0;
y0 -= dy;
cur = -cur; /* swap P0 P2 */
}
xx = 2.0 * (4.0 * w * sx * xx + dx * dx); /* differences 2nd degree */
yy = 2.0 * (4.0 * w * sy * yy + dy * dy);
sx = x0 < x2 ? 1 : -1; /* x step direction */
sy = y0 < y2 ? 1 : -1; /* y step direction */
xy = -2.0 * sx * sy * (2.0 * w * xy + dx * dy);
if (cur * sx * sy < 0) { /* negated curvature? */
xx = -xx;
yy = -yy;
cur = -cur;
xy = -xy;
}
dx = 4.0 * w * (x1 - x0) * sy * cur +
xx / 2.0; /* differences 1st degree */
dy = 4.0 * w * (y0 - y1) * sx * cur + yy / 2.0;
if (w < 0.5 &&
(dx + xx <= 0 || dy + yy >= 0)) { /* flat ellipse, algo fails */
cur = (w + 1.0) / 2.0;
w = std::sqrtf(w);
xy = 1.0 / (w + 1.0);
sx = std::floor((x0 + 2.0 * w * x1 + x2) * xy / 2.0 +
0.5); /* subdivide curve */
sy = std::floor((y0 + 2.0 * w * y1 + y2) * xy / 2.0 +
0.5); /* plot separately */
dx = std::floor((w * x1 + x0) * xy + 0.5);
dy = std::floor((y1 * w + y0) * xy + 0.5);
plotQuadRationalBezierWidthSeg(x0, y0, dx, dy, sx, sy, cur, th);
dx = std::floor((w * x1 + x2) * xy + 0.5);
dy = std::floor((y1 * w + y2) * xy + 0.5);
return plotQuadRationalBezierWidthSeg(sx, sy, dx, dy, x2, y2, cur,
th);
}
for (err = 0;
dy + 2 * yy < 0 && dx + 2 * xx > 0;) /* loop of steep/flat curve */
if (dx + dy + xy < 0) { /* steep curve */
do {
ed = -dy -
2 * dy * dx * dx /
(4. * dy * dy + dx * dx); /* approximate sqrt */
w = (th - 1) * ed; /* scale line width */
x1 = std::floor((err - ed - w / 2) / dy); /* start offset */
e2 = err - x1 * dy - w / 2; /* error value at offset */
x1 = x0 - x1 * sx; /* start point */
setPixel(x1, y0, 255 * e2 / ed); /* aliasing pre-pixel */
for (e2 = -w - dy - e2; e2 - dy < ed; e2 -= dy)
setPixel(x1 += sx, y0); /* pixel on thick line */
setPixel(x1 + sx, y0,
255 * e2 / ed); /* aliasing post-pixel */
if (y0 == y2) return; /* last pixel -> curve finished */
y0 += sy;
dy += xy;
err += dx;
dx += xx; /* y step */
if (2 * err + dy > 0) { /* e_x+e_xy > 0 */
x0 += sx;
dx += xy;
err += dy;
dy += yy; /* x step */
}
if (x0 != x2 && (dx + 2 * xx <= 0 || dy + 2 * yy >= 0))
if (std::abs(y2 - y0) > std::abs(x2 - x0)) {
fullBreak = true;
break;
} else
break; /* other curve near */
} while (dx + dy + xy < 0); /* gradient still steep? */
if (fullBreak) break;
/* change from steep to flat curve */
for (cur = err - dy - w / 2, y1 = y0; cur < ed;
y1 += sy, cur += dx) {
for (e2 = cur, x1 = x0; e2 - dy < ed; e2 -= dy)
setPixel(x1 -= sx, y1); /* pixel on thick line */
setPixel(x1 - sx, y1,
255 * e2 / ed); /* aliasing post-pixel */
}
} else { /* flat curve */
do {
ed = dx +
2 * dx * dy * dy /
(4. * dx * dx + dy * dy); /* approximate sqrt */
w = (th - 1) * ed; /* scale line width */
y1 = std::floor((err + ed + w / 2) / dx); /* start offset */
e2 = y1 * dx - w / 2 - err; /* error value at offset */
y1 = y0 - y1 * sy; /* start point */
setPixel(x0, y1, 255 * e2 / ed); /* aliasing pre-pixel */
for (e2 = dx - e2 - w; e2 + dx < ed; e2 += dx)
setPixel(x0, y1 += sy); /* pixel on thick line */
setPixel(x0, y1 + sy,
255 * e2 / ed); /* aliasing post-pixel */
if (x0 == x2) return; /* last pixel -> curve finished */
x0 += sx;
dx += xy;
err += dy;
dy += yy; /* x step */
if (2 * err + dx < 0) { /* e_y+e_xy < 0 */
y0 += sy;
dy += xy;
err += dx;
dx += xx; /* y step */
}
if (y0 != y2 && (dx + 2 * xx <= 0 || dy + 2 * yy >= 0))
if (std::abs(y2 - y0) <= std::abs(x2 - x0)) {
fullBreak = true;
break;
} else
break; /* other curve near */
} while (dx + dy + xy >= 0); /* gradient still flat? */
if (fullBreak) break;
/* change from flat to steep curve */
for (cur = -err + dx - w / 2, x1 = x0; cur < ed;
x1 += sx, cur -= dy) {
for (e2 = cur, y1 = y0; e2 + dx < ed; e2 += dx)
setPixel(x1, y1 -= sy); /* pixel on thick line */
setPixel(x1, y1 - sy,
255 * e2 / ed); /* aliasing post-pixel */
}
}
}
setLine(x0, y0, x2, y2, th); /* confusing error values */
}
void plotQuadRationalBezierWidth(
int x0, int y0, int x1, int y1, int x2, int y2, int w,
float th) { /* plot any anti-aliased quadratic rational Bezier curve */
int x = x0 - 2 * x1 + x2, y = y0 - 2 * y1 + y2;
double xx = x0 - x1, yy = y0 - y1, ww, t, q;
if (xx * (x2 - x1) > 0) { /* horizontal cut at P4? */
if (yy * (y2 - y1) > 0) /* vertical cut at P6 too? */
if (std::abs(xx * y) > std::abs(yy * x)) { /* which first? */
x0 = x2;
x2 = xx + x1;
y0 = y2;
y2 = yy + y1; /* swap points */
} /* now horizontal cut at P4 comes first */
if (x0 == x2 || w == 1.0)
t = (x0 - x1) / x;
else { /* non-rational or rational case */
q = std::sqrt(4.0 * w * w * (x0 - x1) * (x2 - x1) +
(x2 - x0) * (x2 - x0));
if (x1 < x0) q = -q;
t = (2.0 * w * (x0 - x1) - x0 + x2 + q) /
(2.0 * (1.0 - w) * (x2 - x0)); /* t at P4 */
}
q = 1.0 / (2.0 * t * (1.0 - t) * (w - 1.0) + 1.0); /* sub-divide at t */
xx = (t * t * (x0 - 2.0 * w * x1 + x2) + 2.0 * t * (w * x1 - x0) + x0) *
q; /* = P4 */
yy = (t * t * (y0 - 2.0 * w * y1 + y2) + 2.0 * t * (w * y1 - y0) + y0) *
q;
ww = t * (w - 1.0) + 1.0;
ww *= ww * q; /* squared weight P3 */
w = ((1.0 - t) * (w - 1.0) + 1.0) * std::sqrt(q); /* weight P8 */
x = std::floor(xx + 0.5);
y = std::floor(yy + 0.5); /* P4 */
yy = (xx - x0) * (y1 - y0) / (x1 - x0) + y0; /* intersect P3 | P0 P1 */
plotQuadRationalBezierWidthSeg(x0, y0, x, std::floor(yy + 0.5), x, y,
ww, th);
yy = (xx - x2) * (y1 - y2) / (x1 - x2) + y2; /* intersect P4 | P1 P2 */
y1 = std::floor(yy + 0.5);
x0 = x1 = x;
y0 = y; /* P0 = P4, P1 = P8 */
}
if ((y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */
if (y0 == y2 || w == 1.0)
t = (y0 - y1) / (y0 - 2.0 * y1 + y2);
else { /* non-rational or rational case */
q = std::sqrt(4.0 * w * w * (y0 - y1) * (y2 - y1) +
(y2 - y0) * (y2 - y0));
if (y1 < y0) q = -q;
t = (2.0 * w * (y0 - y1) - y0 + y2 + q) /
(2.0 * (1.0 - w) * (y2 - y0)); /* t at P6 */
}
q = 1.0 / (2.0 * t * (1.0 - t) * (w - 1.0) + 1.0); /* sub-divide at t */
xx = (t * t * (x0 - 2.0 * w * x1 + x2) + 2.0 * t * (w * x1 - x0) + x0) *
q; /* = P6 */
yy = (t * t * (y0 - 2.0 * w * y1 + y2) + 2.0 * t * (w * y1 - y0) + y0) *
q;
ww = t * (w - 1.0) + 1.0;
ww *= ww * q; /* squared weight P5 */
w = ((1.0 - t) * (w - 1.0) + 1.0) * std::sqrt(q); /* weight P7 */
x = std::floor(xx + 0.5);
y = std::floor(yy + 0.5); /* P6 */
xx = (x1 - x0) * (yy - y0) / (y1 - y0) + x0; /* intersect P6 | P0 P1 */
plotQuadRationalBezierWidthSeg(x0, y0, std::floor(xx + 0.5), y, x, y,
ww, th);
xx = (x1 - x2) * (yy - y2) / (y1 - y2) + x2; /* intersect P7 | P1 P2 */
x1 = std::floor(xx + 0.5);
x0 = x;
y0 = y1 = y; /* P0 = P6, P1 = P7 */
}
plotQuadRationalBezierWidthSeg(x0, y0, x1, y1, x2, y2, w * w, th);
}
void plotQuadBezierWidth(int x0, int y0, int x1, int y1, int x2, int y2, int wd) {
plotQuadRationalBezierWidth(x0, y0, x1, y1, x2, y2, 1, wd);
}
示例(粗细 5px):
plotQuadBezierWidth(10, 10, 10, 300, 100, 500, 5);
示例(粗细 1px):
plotQuadBezierWidth(10, 10, 10, 300, 100, 500, 1);