Coverage Report

Created: 2025-06-10 07:24

/src/ghostpdl/base/gxpath2.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
/* Path tracing procedures for Ghostscript library */
18
#include "math_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gspath.h"   /* for gs_path_enum_alloc prototype */
22
#include "gsstruct.h"
23
#include "gxfixed.h"
24
#include "gxarith.h"
25
#include "gzpath.h"
26
27
/* Define the enumeration structure. */
28
public_st_path_enum();
29
30
/* Check whether current path has valid point */
31
bool
32
gx_path_position_valid(const gx_path *ppath)
33
0
{
34
0
    return path_position_valid(ppath);
35
0
}
36
37
/* Read the current point of a path. */
38
int
39
gx_path_current_point(const gx_path * ppath, gs_fixed_point * ppt)
40
4.59M
{
41
4.59M
    if (!path_position_valid(ppath))
42
441k
        return_error(gs_error_nocurrentpoint);
43
    /* Copying the coordinates individually */
44
    /* is much faster on a PC, and almost as fast on other machines.... */
45
4.15M
    ppt->x = ppath->position.x, ppt->y = ppath->position.y;
46
4.15M
    return 0;
47
4.59M
}
48
49
/* Read the start point of the current subpath. */
50
int
51
gx_path_subpath_start_point(const gx_path * ppath, gs_fixed_point * ppt)
52
0
{
53
0
    const subpath *psub = ppath->current_subpath;
54
55
0
    if (!psub)
56
0
        return_error(gs_error_nocurrentpoint);
57
0
    *ppt = psub->pt;
58
0
    return 0;
59
0
}
60
61
/* Read the bounding box of a path. */
62
/* Note that if the last element of the path is a moveto, */
63
/* the bounding box does not include this point, */
64
/* unless this is the only element of the path. */
65
int
66
gx_path_bbox(gx_path * ppath, gs_fixed_rect * pbox)
67
6.70M
{
68
6.70M
    if (ppath == NULL) {
69
0
        return_error(gs_error_unknownerror) ;
70
0
    }
71
6.70M
    if (ppath->bbox_accurate) {
72
        /* The bounding box was set by setbbox. */
73
111k
        *pbox = ppath->bbox;
74
111k
        return 0;
75
111k
    }
76
6.59M
    if (ppath->first_subpath == 0) {
77
        /* The path is empty, use the current point if any. */
78
473k
        int code = gx_path_current_point(ppath, &pbox->p);
79
80
473k
        if (code < 0) {
81
            /*
82
             * Don't return garbage, in case the caller doesn't
83
             * check the return code.
84
             */
85
441k
            pbox->p.x = pbox->p.y = 0;
86
441k
        }
87
473k
        pbox->q = pbox->p;
88
473k
        return code;
89
473k
    }
90
    /* The stored bounding box may not be up to date. */
91
    /* Correct it now if necessary. */
92
6.12M
    if (ppath->box_last == ppath->current_subpath->last) {
93
        /* Box is up to date */
94
917k
        *pbox = ppath->bbox;
95
5.20M
    } else {
96
5.20M
        fixed px, py, qx, qy;
97
5.20M
        const segment *pseg = ppath->box_last;
98
99
5.20M
        if (pseg == 0) { /* box is uninitialized */
100
5.20M
            pseg = (const segment *)ppath->first_subpath;
101
5.20M
            px = qx = pseg->pt.x;
102
5.20M
            py = qy = pseg->pt.y;
103
5.20M
        } else {
104
0
            px = ppath->bbox.p.x, py = ppath->bbox.p.y;
105
0
            qx = ppath->bbox.q.x, qy = ppath->bbox.q.y;
106
0
        }
107
108
/* Macro for adjusting the bounding box when adding a point */
109
5.20M
#define ADJUST_BBOX(pt)\
110
55.3M
  if ((pt).x < px) px = (pt).x;\
111
55.3M
  else if ((pt).x > qx) qx = (pt).x;\
112
55.3M
  if ((pt).y < py) py = (pt).y;\
113
55.3M
  else if ((pt).y > qy) qy = (pt).y
114
115
54.6M
        while ((pseg = pseg->next) != 0) {
116
49.4M
            switch (pseg->type) {
117
2.95M
                case s_curve:
118
2.95M
                    ADJUST_BBOX(((const curve_segment *)pseg)->p1);
119
2.95M
                    ADJUST_BBOX(((const curve_segment *)pseg)->p2);
120
                    /* falls through */
121
49.4M
                default:
122
49.4M
                    ADJUST_BBOX(pseg->pt);
123
49.4M
            }
124
49.4M
        }
125
5.20M
#undef ADJUST_BBOX
126
127
5.20M
#define STORE_BBOX(b)\
128
10.4M
  (b).p.x = px, (b).p.y = py, (b).q.x = qx, (b).q.y = qy;
129
5.20M
        STORE_BBOX(*pbox);
130
5.20M
        STORE_BBOX(ppath->bbox);
131
5.20M
#undef STORE_BBOX
132
133
5.20M
        ppath->box_last = ppath->current_subpath->last;
134
5.20M
    }
135
6.12M
    return 0;
136
6.12M
}
137
138
/* A variation of gs_path_bbox, to be used by the patbbox operator */
139
int
140
gx_path_bbox_set(gx_path * ppath, gs_fixed_rect * pbox)
141
2.68k
{
142
2.68k
    if (ppath->bbox_set) {
143
        /* The bounding box was set by setbbox. */
144
0
        *pbox = ppath->bbox;
145
0
        return 0;
146
0
    } else
147
2.68k
        return gx_path_bbox(ppath, pbox);
148
2.68k
}
149
150
/* Test if a path has any curves. */
151
#undef gx_path_has_curves
152
bool
153
gx_path_has_curves(const gx_path * ppath)
154
0
{
155
0
    return gx_path_has_curves_inline(ppath);
156
0
}
157
#define gx_path_has_curves(ppath)\
158
  gx_path_has_curves_inline(ppath)
