summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2015-03-05 17:31:05 -0700
committerRobin Watts <robin.watts@artifex.com>2015-03-24 19:50:00 +0000
commit5f161e45d5daacb696d02b8fad23d0c23f5bc8bc (patch)
tree6e8d84a383d580ee38b23dfb345bc96d0ba1e63e /source
parent563c482145d65c4006f5842c8860ab1b09f5a229 (diff)
downloadmupdf-5f161e45d5daacb696d02b8fad23d0c23f5bc8bc.tar.xz
Path rework for improved memory usage.
Firstly, we make the definition of the path structures local to path.c. This is achieved by using an fz_path_processor function to step through paths enumerating each section using callback functions. Next, we extend the internal path representation to include other section types, including quads, beziers with common control points rectangles, horizontal, vertical and degenerate lines. We also roll close path sections up into the previous sections commands. The hairiest part of this is that fz_transform_path has to cope with changing the path commands depending on the matrix. This is a relatively rare operation though.
Diffstat (limited to 'source')
-rw-r--r--source/fitz/draw-path.c822
-rw-r--r--source/fitz/font.c9
-rw-r--r--source/fitz/path.c1032
-rw-r--r--source/fitz/svg-device.c78
-rw-r--r--source/fitz/trace-device.c98
-rw-r--r--source/pdf/pdf-device.c80
-rw-r--r--source/pdf/pdf-op-run.c6
-rw-r--r--source/xps/xps-path.c11
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;