diff options
-rw-r--r-- | draw/draw_path.c | 78 | ||||
-rw-r--r-- | fitz/base_geometry.c | 16 | ||||
-rw-r--r-- | fitz/fitz.h | 21 | ||||
-rw-r--r-- | fitz/res_font.c | 15 | ||||
-rw-r--r-- | fitz/res_path.c | 8 | ||||
-rw-r--r-- | pdf/pdf_interpret.c | 8 | ||||
-rw-r--r-- | xps/xps_path.c | 41 |
7 files changed, 121 insertions, 66 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; diff --git a/fitz/base_geometry.c b/fitz/base_geometry.c index 3269605d..e8d9032f 100644 --- a/fitz/base_geometry.c +++ b/fitz/base_geometry.c @@ -121,6 +121,22 @@ fz_matrix_expansion(fz_matrix m) return sqrtf(fabsf(m.a * m.d - m.b * m.c)); } +float +fz_matrix_max_expansion(fz_matrix m) +{ + float max = fabsf(m.a); + float x = fabsf(m.b); + if (max < x) + max = x; + x = fabsf(m.c); + if (max < x) + max = x; + x = fabsf(m.d); + if (max < x) + max = x; + return max; +} + fz_point fz_transform_point(fz_matrix m, fz_point p) { diff --git a/fitz/fitz.h b/fitz/fitz.h index 6aebdf61..bfc4723a 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -520,6 +520,7 @@ fz_matrix fz_translate(float tx, float ty); fz_matrix fz_invert_matrix(fz_matrix m); int fz_is_rectilinear(fz_matrix m); float fz_matrix_expansion(fz_matrix m); +float fz_matrix_max_expansion(fz_matrix m); fz_bbox fz_round_rect(fz_rect r); fz_bbox fz_intersect_bbox(fz_bbox a, fz_bbox b); @@ -1102,6 +1103,22 @@ typedef enum fz_path_item_kind_e FZ_CLOSE_PATH } fz_path_item_kind; +typedef enum fz_linecap_e +{ + FZ_LINECAP_BUTT = 0, + FZ_LINECAP_ROUND = 1, + FZ_LINECAP_SQUARE = 2, + FZ_LINECAP_TRIANGLE = 3 +} fz_linecap; + +typedef enum fz_linejoin_e +{ + FZ_LINEJOIN_MITER = 0, + FZ_LINEJOIN_ROUND = 1, + FZ_LINEJOIN_BEVEL = 2, + FZ_LINEJOIN_MITER_XPS = 3 +} fz_linejoin; + union fz_path_item_s { fz_path_item_kind k; @@ -1116,8 +1133,8 @@ struct fz_path_s struct fz_stroke_state_s { - int start_cap, dash_cap, end_cap; - int linejoin; + fz_linecap start_cap, dash_cap, end_cap; + fz_linejoin linejoin; float linewidth; float miterlimit; float dash_phase; diff --git a/fitz/res_font.c b/fitz/res_font.c index c3e2e0bd..4a1c9dfa 100644 --- a/fitz/res_font.c +++ b/fitz/res_font.c @@ -449,6 +449,7 @@ fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix tr FT_Glyph glyph; FT_BitmapGlyph bitmap; fz_pixmap *pixmap; + FT_Stroker_LineJoin line_join; trm = fz_adjust_ft_glyph_width(ctx, font, gid, trm); @@ -485,7 +486,19 @@ fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix tr return NULL; } - FT_Stroker_Set(stroker, linewidth, state->start_cap, state->linejoin, state->miterlimit * 65536); +#if 0 + line_join = state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER_FIXED : + state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND : + state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL : + FT_STROKER_LINEJOIN_MITER_VARIABLE; +#else + /* Until we upgrade freetype */ + line_join = state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER : + state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND : + state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL : + FT_STROKER_LINEJOIN_MITER; +#endif + FT_Stroker_Set(stroker, linewidth, state->start_cap, line_join, state->miterlimit * 65536); fterr = FT_Get_Glyph(face->glyph, &glyph); if (fterr) diff --git a/fitz/res_path.c b/fitz/res_path.c index d8838899..a062f813 100644 --- a/fitz/res_path.c +++ b/fitz/res_path.c @@ -189,9 +189,11 @@ fz_bound_path(fz_path *path, fz_stroke_state *stroke, fz_matrix ctm) if (stroke) { - float miterlength = stroke->miterlimit; - float linewidth = stroke->linewidth; - float expand = MAX(miterlength, linewidth) * 0.5f; + float expand = stroke->linewidth; + if (expand == 0) + expand = 1.0f; + expand *= fz_matrix_max_expansion(ctm); + expand *= stroke->miterlimit; r.x0 -= expand; r.y0 -= expand; r.x1 += expand; diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c index fe0e1ce8..0976912e 100644 --- a/pdf/pdf_interpret.c +++ b/pdf/pdf_interpret.c @@ -818,10 +818,10 @@ pdf_init_gstate(pdf_gstate *gs, fz_matrix ctm) gs->ctm = ctm; gs->clip_depth = 0; - gs->stroke_state.start_cap = 0; - gs->stroke_state.dash_cap = 0; - gs->stroke_state.end_cap = 0; - gs->stroke_state.linejoin = 0; + gs->stroke_state.start_cap = FZ_LINECAP_BUTT; + gs->stroke_state.dash_cap = FZ_LINECAP_BUTT; + gs->stroke_state.end_cap = FZ_LINECAP_BUTT; + gs->stroke_state.linejoin = FZ_LINEJOIN_MITER; gs->stroke_state.linewidth = 1; gs->stroke_state.miterlimit = 10; gs->stroke_state.dash_phase = 0; diff --git a/xps/xps_path.c b/xps/xps_path.c index 402ca532..8b66e1ff 100644 --- a/xps/xps_path.c +++ b/xps/xps_path.c @@ -39,6 +39,9 @@ fz_currentpoint(fz_path *path) * line segments. We cannot use the fz_arc function because they only draw * circular arcs, we need to transform the line to make them elliptical but * without transforming the line width. + * + * We are guaranteed that on entry the point is at the point that would be + * calculated by th0, and on exit, a point is generated for us at th0. */ static void xps_draw_arc_segment(fz_context *doc, fz_path *path, fz_matrix mtx, float th0, float th1, int iscw) @@ -53,40 +56,24 @@ xps_draw_arc_segment(fz_context *doc, fz_path *path, fz_matrix mtx, float th0, f if (iscw) { - p.x = cosf(th0); - p.y = sinf(th0); - p = fz_transform_point(mtx, p); - fz_lineto(doc, path, p.x, p.y); - for (t = th0; t < th1; t += d) + for (t = th0 + d; t < th1 - d/2; t += d) { p.x = cosf(t); p.y = sinf(t); p = fz_transform_point(mtx, p); fz_lineto(doc, path, p.x, p.y); } - p.x = cosf(th1); - p.y = sinf(th1); - p = fz_transform_point(mtx, p); - fz_lineto(doc, path, p.x, p.y); } else { th0 += (float)M_PI * 2; - p.x = cosf(th0); - p.y = sinf(th0); - p = fz_transform_point(mtx, p); - fz_lineto(doc, path, p.x, p.y); - for (t = th0; t > th1; t -= d) + for (t = th0 - d; t > th1 + d/2; t -= d) { p.x = cosf(t); p.y = sinf(t); p = fz_transform_point(mtx, p); fz_lineto(doc, path, p.x, p.y); } - p.x = cosf(th1); - p.y = sinf(th1); - p = fz_transform_point(mtx, p); - fz_lineto(doc, path, p.x, p.y); } } @@ -106,6 +93,16 @@ angle_between(const fz_point u, const fz_point v) return sign * acosf(t); } +/* Some explaination of the parameters here is warranted. See: + * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + * Add an arc segment to path, that describes a section of an elliptical arc + * from the current point of path to (point_x,point_y), such that: + * the arc segment is taken from an elliptical arc of semi major radius + * size_x, semi minor radius size_y, where the semi major axis of the + * ellipse is rotated by rotation_angle. + * if is_large_arc, then the arc segment is selected to be > 180 degrees. + * if is_clockwise, then the arc sweeps clockwise. + */ static void xps_draw_arc(fz_context *doc, fz_path *path, float size_x, float size_y, float rotation_angle, @@ -884,12 +881,12 @@ xps_parse_path(xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *d stroke.dash_cap = xps_parse_line_cap(stroke_dash_cap_att); stroke.end_cap = xps_parse_line_cap(stroke_end_line_cap_att); - stroke.linejoin = 0; + stroke.linejoin = FZ_LINEJOIN_MITER_XPS; if (stroke_line_join_att) { - if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0; - if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1; - if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2; + if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = FZ_LINEJOIN_MITER_XPS; + if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = FZ_LINEJOIN_ROUND; + if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = FZ_LINEJOIN_BEVEL; } stroke.miterlimit = 10; |