159
160
/* Test if a path has no segments. */
161
#undef gx_path_is_void
162
bool
163
gx_path_is_void(const gx_path * ppath)
164
0
{
165
0
    return gx_path_is_void_inline(ppath);
166
0
}
167
#define gx_path_is_void(ppath)\
168
10.7k
  gx_path_is_void_inline(ppath)
169
170
/* Test if a path has no elements at all. */
171
bool
172
gx_path_is_null(const gx_path * ppath)
173
10.7k
{
174
10.7k
    return gx_path_is_null_inline(ppath);
175
10.7k
}
176
177
/*
178
 * Test if a subpath is a rectangle; if so, return its bounding box
179
 * and the start of the next subpath.
180
 * Note that this must recognize:
181
 *      ordinary closed rectangles (M, L, L, L, C);
182
 *      open rectangles (M, L, L, L);
183
 *      rectangles closed with lineto (Mo, L, L, L, Lo);
184
 *      rectangles closed with *both* lineto and closepath (bad PostScript,
185
 *        but unfortunately not rare) (Mo, L, L, L, Lo, C).
186
 */
187
gx_path_rectangular_type
188
gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox,
189
                          const subpath ** ppnext)
190
604k
{
191
604k
    const segment *pseg1, *pseg2, *pseg3, *pseg4;
192
604k
    gx_path_rectangular_type type = prt_none;
193
604k
    fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y;
194
604k
    fixed x1, y1, x2, y2, x3, y3;
195
196
604k
    pseg1 = (const segment *)pseg0;
197
677k
    do {
198
677k
        pseg1 = pseg1->next;
199
677k
        if (pseg1 == NULL)
200
2.24k
            return prt_none;
201
675k
        x1 = pseg1->pt.x;
202
675k
        y1 = pseg1->pt.y;
203
675k
        if (pseg1->type == s_curve) {
204
33.7k
            if (gx_curve_is_really_point(x0, y0, pseg1))
205
39
                continue; /* Ignore this one and try again */
206
33.6k
            if (gx_curve_is_really_line(x0, y0, pseg1))
207
172
                break; /* That'll do! */
208
33.5k
            return prt_none;
209
641k
        } else if (pseg1->type != s_line && pseg1->type != s_gap)
210
14.8k
            return prt_none;
211
675k
    } while (x1 == x0 && y1 == y0);
212
213
553k
    pseg2 = pseg1;
214
556k
    do {
215
556k
        pseg2 = pseg2->next;
216
556k
        if (pseg2 == NULL)
217
157k
            return prt_none;
218
399k
        x2 = pseg2->pt.x;
219
399k
        y2 = pseg2->pt.y;
220
399k
        if (pseg2->type == s_curve) {
221
4.65k
            if (gx_curve_is_really_point(x1, y1, pseg2))
222
6
                continue; /* Ignore this one and try again */
223
4.65k
            if (gx_curve_is_really_line(x1, y1, pseg2))
224
169
                break; /* That'll do! */
225
4.48k
            return prt_none;
226
394k
        } else if (pseg2->type != s_line && pseg2->type != s_gap)
227
2.52k
            return prt_none;
228
399k
    } while (x2 == x1 && y2 == y1);
229
230
389k
    pseg3 = pseg2;
231
390k
    do {
232
390k
        pseg3 = pseg3->next;
233
390k
        if (pseg3 == NULL)
234
1.26k
            return prt_none;
235
388k
        x3 = pseg3->pt.x;
236
388k
        y3 = pseg3->pt.y;
237
388k
        if (pseg3->type == s_curve) {
238
617
            if (gx_curve_is_really_point(x2, y2, pseg3))
239
0
                continue; /* Ignore this one and try again */
240
617
            if (gx_curve_is_really_line(x2, y2, pseg3))
241
129
                break; /* That'll do! */
242
488
            return prt_none;
243
388k
        } else if (pseg3->type != s_line && pseg3->type != s_gap)
244
21.0k
            return prt_none;
245
388k
    } while (x3 == x2 && y3 == y2);
246
247
366k
    pseg4 = pseg3;
248
367k
    do {
249
367k
        pseg4 = pseg4->next;
250
367k
        if (pseg4 == NULL || pseg4->type == s_start) {
251
39.7k
            type = prt_open; /* M, L, L, L */
252
39.7k
            goto type_known;
253
39.7k
        }
254
327k
        if (pseg4->type == s_curve) {
255
444
            if (gx_curve_is_really_point(x3, y3, pseg4))
256
0
                continue; /* Ignore this one and try again */
257
444
            if (gx_curve_is_really_line(x3, y3, pseg4))
258
122
                break; /* That'll do! */
259
322
            return prt_none;
260
326k
        } else if (pseg4->type == s_line_close) {
261
266k
            type = prt_closed; /* M, L, L, L, C */
262
266k
            goto type_known;
263
266k
        }
264
327k
    } while (pseg4->pt.x == x3 && pseg4->pt.y == y3);
265
266
60.7k
    if (pseg4->pt.x != pseg0->pt.x || pseg4->pt.y != pseg0->pt.y)
267
57.3k
        return prt_none;
268
3.38k
    else if (pseg4->next == NULL || pseg4->next->type == s_start)
269
746
        type = prt_fake_closed;  /* Mo, L, L, L, L, Mo */
270
2.63k
    else
271
2.63k
        return prt_none;
272
273
306k
type_known:
274
275
306k
    if ((x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) ||
276
306k
        (x0 == x3 && y3 == y2 && x2 == x1 && y1 == y0)) {
277
        /* Path is a rectangle.  Return the bounding box. */
278
294k
        if (x0 < x2)
279
241k
            pbox->p.x = x0, pbox->q.x = x2;
280
52.4k
        else
281
52.4k
            pbox->p.x = x2, pbox->q.x = x0;
282
294k
        if (y0 < y2)
283
177k
            pbox->p.y = y0, pbox->q.y = y2;
284
116k
        else
285
116k
            pbox->p.y = y2, pbox->q.y = y0;
286
550k
        while (pseg4 != 0 && pseg4->type != s_start)
287
256k
            pseg4 = pseg4->next;
288
294k
        *ppnext = (const subpath *)pseg4;
289
294k
        return type;
290
294k
    }
291
12.1k
    return prt_none;
292
306k
}
293
/* Test if an entire path to be filled is a rectangle. */
294
gx_path_rectangular_type
295
gx_path_is_rectangular(const gx_path * ppath, gs_fixed_rect * pbox)
296
639k
{
297
639k
    const subpath *pnext;
298
299
639k
    return
300
639k
        (gx_path_subpath_count(ppath) == 1 ?
301
604k
         gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) :
302
639k
         prt_none);
