Coverage Report

Created: 2025-06-24 07:01

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