summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-11-09 15:54:15 +0000
committerRobin Watts <robin.watts@artifex.com>2017-11-13 15:50:41 +0000
commita8047c876f7f82cbce82c5210a865991f83caa26 (patch)
treedaa1a20458823fd56f2e1c30b497635527a35a27
parent97c9056b6af80e8b6703031a0ae45fe6dc0e2766 (diff)
downloadmupdf-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.c90
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);