diff options
Diffstat (limited to 'source/fitz/path.c')
-rw-r--r-- | source/fitz/path.c | 1032 |
1 files changed, 959 insertions, 73 deletions
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; } } |