303
639k
}
304
305
/* Translate an already-constructed path (in device space). */
306
/* Don't bother to update the cbox. */
307
int
308
gx_path_translate(gx_path * ppath, fixed dx, fixed dy)
309
657k
{
310
657k
    segment *pseg;
311
312
657k
#define update_xy(pt)\
313
657k
  pt.x += dx, pt.y += dy
314
657k
    if (ppath->box_last != 0) {
315
0
        update_xy(ppath->bbox.p);
316
0
        update_xy(ppath->bbox.q);
317
0
    }
318
657k
    if (path_position_valid(ppath))
319
0
        update_xy(ppath->position);
320
657k
    for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
321
657k
         pseg = pseg->next
322
657k
        )
323
0
        switch (pseg->type) {
324
0
            case s_curve:
325
0
#define pcseg ((curve_segment *)pseg)
326
0
                update_xy(pcseg->p1);
327
0
                update_xy(pcseg->p2);
328
0
#undef pcseg
329
                /* fall through */
330
0
            default:
331
0
                update_xy(pseg->pt);
332
0
        }
333
657k
#undef update_xy
334
657k
    return 0;
335
657k
}
336
337
/* Scale an existing path by a power of 2 (positive or negative).
338
 * Currently the path drawing routines can't handle values
339
 * close to the edge of the representable space.
340
 * Also see clamp_point() in gspath.c .
341
 */
