diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/fitz/draw-path.c | 822 | ||||
-rw-r--r-- | source/fitz/font.c | 9 | ||||
-rw-r--r-- | source/fitz/path.c | 1032 | ||||
-rw-r--r-- | source/fitz/svg-device.c | 78 | ||||
-rw-r--r-- | source/fitz/trace-device.c | 98 | ||||
-rw-r--r-- | source/pdf/pdf-device.c | 80 | ||||
-rw-r--r-- | source/pdf/pdf-op-run.c | 6 | ||||
-rw-r--r-- | source/xps/xps-path.c | 11 |
8 files changed, 1630 insertions, 506 deletions
diff --git a/source/fitz/draw-path.c b/source/fitz/draw-path.c index 8ad0b334..2e751222 100644 --- a/source/fitz/draw-path.c +++ b/source/fitz/draw-path.c @@ -67,67 +67,137 @@ bezier(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, bezier(ctx, gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); } -void -fz_flatten_fill_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_matrix *ctm, float flatness) +static void +quad(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, + float xa, float ya, + float xb, float yb, + float xc, float yc, int depth) { - float x1, y1, x2, y2, x3, y3; - float cx = 0; - float cy = 0; - float bx = 0; - float by = 0; - int i = 0, k = 0; - - while (i < path->cmd_len) + float dmax; + float xab, yab; + float xbc, ybc; + float xabc, yabc; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xc - xb)); + dmax = fz_max(dmax, fz_abs(yc - yb)); + if (dmax < flatness || depth >= MAX_DEPTH) { - switch (path->cmds[i++]) - { - case FZ_MOVETO: - /* implicit closepath before moveto */ - if (cx != bx || cy != by) - line(ctx, gel, ctm, cx, cy, bx, by); - x1 = path->coords[k++]; - y1 = path->coords[k++]; - cx = bx = x1; - cy = by = y1; - break; - - case FZ_LINETO: - x1 = path->coords[k++]; - y1 = path->coords[k++]; - line(ctx, gel, ctm, cx, cy, x1, y1); - cx = x1; - cy = y1; - break; - - case FZ_CURVETO: - x1 = path->coords[k++]; - y1 = path->coords[k++]; - x2 = path->coords[k++]; - y2 = path->coords[k++]; - x3 = path->coords[k++]; - y3 = path->coords[k++]; - bezier(ctx, gel, ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3, 0); - cx = x3; - cy = y3; - break; - - case FZ_CLOSE_PATH: - line(ctx, gel, ctm, cx, cy, bx, by); - cx = bx; - cy = by; - break; - } + line(ctx, gel, ctm, xa, ya, xc, yc); + return; } - if (cx != bx || cy != by) - line(ctx, gel, ctm, cx, cy, bx, by); + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + + xabc = xab + xbc; + yabc = yab + ybc; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + + quad(ctx, gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, depth + 1); + quad(ctx, gel, ctm, flatness, xabc, yabc, xbc, ybc, xc, yc, depth + 1); } -struct sctx +typedef struct { fz_gel *gel; const fz_matrix *ctm; float flatness; + fz_point b; + fz_point c; +} +flatten_arg; + +static void +flatten_moveto(fz_context *ctx, void *arg_, float x, float y) +{ + flatten_arg *arg = (flatten_arg *)arg_; + + /* implicit closepath before moveto */ + if (arg->c.x != arg->b.x || arg->c.y != arg->b.y) + line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); + arg->c.x = arg->b.x = x; + arg->c.y = arg->b.y = y; +} + +static void +flatten_lineto(fz_context *ctx, void *arg_, float x, float y) +{ + flatten_arg *arg = (flatten_arg *)arg_; + + line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, x, y); + arg->c.x = x; + arg->c.y = y; +} + +static void +flatten_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3) +{ + flatten_arg *arg = (flatten_arg *)arg_; + + bezier(ctx, arg->gel, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0); + arg->c.x = x3; + arg->c.y = y3; +} + +static void +flatten_quadto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2) +{ + flatten_arg *arg = (flatten_arg *)arg_; + + quad(ctx, arg->gel, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, 0); + arg->c.x = x2; + arg->c.y = y2; +} + +static void +flatten_close(fz_context *ctx, void *arg_) +{ + flatten_arg *arg = (flatten_arg *)arg_; + + line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); + arg->c.x = arg->b.x; + arg->c.y = arg->b.y; +} + +static const fz_path_processor flatten_proc = +{ + flatten_moveto, + flatten_lineto, + flatten_curveto, + flatten_close, + flatten_quadto +}; + +void +fz_flatten_fill_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_matrix *ctm, float flatness) +{ + flatten_arg arg; + + arg.gel = gel; + arg.ctm = ctm; + arg.flatness = flatness; + arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0; + + fz_process_path(ctx, &flatten_proc, &arg, path); + if (arg.c.x != arg.b.x || arg.c.y != arg.b.y) + line(ctx, gel, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y); +} + +typedef struct sctx +{ + fz_gel *gel; + const fz_matrix *ctm; + float flatness; + const fz_stroke_state *stroke; int linejoin; float linewidth; @@ -137,6 +207,7 @@ struct sctx int sn; int dot; int from_bezier; + fz_point cur; fz_rect rect; const float *dash_list; @@ -146,11 +217,12 @@ struct sctx int toggle, cap; int offset; float phase; - fz_point cur; -}; + fz_point dash_cur; + fz_point dash_beg; +} sctx; static void -fz_add_line(fz_context *ctx, struct sctx *s, float x0, float y0, float x1, float y1) +fz_add_line(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1) { float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e; float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f; @@ -160,7 +232,7 @@ fz_add_line(fz_context *ctx, struct sctx *s, float x0, float y0, float x1, float } static void -fz_add_arc(fz_context *ctx, struct sctx *s, +fz_add_arc(fz_context *ctx, sctx *s, float xc, float yc, float x0, float y0, float x1, float y1) @@ -204,19 +276,20 @@ fz_add_arc(fz_context *ctx, struct sctx *s, } static void -fz_add_line_stroke(fz_context *ctx, struct sctx *s, fz_point a, fz_point b) +fz_add_line_stroke(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by) { - float dx = b.x - a.x; - float dy = b.y - a.y; + float dx = bx - ax; + float dy = by - ay; float scale = s->linewidth / sqrtf(dx * dx + dy * dy); float dlx = dy * scale; float dly = -dx * scale; - fz_add_line(ctx, s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly); - fz_add_line(ctx, s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly); + + fz_add_line(ctx, s, ax - dlx, ay - dly, bx - dlx, by - dly); + fz_add_line(ctx, s, bx + dlx, by + dly, ax + dlx, ay + dly); } static void -fz_add_line_join(fz_context *ctx, struct sctx *s, fz_point a, fz_point b, fz_point c, int join_under) +fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, float cx, float cy, int join_under) { float miterlimit = s->miterlimit; float linewidth = s->linewidth; @@ -231,11 +304,11 @@ fz_add_line_join(fz_context *ctx, struct sctx *s, fz_point a, fz_point b, fz_poi float cross; float len0, len1; - dx0 = b.x - a.x; - dy0 = b.y - a.y; + dx0 = bx - ax; + dy0 = by - ay; - dx1 = c.x - b.x; - dy1 = c.y - b.y; + dx1 = cx - bx; + dy1 = cy - by; cross = dx1 * dy0 - dx0 * dy1; /* Ensure that cross >= 0 */ @@ -284,12 +357,12 @@ fz_add_line_join(fz_context *ctx, struct sctx *s, fz_point a, fz_point b, fz_poi if (join_under) { - fz_add_line(ctx, s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0); } else { - fz_add_line(ctx, s, b.x + dlx1, b.y + dly1, b.x, b.y); - fz_add_line(ctx, s, b.x, b.y, b.x + dlx0, b.y + dly0); + fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by); + fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0); } /* XPS miter joins are clipped at miterlength, rather than simply @@ -307,104 +380,118 @@ fz_add_line_join(fz_context *ctx, struct sctx *s, fz_point a, fz_point b, fz_poi dmx *= scale; dmy *= scale; k = (scale - linewidth * miterlimit / sqrtf(dmr2)) / (scale - 1); - t0x = b.x - dmx + k * (dmx - dlx0); - t0y = b.y - dmy + k * (dmy - dly0); - t1x = b.x - dmx + k * (dmx - dlx1); - t1y = b.y - dmy + k * (dmy - dly1); + t0x = bx - dmx + k * (dmx - dlx0); + t0y = by - dmy + k * (dmy - dly0); + t1x = bx - dmx + k * (dmx - dlx1); + t1y = by - dmy + k * (dmy - dly1); - fz_add_line(ctx, s, b.x - dlx0, b.y - dly0, t0x, t0y); + fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y); fz_add_line(ctx, s, t0x, t0y, t1x, t1y); - fz_add_line(ctx, s, t1x, t1y, b.x - dlx1, b.y - dly1); + fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1); } } else if (linejoin == FZ_LINEJOIN_MITER) if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) linejoin = FZ_LINEJOIN_BEVEL; - if (linejoin == FZ_LINEJOIN_MITER) + switch (linejoin) { + case FZ_LINEJOIN_MITER_XPS: + break; + + case FZ_LINEJOIN_MITER: scale = linewidth * linewidth / dmr2; dmx *= scale; dmy *= scale; - fz_add_line(ctx, s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); - fz_add_line(ctx, s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); - } + fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy); + fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1); + break; - if (linejoin == FZ_LINEJOIN_BEVEL) - { - fz_add_line(ctx, s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); - } + case FZ_LINEJOIN_BEVEL: + fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1); + break; - if (linejoin == FZ_LINEJOIN_ROUND) - { - fz_add_arc(ctx, s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); + case FZ_LINEJOIN_ROUND: + fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1); + break; + + default: + assert("Invalid line join" == NULL); } } static void -fz_add_line_cap(fz_context *ctx, struct sctx *s, fz_point a, fz_point b, fz_linecap linecap) +fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, fz_linecap linecap) { float flatness = s->flatness; float linewidth = s->linewidth; - float dx = b.x - a.x; - float dy = b.y - a.y; + float dx = bx - ax; + float dy = by - ay; float scale = linewidth / sqrtf(dx * dx + dy * dy); float dlx = dy * scale; float dly = -dx * scale; - if (linecap == FZ_LINECAP_BUTT) - fz_add_line(ctx, s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); + switch (linecap) + { + case FZ_LINECAP_BUTT: + fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly); + break; - if (linecap == FZ_LINECAP_ROUND) + case FZ_LINECAP_ROUND: { int i; int n = ceilf((float)M_PI / (2.0f * (float)M_SQRT2 * sqrtf(flatness / linewidth))); - float ox = b.x - dlx; - float oy = b.y - dly; + float ox = bx - dlx; + float oy = by - dly; for (i = 1; i < n; i++) { float theta = (float)M_PI * i / n; float cth = cosf(theta); float sth = sinf(theta); - float nx = b.x - dlx * cth - dly * sth; - float ny = b.y - dly * cth + dlx * sth; + float nx = bx - dlx * cth - dly * sth; + float ny = by - dly * cth + dlx * sth; fz_add_line(ctx, s, ox, oy, nx, ny); ox = nx; oy = ny; } - fz_add_line(ctx, s, ox, oy, b.x + dlx, b.y + dly); + fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly); + break; } - if (linecap == FZ_LINECAP_SQUARE) - { - fz_add_line(ctx, s, b.x - dlx, b.y - dly, - b.x - dlx - dly, b.y - dly + dlx); - fz_add_line(ctx, s, b.x - dlx - dly, b.y - dly + dlx, - b.x + dlx - dly, b.y + dly + dlx); - fz_add_line(ctx, s, b.x + dlx - dly, b.y + dly + dlx, - b.x + dlx, b.y + dly); - } + case FZ_LINECAP_SQUARE: + fz_add_line(ctx, s, bx - dlx, by - dly, + bx - dlx - dly, by - dly + dlx); + fz_add_line(ctx, s, bx - dlx - dly, by - dly + dlx, + bx + dlx - dly, by + dly + dlx); + fz_add_line(ctx, s, bx + dlx - dly, by + dly + dlx, + bx + dlx, by + dly); + break; - if (linecap == FZ_LINECAP_TRIANGLE) + case FZ_LINECAP_TRIANGLE: { float mx = -dly; float my = dlx; - fz_add_line(ctx, s, b.x - dlx, b.y - dly, b.x + mx, b.y + my); - fz_add_line(ctx, s, b.x + mx, b.y + my, b.x + dlx, b.y + dly); + fz_add_line(ctx, s, bx - dlx, by - dly, bx + mx, by + my); + fz_add_line(ctx, s, bx + mx, by + my, bx + dlx, by + dly); + break; + } + + default: + assert("Invalid line cap" == NULL); } } static void -fz_add_line_dot(fz_context *ctx, struct sctx *s, fz_point a) +fz_add_line_dot(fz_context *ctx, sctx *s, float ax, float ay) { float flatness = s->flatness; float linewidth = s->linewidth; int n = ceilf((float)M_PI / ((float)M_SQRT2 * sqrtf(flatness / linewidth))); - float ox = a.x - linewidth; - float oy = a.y; + float ox = ax - linewidth; + float oy = ay; int i; for (i = 1; i < n; i++) @@ -412,45 +499,47 @@ fz_add_line_dot(fz_context *ctx, struct sctx *s, fz_point a) float theta = (float)M_PI * 2 * i / n; float cth = cosf(theta); float sth = sinf(theta); - float nx = a.x - cth * linewidth; - float ny = a.y + sth * linewidth; + float nx = ax - cth * linewidth; + float ny = ay + sth * linewidth; fz_add_line(ctx, s, ox, oy, nx, ny); ox = nx; oy = ny; } - fz_add_line(ctx, s, ox, oy, a.x - linewidth, a.y); + fz_add_line(ctx, s, ox, oy, ax - linewidth, ay); } static void -fz_stroke_flush(fz_context *ctx, struct sctx *s, fz_linecap start_cap, fz_linecap end_cap) +fz_stroke_flush(fz_context *ctx, sctx *s, fz_linecap start_cap, fz_linecap end_cap) { if (s->sn == 2) { - fz_add_line_cap(ctx, s, s->beg[1], s->beg[0], start_cap); - fz_add_line_cap(ctx, s, s->seg[0], s->seg[1], end_cap); + fz_add_line_cap(ctx, s, s->beg[1].x, s->beg[1].y, s->beg[0].x, s->beg[0].y, start_cap); + fz_add_line_cap(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, end_cap); } else if (s->dot) { - fz_add_line_dot(ctx, s, s->beg[0]); + fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y); } } static void -fz_stroke_moveto(fz_context *ctx, struct sctx *s, fz_point cur) +fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y) { - s->seg[0] = cur; - s->beg[0] = cur; + struct sctx *s = (struct sctx *)s_; + + s->seg[0].x = s->beg[0].x = x; + s->seg[0].y = s->beg[0].y = y; s->sn = 1; s->dot = 0; s->from_bezier = 0; } static void -fz_stroke_lineto(fz_context *ctx, struct sctx *s, fz_point cur, int from_bezier) +fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier) { - float dx = cur.x - s->seg[s->sn-1].x; - float dy = cur.y - s->seg[s->sn-1].y; + float dx = x - s->seg[s->sn-1].x; + float dy = y - s->seg[s->sn-1].y; if (dx * dx + dy * dy < FLT_EPSILON) { @@ -459,37 +548,38 @@ fz_stroke_lineto(fz_context *ctx, struct sctx *s, fz_point cur, int from_bezier) return; } - fz_add_line_stroke(ctx, s, s->seg[s->sn-1], cur); + fz_add_line_stroke(ctx, s, s->seg[s->sn-1].x, s->seg[s->sn-1].y, x, y); if (s->sn == 2) { - fz_add_line_join(ctx, s, s->seg[0], s->seg[1], cur, s->from_bezier & from_bezier); + fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, x, y, s->from_bezier & from_bezier); s->seg[0] = s->seg[1]; - s->seg[1] = cur; + s->seg[1].x = x; + s->seg[1].y = y; } else { - s->seg[1] = cur; - s->beg[1] = cur; + s->seg[1].x = s->beg[1].x = x; + s->seg[1].y = s->beg[1].y = y; s->sn = 2; } s->from_bezier = from_bezier; } static void -fz_stroke_closepath(fz_context *ctx, struct sctx *s) +fz_stroke_closepath(fz_context *ctx, sctx *s) { if (s->sn == 2) { - fz_stroke_lineto(ctx, s, s->beg[0], 0); + 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], s->beg[0], s->beg[1], 0); + 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], s->beg[0], s->beg[1], 0); + 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); } else if (s->dot) { - fz_add_line_dot(ctx, s, s->beg[0]); + fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y); } s->seg[0] = s->beg[0]; @@ -520,10 +610,7 @@ fz_stroke_bezier(fz_context *ctx, struct sctx *s, dmax = fz_max(dmax, fz_abs(yd - yc)); if (dmax < s->flatness || depth >= MAX_DEPTH) { - fz_point p; - p.x = xd; - p.y = yd; - fz_stroke_lineto(ctx, s, p, 1); + fz_stroke_lineto(ctx, s, xd, yd, 1); return; } @@ -555,13 +642,109 @@ fz_stroke_bezier(fz_context *ctx, struct sctx *s, fz_stroke_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); } +static void +fz_stroke_quad(fz_context *ctx, struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, int depth) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xabc, yabc; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xc - xb)); + dmax = fz_max(dmax, fz_abs(yc - yb)); + if (dmax < s->flatness || depth >= MAX_DEPTH) + { + fz_stroke_lineto(ctx, s, xc, yc, 1); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + + xabc = xab + xbc; + yabc = yab + ybc; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + + fz_stroke_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1); + fz_stroke_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1); +} + +static void +stroke_moveto(fz_context *ctx, void *s_, float x, float y) +{ + sctx *s = (sctx *)s_; + + fz_stroke_flush(ctx, s, s->stroke->start_cap, s->stroke->end_cap); + fz_stroke_moveto(ctx, s, x, y); + s->cur.x = x; + s->cur.y = y; +} + +static void +stroke_lineto(fz_context *ctx, void *s_, float x, float y) +{ + sctx *s = (sctx *)s_; + + fz_stroke_lineto(ctx, s, x, y, 0); + s->cur.x = x; + s->cur.y = y; +} + +static void +stroke_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3) +{ + sctx *s = (sctx *)s_; + + fz_stroke_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0); + s->cur.x = x3; + s->cur.y = y3; +} + +static void +stroke_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2) +{ + sctx *s = (sctx *)s_; + + fz_stroke_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0); + s->cur.x = x2; + s->cur.y = y2; +} + +static void +stroke_close(fz_context *ctx, void *s_) +{ + sctx *s = (sctx *)s_; + + fz_stroke_closepath(ctx, s); +} + +static const fz_path_processor stroke_proc = +{ + stroke_moveto, + stroke_lineto, + stroke_curveto, + stroke_close, + stroke_quadto +}; + void fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) { struct sctx s; - fz_point p0, p1, p2, p3; - int i, k; + s.stroke = stroke; s.gel = gel; s.ctm = ctm; s.flatness = flatness; @@ -581,53 +764,15 @@ fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_str s.cap = stroke->start_cap; - if (path->cmd_len > 0 && path->cmds[0] != FZ_MOVETO) - return; - - p0.x = p0.y = 0; - - i = k = 0; - while (i < path->cmd_len) - { - switch (path->cmds[i++]) - { - case FZ_MOVETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - fz_stroke_flush(ctx, &s, stroke->start_cap, stroke->end_cap); - fz_stroke_moveto(ctx, &s, p1); - p0 = p1; - break; - - case FZ_LINETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - fz_stroke_lineto(ctx, &s, p1, 0); - p0 = p1; - break; - - case FZ_CURVETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - p2.x = path->coords[k++]; - p2.y = path->coords[k++]; - p3.x = path->coords[k++]; - p3.y = path->coords[k++]; - fz_stroke_bezier(ctx, &s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0); - p0 = p3; - break; - - case FZ_CLOSE_PATH: - fz_stroke_closepath(ctx, &s); - break; - } - } + s.cur.x = s.cur.y = 0; + s.stroke = stroke; + fz_process_path(ctx, &stroke_proc, &s, path); fz_stroke_flush(ctx, &s, stroke->start_cap, stroke->end_cap); } static void -fz_dash_moveto(fz_context *ctx, struct sctx *s, fz_point a, fz_linecap start_cap, fz_linecap end_cap) +fz_dash_moveto(fz_context *ctx, struct sctx *s, float x, float y) { s->toggle = 1; s->offset = 0; @@ -642,107 +787,114 @@ fz_dash_moveto(fz_context *ctx, struct sctx *s, fz_point a, fz_linecap start_cap s->offset = 0; } - s->cur = a; + s->dash_cur.x = x; + s->dash_cur.y = y; if (s->toggle) { - fz_stroke_flush(ctx, s, s->cap, end_cap); - s->cap = start_cap; - fz_stroke_moveto(ctx, s, a); + fz_stroke_flush(ctx, s, s->cap, s->stroke->end_cap); + s->cap = s->stroke->start_cap; + fz_stroke_moveto(ctx, s, x, y); } } static void -fz_dash_lineto(fz_context *ctx, struct sctx *s, fz_point b, int dash_cap, int from_bezier) +fz_dash_lineto(fz_context *ctx, struct sctx *s, float bx, float by, int from_bezier) { float dx, dy, d; float total, used, ratio, tail; - fz_point a; - fz_point m; - fz_point old_b; + float ax, ay; + float mx, my; + float old_bx, old_by; int n; + int dash_cap = s->stroke->dash_cap; - a = s->cur; - dx = b.x - a.x; - dy = b.y - a.y; + ax = s->dash_cur.x; + ay = s->dash_cur.y; + dx = bx - ax; + dy = by - ay; used = 0; tail = 0; total = sqrtf(dx * dx + dy * dy); /* If a is off screen, bring it onto the screen. First * horizontally... */ - if ((d = s->rect.x0 - a.x) > 0) + if ((d = s->rect.x0 - ax) > 0) { - if (b.x < s->rect.x0) + if (bx < s->rect.x0) { /* Entirely off screen */ tail = total; - old_b = b; + old_bx = bx; + old_by = by; goto adjust_for_tail; } - a.x = s->rect.x0; /* d > 0, dx > 0 */ + ax = s->rect.x0; /* d > 0, dx > 0 */ goto a_moved_horizontally; } - else if (d < 0 && (d = (s->rect.x1 - a.x)) < 0) + else if (d < 0 && (d = (s->rect.x1 - ax)) < 0) { - if (b.x > s->rect.x1) + if (bx > s->rect.x1) { /* Entirely off screen */ tail = total; - old_b = b; + old_bx = bx; + old_by = by; goto adjust_for_tail; } - a.x = s->rect.x1; /* d < 0, dx < 0 */ + ax = s->rect.x1; /* d < 0, dx < 0 */ a_moved_horizontally: /* d and dx have the same sign */ - a.y += dy * d/dx; + ay += dy * d/dx; used = total * d/dx; total -= used; - dx = b.x - a.x; - dy = b.y - a.y; + dx = bx - ax; + dy = by - ay; } /* Then vertically... */ - if ((d = s->rect.y0 - a.y) > 0) + if ((d = s->rect.y0 - ay) > 0) { - if (b.y < s->rect.y0) + if (by < s->rect.y0) { /* Entirely off screen */ tail = total; - old_b = b; + old_bx = bx; + old_by = by; goto adjust_for_tail; } - a.y = s->rect.y0; /* d > 0, dy > 0 */ + ay = s->rect.y0; /* d > 0, dy > 0 */ goto a_moved_vertically; } - else if (d < 0 && (d = (s->rect.y1 - a.y)) < 0) + else if (d < 0 && (d = (s->rect.y1 - ay)) < 0) { - if (b.y > s->rect.y1) + if (by > s->rect.y1) { /* Entirely off screen */ tail = total; - old_b = b; + old_bx = bx; + old_by = by; goto adjust_for_tail; } - a.y = s->rect.y1; /* d < 0, dy < 0 */ + ay = s->rect.y1; /* d < 0, dy < 0 */ a_moved_vertically: /* d and dy have the same sign */ - a.x += dx * d/dy; + ax += dx * d/dy; d = total * d/dy; total -= d; used += d; - dx = b.x - a.x; - dy = b.y - a.y; + dx = bx - ax; + dy = by - ay; } if (used != 0.0f) { /* Update the position in the dash array */ if (s->toggle) { - fz_stroke_lineto(ctx, s, a, from_bezier); + fz_stroke_lineto(ctx, s, ax, ay, from_bezier); } else { - fz_stroke_flush(ctx, s, s->cap, dash_cap); - s->cap = dash_cap; - fz_stroke_moveto(ctx, s, a); + fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap); + s->cap = s->stroke->dash_cap; + fz_stroke_moveto(ctx, s, ax, ay); } used += s->phase; n = used/s->dash_total; @@ -759,73 +911,77 @@ a_moved_vertically: /* d and dy have the same sign */ } if (s->toggle) { - fz_stroke_lineto(ctx, s, a, from_bezier); + fz_stroke_lineto(ctx, s, ax, ay, from_bezier); } else { - fz_stroke_flush(ctx, s, s->cap, dash_cap); - s->cap = dash_cap; - fz_stroke_moveto(ctx, s, a); + fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap); + s->cap = s->stroke->dash_cap; + fz_stroke_moveto(ctx, s, ax, ay); } s->phase = used; used = 0; } - /* Now if b.x is off screen, bring it back */ - if ((d = b.x - s->rect.x0) < 0) + /* Now if bx is off screen, bring it back */ + if ((d = bx - s->rect.x0) < 0) { - old_b = b; - b.x = s->rect.x0; /* d < 0, dx < 0 */ + old_bx = bx; + old_by = by; + bx = s->rect.x0; /* d < 0, dx < 0 */ goto b_moved_horizontally; } - else if (d > 0 && (d = (b.x - s->rect.x1)) > 0) + else if (d > 0 && (d = (bx - s->rect.x1)) > 0) { - old_b = b; - b.x = s->rect.x1; /* d > 0, dx > 0 */ + old_bx = bx; + old_by = by; + bx = s->rect.x1; /* d > 0, dx > 0 */ b_moved_horizontally: /* d and dx have the same sign */ - b.y -= dy * d/dx; + by -= dy * d/dx; tail = total * d/dx; total -= tail; - dx = b.x - a.x; - dy = b.y - a.y; + dx = bx - ax; + dy = by - ay; } /* Then vertically... */ - if ((d = b.y - s->rect.y0) < 0) + if ((d = by - s->rect.y0) < 0) { - old_b = b; - b.y = s->rect.y0; /* d < 0, dy < 0 */ + old_bx = bx; + old_by = by; + by = s->rect.y0; /* d < 0, dy < 0 */ goto b_moved_vertically; } - else if (d > 0 && (d = (b.y - s->rect.y1)) > 0) + else if (d > 0 && (d = (by - s->rect.y1)) > 0) { float t; - old_b = b; - b.y = s->rect.y1; /* d > 0, dy > 0 */ + old_bx = bx; + old_by = by; + by = s->rect.y1; /* d > 0, dy > 0 */ b_moved_vertically: /* d and dy have the same sign */ - b.x -= dx * d/dy; + bx -= dx * d/dy; t = total * d/dy; tail += t; total -= t; - dx = b.x - a.x; - dy = b.y - a.y; + dx = bx - ax; + dy = by - ay; } while (total - used > s->dash_list[s->offset] - s->phase) { used += s->dash_list[s->offset] - s->phase; ratio = used / total; - m.x = a.x + ratio * dx; - m.y = a.y + ratio * dy; + mx = ax + ratio * dx; + my = ay + ratio * dy; if (s->toggle) { - fz_stroke_lineto(ctx, s, m, from_bezier); + fz_stroke_lineto(ctx, s, mx, my, from_bezier); } else { fz_stroke_flush(ctx, s, s->cap, dash_cap); s->cap = dash_cap; - fz_stroke_moveto(ctx, s, m); + fz_stroke_moveto(ctx, s, mx, my); } s->toggle = !s->toggle; @@ -839,27 +995,29 @@ b_moved_vertically: /* d and dy have the same sign */ if (tail == 0.0f) { - s->cur = b; + s->dash_cur.x = bx; + s->dash_cur.y = by; if (s->toggle) { - fz_stroke_lineto(ctx, s, b, from_bezier); + fz_stroke_lineto(ctx, s, bx, by, from_bezier); } } else { adjust_for_tail: - s->cur = old_b; + s->dash_cur.x = old_bx; + s->dash_cur.y = old_by; /* Update the position in the dash array */ if (s->toggle) { - fz_stroke_lineto(ctx, s, old_b, from_bezier); + fz_stroke_lineto(ctx, s, old_bx, old_by, from_bezier); } else { fz_stroke_flush(ctx, s, s->cap, dash_cap); s->cap = dash_cap; - fz_stroke_moveto(ctx, s, old_b); + fz_stroke_moveto(ctx, s, old_bx, old_by); } tail += s->phase; n = tail/s->dash_total; @@ -876,13 +1034,13 @@ adjust_for_tail: } if (s->toggle) { - fz_stroke_lineto(ctx, s, old_b, from_bezier); + fz_stroke_lineto(ctx, s, old_bx, old_by, from_bezier); } else { fz_stroke_flush(ctx, s, s->cap, dash_cap); s->cap = dash_cap; - fz_stroke_moveto(ctx, s, old_b); + fz_stroke_moveto(ctx, s, old_bx, old_by); } s->phase = tail; } @@ -893,8 +1051,7 @@ fz_dash_bezier(fz_context *ctx, struct sctx *s, float xa, float ya, float xb, float yb, float xc, float yc, - float xd, float yd, int depth, - int dash_cap) + float xd, float yd, int depth) { float dmax; float xab, yab; @@ -911,10 +1068,7 @@ fz_dash_bezier(fz_context *ctx, struct sctx *s, dmax = fz_max(dmax, fz_abs(yd - yc)); if (dmax < s->flatness || depth >= MAX_DEPTH) { - fz_point p; - p.x = xd; - p.y = yd; - fz_dash_lineto(ctx, s, p, dash_cap, 1); + fz_dash_lineto(ctx, s, xd, yd, 1); return; } @@ -942,19 +1096,117 @@ fz_dash_bezier(fz_context *ctx, struct sctx *s, xabcd *= 0.125f; yabcd *= 0.125f; - fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1, dash_cap); - fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1, dash_cap); + fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); + fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); +} + +static void +fz_dash_quad(fz_context *ctx, struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, int depth) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xabc, yabc; + + /* termination check */ + dmax = fz_abs(xa - xb); + dmax = fz_max(dmax, fz_abs(ya - yb)); + dmax = fz_max(dmax, fz_abs(xc - xb)); + dmax = fz_max(dmax, fz_abs(yc - yb)); + if (dmax < s->flatness || depth >= MAX_DEPTH) + { + fz_dash_lineto(ctx, s, xc, yc, 1); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + + xabc = xab + xbc; + yabc = yab + ybc; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + + fz_dash_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1); + fz_dash_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1); +} + +static void +dash_moveto(fz_context *ctx, void *s_, float x, float y) +{ + sctx *s = (sctx *)s_; + + fz_dash_moveto(ctx, s, x, y); + s->dash_beg.x = s->cur.x = x; + s->dash_beg.y = s->cur.y = y; +} + +static void +dash_lineto(fz_context *ctx, void *s_, float x, float y) +{ + sctx *s = (sctx *)s_; + + fz_dash_lineto(ctx, s, x, y, 0); + s->cur.x = x; + s->cur.y = y; +} + +static void +dash_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3) +{ + sctx *s = (sctx *)s_; + + fz_dash_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0); + s->cur.x = x3; + s->cur.y = y3; +} + +static void +dash_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2) +{ + sctx *s = (sctx *)s_; + + fz_dash_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0); + s->cur.x = x2; + s->cur.y = y2; +} + +static void +dash_close(fz_context *ctx, void *s_) +{ + sctx *s = (sctx *)s_; + + fz_dash_lineto(ctx, s, s->dash_beg.x, s->dash_beg.y, 0); + s->cur.x = s->dash_beg.x; + s->cur.y = s->dash_beg.y; } +static const fz_path_processor dash_proc = +{ + dash_moveto, + dash_lineto, + dash_curveto, + dash_close, + dash_quadto +}; + void fz_flatten_dash_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) { struct sctx s; - fz_point p0, p1, p2, p3, beg; float phase_len, max_expand; - int i, k; + int i; fz_matrix inv; + s.stroke = stroke; s.gel = gel; s.ctm = ctm; s.flatness = flatness; @@ -974,9 +1226,6 @@ fz_flatten_dash_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_strok s.cap = stroke->start_cap; - if (path->cmd_len > 0 && path->cmds[0] != FZ_MOVETO) - return; - phase_len = 0; for (i = 0; i < stroke->dash_len; i++) phase_len += stroke->dash_list[i]; @@ -999,44 +1248,7 @@ fz_flatten_dash_path(fz_context *ctx, fz_gel *gel, fz_path *path, const fz_strok } s.dash_total = phase_len; - p0.x = p0.y = 0; - i = k = 0; - - while (i < path->cmd_len) - { - switch (path->cmds[i++]) - { - case FZ_MOVETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - fz_dash_moveto(ctx, &s, p1, stroke->start_cap, stroke->end_cap); - beg = p0 = p1; - break; - - case FZ_LINETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - fz_dash_lineto(ctx, &s, p1, stroke->dash_cap, 0); - p0 = p1; - break; - - case FZ_CURVETO: - p1.x = path->coords[k++]; - p1.y = path->coords[k++]; - p2.x = path->coords[k++]; - p2.y = path->coords[k++]; - p3.x = path->coords[k++]; - p3.y = path->coords[k++]; - fz_dash_bezier(ctx, &s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, 0, stroke->dash_cap); - p0 = p3; - break; - - case FZ_CLOSE_PATH: - fz_dash_lineto(ctx, &s, beg, stroke->dash_cap, 0); - p0 = p1 = beg; - break; - } - } - + s.cur.x = s.cur.y = 0; + fz_process_path(ctx, &dash_proc, &s, path); fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap); } diff --git a/source/fitz/font.c b/source/fitz/font.c index 3d4a06e5..64cea107 100644 --- a/source/fitz/font.c +++ b/source/fitz/font.c @@ -892,18 +892,11 @@ static int conic_to(const FT_Vector *c, const FT_Vector *p, void *cc_) fz_context *ctx = cc->ctx; fz_path *path = cc->path; fz_point ct, pt; - fz_point s, c1, c2; fz_transform_point_xy(&ct, &cc->trm, c->x, c->y); fz_transform_point_xy(&pt, &cc->trm, p->x, p->y); - s = fz_currentpoint(ctx, path); - c1.x = (s.x + ct.x * 2) / 3; - c1.y = (s.y + ct.y * 2) / 3; - c2.x = (pt.x + ct.x * 2) / 3; - c2.y = (pt.y + ct.y * 2) / 3; - - fz_curveto(ctx, path, c1.x, c1.y, c2.x, c2.y, pt.x, pt.y); + fz_quadto(ctx, path, ct.x, ct.y, pt.x, pt.y); return 0; } diff --git a/source/fitz/path.c b/source/fitz/path.c index 8deb85b8..fed574da 100644 --- a/source/fitz/path.c +++ b/source/fitz/path.c @@ -1,6 +1,54 @@ #include <assert.h> #include "mupdf/fitz.h" +// Thoughts for further optimisations: +// All paths start with MoveTo. We could probably avoid most cases where +// we store that. The next thing after a close must be a move. +// Commands are MOVE, LINE, HORIZ, VERT, DEGEN, CURVE, CURVEV, CURVEY, QUAD, RECT. +// We'd need to drop 2 to get us down to 3 bits. +// Commands can be followed by CLOSE. Use 1 bit for close. +// PDF 'RECT' implies close according to the spec, but I suspect +// we can ignore this as filling closes implicitly. +// We use a single bit in the path header to tell us whether we have +// a trailing move. Trailing moves can always be stripped when path +// construction completes. + +typedef enum fz_path_command_e +{ + FZ_MOVETO = 'M', + FZ_LINETO = 'L', + FZ_DEGENLINETO = 'D', + FZ_CURVETO = 'C', + FZ_CURVETOV = 'V', + FZ_CURVETOY = 'Y', + FZ_HORIZTO = 'H', + FZ_VERTTO = 'I', + FZ_QUADTO = 'Q', + FZ_RECTTO = 'R', + FZ_MOVETOCLOSE = 'm', + FZ_LINETOCLOSE = 'l', + FZ_DEGENLINETOCLOSE = 'd', + FZ_CURVETOCLOSE = 'c', + FZ_CURVETOVCLOSE = 'v', + FZ_CURVETOYCLOSE = 'y', + FZ_HORIZTOCLOSE = 'h', + FZ_VERTTOCLOSE = 'i', + FZ_QUADTOCLOSE = 'q', +} fz_path_item_kind; + +struct fz_path_s +{ + int refs; + int cmd_len, cmd_cap; + unsigned char *cmds; + int coord_len, coord_cap; + float *coords; + fz_point current; + fz_point begin; +}; + +#define LAST_CMD(path) ((path)->cmd_len > 0 ? (path)->cmds[(path)->cmd_len-1] : 0) + fz_path * fz_new_path(fz_context *ctx) { @@ -8,7 +56,6 @@ fz_new_path(fz_context *ctx) path = fz_malloc_struct(ctx, fz_path); path->refs = 1; - path->last_cmd = 0; path->current.x = 0; path->current.y = 0; path->begin.x = 0; @@ -50,7 +97,6 @@ push_cmd(fz_context *ctx, fz_path *path, int cmd) } path->cmds[path->cmd_len++] = cmd; - path->last_cmd = cmd; } static void @@ -70,6 +116,24 @@ push_coord(fz_context *ctx, fz_path *path, float x, float y) path->current.y = y; } +static void +push_ord(fz_context *ctx, fz_path *path, float xy, int isx) +{ + if (path->coord_len + 1 >= path->coord_cap) + { + int new_coord_cap = fz_maxi(32, path->coord_cap * 2); + path->coords = fz_resize_array(ctx, path->coords, new_coord_cap, sizeof(float)); + path->coord_cap = new_coord_cap; + } + + path->coords[path->coord_len++] = xy; + + if (isx) + path->current.x = xy; + else + path->current.y = xy; +} + fz_point fz_currentpoint(fz_context *ctx, fz_path *path) { @@ -79,7 +143,7 @@ fz_currentpoint(fz_context *ctx, fz_path *path) void fz_moveto(fz_context *ctx, fz_path *path, float x, float y) { - if (path->cmd_len > 0 && path->last_cmd == FZ_MOVETO) + if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO) { /* Collapse moveto followed by moveto. */ path->coords[path->coord_len-2] = x; @@ -108,12 +172,34 @@ fz_lineto(fz_context *ctx, fz_path *path, float x, float y) return; } - /* Anything other than MoveTo followed by LineTo the same place is a nop */ - if (path->last_cmd != FZ_MOVETO && x0 == x && y0 == y) + /* (Anything other than MoveTo) followed by (LineTo the same place) is a nop */ + if (LAST_CMD(path) != FZ_MOVETO && x0 == x && y0 == y) return; - push_cmd(ctx, path, FZ_LINETO); - push_coord(ctx, path, x, y); + if (x0 == x) + { + if (y0 == y) + { + if (LAST_CMD(path) != FZ_MOVETO) + return; + push_cmd(ctx, path, FZ_DEGENLINETO); + } + else + { + push_cmd(ctx, path, FZ_VERTTO); + push_ord(ctx, path, y, 0); + } + } + else if (y0 == y) + { + push_cmd(ctx, path, FZ_HORIZTO); + push_ord(ctx, path, x, 1); + } + else + { + push_cmd(ctx, path, FZ_LINETO); + push_coord(ctx, path, x, y); + } } void @@ -137,23 +223,29 @@ fz_curveto(fz_context *ctx, fz_path *path, if (x2 == x3 && y2 == y3) { /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */ - if (x1 == x2 && y1 == y2 && path->last_cmd != FZ_MOVETO) + if (x1 == x2 && y1 == y2 && LAST_CMD(path) != FZ_MOVETO) return; /* Otherwise a line will suffice */ fz_lineto(ctx, path, x3, y3); - return; } - if (x1 == x2 && y1 == y2) + else if (x1 == x2 && y1 == y2) { /* A line will suffice */ fz_lineto(ctx, path, x3, y3); - return; } + else + fz_curvetov(ctx, path, x2, y2, x3, y3); + return; } - else if (x1 == x2 && y1 == y2 && x2 == x3 && y2 == y3) + else if (x2 == x3 && y2 == y3) { - /* A line will suffice */ - fz_lineto(ctx, path, x3, y3); + if (x1 == x2 && y1 == y2) + { + /* A line will suffice */ + fz_lineto(ctx, path, x3, y3); + } + else + fz_curvetoy(ctx, path, x1, y1, x3, y3); return; } @@ -164,40 +256,170 @@ fz_curveto(fz_context *ctx, fz_path *path, } void +fz_quadto(fz_context *ctx, fz_path *path, + float x1, float y1, + float x2, float y2) +{ + float x0 = path->current.x; + float y0 = path->current.y; + + if (path->cmd_len == 0) + { + fz_warn(ctx, "quadto with no current point"); + return; + } + + /* Check for degenerate cases: */ + if ((x0 == x1 && y0 == y1) || (x1 == x2 && y1 == y2)) + { + if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO) + return; + /* A line will suffice */ + fz_lineto(ctx, path, x2, y2); + return; + } + + push_cmd(ctx, path, FZ_QUADTO); + push_coord(ctx, path, x1, y1); + push_coord(ctx, path, x2, y2); +} + +void fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3) { - float x1 = path->current.x; - float y1 = path->current.y; + float x0 = path->current.x; + float y0 = path->current.y; if (path->cmd_len == 0) { - fz_warn(ctx, "curvetov with no current point"); + fz_warn(ctx, "curveto with no current point"); return; } - fz_curveto(ctx, path, x1, y1, x2, y2, x3, y3); + /* Check for degenerate cases: */ + if (x2 == x3 && y2 == y3) + { + /* If (x0,y0)==(x2,y2) and prev wasn't a moveto, then skip */ + if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO) + return; + /* Otherwise a line will suffice */ + fz_lineto(ctx, path, x3, y3); + } + else if (x0 == x2 && y0 == y2) + { + /* A line will suffice */ + fz_lineto(ctx, path, x3, y3); + } + + push_cmd(ctx, path, FZ_CURVETOV); + push_coord(ctx, path, x2, y2); + push_coord(ctx, path, x3, y3); } void fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3) { - fz_curveto(ctx, path, x1, y1, x3, y3, x3, y3); + float x0 = path->current.x; + float y0 = path->current.y; + + if (path->cmd_len == 0) + { + fz_warn(ctx, "curveto with no current point"); + return; + } + + /* Check for degenerate cases: */ + if (x1 == x3 && y1 == y3) + { + /* If (x0,y0)==(x1,y1) and prev wasn't a moveto, then skip */ + if (x0 == x1 && y0 == y1 && LAST_CMD(path) != FZ_MOVETO) + return; + /* Otherwise a line will suffice */ + fz_lineto(ctx, path, x3, y3); + } + + push_cmd(ctx, path, FZ_CURVETOY); + push_coord(ctx, path, x1, y1); + push_coord(ctx, path, x3, y3); } void fz_closepath(fz_context *ctx, fz_path *path) { + uint8_t rep; + if (path->cmd_len == 0) { fz_warn(ctx, "closepath with no current point"); return; } - /* CLOSE following a CLOSE is a NOP */ - if (path->last_cmd == FZ_CLOSE_PATH) + switch(LAST_CMD(path)) + { + case FZ_MOVETO: + rep = FZ_MOVETOCLOSE; + break; + case FZ_LINETO: + rep = FZ_LINETOCLOSE; + break; + case FZ_DEGENLINETO: + rep = FZ_DEGENLINETOCLOSE; + break; + case FZ_CURVETO: + rep = FZ_CURVETOCLOSE; + break; + case FZ_CURVETOV: + rep = FZ_CURVETOVCLOSE; + break; + case FZ_CURVETOY: + rep = FZ_CURVETOYCLOSE; + break; + case FZ_HORIZTO: + rep = FZ_HORIZTOCLOSE; + break; + case FZ_VERTTO: + rep = FZ_VERTTOCLOSE; + break; + case FZ_QUADTO: + rep = FZ_QUADTOCLOSE; + break; + case FZ_RECTTO: + /* RectTo implies close */ + return; + case FZ_MOVETOCLOSE: + case FZ_LINETOCLOSE: + case FZ_DEGENLINETOCLOSE: + case FZ_CURVETOCLOSE: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_HORIZTOCLOSE: + case FZ_VERTTOCLOSE: + case FZ_QUADTOCLOSE: + /* CLOSE following a CLOSE is a NOP */ return; + case 0: + /* Closing an empty path is a NOP */ + return; + } + + path->cmds[path->cmd_len-1] = rep; + + path->current = path->begin; +} + +void +fz_rectto(fz_context *ctx, fz_path *path, float x1, float y1, float x2, float y2) +{ + if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO) + { + /* Collapse moveto followed by rectto. */ + path->coord_len -= 2; + path->cmd_len--; + } - push_cmd(ctx, path, FZ_CLOSE_PATH); + push_cmd(ctx, path, FZ_RECTTO); + push_coord(ctx, path, x1, y1); + push_coord(ctx, path, x2, y2); path->current = path->begin; } @@ -211,67 +433,334 @@ static inline fz_rect *bound_expand(fz_rect *r, const fz_point *p) return r; } -fz_rect * -fz_bound_path(fz_context *ctx, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *r) +void fz_process_path(fz_context *ctx, const fz_path_processor *proc, void *arg, const fz_path *path) { - fz_point p; - int i = 0, k = 0; - - /* If the path is empty, return the empty rectangle here - don't wait - * for it to be expanded in the stroked case below. - * A path must start with a moveto - and if that's all there is - * then the path is empty. */ - if (path->cmd_len == 0 || path->cmd_len == 1) - { - *r = fz_empty_rect; - return r; - } + int i, k; + float x, y, sx, sy; - /* Initial moveto point */ - p.x = path->coords[0]; - p.y = path->coords[1]; - fz_transform_point(&p, ctm); - r->x0 = r->x1 = p.x; - r->y0 = r->y1 = p.y; + if (path->cmd_len == 0) + return; - while (i < path->cmd_len) + for (k=0, i = 0; i < path->cmd_len; i++) { - switch (path->cmds[i++]) + uint8_t cmd = path->cmds[i]; + + switch (cmd) { case FZ_CURVETO: - p.x = path->coords[k++]; - p.y = path->coords[k++]; - bound_expand(r, fz_transform_point(&p, ctm)); - p.x = path->coords[k++]; - p.y = path->coords[k++]; - bound_expand(r, fz_transform_point(&p, ctm)); - p.x = path->coords[k++]; - p.y = path->coords[k++]; - bound_expand(r, fz_transform_point(&p, ctm)); + case FZ_CURVETOCLOSE: + proc->curveto(ctx, arg, + path->coords[k], + path->coords[k+1], + path->coords[k+2], + path->coords[k+3], + x = path->coords[k+4], + y = path->coords[k+5]); + k += 6; + if (cmd == FZ_CURVETOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_CURVETOV: + case FZ_CURVETOVCLOSE: + if (proc->curvetov) + proc->curvetov(ctx, arg, + path->coords[k], + path->coords[k+1], + x = path->coords[k+2], + y = path->coords[k+3]); + else + { + proc->curveto(ctx, arg, + x, + y, + path->coords[k], + path->coords[k+1], + path->coords[k+2], + path->coords[k+3]); + x = path->coords[k+2]; + y = path->coords[k+3]; + } + k += 4; + if (cmd == FZ_CURVETOVCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_CURVETOY: + case FZ_CURVETOYCLOSE: + if (proc->curvetoy) + proc->curvetoy(ctx, arg, + path->coords[k], + path->coords[k+1], + x = path->coords[k+2], + y = path->coords[k+3]); + else + proc->curveto(ctx, arg, + path->coords[k], + path->coords[k+1], + path->coords[k+2], + path->coords[k+3], + x = path->coords[k+2], + y = path->coords[k+3]); + k += 4; + if (cmd == FZ_CURVETOYCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_QUADTO: + case FZ_QUADTOCLOSE: + if (proc->quadto) + proc->quadto(ctx, arg, + path->coords[k], + path->coords[k+1], + x = path->coords[k+2], + y = path->coords[k+3]); + else + { + float c2x = path->coords[k] * 2; + float c2y = path->coords[k+1] * 2; + float c1x = (x + c2x) / 3; + float c1y = (y + c2y) / 3; + x = path->coords[k+2]; + y = path->coords[k+3]; + c2x = (c2x + x) / 3; + c2y = (c2y + y) / 3; + + proc->curveto(ctx, arg, + c1x, + c1y, + c2x, + c2y, + x, + y); + } + k += 4; + if (cmd == FZ_QUADTOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } break; case FZ_MOVETO: - if (k + 2 == path->coord_len) + case FZ_MOVETOCLOSE: + proc->moveto(ctx, arg, + x = path->coords[k], + y = path->coords[k+1]); + k += 2; + sx = x; + sy = y; + if (cmd == FZ_MOVETOCLOSE) { - /* Trailing Moveto - cannot affect bbox */ - k += 2; - break; + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; } - /* fallthrough */ + break; case FZ_LINETO: - p.x = path->coords[k++]; - p.y = path->coords[k++]; - bound_expand(r, fz_transform_point(&p, ctm)); + case FZ_LINETOCLOSE: + proc->lineto(ctx, arg, + x = path->coords[k], + y = path->coords[k+1]); + k += 2; + if (cmd == FZ_LINETOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } break; - case FZ_CLOSE_PATH: + case FZ_HORIZTO: + case FZ_HORIZTOCLOSE: + proc->lineto(ctx, arg, + x = path->coords[k], + y); + k += 1; + if (cmd == FZ_HORIZTOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_VERTTO: + case FZ_VERTTOCLOSE: + proc->lineto(ctx, arg, + x, + y = path->coords[k]); + k += 1; + if (cmd == FZ_VERTTOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_DEGENLINETO: + case FZ_DEGENLINETOCLOSE: + proc->lineto(ctx, arg, + x, + y); + if (cmd == FZ_DEGENLINETOCLOSE) + { + if (proc->close) + proc->close(ctx, arg); + x = sx; + y = sy; + } + break; + case FZ_RECTTO: + if (proc->rectto) + { + proc->rectto(ctx, arg, + x = path->coords[k], + y = path->coords[k+1], + path->coords[k+2], + path->coords[k+3]); + } + else + { + proc->moveto(ctx, arg, + x = path->coords[k], + y = path->coords[k+1]); + proc->lineto(ctx, arg, + path->coords[k+2], + path->coords[k+1]); + proc->lineto(ctx, arg, + path->coords[k+2], + path->coords[k+3]); + proc->lineto(ctx, arg, + path->coords[k], + path->coords[k+3]); + if (proc->close) + proc->close(ctx, arg); + } + sx = x; + sy = y; + k += 4; break; } } +} + +typedef struct +{ + const fz_matrix *ctm; + fz_rect rect; + fz_point move; + int trailing_move; + int first; +} bound_path_arg; + +static void +bound_moveto(fz_context *ctx, void *arg_, float x, float y) +{ + bound_path_arg *arg = (bound_path_arg *)arg_; + + arg->move.x = x; + arg->move.y = y; + fz_transform_point(&arg->move, arg->ctm); + arg->trailing_move = 1; +} + +static void +bound_lineto(fz_context *ctx, void *arg_, float x, float y) +{ + bound_path_arg *arg = (bound_path_arg *)arg_; + fz_point p; + + p.x = x; + p.y = y; + fz_transform_point(&p, arg->ctm); + if (arg->first) + { + arg->rect.x0 = arg->rect.x1 = p.x; + arg->rect.y0 = arg->rect.y1 = p.y; + arg->first = 0; + } + else + bound_expand(&arg->rect, &p); + + if (arg->trailing_move) + { + arg->trailing_move = 0; + bound_expand(&arg->rect, &arg->move); + } +} + +static void +bound_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3) +{ + bound_path_arg *arg = (bound_path_arg *)arg_; + fz_point p; + + p.x = x1; + p.y = y1; + fz_transform_point(&p, arg->ctm); + if (arg->first) + { + arg->rect.x0 = arg->rect.x1 = p.x; + arg->rect.y0 = arg->rect.y1 = p.y; + arg->first = 0; + } + else + bound_expand(&arg->rect, &p); + p.x = x2; + p.y = y2; + bound_expand(&arg->rect, fz_transform_point(&p, arg->ctm)); + p.x = x3; + p.y = y3; + bound_expand(&arg->rect, fz_transform_point(&p, arg->ctm)); + if (arg->trailing_move) + { + arg->trailing_move = 0; + bound_expand(&arg->rect, &arg->move); + } +} + +static const fz_path_processor bound_path_proc = +{ + bound_moveto, + bound_lineto, + bound_curveto, + NULL +}; + +fz_rect * +fz_bound_path(fz_context *ctx, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *r) +{ + bound_path_arg arg; + + arg.ctm = ctm; + arg.rect = fz_empty_rect; + arg.trailing_move = 0; + arg.first = 1; - if (stroke) + fz_process_path(ctx, &bound_path_proc, &arg, path); + + if (!arg.first && stroke) { - fz_adjust_rect_for_stroke(ctx, r, stroke, ctm); + fz_adjust_rect_for_stroke(ctx, &arg.rect, stroke, ctm); } + *r = arg.rect; return r; } @@ -300,9 +789,371 @@ fz_adjust_rect_for_stroke(fz_context *ctx, fz_rect *r, const fz_stroke_state *st void fz_transform_path(fz_context *ctx, fz_path *path, const fz_matrix *ctm) { - int i; - for (i = 0; i < path->coord_len; i += 2) - fz_transform_point((fz_point *)&path->coords[i], ctm); + int i, k, n; + fz_point p, p1, p2, p3, q, s; + + if (ctm->b == 0 && ctm->c == 0) + { + /* Simple, in place transform */ + i = 0; + k = 0; + while (i < path->cmd_len) + { + uint8_t cmd = path->cmds[i]; + + switch (cmd) + { + case FZ_MOVETO: + case FZ_LINETO: + case FZ_MOVETOCLOSE: + case FZ_LINETOCLOSE: + n = 1; + break; + case FZ_DEGENLINETO: + case FZ_DEGENLINETOCLOSE: + n = 0; + break; + case FZ_CURVETO: + case FZ_CURVETOCLOSE: + n = 3; + break; + case FZ_RECTTO: + s.x = path->coords[k]; + s.y = path->coords[k+1]; + n = 2; + break; + case FZ_CURVETOV: + case FZ_CURVETOY: + case FZ_QUADTO: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_QUADTOCLOSE: + n = 2; + break; + case FZ_HORIZTO: + case FZ_HORIZTOCLOSE: + q.x = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.x; + n = 0; + break; + case FZ_VERTTO: + case FZ_VERTTOCLOSE: + q.y = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.y; + n = 0; + break; + default: + assert("Unknown path cmd" == NULL); + } + while (n > 0) + { + q.x = path->coords[k]; + q.y = path->coords[k+1]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.x; + path->coords[k++] = p.y; + n--; + } + switch (cmd) + { + case FZ_MOVETO: + case FZ_MOVETOCLOSE: + s = q; + break; + case FZ_LINETOCLOSE: + case FZ_DEGENLINETOCLOSE: + case FZ_CURVETOCLOSE: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_QUADTOCLOSE: + case FZ_HORIZTOCLOSE: + case FZ_VERTTOCLOSE: + case FZ_RECTTO: + q = s; + break; + } + i++; + } + } + else if (ctm->a == 0 && ctm->d == 0) + { + /* In place transform with command rewriting */ + i = 0; + k = 0; + while (i < path->cmd_len) + { + uint8_t cmd = path->cmds[i]; + + switch (cmd) + { + case FZ_MOVETO: + case FZ_LINETO: + case FZ_MOVETOCLOSE: + case FZ_LINETOCLOSE: + n = 1; + break; + case FZ_DEGENLINETO: + case FZ_DEGENLINETOCLOSE: + n = 0; + break; + case FZ_CURVETO: + case FZ_CURVETOCLOSE: + n = 3; + break; + case FZ_RECTTO: + s.x = path->coords[k]; + s.y = path->coords[k+1]; + n = 2; + break; + case FZ_CURVETOV: + case FZ_CURVETOY: + case FZ_QUADTO: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_QUADTOCLOSE: + n = 2; + break; + case FZ_HORIZTO: + q.x = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.y; + path->cmds[i] = FZ_VERTTO; + n = 0; + break; + case FZ_HORIZTOCLOSE: + q.x = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.y; + path->cmds[i] = FZ_VERTTOCLOSE; + n = 0; + break; + case FZ_VERTTO: + q.y = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.x; + path->cmds[i] = FZ_HORIZTO; + n = 0; + break; + case FZ_VERTTOCLOSE: + q.y = path->coords[k]; + p = q; + fz_transform_point(&p, ctm); + path->coords[k++] = p.x; + path->cmds[i] = FZ_HORIZTOCLOSE; + n = 0; + break; + default: + assert("Unknown path cmd" == NULL); + } + while (n > 0) + { + p.x = path->coords[k]; + p.y = path->coords[k+1]; + q = p; + fz_transform_point(&p, ctm); + path->coords[k++] = p.x; + path->coords[k++] = p.y; + n--; + } + switch (cmd) + { + case FZ_MOVETO: + case FZ_MOVETOCLOSE: + s = q; + break; + case FZ_LINETOCLOSE: + case FZ_DEGENLINETOCLOSE: + case FZ_CURVETOCLOSE: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_QUADTOCLOSE: + case FZ_HORIZTOCLOSE: + case FZ_VERTTOCLOSE: + case FZ_RECTTO: + q = s; + break; + } + i++; + } + } + else + { + int extra_coord = 0; + int extra_cmd = 0; + int coord_read, coord_write, cmd_read, cmd_write; + + /* General case. Have to allow for rects/horiz/verts + * becoming non-rects/horiz/verts. */ + for (i = 0; i < path->cmd_len; i++) + { + uint8_t cmd = path->cmds[i]; + switch (cmd) + { + case FZ_HORIZTO: + case FZ_VERTTO: + case FZ_HORIZTOCLOSE: + case FZ_VERTTOCLOSE: + extra_coord += 1; + break; + case FZ_RECTTO: + extra_coord += 2; + extra_cmd += 3; + break; + default: + /* Do nothing */ + break; + } + } + if (path->cmd_len + extra_cmd < path->cmd_cap) + { + path->cmds = fz_resize_array(ctx, path->cmds, path->cmd_len + extra_cmd, sizeof(unsigned char)); + path->cmd_cap = path->cmd_len + extra_cmd; + } + if (path->coord_len + extra_coord < path->coord_cap) + { + path->coords = fz_resize_array(ctx, path->coords, path->coord_len + extra_coord, sizeof(float)); + path->coord_cap = path->coord_len + extra_coord; + } + memmove(path->cmds + extra_cmd, path->cmds, path->cmd_len * sizeof(unsigned char)); + path->cmd_len += extra_cmd; + memmove(path->coords + extra_coord, path->coords, path->coord_len * sizeof(float)); + path->coord_len += extra_coord; + + for (cmd_write = 0, cmd_read = extra_cmd, coord_write = 0, coord_read = extra_coord; cmd_read < path->cmd_len; i += 2) + { + uint8_t cmd = path->cmds[cmd_write++] = path->cmds[cmd_read++]; + + switch (cmd) + { + case FZ_MOVETO: + case FZ_LINETO: + case FZ_MOVETOCLOSE: + case FZ_LINETOCLOSE: + n = 1; + break; + case FZ_DEGENLINETO: + case FZ_DEGENLINETOCLOSE: + n = 0; + break; + case FZ_CURVETO: + case FZ_CURVETOCLOSE: + n = 3; + break; + case FZ_CURVETOV: + case FZ_CURVETOY: + case FZ_QUADTO: + case FZ_CURVETOVCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_QUADTOCLOSE: + n = 2; + break; + case FZ_RECTTO: + p.x = path->coords[coord_read++]; + p.y = path->coords[coord_read++]; + p2.x = path->coords[coord_read++]; + p2.y = path->coords[coord_read++]; + p1.x = p2.x; + p1.y = p.y; + p3.x = p.x; + p3.y = p2.y; + s = p; + fz_transform_point(&p, ctm); + fz_transform_point(&p1, ctm); + fz_transform_point(&p2, ctm); + fz_transform_point(&p3, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + path->coords[coord_write++] = p1.x; + path->coords[coord_write++] = p1.y; + path->coords[coord_write++] = p2.x; + path->coords[coord_write++] = p2.y; + path->coords[coord_write++] = p3.x; + path->coords[coord_write++] = p3.y; + path->cmds[cmd_write-1] = FZ_MOVETO; + path->cmds[cmd_write++] = FZ_LINETO; + path->cmds[cmd_write++] = FZ_LINETO; + path->cmds[cmd_write++] = FZ_LINETOCLOSE; + n = 0; + break; + case FZ_HORIZTO: + q.x = path->coords[coord_read++]; + p = q; + fz_transform_point(&p, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + path->cmds[cmd_write-1] = FZ_LINETO; + n = 0; + break; + case FZ_HORIZTOCLOSE: + p.x = path->coords[coord_read++]; + p.y = q.y; + fz_transform_point(&p, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + path->cmds[cmd_write-1] = FZ_LINETOCLOSE; + q = s; + n = 0; + break; + case FZ_VERTTO: + q.y = path->coords[coord_read++]; + p = q; + fz_transform_point(&p, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + path->cmds[cmd_write-1] = FZ_LINETO; + n = 0; + break; + case FZ_VERTTOCLOSE: + p.x = q.x; + p.y = path->coords[coord_read++]; + fz_transform_point(&p, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + path->cmds[cmd_write-1] = FZ_LINETOCLOSE; + q = s; + n = 0; + break; + default: + assert("Unknown path cmd" == NULL); + } + while (n > 0) + { + p.x = path->coords[coord_read++]; + p.y = path->coords[coord_read++]; + q = p; + fz_transform_point(&p, ctm); + path->coords[coord_write++] = p.x; + path->coords[coord_write++] = p.y; + n--; + } + switch (cmd) + { + case FZ_MOVETO: + case FZ_MOVETOCLOSE: + s = q; + break; + case FZ_LINETOCLOSE: + case FZ_DEGENLINETOCLOSE: + case FZ_CURVETOCLOSE: + case FZ_CURVETOYCLOSE: + case FZ_CURVETOVCLOSE: + case FZ_QUADTOCLOSE: + case FZ_HORIZTOCLOSE: + case FZ_VERTTOCLOSE: + case FZ_RECTTO: + q = s; + break; + } + } + } } void fz_trim_path(fz_context *ctx, fz_path *path) @@ -328,20 +1179,39 @@ fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent) int n; while (i < path->cmd_len) { + uint8_t cmd = path->cmds[i++]; + for (n = 0; n < indent; n++) fputc(' ', out); - switch (path->cmds[i++]) + switch (cmd) { case FZ_MOVETO: + case FZ_MOVETOCLOSE: x = path->coords[k++]; y = path->coords[k++]; - fprintf(out, "%g %g m\n", x, y); + fprintf(out, "%g %g m%s\n", x, y, cmd == FZ_MOVETOCLOSE ? " z" : ""); break; case FZ_LINETO: + case FZ_LINETOCLOSE: x = path->coords[k++]; y = path->coords[k++]; - fprintf(out, "%g %g l\n", x, y); + fprintf(out, "%g %g l%s\n", x, y, cmd == FZ_LINETOCLOSE ? " z" : ""); + break; + case FZ_DEGENLINETO: + case FZ_DEGENLINETOCLOSE: + fprintf(out, "d%s\n", cmd == FZ_DEGENLINETOCLOSE ? " z" : ""); + break; + case FZ_HORIZTO: + case FZ_HORIZTOCLOSE: + x = path->coords[k++]; + fprintf(out, "%g h%s\n", x, cmd == FZ_HORIZTOCLOSE ? " z" : ""); break; + case FZ_VERTTOCLOSE: + case FZ_VERTTO: + y = path->coords[k++]; + fprintf(out, "%g i%s\n", y, cmd == FZ_VERTTOCLOSE ? " z" : ""); + break; + case FZ_CURVETOCLOSE: case FZ_CURVETO: x = path->coords[k++]; y = path->coords[k++]; @@ -351,10 +1221,26 @@ fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent) fprintf(out, "%g %g ", x, y); x = path->coords[k++]; y = path->coords[k++]; - fprintf(out, "%g %g c\n", x, y); + fprintf(out, "%g %g c%s\n", x, y, cmd == FZ_CURVETOCLOSE ? " z" : ""); + break; + case FZ_CURVETOVCLOSE: + case FZ_CURVETOV: + case FZ_CURVETOYCLOSE: + case FZ_CURVETOY: + x = path->coords[k++]; + y = path->coords[k++]; + fprintf(out, "%g %g ", x, y); + x = path->coords[k++]; + y = path->coords[k++]; + fprintf(out, "%g %g %c%s\n", x, y, (cmd == FZ_CURVETOVCLOSE || cmd == FZ_CURVETOV ? 'v' : 'y'), (cmd == FZ_CURVETOVCLOSE || cmd == FZ_CURVETOYCLOSE) ? " z" : ""); break; - case FZ_CLOSE_PATH: - fprintf(out, "h\n"); + case FZ_RECTTO: + x = path->coords[k++]; + y = path->coords[k++]; + fprintf(out, "%g %g ", x, y); + x = path->coords[k++]; + y = path->coords[k++]; + fprintf(out, "%g %g r\n", x, y); break; } } diff --git a/source/fitz/svg-device.c b/source/fitz/svg-device.c index 4345c225..4461f79e 100644 --- a/source/fitz/svg-device.c +++ b/source/fitz/svg-device.c @@ -88,43 +88,51 @@ end_def(fz_context *ctx, svg_device *sdev) /* Helper functions */ static void +svg_path_moveto(fz_context *ctx, void *arg, float x, float y) +{ + fz_output *out = (fz_output *)arg; + + fz_printf(ctx, out, "M %g %g ", x, y); +} + +static void +svg_path_lineto(fz_context *ctx, void *arg, float x, float y) +{ + fz_output *out = (fz_output *)arg; + + fz_printf(ctx, out, "L %g %g ", x, y); +} + +static void +svg_path_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3) +{ + fz_output *out = (fz_output *)arg; + + fz_printf(ctx, out, "C %g %g %g %g %g %g ", x1, y1, x2, y2, x3, y3); +} + +static void +svg_path_close(fz_context *ctx, void *arg) +{ + fz_output *out = (fz_output *)arg; + + fz_printf(ctx, out, "Z "); +} + +static const fz_path_processor svg_path_proc = +{ + svg_path_moveto, + svg_path_lineto, + svg_path_curveto, + svg_path_close +}; + +static void svg_dev_path(fz_context *ctx, svg_device *sdev, fz_path *path) { - fz_output *out = sdev->out; - float x, y; - int i, k; - fz_printf(ctx, out, " d=\""); - for (i = 0, k = 0; i < path->cmd_len; i++) - { - switch (path->cmds[i]) - { - case FZ_MOVETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_printf(ctx, out, "M %g %g ", x, y); - break; - case FZ_LINETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_printf(ctx, out, "L %g %g ", x, y); - break; - case FZ_CURVETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_printf(ctx, out, "C %g %g ", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - fz_printf(ctx, out, "%g %g ", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - fz_printf(ctx, out, "%g %g ", x, y); - break; - case FZ_CLOSE_PATH: - fz_printf(ctx, out, "Z "); - break; - } - } - fz_printf(ctx, out, "\""); + fz_printf(ctx, sdev->out, " d=\""); + fz_process_path(ctx, &svg_path_proc, sdev->out, path); + fz_printf(ctx, sdev->out, "\""); } static void diff --git a/source/fitz/trace-device.c b/source/fitz/trace-device.c index 97dd4f79..105f7e6b 100644 --- a/source/fitz/trace-device.c +++ b/source/fitz/trace-device.c @@ -27,42 +27,62 @@ fz_trace_color(fz_colorspace *colorspace, float *color, float alpha) } static void -fz_trace_path(fz_path *path, int indent) +trace_moveto(fz_context *ctx, void *arg, float x, float y) { - float x, y; - int i = 0, k = 0, n; - while (i < path->cmd_len) - { - for (n = 0; n < indent; n++) - putchar(' '); - switch (path->cmds[i++]) - { - case FZ_MOVETO: - x = path->coords[k++]; - y = path->coords[k++]; - printf("<moveto x=\"%g\" y=\"%g\"/>\n", x, y); - break; - case FZ_LINETO: - x = path->coords[k++]; - y = path->coords[k++]; - printf("<lineto x=\"%g\" y=\"%g\"/>\n", x, y); - break; - case FZ_CURVETO: - x = path->coords[k++]; - y = path->coords[k++]; - printf("<curveto x1=\"%g\" y1=\"%g\"", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - printf(" x2=\"%g\" y2=\"%g\"", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - printf(" x3=\"%g\" y3=\"%g\"/>\n", x, y); - break; - case FZ_CLOSE_PATH: - printf("<closepath/>\n"); - break; - } - } + int indent = (int)(intptr_t)arg; + int n; + + for (n = 0; n < indent; n++) + putchar(' '); + printf("<moveto x=\"%g\" y=\"%g\"/>\n", x, y); +} + +static void +trace_lineto(fz_context *ctx, void *arg, float x, float y) +{ + int indent = (int)(intptr_t)arg; + int n; + + for (n = 0; n < indent; n++) + putchar(' '); + printf("<lineto x=\"%g\" y=\"%g\"/>\n", x, y); +} + +static void +trace_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3) +{ + int indent = (int)(intptr_t)arg; + int n; + + for (n = 0; n < indent; n++) + putchar(' '); + printf("<curveto x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\" x3=\"%g\" y3=\"%g\"/>\n", x1, y1, x2, y2, x3, y3); +} + +static void +trace_close(fz_context *ctx, void *arg) +{ + int indent = (int)(intptr_t)arg; + int n; + + for (n = 0; n < indent; n++) + putchar(' '); + printf("<closepath/>\n"); +} + + +static const fz_path_processor trace_path_proc = +{ + trace_moveto, + trace_lineto, + trace_curveto, + trace_close +}; + +static void +fz_trace_path(fz_context *ctx, fz_path *path, int indent) +{ + fz_process_path(ctx, &trace_path_proc, (void *)(intptr_t)indent, path); } static void @@ -91,7 +111,7 @@ fz_trace_fill_path(fz_context *ctx, fz_device *dev, fz_path *path, int even_odd, fz_trace_color(colorspace, color, alpha); fz_trace_matrix(ctm); printf(">\n"); - fz_trace_path(path, 0); + fz_trace_path(ctx, path, 0); printf("</fill_path>\n"); } @@ -119,7 +139,7 @@ fz_trace_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, fz_stroke_s fz_trace_matrix(ctm); printf(">\n"); - fz_trace_path(path, 0); + fz_trace_path(ctx, path, 0); printf("</stroke_path>\n"); } @@ -137,7 +157,7 @@ fz_trace_clip_path(fz_context *ctx, fz_device *dev, fz_path *path, const fz_rect printf(" contentbbox=\"%g %g %g %g\">\n", rect->x0, rect->y0, rect->x1, rect->y1); else printf(">\n"); - fz_trace_path(path, 0); + fz_trace_path(ctx, path, 0); printf("</clip_path>\n"); } @@ -147,7 +167,7 @@ fz_trace_clip_stroke_path(fz_context *ctx, fz_device *dev, fz_path *path, const printf("<clip_stroke_path"); fz_trace_matrix(ctm); printf(">\n"); - fz_trace_path(path, 0); + fz_trace_path(ctx, path, 0); printf("</clip_stroke_path>\n"); } diff --git a/source/pdf/pdf-device.c b/source/pdf/pdf-device.c index 553810f6..e3b63dc5 100644 --- a/source/pdf/pdf-device.c +++ b/source/pdf/pdf-device.c @@ -360,42 +360,58 @@ pdf_dev_stroke_state(fz_context *ctx, pdf_device *pdev, fz_stroke_state *stroke_ gs->stroke_state = fz_keep_stroke_state(ctx, stroke_state); } +typedef struct +{ + fz_context *ctx; + fz_buffer *buf; +} pdf_dev_path_arg; + +static void +pdf_dev_path_moveto(fz_context *ctx, void *arg, float x, float y) +{ + fz_buffer *buf = (fz_buffer *)arg; + + fz_buffer_printf(ctx, buf, "%f %f m\n", x, y); +} + +static void +pdf_dev_path_lineto(fz_context *ctx, void *arg, float x, float y) +{ + fz_buffer *buf = (fz_buffer *)arg; + + fz_buffer_printf(ctx, buf, "%f %f l\n", x, y); +} + +static void +pdf_dev_path_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3) +{ + fz_buffer *buf = (fz_buffer *)arg; + + fz_buffer_printf(ctx, buf, "%f %f %f %f %f %f c\n", x1, y1, x2, y2, x3, y3); +} + +static void +pdf_dev_path_close(fz_context *ctx, void *arg) +{ + fz_buffer *buf = (fz_buffer *)arg; + + fz_buffer_printf(ctx, buf, "h\n"); +} + +static const fz_path_processor pdf_dev_path_proc = +{ + pdf_dev_path_moveto, + pdf_dev_path_lineto, + pdf_dev_path_curveto, + pdf_dev_path_close +}; + static void pdf_dev_path(fz_context *ctx, pdf_device *pdev, fz_path *path) { gstate *gs = CURRENT_GSTATE(pdev); - float x, y; - int i = 0, k = 0; - while (i < path->cmd_len) - { - switch (path->cmds[i++]) - { - case FZ_MOVETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_buffer_printf(ctx, gs->buf, "%f %f m\n", x, y); - break; - case FZ_LINETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_buffer_printf(ctx, gs->buf, "%f %f l\n", x, y); - break; - case FZ_CURVETO: - x = path->coords[k++]; - y = path->coords[k++]; - fz_buffer_printf(ctx, gs->buf, "%f %f ", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - fz_buffer_printf(ctx, gs->buf, "%f %f ", x, y); - x = path->coords[k++]; - y = path->coords[k++]; - fz_buffer_printf(ctx, gs->buf, "%f %f c\n", x, y); - break; - case FZ_CLOSE_PATH: - fz_buffer_printf(ctx, gs->buf, "h\n"); - break; - } - } + + fz_process_path(ctx, &pdf_dev_path_proc, (void *)gs->buf, path); } static void diff --git a/source/pdf/pdf-op-run.c b/source/pdf/pdf-op-run.c index 2bdd3b55..e153dbd1 100644 --- a/source/pdf/pdf-op-run.c +++ b/source/pdf/pdf-op-run.c @@ -1516,11 +1516,7 @@ static void pdf_run_h(fz_context *ctx, pdf_processor *proc) static void pdf_run_re(fz_context *ctx, pdf_processor *proc, float x, float y, float w, float h) { pdf_run_processor *pr = (pdf_run_processor *)proc; - fz_moveto(ctx, pr->path, x, y); - fz_lineto(ctx, pr->path, x + w, y); - fz_lineto(ctx, pr->path, x + w, y + h); - fz_lineto(ctx, pr->path, x, y + h); - fz_closepath(ctx, pr->path); + fz_rectto(ctx, pr->path, x, y, x+w, y+h); } /* path painting */ diff --git a/source/xps/xps-path.c b/source/xps/xps-path.c index e1245a9a..8c352272 100644 --- a/source/xps/xps-path.c +++ b/source/xps/xps-path.c @@ -397,15 +397,11 @@ xps_parse_abbreviated_geometry(fz_context *ctx, xps_document *doc, char *geom, i case 'Q': if (i + 3 >= n) break; - pt = fz_currentpoint(ctx, path); x1 = fz_atof(args[i+0]); y1 = fz_atof(args[i+1]); x2 = fz_atof(args[i+2]); y2 = fz_atof(args[i+3]); - fz_curveto(ctx, path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); + fz_quadto(ctx, path, x1, y1, x2, y2); i += 4; break; case 'q': @@ -415,10 +411,7 @@ xps_parse_abbreviated_geometry(fz_context *ctx, xps_document *doc, char *geom, i y1 = fz_atof(args[i+1]) + pt.y; x2 = fz_atof(args[i+2]) + pt.x; y2 = fz_atof(args[i+3]) + pt.y; - fz_curveto(ctx, path, - (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, - (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, - x2, y2); + fz_quadto(ctx, path, x1, y1, x2, y2); i += 4; break; |