diff options
author | Robin Watts <robin.watts@artifex.com> | 2017-11-09 15:54:15 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2017-11-13 15:50:41 +0000 |
commit | a8047c876f7f82cbce82c5210a865991f83caa26 (patch) | |
tree | daa1a20458823fd56f2e1c30b497635527a35a27 | |
parent | 97c9056b6af80e8b6703031a0ae45fe6dc0e2766 (diff) | |
download | mupdf-a8047c876f7f82cbce82c5210a865991f83caa26.tar.xz |
Bug 698369: Fix stroking issue when closepath is infinitessimal.
If the move due to a closepath is infinitessimal, fz_stroke_lineto
skips it as it cannot accurately figure out what the direction is.
It appears we have had some code in fz_stroke_closepath to cope
with exactly this case, but it does the wrong thing. This dates
back all the way to the initial import of the code, so presumably
it worked at some point, and stopped working somewhere along the
line.
-rw-r--r-- | source/fitz/draw-path.c | 90 |
1 files changed, 55 insertions, 35 deletions
diff --git a/source/fitz/draw-path.c b/source/fitz/draw-path.c index 09cb06db..deb17932 100644 --- a/source/fitz/draw-path.c +++ b/source/fitz/draw-path.c @@ -472,6 +472,48 @@ fz_add_arc(fz_context *ctx, sctx *s, } } +/* FLT_TINY * FLT_TINY is approximately FLT_EPSILON */ +#define FLT_TINY 3.4e-4F +static int find_normal_vectors(float dx, float dy, float linewidth, float *dlx, float *dly) +{ + if (dx == 0) + { + if (dy < FLT_TINY && dy > - FLT_TINY) + goto tiny; + else if (dy > 0) + *dlx = linewidth; + else + *dlx = -linewidth; + *dly = 0; + } + else if (dy == 0) + { + if (dx < FLT_TINY && dx > - FLT_TINY) + goto tiny; + else if (dx > 0) + *dly = -linewidth; + else + *dly = linewidth; + *dlx = 0; + } + else + { + float sq = dx * dx + dy * dy; + float scale; + + if (sq < FLT_EPSILON) + goto tiny; + scale = linewidth / sqrtf(sq); + *dlx = dy * scale; + *dly = -dx * scale; + } + return 0; +tiny: + *dlx = 0; + *dly = 0; + return 1; +} + static void fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, float cx, float cy, int join_under) { @@ -486,7 +528,6 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b float dmr2; float scale; float cross; - float len0, len1; int rev = 0; dx0 = bx - ax; @@ -506,33 +547,11 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b rev = !rev; } - len0 = dx0 * dx0 + dy0 * dy0; - if (len0 < FLT_EPSILON) - { + if (find_normal_vectors(dx0, dy0, linewidth, &dlx0, &dly0)) linejoin = FZ_LINEJOIN_BEVEL; - dlx0 = 0; - dly0 = 0; - } - else - { - scale = linewidth / sqrtf(len0); - dlx0 = dy0 * scale; - dly0 = -dx0 * scale; - } - len1 = dx1 * dx1 + dy1 * dy1; - if (len1 < FLT_EPSILON) - { + if (find_normal_vectors(dx1, dy1, linewidth, &dlx1, &dly1)) linejoin = FZ_LINEJOIN_BEVEL; - dlx1 = 0; - dly1 = 0; - } - else - { - scale = linewidth / sqrtf(len1); - dlx1 = dy1 * scale; - dly1 = -dx1 * scale; - } dmx = (dlx0 + dlx1) * 0.5f; dmy = (dly0 + dly1) * 0.5f; @@ -750,9 +769,9 @@ fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier) float oy = s->seg[s->sn-1].y; float dx = x - ox; float dy = y - oy; - float scale, dlx, dly; + float dlx, dly; - if (dx * dx + dy * dy < FLT_EPSILON) + if (find_normal_vectors(dx, dy, s->linewidth, &dlx, &dly)) { if (s->dot == ONLY_MOVES && (s->cap == FZ_LINECAP_ROUND || s->dash_list)) s->dot = NULL_LINE; @@ -763,10 +782,6 @@ fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier) if (s->sn == 2) fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, ox, oy, x, y, s->from_bezier & from_bezier); - scale = s->linewidth / sqrtf(dx * dx + dy * dy); - dlx = dy * scale; - dly = -dx * scale; - #if 1 if (0 && dx == 0) { @@ -805,10 +820,15 @@ fz_stroke_closepath(fz_context *ctx, sctx *s) if (s->sn == 2) { fz_stroke_lineto(ctx, s, s->beg[0].x, s->beg[0].y, 0); - if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y) - fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0); - else - fz_add_line_join(ctx, s, s->seg[1].x, s->seg[1].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0); + /* fz_stroke_lineto will *normally* end up with s->seg[1] being the x,y coords passed in. + * As such, the following line should draw a linejoin between the closing segment of this + * subpath (seg[0]->seg[1]) == (seg[0]->beg[0]) and the first segment of this subpath + * (beg[0]->beg[1]). + * In cases where the line was already at an x,y infinitessimally close to s->beg[0], + * fz_stroke_lineto may exit without doing any processing. This leaves seg[0]->seg[1] + * pointing at the penultimate line segment. Thus this draws a linejoin between that + * penultimate segment and the end segment. This is what we want. */ + fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0); } else if (s->dot == NULL_LINE) fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y); |