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