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