diff options
Diffstat (limited to 'source/fitz/draw-path.c')
-rw-r--r-- | source/fitz/draw-path.c | 822 |
1 files changed, 517 insertions, 305 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); } |