342
void
343
gx_point_scale_exp2(gs_fixed_point * pt, int sx, int sy)
344
0
{
345
0
    int v;
346
347
0
    if (sx > 0) {
348
0
        v = (max_int - int2fixed(1000)) >> sx; /* arbitrary */
349
0
        if (pt->x > v)
350
0
            pt->x = v;
351
0
        else if (pt->x < -v)
352
0
            pt->x = -v;
353
0
        pt->x <<= sx;
354
0
    } else
355
0
        pt->x >>= -sx;
356
357
0
    if (sy > 0) {
358
0
        v = (max_int - int2fixed(1000)) >> sy;
359
0
        if (pt->y > v)
360
0
            pt->y = v;
361
0
        else if (pt->y < -v)
362
0
            pt->y = -v;
363
0
        pt->y <<= sy;
364
0
    } else
365
0
        pt->y >>= -sy;
366
0
}
367
void
368
gx_rect_scale_exp2(gs_fixed_rect * pr, int sx, int sy)
369
0
{
370
0
    gx_point_scale_exp2(&pr->p, sx, sy);
371
0
    gx_point_scale_exp2(&pr->q, sx, sy);
372
0
}
373
int
374
gx_path_scale_exp2_shared(gx_path * ppath, int log2_scale_x, int log2_scale_y,
375
                          bool segments_shared)
376
0
{
377
0
    segment *pseg;
378
379
0
    gx_rect_scale_exp2(&ppath->bbox, log2_scale_x, log2_scale_y);
380
0
#define SCALE_XY(pt) gx_point_scale_exp2(&pt, log2_scale_x, log2_scale_y)
381
0
    SCALE_XY(ppath->position);
382
0
    if (!segments_shared) {
383
0
        for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
384
0
             pseg = pseg->next
385
0
             )
386
0
            switch (pseg->type) {
387
0
            case s_curve:
388
0
                SCALE_XY(((curve_segment *)pseg)->p1);
389
0
                SCALE_XY(((curve_segment *)pseg)->p2);
390
                /* fall through */
391
0
            default:
392
0
                SCALE_XY(pseg->pt);
393
0
            }
394
0
    }
395
0
#undef SCALE_XY
396
0
    return 0;
397
0
}
398
399
/*
400
 * Reverse a path.  We know ppath != ppath_old.
401
 * NOTE: in releases 5.01 and earlier, the implicit line added by closepath
402
 * became the first segment of the reversed path.  Starting in release
403
 * 5.02, the code follows the Adobe implementation (and LanguageLevel 3
404
 * specification), in which this line becomes the *last* segment of the
405
 * reversed path.  This can produce some quite unintuitive results.
406
 *
407
 * The order of the subpaths is unspecified in the PLRM, but the CPSI
408
 * reverses the subpaths, and the CET (11-05 p6, test 3) tests for it.
409
 */
