Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
 
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
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
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
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
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
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
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
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
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
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
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
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
}