diff options
author | Robin Watts <robin.watts@artifex.com> | 2012-01-12 15:34:30 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2012-01-12 20:15:09 +0000 |
commit | f0d427993c1b9b40f1bc0b77ed9c0433099f2a5d (patch) | |
tree | c89144197a2aafec4dac4a05506dad93e098ebdc /draw/draw_path.c | |
parent | 269a44d92e2fc15631b86cc0c7135baf2023f312 (diff) | |
download | mupdf-f0d427993c1b9b40f1bc0b77ed9c0433099f2a5d.tar.xz |
Support proper XPS mitering. (And stroke fixes).
XPS differs from PS/PDF/etc in the way it handles miters; rather than
simply converting a miter that's overly long to a bevel, it truncates
it at the miter limit. As such it needs to be handled correctly.
For clarity, expose new enumerated types for linejoins and linecaps,
and use these throughout code.
When we upgrade our freetype, we can move to using proper xps mitering
in that too.
Add new fz_matrix_max_expansion function to return a safer expansion
value that works in the case where we scale up in one direction and
down in another.
In the xps path drawing code, avoid generating unnecessary linetos.
Thanks to Zeniko for spotting these and providing implementations.
Diffstat (limited to 'draw/draw_path.c')
-rw-r--r-- | draw/draw_path.c | 78 |
1 files changed, 44 insertions, 34 deletions
diff --git a/draw/draw_path.c b/draw/draw_path.c index d6f9934b..29614ec5 100644 --- a/draw/draw_path.c +++ b/draw/draw_path.c @@ -2,8 +2,6 @@ #define MAX_DEPTH 8 -enum { BUTT = 0, ROUND = 1, SQUARE = 2, TRIANGLE = 3, MITER = 0, BEVEL = 2 }; - static void line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1) { @@ -218,7 +216,7 @@ fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c) { float miterlimit = s->miterlimit; float linewidth = s->linewidth; - int linejoin = s->linejoin; + fz_linejoin linejoin = s->linejoin; float dx0, dy0; float dx1, dy1; float dlx0, dly0; @@ -234,10 +232,20 @@ fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c) dx1 = c.x - b.x; dy1 = c.y - b.y; + cross = dx1 * dy0 - dx0 * dy1; + /* Ensure that cross >= 0 */ + if (cross < 0) + { + float tmp; + tmp = dx1; dx1 = -dx0; dx0 = -tmp; + tmp = dy1; dy1 = -dy0; dy0 = -tmp; + cross = -cross; + } + if (dx0 * dx0 + dy0 * dy0 < FLT_EPSILON) - linejoin = BEVEL; + linejoin = FZ_LINEJOIN_BEVEL; if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON) - linejoin = BEVEL; + linejoin = FZ_LINEJOIN_BEVEL; scale = linewidth / sqrtf(dx0 * dx0 + dy0 * dy0); dlx0 = dy0 * scale; @@ -247,36 +255,46 @@ fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c) dlx1 = dy1 * scale; dly1 = -dx1 * scale; - cross = dx1 * dy0 - dx0 * dy1; - dmx = (dlx0 + dlx1) * 0.5f; dmy = (dly0 + dly1) * 0.5f; dmr2 = dmx * dmx + dmy * dmy; if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0) - linejoin = BEVEL; + linejoin = FZ_LINEJOIN_BEVEL; - if (linejoin == MITER) + if (linejoin == FZ_LINEJOIN_MITER) if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) - linejoin = BEVEL; + linejoin = FZ_LINEJOIN_BEVEL; - if (linejoin == BEVEL) + if (linejoin == FZ_LINEJOIN_BEVEL) { fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); } - if (linejoin == MITER) + if (linejoin == FZ_LINEJOIN_MITER) + { + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + + fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); + fz_add_line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); + } + + /* XPS miter joins are clipped at miterlength, rather than simply + * being converted to bevelled joins. */ + if (linejoin == FZ_LINEJOIN_MITER_XPS) { scale = linewidth * linewidth / dmr2; dmx *= scale; dmy *= scale; - if (cross < 0) + if (cross == 0) { + fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); - fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy); - fz_add_line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0); } else { @@ -286,23 +304,15 @@ fz_add_line_join(struct sctx *s, fz_point a, fz_point b, fz_point c) } } - if (linejoin == ROUND) + if (linejoin == FZ_LINEJOIN_ROUND) { - if (cross < 0) - { - fz_add_line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); - fz_add_arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0); - } - else - { - fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); - fz_add_arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); - } + fz_add_line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + fz_add_arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); } } static void -fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, int linecap) +fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, fz_linecap linecap) { float flatness = s->flatness; float linewidth = s->linewidth; @@ -314,10 +324,10 @@ fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, int linecap) float dlx = dy * scale; float dly = -dx * scale; - if (linecap == BUTT) + if (linecap == FZ_LINECAP_BUTT) fz_add_line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); - if (linecap == ROUND) + if (linecap == FZ_LINECAP_ROUND) { int i; int n = ceilf((float)M_PI / (2.0f * (float)M_SQRT2 * sqrtf(flatness / linewidth))); @@ -337,7 +347,7 @@ fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, int linecap) fz_add_line(s, ox, oy, b.x + dlx, b.y + dly); } - if (linecap == SQUARE) + if (linecap == FZ_LINECAP_SQUARE) { fz_add_line(s, b.x - dlx, b.y - dly, b.x - dlx - dly, b.y - dly + dlx); @@ -347,7 +357,7 @@ fz_add_line_cap(struct sctx *s, fz_point a, fz_point b, int linecap) b.x + dlx, b.y + dly); } - if (linecap == TRIANGLE) + if (linecap == FZ_LINECAP_TRIANGLE) { float mx = -dly; float my = dlx; @@ -382,7 +392,7 @@ fz_add_line_dot(struct sctx *s, fz_point a) } static void -fz_stroke_flush(struct sctx *s, int start_cap, int end_cap) +fz_stroke_flush(struct sctx *s, fz_linecap start_cap, fz_linecap end_cap) { if (s->sn == 2) { @@ -413,7 +423,7 @@ fz_stroke_lineto(struct sctx *s, fz_point cur) if (dx * dx + dy * dy < FLT_EPSILON) { - if (s->cap == ROUND || s->dash_list) + if (s->cap == FZ_LINECAP_ROUND || s->dash_list) s->dot = 1; return; } @@ -586,7 +596,7 @@ fz_flatten_stroke_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_m } static void -fz_dash_moveto(struct sctx *s, fz_point a, int start_cap, int end_cap) +fz_dash_moveto(struct sctx *s, fz_point a, fz_linecap start_cap, fz_linecap end_cap) { s->toggle = 1; s->offset = 0; |