410
int
411
gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath)
412
2
{
413
2
    const subpath *psub = ppath_old->current_subpath;
414
415
#ifdef DEBUG
416
    if (gs_debug_c('P'))
417
        gx_dump_path(ppath_old, "before reversepath");
418
#endif
419
2
 nsp:
420
2
    if (psub) {
421
0
        const segment *prev = psub->last;
422
0
        const segment *pseg;
423
0
        segment_notes notes =
424
0
            (prev == (const segment *)psub ? sn_none :
425
0
             psub->next->notes);
426
0
        segment_notes prev_notes;
427
0
        int code;
428
429
0
        if (!psub->is_closed) {
430
0
            code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y);
431
0
            if (code < 0)
432
0
                return code;
433
0
        }
434
        /*
435
         * The do ... while structure of this loop is artificial,
436
         * designed solely to keep compilers from complaining about
437
         * 'statement not reached' or 'end-of-loop code not reached'.
438
         * The normal exit from this loop is the goto statement in
439
         * the s_start arm of the switch.
440
         */
441
0
        do {
442
0
            pseg = prev;
443
0
            prev_notes = notes;
444
0
            prev = pseg->prev;
445
0
            notes = pseg->notes;
446
0
            prev_notes = (prev_notes & sn_not_first) |
447
0
                (notes & ~sn_not_first);
448
0
            switch (pseg->type) {
449
0
                case s_start:
450
                    /* Finished subpath */
451
0
                    if (psub->is_closed) {
452
0
                        code =
453
0
                            gx_path_close_subpath_notes(ppath, prev_notes);
454
0
                        if (code < 0)
455
0
                            return code;
456
0
                    }
457
0
                    do {
458
0
                        psub = (const subpath *)psub->prev;
459
0
                    } while (psub && psub->type != s_start);
460
0
                    goto nsp;
461
0
                case s_curve:
462
0
                    {
463
0
                        const curve_segment *pc =
464
0
                        (const curve_segment *)pseg;
465
466
0
                        code = gx_path_add_curve_notes(ppath,
467
0
                                                       pc->p2.x, pc->p2.y,
468
0
                                                       pc->p1.x, pc->p1.y,
469
0
                                        prev->pt.x, prev->pt.y, prev_notes);
470
0
                        break;
471
0
                    }
472
0
                case s_line:
473
0
                    code = gx_path_add_line_notes(ppath,
474
0
                                        prev->pt.x, prev->pt.y, prev_notes);
475
0
                    break;
476
0
                case s_gap:
477
0
                    code = gx_path_add_gap_notes(ppath,
478
0
                                        prev->pt.x, prev->pt.y, prev_notes);
479
0
                    break;
480
0
                case s_line_close:
481
                    /* Skip the closing line. */
482
0
                    code = gx_path_add_point(ppath, prev->pt.x,
483
0
                                             prev->pt.y);
484
0
                    break;
485
0
                default:  /* not possible */
486
0
                    return_error(gs_error_Fatal);
487
0
            }
488
0
        } while (code >= 0);
489
0
        return code;   /* only reached if code < 0 */
490
0
    }
491
2
#undef sn_not_end
492
    /*
493
     * In the Adobe implementations, reversepath discards a trailing
494
     * moveto unless the path consists only of a moveto.  We reproduce
495
     * this behavior here, even though we consider it a bug.
496
     */
497
2
    if (ppath_old->first_subpath == 0 &&
498
2
        path_last_is_moveto(ppath_old)
499
2
        ) {
500
1
        int code = gx_path_add_point(ppath, ppath_old->position.x,
501
1
                                     ppath_old->position.y);
502
503
1
        if (code < 0)
504
0
            return code;
505
1
    }
506
#ifdef DEBUG
507
    if (gs_debug_c('P'))
508
        gx_dump_path(ppath, "after reversepath");
509
#endif
510
2
    return 0;
