summaryrefslogtreecommitdiff
path: root/base/gspath1.c
diff options
context:
space:
mode:
Diffstat (limited to 'base/gspath1.c')
-rw-r--r--base/gspath1.c665
1 files changed, 665 insertions, 0 deletions
diff --git a/base/gspath1.c b/base/gspath1.c
new file mode 100644
index 000000000..25644c2ba
--- /dev/null
+++ b/base/gspath1.c
@@ -0,0 +1,665 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Additional PostScript Level 1 path routines for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxfarith.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gspath.h"
+#include "gzpath.h"
+#include "gscoord.h" /* gs_itransform prototype */
+
+/* ------ Arcs ------ */
+
+/* Conversion parameters */
+#define degrees_to_radians (M_PI / 180.0)
+
+typedef enum {
+ arc_nothing,
+ arc_moveto,
+ arc_lineto
+} arc_action;
+
+typedef struct arc_curve_params_s {
+ /* The following are set once. */
+ gx_path *ppath;
+ gs_imager_state *pis;
+ gs_point center; /* (not used by arc_add) */
+ double radius;
+ /* The following may be updated dynamically. */
+ arc_action action;
+ segment_notes notes;
+ gs_point p0, p3, pt;
+ gs_sincos_t sincos; /* (not used by arc_add) */
+ double angle; /* (not used by arc_add) */
+ int fast_quadrant; /* 0 = not calculated, -1 = not fast, */
+ /* 1 = fast (only used for quadrants) */
+ /* The following are set once iff fast_quadrant > 0. */
+ fixed scaled_radius; /* radius * CTM scale */
+ fixed quadrant_delta; /* scaled_radius * quarter_arc_fraction */
+} arc_curve_params_t;
+
+/* Forward declarations */
+static int arc_add(const arc_curve_params_t *arc, bool is_quadrant);
+static int gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
+ double axc, double ayc, double arad, double aang1, double aang2,
+ bool add_line, gs_point *p3);
+
+int
+gx_setcurrentpoint_from_path(gs_imager_state *pis, gx_path *path)
+{
+ gs_point pt;
+
+ pt.x = fixed2float(path->position.x);
+ pt.y = fixed2float(path->position.y);
+ gx_setcurrentpoint(pis, pt.x, pt.y);
+ pis->current_point_valid = true;
+ return 0;
+}
+
+static inline int
+gs_arc_add_inline(gs_state *pgs, bool cw, double xc, double yc, double rad,
+ double a1, double a2, bool add)
+{
+ gs_point p3;
+ int code = gs_imager_arc_add(pgs->path, (gs_imager_state *)pgs, cw, xc, yc, rad, a1, a2, add, &p3);
+
+ if (code < 0)
+ return code;
+
+#if !PRECISE_CURRENTPOINT
+ return gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
+#else
+ pgs->current_point_valid = true;
+ return gs_point_transform(p3.x, p3.y, &ctm_only(pgs), &pgs->current_point);
+#endif
+
+}
+
+int
+gs_arc(gs_state * pgs,
+ double xc, double yc, double r, double ang1, double ang2)
+{
+ return gs_arc_add_inline(pgs, false, xc, yc, r, ang1, ang2, true);
+}
+
+int
+gs_arcn(gs_state * pgs,
+ double xc, double yc, double r, double ang1, double ang2)
+{
+ return gs_arc_add_inline(pgs, true, xc, yc, r, ang1, ang2, true);
+}
+
+int
+gs_arc_add(gs_state * pgs, bool clockwise, double axc, double ayc,
+ double arad, double aang1, double aang2, bool add_line)
+{
+ return gs_arc_add_inline(pgs, clockwise, axc, ayc, arad,
+ aang1, aang2, add_line);
+}
+
+/* Compute the next curve as part of an arc. */
+static int
+next_arc_curve(arc_curve_params_t * arc, double anext)
+{
+ double x0 = arc->p0.x = arc->p3.x;
+ double y0 = arc->p0.y = arc->p3.y;
+ double trad = arc->radius *
+ tan((anext - arc->angle) *
+ (degrees_to_radians / 2));
+
+ arc->pt.x = x0 - trad * arc->sincos.sin;
+ arc->pt.y = y0 + trad * arc->sincos.cos;
+ gs_sincos_degrees(anext, &arc->sincos);
+ arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos;
+ arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin;
+ arc->angle = anext;
+ return arc_add(arc, false);
+}
+/*
+ * Use this when both arc.angle and anext are multiples of 90 degrees,
+ * and anext = arc.angle +/- 90.
+ */
+static int
+next_arc_quadrant(arc_curve_params_t * arc, double anext)
+{
+ double x0 = arc->p0.x = arc->p3.x;
+ double y0 = arc->p0.y = arc->p3.y;
+
+ if (!arc->fast_quadrant) {
+ /*
+ * If the CTM is well-behaved, we can pre-calculate the delta
+ * from the arc points to the control points.
+ */
+ const gs_imager_state *pis = arc->pis;
+ double scale = 0; /* Quiet gcc warning. */
+
+ if (is_fzero2(pis->ctm.xy, pis->ctm.yx) ?
+ (scale = fabs(pis->ctm.xx)) == fabs(pis->ctm.yy) :
+ is_fzero2(pis->ctm.xx, pis->ctm.yy) ?
+ (scale = fabs(pis->ctm.xy)) == fabs(pis->ctm.yx) :
+ 0
+ ) {
+ double scaled_radius = arc->radius * scale;
+
+ arc->scaled_radius = float2fixed(scaled_radius);
+ arc->quadrant_delta =
+ float2fixed(scaled_radius * quarter_arc_fraction);
+ arc->fast_quadrant = 1;
+ } else {
+ arc->fast_quadrant = -1;
+ }
+ }
+ /*
+ * We know that anext is a multiple of 90 (as a fixed); we want
+ * (anext / 90) & 3. The following is much faster than a division.
+ */
+ switch (((int)anext >> 1) & 3) {
+ case 0:
+ arc->sincos.sin = 0, arc->sincos.cos = 1;
+ arc->p3.x = x0 = arc->center.x + arc->radius;
+ arc->p3.y = arc->center.y;
+ break;
+ case 1:
+ arc->sincos.sin = 1, arc->sincos.cos = 0;
+ arc->p3.x = arc->center.x;
+ arc->p3.y = y0 = arc->center.y + arc->radius;
+ break;
+ case 2:
+ arc->sincos.sin = 0, arc->sincos.cos = -1;
+ arc->p3.x = x0 = arc->center.x - arc->radius;
+ arc->p3.y = arc->center.y;
+ break;
+ case 3:
+ arc->sincos.sin = -1, arc->sincos.cos = 0;
+ arc->p3.x = arc->center.x;
+ arc->p3.y = y0 = arc->center.y - arc->radius;
+ break;
+ }
+ arc->pt.x = x0, arc->pt.y = y0;
+ arc->angle = anext;
+ return arc_add(arc, true);
+}
+
+static int
+gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
+ double axc, double ayc, double arad, double aang1, double aang2,
+ bool add_line, gs_point *p3)
+{
+ double ar = arad;
+ double ang1 = aang1, ang2 = aang2, anext;
+ double ang1r; /* reduced angle */
+ arc_curve_params_t arc;
+ int code;
+
+ arc.ppath = ppath;
+ arc.pis = pis;
+ arc.center.x = axc;
+ arc.center.y = ayc;
+ if (ar < 0) {
+ ang1 += 180;
+ ang2 += 180;
+ ar = -ar;
+ }
+ arc.radius = ar;
+ arc.action = (add_line ? arc_lineto : arc_moveto);
+ arc.notes = sn_none;
+ arc.fast_quadrant = 0;
+ ang1r = fmod(ang1, 360);
+ gs_sincos_degrees(ang1r, &arc.sincos);
+ arc.p3.x = axc + ar * arc.sincos.cos;
+ arc.p3.y = ayc + ar * arc.sincos.sin;
+ if (clockwise) {
+ while (ang1 < ang2)
+ ang2 -= 360;
+ if (ang2 < 0) {
+ double adjust = ceil(-ang2 / 360) * 360;
+
+ ang1 += adjust, ang2 += adjust;
+ }
+ arc.angle = ang1;
+ if (ang1 == ang2)
+ goto last;
+ /* Do the first part, up to a multiple of 90 degrees. */
+ if (!arc.sincos.orthogonal) {
+ anext = floor(arc.angle / 90) * 90;
+ if (anext < ang2)
+ goto last;
+ code = next_arc_curve(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ /* Do multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0. */
+ while ((anext = arc.angle - 90) >= ang2) {
+ code = next_arc_quadrant(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ } else {
+ while (ang2 < ang1)
+ ang2 += 360;
+ if (ang1 < 0) {
+ double adjust = ceil(-ang1 / 360) * 360;
+
+ ang1 += adjust, ang2 += adjust;
+ }
+ arc.angle = ang1;
+ if (ang1 == ang2) {
+ code = next_arc_curve(&arc, ang2);
+ if (code < 0)
+ return code;
+ *p3 = arc.p3;
+ }
+ /* Do the first part, up to a multiple of 90 degrees. */
+ if (!arc.sincos.orthogonal) {
+ anext = ceil(arc.angle / 90) * 90;
+ if (anext > ang2)
+ goto last;
+ code = next_arc_curve(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ /* Do multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2. */
+ while ((anext = arc.angle + 90) <= ang2) {
+ code = next_arc_quadrant(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
+ }
+ }
+ /*
+ * Do the last curve of the arc, if any.
+ */
+ if (arc.angle == ang2) {
+ *p3 = arc.p3;
+ return 0;
+ }
+last:
+ code = next_arc_curve(&arc, ang2);
+ if (code < 0)
+ return code;
+ *p3 = arc.p3;
+ return 0;
+}
+
+int
+gs_arcto(gs_state * pgs,
+double ax1, double ay1, double ax2, double ay2, double arad, float retxy[4])
+{
+ double xt0, yt0, xt2, yt2;
+ gs_point up0;
+
+#define ax0 up0.x
+#define ay0 up0.y
+ /* Transform the current point back into user coordinates. */
+ int code = gs_currentpoint(pgs, &up0);
+
+ if (code < 0)
+ return code;
+ {
+ double dx0, dy0, dx2, dy2, sql0, sql2;
+
+ /* Now we have to compute the tangent points. */
+ /* Basically, the idea is to compute the tangent */
+ /* of the bisector by using tan(x+y) and tan(z/2) */
+ /* formulas, without ever using any trig. */
+ dx0 = ax0 - ax1; dy0 = ay0 - ay1;
+ dx2 = ax2 - ax1; dy2 = ay2 - ay1;
+
+ /* Compute the squared lengths from p1 to p0 and p2. */
+ sql0 = dx0 * dx0 + dy0 * dy0;
+ sql2 = dx2 * dx2 + dy2 * dy2;
+
+ if (sql0 == 0. || sql2 == 0.)
+ return_error(gs_error_undefinedresult); /* for CET 11-04 */
+
+ /* Check for collinear points. */
+ if (dx0*dy2 == dy0*dx2) {
+ code = gs_lineto(pgs, ax1, ay1);
+ xt0 = xt2 = ax1;
+ yt0 = yt2 = ay1;
+ } else { /* not collinear */
+ /* Compute the distance from p1 to the tangent points. */
+ /* This is the only messy part. */
+ double num = dy0 * dx2 - dy2 * dx0;
+ double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
+
+ double dist = fabs(arad * num / denom);
+ double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
+ arc_curve_params_t arc;
+
+ arc.ppath = pgs->path;
+ arc.pis = (gs_imager_state *) pgs;
+ arc.radius = arad;
+ arc.action = arc_lineto;
+ arc.notes = sn_none;
+ if (arad < 0)
+ l0 = -l0, l2 = -l2;
+ arc.p0.x = xt0 = ax1 + dx0 * l0;
+ arc.p0.y = yt0 = ay1 + dy0 * l0;
+ arc.p3.x = xt2 = ax1 + dx2 * l2;
+ arc.p3.y = yt2 = ay1 + dy2 * l2;
+ arc.pt.x = ax1;
+ arc.pt.y = ay1;
+ code = arc_add(&arc, false);
+ if (code == 0)
+ code = gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
+ }
+ }
+ if (retxy != 0) {
+ retxy[0] = xt0;
+ retxy[1] = yt0;
+ retxy[2] = xt2;
+ retxy[3] = yt2;
+ }
+ return code;
+}
+
+/* Internal routine for adding an arc to the path. */
+static int
+arc_add(const arc_curve_params_t * arc, bool is_quadrant)
+{
+ gx_path *path = arc->ppath;
+ gs_imager_state *pis = arc->pis;
+ double x0 = arc->p0.x, y0 = arc->p0.y;
+ double xt = arc->pt.x, yt = arc->pt.y;
+ double fraction;
+ gs_fixed_point p0, p2, p3, pt;
+ int code;
+
+ if ((arc->action != arc_nothing &&
+#if !PRECISE_CURRENTPOINT
+ (code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0) ||
+ (code = gs_point_transform2fixed(&pis->ctm, xt, yt, &pt)) < 0 ||
+ (code = gs_point_transform2fixed(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0
+#else
+ (code = gs_point_transform2fixed_rounding(&pis->ctm, x0, y0, &p0)) < 0) ||
+ (code = gs_point_transform2fixed_rounding(&pis->ctm, xt, yt, &pt)) < 0 ||
+ (code = gs_point_transform2fixed_rounding(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0
+#endif
+ )
+ return code;
+#if PRECISE_CURRENTPOINT
+ if (!path_position_valid(path))
+ gs_point_transform(arc->p0.x, arc->p0.y, &ctm_only(arc->pis), &pis->subpath_start);
+#endif
+ code = (arc->action == arc_nothing ?
+ (p0.x = path->position.x, p0.y = path->position.y, 0) :
+ arc->action == arc_lineto && path_position_valid(path) ?
+ gx_path_add_line(path, p0.x, p0.y) :
+ /* action == arc_moveto, or lineto with no current point */
+ gx_path_add_point(path, p0.x, p0.y));
+ if (code < 0)
+ return code;
+ /* Compute the fraction coefficient for the curve. */
+ /* See gx_path_add_partial_arc for details. */
+ if (is_quadrant) {
+ /* one of |dx| and |dy| is r, the other is zero */
+ fraction = quarter_arc_fraction;
+ if (arc->fast_quadrant > 0) {
+ /*
+ * The CTM is well-behaved, and we have pre-calculated the delta
+ * from the circumference points to the control points.
+ */
+ fixed delta = arc->quadrant_delta;
+
+ if (pt.x != p0.x)
+ p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta);
+ if (pt.y != p0.y)
+ p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta);
+ p2.x = (pt.x == p3.x ? p3.x :
+ pt.x > p3.x ? p3.x + delta : p3.x - delta);
+ p2.y = (pt.y == p3.y ? p3.y :
+ pt.y > p3.y ? p3.y + delta : p3.y - delta);
+ goto add;
+ }
+ } else {
+ double r = arc->radius;
+ double dx = xt - x0, dy = yt - y0;
+ double dist = dx * dx + dy * dy;
+ double r2 = r * r;
+
+ if (dist >= r2 * 1.0e8) /* almost zero radius; */
+ /* the >= catches dist == r == 0 */
+ fraction = 0.0;
+ else
+ fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2));
+ }
+ p0.x += (fixed)((pt.x - p0.x) * fraction);
+ p0.y += (fixed)((pt.y - p0.y) * fraction);
+ p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction);
+ p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction);
+add:
+ if_debug8m('r', path->memory,
+ "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
+ fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y,
+ (int)arc->action);
+
+ /* Open-code gx_path_add_partial_arc_notes */
+ return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y,
+ arc->notes | sn_from_arc);
+}
+
+void
+make_quadrant_arc(gs_point *p, const gs_point *c,
+ const gs_point *p0, const gs_point *p1, double r)
+{
+ p[0].x = c->x + p0->x * r;
+ p[0].y = c->y + p0->y * r;
+ p[1].x = c->x + p0->x * r + p1->x * r * quarter_arc_fraction;
+ p[1].y = c->y + p0->y * r + p1->y * r * quarter_arc_fraction;
+ p[2].x = c->x + p0->x * r * quarter_arc_fraction + p1->x * r;
+ p[2].y = c->y + p0->y * r * quarter_arc_fraction + p1->y * r;
+ p[3].x = c->x + p1->x * r;
+ p[3].y = c->y + p1->y * r;
+}
+
+/* ------ Path transformers ------ */
+
+int
+gs_dashpath(gs_state * pgs)
+{
+ gx_path *ppath;
+ gx_path fpath;
+ int code;
+
+ if (gs_currentdash_length(pgs) == 0)
+ return 0; /* no dash pattern */
+ code = gs_flattenpath(pgs);
+ if (code < 0)
+ return code;
+ ppath = pgs->path;
+ gx_path_init_local(&fpath, ppath->memory);
+ code = gx_path_add_dash_expansion(ppath, &fpath, (gs_imager_state *)pgs);
+ if (code < 0) {
+ gx_path_free(&fpath, "gs_dashpath");
+ return code;
+ }
+ gx_path_assign_free(pgs->path, &fpath);
+ return 0;
+}
+
+int
+gs_flattenpath(gs_state * pgs)
+{
+ gx_path *ppath = pgs->path;
+ gx_path fpath;
+ int code;
+
+ if (!gx_path_has_curves(ppath))
+ return 0; /* nothing to do */
+ gx_path_init_local(&fpath, ppath->memory);
+ code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness,
+ pgs->accurate_curves);
+ if (code < 0) {
+ gx_path_free(&fpath, "gs_flattenpath");
+ return code;
+ }
+ gx_path_assign_free(ppath, &fpath);
+ return 0;
+}
+
+int
+gs_reversepath(gs_state * pgs)
+{
+ gx_path *ppath = pgs->path;
+ gx_path rpath;
+ int code;
+
+ gx_path_init_local(&rpath, ppath->memory);
+ code = gx_path_copy_reversed(ppath, &rpath);
+ if (code < 0) {
+ gx_path_free(&rpath, "gs_reversepath");
+ return code;
+ }
+ if (pgs->current_point_valid) {
+ /* Not empty. */
+ gx_setcurrentpoint(pgs, fixed2float(rpath.position.x),
+ fixed2float(rpath.position.y));
+ if (rpath.first_subpath != 0) {
+ pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x);
+ pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y);
+ }
+ }
+ gx_path_assign_free(ppath, &rpath);
+ return 0;
+}
+
+/* ------ Accessors ------ */
+
+int
+gs_upathbbox(gs_state * pgs, gs_rect * pbox, bool include_moveto)
+{
+ gs_fixed_rect fbox; /* box in device coordinates */
+ gs_rect dbox;
+ int code = gx_path_bbox_set(pgs->path, &fbox);
+
+ if (code < 0)
+ return code;
+ /* If the path ends with a moveto and include_moveto is true, */
+ /* include the moveto in the bounding box. */
+ if (path_last_is_moveto(pgs->path) && include_moveto) {
+ gs_fixed_point pt;
+
+ code = gx_path_current_point_inline(pgs, &pt);
+ if (code < 0)
+ return code;
+ if (pt.x < fbox.p.x)
+ fbox.p.x = pt.x;
+ if (pt.y < fbox.p.y)
+ fbox.p.y = pt.y;
+ if (pt.x > fbox.q.x)
+ fbox.q.x = pt.x;
+ if (pt.y > fbox.q.y)
+ fbox.q.y = pt.y;
+ }
+ /* Transform the result back to user coordinates. */
+ dbox.p.x = fixed2float(fbox.p.x);
+ dbox.p.y = fixed2float(fbox.p.y);
+ dbox.q.x = fixed2float(fbox.q.x);
+ dbox.q.y = fixed2float(fbox.q.y);
+ return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox);
+}
+
+/* ------ Enumerators ------ */
+
+/* Start enumerating a path */
+int
+gs_path_enum_copy_init(gs_memory_t *mem, gs_path_enum * penum, const gs_state * pgs, bool copy)
+{
+ if (copy) {
+ gx_path *copied_path =
+ gx_path_alloc(mem, "gs_path_enum_init");
+ int code;
+
+ if (copied_path == 0)
+ return_error(gs_error_VMerror);
+ code = gx_path_copy(pgs->path, copied_path);
+ if (code < 0) {
+ gx_path_free(copied_path, "gs_path_enum_init");
+ return code;
+ }
+ gx_path_enum_init(penum, copied_path);
+ penum->copied_path = copied_path;
+ } else {
+ gx_path_enum_init(penum, pgs->path);
+ }
+ penum->memory = mem;
+ gs_currentmatrix(pgs, &penum->mat);
+ return 0;
+}
+
+/* Enumerate the next element of a path. */
+/* If the path is finished, return 0; */
+/* otherwise, return the element type. */
+int
+gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3])
+{
+ gs_fixed_point fpts[3];
+ int pe_op = gx_path_enum_next(penum, fpts);
+ int code;
+
+ switch (pe_op) {
+ case 0: /* all done */
+ case gs_pe_closepath:
+ break;
+ case gs_pe_curveto:
+ if ((code = gs_point_transform_inverse(
+ fixed2float(fpts[1].x),
+ fixed2float(fpts[1].y),
+ &penum->mat, &ppts[1])) < 0 ||
+ (code = gs_point_transform_inverse(
+ fixed2float(fpts[2].x),
+ fixed2float(fpts[2].y),
+ &penum->mat, &ppts[2])) < 0)
+ return code;
+ /* falls through */
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ case gs_pe_gapto:
+ if ((code = gs_point_transform_inverse(
+ fixed2float(fpts[0].x),
+ fixed2float(fpts[0].y),
+ &penum->mat, &ppts[0])) < 0)
+ return code;
+ default: /* error */
+ break;
+ }
+ return pe_op;
+}
+
+/* Clean up after a pathforall. */
+void
+gs_path_enum_cleanup(gs_path_enum * penum)
+{
+ if (penum->copied_path != 0) {
+ gx_path_free(penum->copied_path, "gs_path_enum_cleanup");
+ penum->path = 0;
+ penum->copied_path = 0;
+ }
+}