| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- #include <assert.h>
- #include "fitz-internal.h"
- fz_path *
- fz_new_path(fz_context *ctx)
- {
- fz_path *path;
- path = fz_malloc_struct(ctx, fz_path);
- path->len = 0;
- path->cap = 0;
- path->items = NULL;
- path->last = -1;
- return path;
- }
- fz_path *
- fz_clone_path(fz_context *ctx, fz_path *old)
- {
- fz_path *path;
- assert(old);
- path = fz_malloc_struct(ctx, fz_path);
- fz_try(ctx)
- {
- path->len = old->len;
- path->cap = old->len;
- path->items = fz_malloc_array(ctx, path->cap, sizeof(fz_path_item));
- memcpy(path->items, old->items, sizeof(fz_path_item) * path->len);
- }
- fz_catch(ctx)
- {
- fz_free(ctx, path);
- fz_rethrow(ctx);
- }
- return path;
- }
- void
- fz_free_path(fz_context *ctx, fz_path *path)
- {
- if (path == NULL)
- return;
- fz_free(ctx, path->items);
- fz_free(ctx, path);
- }
- static void
- grow_path(fz_context *ctx, fz_path *path, int n)
- {
- int newcap = path->cap;
- if (path->len + n <= path->cap)
- {
- path->last = path->len;
- return;
- }
- while (path->len + n > newcap)
- newcap = newcap + 36;
- path->items = fz_resize_array(ctx, path->items, newcap, sizeof(fz_path_item));
- path->cap = newcap;
- path->last = path->len;
- }
- fz_point
- fz_currentpoint(fz_context *ctx, fz_path *path)
- {
- fz_point c, m;
- int i;
- c.x = c.y = m.x = m.y = 0;
- i = 0;
- while (i < path->len)
- {
- switch (path->items[i++].k)
- {
- case FZ_MOVETO:
- m.x = c.x = path->items[i++].v;
- m.y = c.y = path->items[i++].v;
- break;
- case FZ_LINETO:
- c.x = path->items[i++].v;
- c.y = path->items[i++].v;
- break;
- case FZ_CURVETO:
- i += 4;
- c.x = path->items[i++].v;
- c.y = path->items[i++].v;
- break;
- case FZ_CLOSE_PATH:
- c = m;
- }
- }
- return c;
- }
- void
- fz_moveto(fz_context *ctx, fz_path *path, float x, float y)
- {
- if (path->last >= 0 && path->items[path->last].k == FZ_MOVETO)
- {
- /* No point in having MOVETO then MOVETO */
- path->len = path->last;
- }
- grow_path(ctx, path, 3);
- path->items[path->len++].k = FZ_MOVETO;
- path->items[path->len++].v = x;
- path->items[path->len++].v = y;
- }
- void
- fz_lineto(fz_context *ctx, fz_path *path, float x, float y)
- {
- float x0, y0;
- if (path->last < 0)
- {
- fz_warn(ctx, "lineto with no current point");
- return;
- }
- if (path->items[path->last].k == FZ_CLOSE_PATH)
- {
- x0 = path->items[path->last-2].v;
- y0 = path->items[path->last-1].v;
- }
- else
- {
- x0 = path->items[path->len-2].v;
- y0 = path->items[path->len-1].v;
- }
- /* Anything other than MoveTo followed by LineTo the same place is a nop */
- if (path->items[path->last].k != FZ_MOVETO && x0 == x && y0 == y)
- return;
- grow_path(ctx, path, 3);
- path->items[path->len++].k = FZ_LINETO;
- path->items[path->len++].v = x;
- path->items[path->len++].v = y;
- }
- void
- fz_curveto(fz_context *ctx, fz_path *path,
- float x1, float y1,
- float x2, float y2,
- float x3, float y3)
- {
- float x0, y0;
- if (path->last < 0)
- {
- fz_warn(ctx, "curveto with no current point");
- return;
- }
- if (path->items[path->last].k == FZ_CLOSE_PATH)
- {
- x0 = path->items[path->last-2].v;
- y0 = path->items[path->last-1].v;
- }
- else
- {
- x0 = path->items[path->len-2].v;
- y0 = path->items[path->len-1].v;
- }
- /* Check for degenerate cases: */
- if (x0 == x1 && y0 == y1)
- {
- if (x2 == x3 && y2 == y3)
- {
- /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */
- if (x1 == x2 && y1 == y2 && path->items[path->last].k != FZ_MOVETO)
- return;
- /* Otherwise a line will suffice */
- fz_lineto(ctx, path, x3, y3);
- return;
- }
- if (x1 == x2 && y1 == y2)
- {
- /* A line will suffice */
- fz_lineto(ctx, path, x3, y3);
- return;
- }
- }
- else if (x1 == x2 && y1 == y2 && x2 == x3 && y2 == y3)
- {
- /* A line will suffice */
- fz_lineto(ctx, path, x3, y3);
- return;
- }
- grow_path(ctx, path, 7);
- path->items[path->len++].k = FZ_CURVETO;
- path->items[path->len++].v = x1;
- path->items[path->len++].v = y1;
- path->items[path->len++].v = x2;
- path->items[path->len++].v = y2;
- path->items[path->len++].v = x3;
- path->items[path->len++].v = y3;
- }
- void
- fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3)
- {
- float x1, y1;
- if (path->last < 0)
- {
- fz_warn(ctx, "curvetov with no current point");
- return;
- }
- if (path->items[path->last].k == FZ_CLOSE_PATH)
- {
- x1 = path->items[path->last-2].v;
- y1 = path->items[path->last-1].v;
- }
- else
- {
- x1 = path->items[path->len-2].v;
- y1 = path->items[path->len-1].v;
- }
- fz_curveto(ctx, path, x1, y1, x2, y2, 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);
- }
- void
- fz_closepath(fz_context *ctx, fz_path *path)
- {
- if (path->last < 0)
- {
- fz_warn(ctx, "closepath with no current point");
- return;
- }
- /* CLOSE following a CLOSE is a NOP */
- if (path->items[path->last].k == FZ_CLOSE_PATH)
- return;
- grow_path(ctx, path, 1);
- path->items[path->len++].k = FZ_CLOSE_PATH;
- }
- static inline fz_rect *bound_expand(fz_rect *r, const fz_point *p)
- {
- if (p->x < r->x0) r->x0 = p->x;
- if (p->y < r->y0) r->y0 = p->y;
- if (p->x > r->x1) r->x1 = p->x;
- if (p->y > r->y1) r->y1 = p->y;
- return r;
- }
- fz_rect *
- fz_bound_path(fz_context *ctx, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *r)
- {
- fz_point p;
- int i = 0;
- /* If the path is empty, return the empty rectangle here - don't wait
- * for it to be expanded in the stroked case below. */
- if (path->len == 0)
- {
- *r = fz_empty_rect;
- return r;
- }
- /* A path must start with a moveto - and if that's all there is
- * then the path is empty. */
- if (path->len == 3)
- {
- *r = fz_empty_rect;
- return r;
- }
- p.x = path->items[1].v;
- p.y = path->items[2].v;
- fz_transform_point(&p, ctm);
- r->x0 = r->x1 = p.x;
- r->y0 = r->y1 = p.y;
- while (i < path->len)
- {
- switch (path->items[i++].k)
- {
- case FZ_CURVETO:
- p.x = path->items[i++].v;
- p.y = path->items[i++].v;
- bound_expand(r, fz_transform_point(&p, ctm));
- p.x = path->items[i++].v;
- p.y = path->items[i++].v;
- bound_expand(r, fz_transform_point(&p, ctm));
- p.x = path->items[i++].v;
- p.y = path->items[i++].v;
- bound_expand(r, fz_transform_point(&p, ctm));
- break;
- case FZ_MOVETO:
- if (i + 2 == path->len)
- {
- /* Trailing Moveto - cannot affect bbox */
- i += 2;
- break;
- }
- /* fallthrough */
- case FZ_LINETO:
- p.x = path->items[i++].v;
- p.y = path->items[i++].v;
- bound_expand(r, fz_transform_point(&p, ctm));
- break;
- case FZ_CLOSE_PATH:
- break;
- }
- }
- if (stroke)
- {
- fz_adjust_rect_for_stroke(r, stroke, ctm);
- }
- return r;
- }
- fz_rect *
- fz_adjust_rect_for_stroke(fz_rect *r, fz_stroke_state *stroke, const fz_matrix *ctm)
- {
- float expand;
- if (!stroke)
- return r;
- expand = stroke->linewidth;
- if (expand == 0)
- expand = 1.0f;
- expand *= fz_matrix_max_expansion(ctm);
- if ((stroke->linejoin == FZ_LINEJOIN_MITER || stroke->linejoin == FZ_LINEJOIN_MITER_XPS) && stroke->miterlimit > 1)
- expand *= stroke->miterlimit;
- r->x0 -= expand;
- r->y0 -= expand;
- r->x1 += expand;
- r->y1 += expand;
- return r;
- }
- void
- fz_transform_path(fz_context *ctx, fz_path *path, const fz_matrix *ctm)
- {
- int k, i = 0;
- while (i < path->len)
- {
- switch (path->items[i++].k)
- {
- case FZ_CURVETO:
- for (k = 0; k < 3; k++)
- {
- fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm);
- i += 2;
- }
- break;
- case FZ_MOVETO:
- case FZ_LINETO:
- fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm);
- i += 2;
- break;
- case FZ_CLOSE_PATH:
- break;
- }
- }
- }
- #ifndef NDEBUG
- void
- fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent)
- {
- float x, y;
- int i = 0;
- int n;
- while (i < path->len)
- {
- for (n = 0; n < indent; n++)
- fputc(' ', out);
- switch (path->items[i++].k)
- {
- case FZ_MOVETO:
- x = path->items[i++].v;
- y = path->items[i++].v;
- fprintf(out, "%g %g m\n", x, y);
- break;
- case FZ_LINETO:
- x = path->items[i++].v;
- y = path->items[i++].v;
- fprintf(out, "%g %g l\n", x, y);
- break;
- case FZ_CURVETO:
- x = path->items[i++].v;
- y = path->items[i++].v;
- fprintf(out, "%g %g ", x, y);
- x = path->items[i++].v;
- y = path->items[i++].v;
- fprintf(out, "%g %g ", x, y);
- x = path->items[i++].v;
- y = path->items[i++].v;
- fprintf(out, "%g %g c\n", x, y);
- break;
- case FZ_CLOSE_PATH:
- fprintf(out, "h\n");
- break;
- }
- }
- }
- #endif
- fz_stroke_state *
- fz_keep_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
- {
- if (!stroke)
- return NULL;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- if (stroke->refs > 0)
- stroke->refs++;
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- return stroke;
- }
- void
- fz_drop_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
- {
- int drop;
- if (!stroke)
- return;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- drop = (stroke->refs > 0 ? --stroke->refs == 0 : 0);
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- if (drop)
- fz_free(ctx, stroke);
- }
- fz_stroke_state *
- fz_new_stroke_state_with_len(fz_context *ctx, int len)
- {
- fz_stroke_state *state;
- len -= nelem(state->dash_list);
- if (len < 0)
- len = 0;
- state = Memento_label(fz_malloc(ctx, sizeof(*state) + sizeof(state->dash_list[0]) * len), "fz_stroke_state");
- state->refs = 1;
- state->start_cap = FZ_LINECAP_BUTT;
- state->dash_cap = FZ_LINECAP_BUTT;
- state->end_cap = FZ_LINECAP_BUTT;
- state->linejoin = FZ_LINEJOIN_MITER;
- state->linewidth = 1;
- state->miterlimit = 10;
- state->dash_phase = 0;
- state->dash_len = 0;
- memset(state->dash_list, 0, sizeof(state->dash_list[0]) * (len + nelem(state->dash_list)));
- return state;
- }
- fz_stroke_state *
- fz_new_stroke_state(fz_context *ctx)
- {
- return fz_new_stroke_state_with_len(ctx, 0);
- }
- fz_stroke_state *
- fz_unshare_stroke_state_with_len(fz_context *ctx, fz_stroke_state *shared, int len)
- {
- int single, unsize, shsize, shlen, drop;
- fz_stroke_state *unshared;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- single = (shared->refs == 1);
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- shlen = shared->dash_len - nelem(shared->dash_list);
- if (shlen < 0)
- shlen = 0;
- shsize = sizeof(*shared) + sizeof(shared->dash_list[0]) * shlen;
- len -= nelem(shared->dash_list);
- if (len < 0)
- len = 0;
- if (single && shlen >= len)
- return shared;
- unsize = sizeof(*unshared) + sizeof(unshared->dash_list[0]) * len;
- unshared = Memento_label(fz_malloc(ctx, unsize), "fz_stroke_state");
- memcpy(unshared, shared, (shsize > unsize ? unsize : shsize));
- unshared->refs = 1;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- drop = (shared->refs > 0 ? --shared->refs == 0 : 0);
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- if (drop)
- fz_free(ctx, shared);
- return unshared;
- }
- fz_stroke_state *
- fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared)
- {
- return fz_unshare_stroke_state_with_len(ctx, shared, shared->dash_len);
- }
|