511
2
}
512
513
int
514
gx_path_append_reversed(const gx_path * ppath_old, gx_path * ppath)
515
0
{
516
0
    const subpath *psub = ppath_old->current_subpath;
517
518
#ifdef DEBUG
519
    if (gs_debug_c('P'))
520
        gx_dump_path(ppath_old, "before reversepath");
521
#endif
522
0
 nsp:
523
0
    if (psub) {
524
0
        const segment *prev = psub->last;
525
0
        const segment *pseg;
526
0
        segment_notes notes =
527
0
            (prev == (const segment *)psub ? sn_none :
528
0
             psub->next->notes);
529
0
        segment_notes prev_notes;
530
0
        int code;
531
532
0
        if (!psub->is_closed) {
533
0
            code = gx_path_add_line(ppath, prev->pt.x, prev->pt.y);
534
0
            if (code < 0)
535
0
                return code;
536
0
        }
537
        /*
538
         * The do ... while structure of this loop is artificial,
539
         * designed solely to keep compilers from complaining about
540
         * 'statement not reached' or 'end-of-loop code not reached'.
541
         * The normal exit from this loop is the goto statement in
542
         * the s_start arm of the switch.
543
         */
544
0
        do {
545
0
            pseg = prev;
546
0
            prev_notes = notes;
547
0
            prev = pseg->prev;
548
0
            notes = pseg->notes;
549
0
            prev_notes = (prev_notes & sn_not_first) |
550
0
                (notes & ~sn_not_first);
551
0
            switch (pseg->type) {
552
0
                case s_start:
553
                    /* Finished subpath */
554
0
                    if (psub->is_closed) {
555
0
                        code =
556
0
                            gx_path_close_subpath_notes(ppath, prev_notes);
557
0
                        if (code < 0)
558
0
                            return code;
559
0
                    }
560
0
                    do {
561
0
                        psub = (const subpath *)psub->prev;
562
0
                    } while (psub && psub->type != s_start);
563
0
                    goto nsp;
564
0
                case s_curve:
565
0
                    {
566
0
                        const curve_segment *pc =
567
0
                        (const curve_segment *)pseg;
568
569
0
                        code = gx_path_add_curve_notes(ppath,
570
0
                                                       pc->p2.x, pc->p2.y,
571
0
                                                       pc->p1.x, pc->p1.y,
572
0
                                        prev->pt.x, prev->pt.y, prev_notes);
573
0
                        break;
574
0
                    }
575
0
                case s_line:
576
0
                    code = gx_path_add_line_notes(ppath,
577
0
                                        prev->pt.x, prev->pt.y, prev_notes);
578
0
                    break;
579
0
                case s_gap:
580
0
                    code = gx_path_add_gap_notes(ppath,
581
0
                                        prev->pt.x, prev->pt.y, prev_notes);
582
0
                    break;
583
0
                case s_line_close:
584
                    /* Skip the closing line. */
585
0
                    code = gx_path_add_point(ppath, prev->pt.x,
586
0
                                             prev->pt.y);
587
0
                    break;
588
0
                default:  /* not possible */
589
0
                    return_error(gs_error_Fatal);
590
0
            }
591
0
        } while (code >= 0);
592
0
        return code;   /* only reached if code < 0 */
593
0
    }
594
0
#undef sn_not_end
595
    /*
596
     * In the Adobe implementations, reversepath discards a trailing
597
     * moveto unless the path consists only of a moveto.  We reproduce
598
     * this behavior here, even though we consider it a bug.
599
     */
600
0
    if (ppath_old->first_subpath == 0 &&
601
0
        path_last_is_moveto(ppath_old)
602
0
        ) {
603
0
        int code = gx_path_add_point(ppath, ppath_old->position.x,
604
0
                                     ppath_old->position.y);
605
606
0
        if (code < 0)
607
0
            return code;
608
0
    }
609
#ifdef DEBUG
610
    if (gs_debug_c('P'))
611
        gx_dump_path(ppath, "after reversepath");
612
#endif
613
0
    return 0;
614
0
}
615
616
/* ------ Path enumeration ------ */
617
618
/* Allocate a path enumerator. */
619
gs_path_enum *
620
gs_path_enum_alloc(gs_memory_t * mem, client_name_t cname)
621
0
{
622
0
    return gs_alloc_struct(mem, gs_path_enum, &st_path_enum, cname);
623
0
}
624
625
/* Start enumerating a path. */
626
int
627
gx_path_enum_init(gs_path_enum * penum, const gx_path * ppath)
628
1.67M
{
629
1.67M
    penum->memory = 0;    /* path not copied */
630
1.67M
    penum->path = ppath;
631
1.67M
    penum->copied_path = 0; /* not copied */
632
1.67M
    penum->pseg = (const segment *)ppath->first_subpath;
633
1.67M
    penum->moveto_done = false;
634
1.67M
    penum->notes = sn_none;
635
1.67M
    return 0;
636
1.67M
}
637
638
/* Enumerate the next element of a path. */
639
/* If the path is finished, return 0; */
640
/* otherwise, return the element type. */
641
int
642
gx_path_enum_next(gs_path_enum * penum, gs_fixed_point ppts[3])
643
12.7M
{
644
12.7M
    const segment *pseg = penum->pseg;
645
646
12.7M
    if (pseg == 0) {   /* We've enumerated all the segments, but there might be */
647
        /* a trailing moveto. */
648
1.39M
        const gx_path *ppath = penum->path;
649
650
1.39M
        if (path_last_is_moveto(ppath) && !penum->moveto_done) { /* Handle a trailing moveto */
651
106k
            penum->moveto_done = true;
652
106k
            penum->notes = sn_none;
653
106k
            ppts[0] = ppath->position;
654
106k
            return gs_pe_moveto;
655
106k
        }
656
1.29M
        return 0;
657
1.39M
    }
658
11.3M
    penum->pseg = pseg->next;
659
11.3M
    penum->notes = pseg->notes;
660
11.3M
    switch (pseg->type) {
661
2.12M
        case s_start:
662
2.12M
            ppts[0] = pseg->pt;
663
2.12M
            return gs_pe_moveto;
664
6.35M
        case s_line:
665
6.35M
            ppts[0] = pseg->pt;
666
6.35M
            return gs_pe_lineto;
667
0
        case s_gap:
668
0
            ppts[0] = pseg->pt;
669
0
            return gs_pe_gapto;
670
1.38M
        case s_line_close:
671
1.38M
            ppts[0] = pseg->pt;
672
1.38M
            return gs_pe_closepath;
673
1.51M
        case s_curve:
674
3.03M
#define pcseg ((const curve_segment *)pseg)
675
1.51M
            ppts[0] = pcseg->p1;
676
1.51M
            ppts[1] = pcseg->p2;
677
1.51M
            ppts[2] = pseg->pt;
678
1.51M
            return gs_pe_curveto;
679
0
#undef pcseg
680
0
        default:
681
0
            lprintf1("bad type %x in gx_path_enum_next!\n", pseg->type);
682
0
            return_error(gs_error_Fatal);
683
11.3M
    }
684
11.3M
}
685
686
/* Return the notes from the last-enumerated segment. */
687
segment_notes
688
gx_path_enum_notes(const gs_path_enum * penum)
689
6.42M
{
690
6.42M
    return penum->notes;
691
6.42M
}
692
693
/* Back up 1 element in the path being enumerated. */
694
/* Return true if successful, false if we are at the beginning of the path. */
695
/* This implementation allows backing up multiple times, */
696
/* but no client currently relies on this. */
697
bool
698
gx_path_enum_backup(gs_path_enum * penum)
699
6.43k
{
700
6.43k
    const segment *pseg = penum->pseg;
701
702
6.43k
    if (pseg != 0) {
703
5.59k
        if ((pseg = pseg->prev) == 0)
704
0
            return false;
705
5.59k
        penum->pseg = pseg;
706
5.59k
        return true;
707
5.59k
    }
708
    /* We're at the end of the path.  Check to see whether */
709
    /* we need to back up over a trailing moveto. */
710
838
    {
711
838
        const gx_path *ppath = penum->path;
712
713
838
        if (path_last_is_moveto(ppath) && penum->moveto_done) {   /* Back up over the trailing moveto. */
714
838
            penum->moveto_done = false;
715
838
            return true;
716
838
        } {
717
0
            const subpath *psub = ppath->current_subpath;
718
719
0
            if (psub == 0) /* empty path */
720
0
                return false;
721
            /* Back up to the last segment of the last subpath. */
722
0
            penum->pseg = psub->last;
723
0
            return true;
724
0
        }
725
0
    }
726
0
}