xref: /plan9/sys/src/cmd/gs/src/gspath1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gspath1.c,v 1.10 2004/08/31 13:23:16 igor Exp $ */
18 /* Additional PostScript Level 1 path routines for Ghostscript library */
19 #include "math_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gxfixed.h"
24 #include "gxfarith.h"
25 #include "gxmatrix.h"
26 #include "gzstate.h"
27 #include "gspath.h"
28 #include "gzpath.h"
29 #include "gscoord.h"		/* gs_itransform prototype */
30 
31 /* ------ Arcs ------ */
32 
33 /* Conversion parameters */
34 #define degrees_to_radians (M_PI / 180.0)
35 
36 typedef enum {
37     arc_nothing,
38     arc_moveto,
39     arc_lineto
40 } arc_action;
41 
42 typedef struct arc_curve_params_s {
43     /* The following are set once. */
44     gx_path *ppath;
45     gs_imager_state *pis;
46     gs_point center;		/* (not used by arc_add) */
47     double radius;
48     /* The following may be updated dynamically. */
49     arc_action action;
50     segment_notes notes;
51     gs_point p0, p3, pt;
52     gs_sincos_t sincos;		/* (not used by arc_add) */
53     fixed angle;		/* (not used by arc_add) */
54     int fast_quadrant;		/* 0 = not calculated, -1 = not fast, */
55 				/* 1 = fast (only used for quadrants) */
56     /* The following are set once iff fast_quadrant > 0. */
57     fixed scaled_radius;	/* radius * CTM scale */
58     fixed quadrant_delta;	/* scaled_radius * quarter_arc_fraction */
59 } arc_curve_params_t;
60 
61 /* Forward declarations */
62 private int arc_add(const arc_curve_params_t *arc, bool is_quadrant);
63 
64 
65 int
gx_setcurrentpoint_from_path(gs_imager_state * pis,gx_path * path)66 gx_setcurrentpoint_from_path(gs_imager_state *pis, gx_path *path)
67 {
68     gs_point pt;
69 
70     pt.x = fixed2float(path->position.x);
71     pt.y = fixed2float(path->position.y);
72     gx_setcurrentpoint(pis, pt.x, pt.y);
73     pis->current_point_valid = true;
74     return 0;
75 }
76 
77 private inline int
gs_arc_add_inline(gs_state * pgs,bool cw,floatp xc,floatp yc,floatp rad,floatp a1,floatp a2,bool add)78 gs_arc_add_inline(gs_state *pgs, bool cw, floatp xc, floatp yc, floatp rad,
79 		    floatp a1, floatp a2, bool add)
80 {
81     int code = gs_imager_arc_add(pgs->path, (gs_imager_state *)pgs, cw, xc, yc, rad, a1, a2, add);
82 
83     if (code < 0)
84 	return code;
85     return gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
86 }
87 
88 int
gs_arc(gs_state * pgs,floatp xc,floatp yc,floatp r,floatp ang1,floatp ang2)89 gs_arc(gs_state * pgs,
90        floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
91 {
92     return gs_arc_add_inline(pgs, false, xc, yc, r, ang1, ang2, true);
93 }
94 
95 int
gs_arcn(gs_state * pgs,floatp xc,floatp yc,floatp r,floatp ang1,floatp ang2)96 gs_arcn(gs_state * pgs,
97 	floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
98 {
99     return gs_arc_add_inline(pgs, true, xc, yc, r, ang1, ang2, true);
100 }
101 
102 int
gs_arc_add(gs_state * pgs,bool clockwise,floatp axc,floatp ayc,floatp arad,floatp aang1,floatp aang2,bool add_line)103 gs_arc_add(gs_state * pgs, bool clockwise, floatp axc, floatp ayc,
104 	   floatp arad, floatp aang1, floatp aang2, bool add_line)
105 {
106     return gs_arc_add_inline(pgs, clockwise, axc, ayc, arad,
107 			     aang1, aang2, add_line);
108 }
109 
110 /* Compute the next curve as part of an arc. */
111 private int
next_arc_curve(arc_curve_params_t * arc,fixed anext)112 next_arc_curve(arc_curve_params_t * arc, fixed anext)
113 {
114     double x0 = arc->p0.x = arc->p3.x;
115     double y0 = arc->p0.y = arc->p3.y;
116     double trad = arc->radius *
117 	tan(fixed2float(anext - arc->angle) *
118 	    (degrees_to_radians / 2));
119 
120     arc->pt.x = x0 - trad * arc->sincos.sin;
121     arc->pt.y = y0 + trad * arc->sincos.cos;
122     gs_sincos_degrees(fixed2float(anext), &arc->sincos);
123     arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos;
124     arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin;
125     arc->angle = anext;
126     return arc_add(arc, false);
127 }
128 /*
129  * Use this when both arc.angle and anext are multiples of 90 degrees,
130  * and anext = arc.angle +/- 90.
131  */
132 private int
next_arc_quadrant(arc_curve_params_t * arc,fixed anext)133 next_arc_quadrant(arc_curve_params_t * arc, fixed anext)
134 {
135     double x0 = arc->p0.x = arc->p3.x;
136     double y0 = arc->p0.y = arc->p3.y;
137 
138     if (!arc->fast_quadrant) {
139 	/*
140 	 * If the CTM is well-behaved, we can pre-calculate the delta
141 	 * from the arc points to the control points.
142 	 */
143 	const gs_imager_state *pis = arc->pis;
144 	double scale;
145 
146 	if (is_fzero2(pis->ctm.xy, pis->ctm.yx) ?
147 	    (scale = fabs(pis->ctm.xx)) == fabs(pis->ctm.yy) :
148 	    is_fzero2(pis->ctm.xx, pis->ctm.yy) ?
149 	    (scale = fabs(pis->ctm.xy)) == fabs(pis->ctm.yx) :
150 	    0
151 	    ) {
152 	    double scaled_radius = arc->radius * scale;
153 
154 	    arc->scaled_radius = float2fixed(scaled_radius);
155 	    arc->quadrant_delta =
156 		float2fixed(scaled_radius * quarter_arc_fraction);
157 	    arc->fast_quadrant = 1;
158 	} else {
159 	    arc->fast_quadrant = -1;
160 	}
161     }
162     /*
163      * We know that anext is a multiple of 90 (as a fixed); we want
164      * (anext / 90) & 3.  The following is much faster than a division.
165      */
166     switch ((fixed2int(anext) >> 1) & 3) {
167     case 0:
168 	arc->sincos.sin = 0, arc->sincos.cos = 1;
169 	arc->p3.x = x0 = arc->center.x + arc->radius;
170 	arc->p3.y = arc->center.y;
171 	break;
172     case 1:
173 	arc->sincos.sin = 1, arc->sincos.cos = 0;
174 	arc->p3.x = arc->center.x;
175 	arc->p3.y = y0 = arc->center.y + arc->radius;
176 	break;
177     case 2:
178 	arc->sincos.sin = 0, arc->sincos.cos = -1;
179 	arc->p3.x = x0 = arc->center.x - arc->radius;
180 	arc->p3.y = arc->center.y;
181 	break;
182     case 3:
183 	arc->sincos.sin = -1, arc->sincos.cos = 0;
184 	arc->p3.x = arc->center.x;
185 	arc->p3.y = y0 = arc->center.y - arc->radius;
186 	break;
187     }
188     arc->pt.x = x0, arc->pt.y = y0;
189     arc->angle = anext;
190     return arc_add(arc, true);
191 }
192 
193 int
gs_imager_arc_add(gx_path * ppath,gs_imager_state * pis,bool clockwise,floatp axc,floatp ayc,floatp arad,floatp aang1,floatp aang2,bool add_line)194 gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
195 	    floatp axc, floatp ayc, floatp arad, floatp aang1, floatp aang2,
196 		  bool add_line)
197 {
198     double ar = arad;
199     fixed ang1 = float2fixed(aang1), ang2 = float2fixed(aang2), anext;
200     double ang1r;		/* reduced angle */
201     arc_curve_params_t arc;
202     int code;
203 
204     arc.ppath = ppath;
205     arc.pis = pis;
206     arc.center.x = axc;
207     arc.center.y = ayc;
208 #define fixed_90 int2fixed(90)
209 #define fixed_180 int2fixed(180)
210 #define fixed_360 int2fixed(360)
211     if (ar < 0) {
212 	ang1 += fixed_180;
213 	ang2 += fixed_180;
214 	ar = -ar;
215     }
216     arc.radius = ar;
217     arc.action = (add_line ? arc_lineto : arc_moveto);
218     arc.notes = sn_none;
219     arc.fast_quadrant = 0;
220     ang1r = fixed2float(ang1 % fixed_360);
221     gs_sincos_degrees(ang1r, &arc.sincos);
222     arc.p3.x = axc + ar * arc.sincos.cos;
223     arc.p3.y = ayc + ar * arc.sincos.sin;
224     if (clockwise) {
225 	while (ang1 < ang2)
226 	    ang2 -= fixed_360;
227 	if (ang2 < 0) {
228 	    fixed adjust = ROUND_UP(-ang2, fixed_360);
229 
230 	    ang1 += adjust, ang2 += adjust;
231 	}
232 	arc.angle = ang1;
233 	if (ang1 == ang2)
234 	    goto last;
235 	/* Do the first part, up to a multiple of 90 degrees. */
236 	if (!arc.sincos.orthogonal) {
237 	    anext = ROUND_DOWN(arc.angle - fixed_epsilon, fixed_90);
238 	    if (anext < ang2)
239 		goto last;
240 	    code = next_arc_curve(&arc, anext);
241 	    if (code < 0)
242 		return code;
243 	    arc.action = arc_nothing;
244 	    arc.notes = sn_not_first;
245 	}
246 	/* Do multiples of 90 degrees.  Invariant: ang1 >= ang2 >= 0. */
247 	while ((anext = arc.angle - fixed_90) >= ang2) {
248 	    code = next_arc_quadrant(&arc, anext);
249 	    if (code < 0)
250 		return code;
251 	    arc.action = arc_nothing;
252 	    arc.notes = sn_not_first;
253 	}
254     } else {
255 	while (ang2 < ang1)
256 	    ang2 += fixed_360;
257 	if (ang1 < 0) {
258 	    fixed adjust = ROUND_UP(-ang1, fixed_360);
259 
260 	    ang1 += adjust, ang2 += adjust;
261 	}
262 	arc.angle = ang1;
263 	if (ang1 == ang2)
264 	    return next_arc_curve(&arc, ang2);
265 	/* Do the first part, up to a multiple of 90 degrees. */
266 	if (!arc.sincos.orthogonal) {
267 	    anext = ROUND_UP(arc.angle + fixed_epsilon, fixed_90);
268 	    if (anext > ang2)
269 		goto last;
270 	    code = next_arc_curve(&arc, anext);
271 	    if (code < 0)
272 		return code;
273 	    arc.action = arc_nothing;
274 	    arc.notes = sn_not_first;
275 	}
276 	/* Do multiples of 90 degrees.  Invariant: 0 <= ang1 <= ang2. */
277 	while ((anext = arc.angle + fixed_90) <= ang2) {
278 	    code = next_arc_quadrant(&arc, anext);
279 	    if (code < 0)
280 		return code;
281 	    arc.action = arc_nothing;
282 	    arc.notes = sn_not_first;
283 	}
284     }
285     /*
286      * Do the last curve of the arc, if any.
287      */
288     if (arc.angle == ang2)
289 	return 0;
290 last:
291     return next_arc_curve(&arc, ang2);
292 }
293 
294 int
gs_arcto(gs_state * pgs,floatp ax1,floatp ay1,floatp ax2,floatp ay2,floatp arad,float retxy[4])295 gs_arcto(gs_state * pgs,
296 floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
297 {
298     double xt0, yt0, xt2, yt2;
299     gs_point up0;
300 
301 #define ax0 up0.x
302 #define ay0 up0.y
303     /* Transform the current point back into user coordinates. */
304     int code = gs_currentpoint(pgs, &up0);
305 
306     if (code < 0)
307 	return code;
308     {				/* Now we have to compute the tangent points. */
309 	/* Basically, the idea is to compute the tangent */
310 	/* of the bisector by using tan(x+y) and tan(z/2) */
311 	/* formulas, without ever using any trig. */
312 	double dx0 = ax0 - ax1, dy0 = ay0 - ay1;
313 	double dx2 = ax2 - ax1, dy2 = ay2 - ay1;
314 
315 	/* Compute the squared lengths from p1 to p0 and p2. */
316 	double sql0 = dx0 * dx0 + dy0 * dy0;
317 	double sql2 = dx2 * dx2 + dy2 * dy2;
318 
319 	/* Compute the distance from p1 to the tangent points. */
320 	/* This is the only messy part. */
321 	double num = dy0 * dx2 - dy2 * dx0;
322 	double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
323 
324 	/* Check for collinear points. */
325 	if (denom == 0) {
326 	    code = gs_lineto(pgs, ax1, ay1);
327 	    xt0 = xt2 = ax1;
328 	    yt0 = yt2 = ay1;
329 	} else {		/* not collinear */
330 	    double dist = fabs(arad * num / denom);
331 	    double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
332 	    arc_curve_params_t arc;
333 
334 	    arc.ppath = pgs->path;
335 	    arc.pis = (gs_imager_state *) pgs;
336 	    arc.radius = arad;
337 	    arc.action = arc_lineto;
338 	    arc.notes = sn_none;
339 	    if (arad < 0)
340 		l0 = -l0, l2 = -l2;
341 	    arc.p0.x = xt0 = ax1 + dx0 * l0;
342 	    arc.p0.y = yt0 = ay1 + dy0 * l0;
343 	    arc.p3.x = xt2 = ax1 + dx2 * l2;
344 	    arc.p3.y = yt2 = ay1 + dy2 * l2;
345 	    arc.pt.x = ax1;
346 	    arc.pt.y = ay1;
347 	    code = arc_add(&arc, false);
348 	    if (code == 0)
349 		code = gx_setcurrentpoint_from_path((gs_imager_state *)pgs, pgs->path);
350 	}
351     }
352     if (retxy != 0) {
353 	retxy[0] = xt0;
354 	retxy[1] = yt0;
355 	retxy[2] = xt2;
356 	retxy[3] = yt2;
357     }
358     return code;
359 }
360 
361 /* Internal routine for adding an arc to the path. */
362 private int
arc_add(const arc_curve_params_t * arc,bool is_quadrant)363 arc_add(const arc_curve_params_t * arc, bool is_quadrant)
364 {
365     gx_path *path = arc->ppath;
366     gs_imager_state *pis = arc->pis;
367     double x0 = arc->p0.x, y0 = arc->p0.y;
368     double xt = arc->pt.x, yt = arc->pt.y;
369     floatp fraction;
370     gs_fixed_point p0, p2, p3, pt;
371     int code;
372 
373     if ((arc->action != arc_nothing &&
374 #if !PRECISE_CURRENTPOINT
375 	 (code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0) ||
376 	(code = gs_point_transform2fixed(&pis->ctm, xt, yt, &pt)) < 0 ||
377 	(code = gs_point_transform2fixed(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 ||
378 #else
379 	 (code = gs_point_transform2fixed_rounding(&pis->ctm, x0, y0, &p0)) < 0) ||
380 	(code = gs_point_transform2fixed_rounding(&pis->ctm, xt, yt, &pt)) < 0 ||
381 	(code = gs_point_transform2fixed_rounding(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 ||
382 #endif
383 	(code =
384 	 (arc->action == arc_nothing ?
385 	  (p0.x = path->position.x, p0.y = path->position.y, 0) :
386 	  arc->action == arc_lineto && path_position_valid(path) ?
387 	  gx_path_add_line(path, p0.x, p0.y) :
388 	  /* action == arc_moveto, or lineto with no current point */
389 	  gx_path_add_point(path, p0.x, p0.y))) < 0
390 	)
391 	return code;
392     /* Compute the fraction coefficient for the curve. */
393     /* See gx_path_add_partial_arc for details. */
394     if (is_quadrant) {
395 	/* one of |dx| and |dy| is r, the other is zero */
396 	fraction = quarter_arc_fraction;
397 	if (arc->fast_quadrant > 0) {
398 	    /*
399 	     * The CTM is well-behaved, and we have pre-calculated the delta
400 	     * from the circumference points to the control points.
401 	     */
402 	    fixed delta = arc->quadrant_delta;
403 
404 	    if (pt.x != p0.x)
405 		p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta);
406 	    if (pt.y != p0.y)
407 		p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta);
408 	    p2.x = (pt.x == p3.x ? p3.x :
409 		    pt.x > p3.x ? p3.x + delta : p3.x - delta);
410 	    p2.y = (pt.y == p3.y ? p3.y :
411 		    pt.y > p3.y ? p3.y + delta : p3.y - delta);
412 	    goto add;
413 	}
414     } else {
415 	double r = arc->radius;
416 	floatp dx = xt - x0, dy = yt - y0;
417 	double dist = dx * dx + dy * dy;
418 	double r2 = r * r;
419 
420 	if (dist >= r2 * 1.0e8)	/* almost zero radius; */
421 	    /* the >= catches dist == r == 0 */
422 	    fraction = 0.0;
423 	else
424 	    fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2));
425     }
426     p0.x += (fixed)((pt.x - p0.x) * fraction);
427     p0.y += (fixed)((pt.y - p0.y) * fraction);
428     p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction);
429     p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction);
430 add:
431     if_debug8('r',
432 	      "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
433 	      fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y,
434 	      (int)arc->action);
435     /* Open-code gx_path_add_partial_arc_notes */
436     return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y,
437 				   arc->notes | sn_from_arc);
438 }
439 
440 void
make_quadrant_arc(gs_point * p,const gs_point * c,const gs_point * p0,const gs_point * p1,double r)441 make_quadrant_arc(gs_point *p, const gs_point *c,
442 	const gs_point *p0, const gs_point *p1, double r)
443 {
444     p[0].x = c->x + p0->x * r;
445     p[0].y = c->y + p0->y * r;
446     p[1].x = c->x + p0->x * r + p1->x * r * quarter_arc_fraction;
447     p[1].y = c->y + p0->y * r + p1->y * r * quarter_arc_fraction;
448     p[2].x = c->x + p0->x * r * quarter_arc_fraction + p1->x * r;
449     p[2].y = c->y + p0->y * r * quarter_arc_fraction + p1->y * r;
450     p[3].x = c->x + p1->x * r;
451     p[3].y = c->y + p1->y * r;
452 }
453 
454 
455 /* ------ Path transformers ------ */
456 
457 int
gs_dashpath(gs_state * pgs)458 gs_dashpath(gs_state * pgs)
459 {
460     gx_path *ppath;
461     gx_path fpath;
462     int code;
463 
464     if (gs_currentdash_length(pgs) == 0)
465 	return 0;		/* no dash pattern */
466     code = gs_flattenpath(pgs);
467     if (code < 0)
468 	return code;
469     ppath = pgs->path;
470     gx_path_init_local(&fpath, ppath->memory);
471     code = gx_path_add_dash_expansion(ppath, &fpath, (gs_imager_state *)pgs);
472     if (code < 0) {
473 	gx_path_free(&fpath, "gs_dashpath");
474 	return code;
475     }
476     gx_path_assign_free(pgs->path, &fpath);
477     return 0;
478 }
479 
480 int
gs_flattenpath(gs_state * pgs)481 gs_flattenpath(gs_state * pgs)
482 {
483     gx_path *ppath = pgs->path;
484     gx_path fpath;
485     int code;
486 
487     if (!gx_path_has_curves(ppath))
488 	return 0;		/* nothing to do */
489     gx_path_init_local(&fpath, ppath->memory);
490     code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness,
491 					  pgs->accurate_curves);
492     if (code < 0) {
493 	gx_path_free(&fpath, "gs_flattenpath");
494 	return code;
495     }
496     gx_path_assign_free(ppath, &fpath);
497     return 0;
498 }
499 
500 int
gs_reversepath(gs_state * pgs)501 gs_reversepath(gs_state * pgs)
502 {
503     gx_path *ppath = pgs->path;
504     gx_path rpath;
505     int code;
506 
507     gx_path_init_local(&rpath, ppath->memory);
508     code = gx_path_copy_reversed(ppath, &rpath);
509     if (code < 0) {
510 	gx_path_free(&rpath, "gs_reversepath");
511 	return code;
512     }
513     if (pgs->current_point_valid) {
514 	/* Not empty. */
515 	gx_setcurrentpoint(pgs, fixed2float(rpath.position.x),
516 				fixed2float(rpath.position.y));
517 	pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x);
518 	pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y);
519     }
520     gx_path_assign_free(ppath, &rpath);
521     return 0;
522 }
523 
524 /* ------ Accessors ------ */
525 
526 int
gs_upathbbox(gs_state * pgs,gs_rect * pbox,bool include_moveto)527 gs_upathbbox(gs_state * pgs, gs_rect * pbox, bool include_moveto)
528 {
529     gs_fixed_rect fbox;		/* box in device coordinates */
530     gs_rect dbox;
531     int code = gx_path_bbox_set(pgs->path, &fbox);
532 
533     if (code < 0)
534 	return code;
535     /* If the path ends with a moveto and include_moveto is true, */
536     /* include the moveto in the bounding box. */
537     if (path_last_is_moveto(pgs->path) && include_moveto) {
538 	gs_fixed_point pt;
539 
540 	gx_path_current_point_inline(pgs->path, &pt);
541 	if (pt.x < fbox.p.x)
542 	    fbox.p.x = pt.x;
543 	if (pt.y < fbox.p.y)
544 	    fbox.p.y = pt.y;
545 	if (pt.x > fbox.q.x)
546 	    fbox.q.x = pt.x;
547 	if (pt.y > fbox.q.y)
548 	    fbox.q.y = pt.y;
549     }
550     /* Transform the result back to user coordinates. */
551     dbox.p.x = fixed2float(fbox.p.x);
552     dbox.p.y = fixed2float(fbox.p.y);
553     dbox.q.x = fixed2float(fbox.q.x);
554     dbox.q.y = fixed2float(fbox.q.y);
555     return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox);
556 }
557 
558 /* ------ Enumerators ------ */
559 
560 /* Start enumerating a path */
561 int
gs_path_enum_copy_init(gs_path_enum * penum,const gs_state * pgs,bool copy)562 gs_path_enum_copy_init(gs_path_enum * penum, const gs_state * pgs, bool copy)
563 {
564     gs_memory_t *mem = pgs->memory;
565 
566     if (copy) {
567 	gx_path *copied_path =
568 	gx_path_alloc(mem, "gs_path_enum_init");
569 	int code;
570 
571 	if (copied_path == 0)
572 	    return_error(gs_error_VMerror);
573 	code = gx_path_copy(pgs->path, copied_path);
574 	if (code < 0) {
575 	    gx_path_free(copied_path, "gs_path_enum_init");
576 	    return code;
577 	}
578 	gx_path_enum_init(penum, copied_path);
579 	penum->copied_path = copied_path;
580     } else {
581 	gx_path_enum_init(penum, pgs->path);
582     }
583     penum->memory = mem;
584     gs_currentmatrix(pgs, &penum->mat);
585     return 0;
586 }
587 
588 /* Enumerate the next element of a path. */
589 /* If the path is finished, return 0; */
590 /* otherwise, return the element type. */
591 int
gs_path_enum_next(gs_path_enum * penum,gs_point ppts[3])592 gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3])
593 {
594     gs_fixed_point fpts[3];
595     int pe_op = gx_path_enum_next(penum, fpts);
596     int code;
597 
598     switch (pe_op) {
599 	case 0:		/* all done */
600 	case gs_pe_closepath:
601 	    break;
602 	case gs_pe_curveto:
603 	    if ((code = gs_point_transform_inverse(
604 						      fixed2float(fpts[1].x),
605 						      fixed2float(fpts[1].y),
606 					      &penum->mat, &ppts[1])) < 0 ||
607 		(code = gs_point_transform_inverse(
608 						      fixed2float(fpts[2].x),
609 						      fixed2float(fpts[2].y),
610 						&penum->mat, &ppts[2])) < 0)
611 		return code;
612 	    /* falls through */
613 	case gs_pe_moveto:
614 	case gs_pe_lineto:
615 	    if ((code = gs_point_transform_inverse(
616 						      fixed2float(fpts[0].x),
617 						      fixed2float(fpts[0].y),
618 						&penum->mat, &ppts[0])) < 0)
619 		return code;
620 	default:		/* error */
621 	    break;
622     }
623     return pe_op;
624 }
625 
626 /* Clean up after a pathforall. */
627 void
gs_path_enum_cleanup(gs_path_enum * penum)628 gs_path_enum_cleanup(gs_path_enum * penum)
629 {
630     if (penum->copied_path != 0) {
631 	gx_path_free(penum->copied_path, "gs_path_enum_cleanup");
632 	penum->path = 0;
633 	penum->copied_path = 0;
634     }
635 }
636