Coverage Report

Created: 2025-06-10 06:56

/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
658k
{
41
658k
    if (!path_position_valid(ppath))
42
327k
        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
330k
    ppt->x = ppath->position.x, ppt->y = ppath->position.y;
46
330k
    return 0;
47
658k
}
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
5.12M
{
68
5.12M
    if (ppath == NULL) {
69
0
        return_error(gs_error_unknownerror) ;
70
0
    }
71
5.12M
    if (ppath->bbox_accurate) {
72
        /* The bounding box was set by setbbox. */
73
0
        *pbox = ppath->bbox;
74
0
        return 0;
75
0
    }
76
5.12M
    if (ppath->first_subpath == 0) {
77
        /* The path is empty, use the current point if any. */
78
354k
        int code = gx_path_current_point(ppath, &pbox->p);
79
80
354k
        if (code < 0) {
81
            /*
82
             * Don't return garbage, in case the caller doesn't
83
             * check the return code.
84
             */
85
327k
            pbox->p.x = pbox->p.y = 0;
86
327k
        }
87
354k
        pbox->q = pbox->p;
88
354k
        return code;
89
354k
    }
90
    /* The stored bounding box may not be up to date. */
91
    /* Correct it now if necessary. */
92
4.77M
    if (ppath->box_last == ppath->current_subpath->last) {
93
        /* Box is up to date */
94
881k
        *pbox = ppath->bbox;
95
3.89M
    } else {
96
3.89M
        fixed px, py, qx, qy;
97
3.89M
        const segment *pseg = ppath->box_last;
98
99
3.89M
        if (pseg == 0) { /* box is uninitialized */
100
3.89M
            pseg = (const segment *)ppath->first_subpath;
101
3.89M
            px = qx = pseg->pt.x;
102
3.89M
            py = qy = pseg->pt.y;
103
3.89M
        } 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
3.89M
#define ADJUST_BBOX(pt)\
110
44.7M
  if ((pt).x < px) px = (pt).x;\
111
44.7M
  else if ((pt).x > qx) qx = (pt).x;\
112
44.7M
  if ((pt).y < py) py = (pt).y;\
113
44.7M
  else if ((pt).y > qy) qy = (pt).y
114
115
44.6M
        while ((pseg = pseg->next) != 0) {
116
40.7M
            switch (pseg->type) {
117
2.02M
                case s_curve:
118
2.02M
                    ADJUST_BBOX(((const curve_segment *)pseg)->p1);
119
2.02M
                    ADJUST_BBOX(((const curve_segment *)pseg)->p2);
120
                    /* falls through */
121
40.7M
                default:
122
40.7M
                    ADJUST_BBOX(pseg->pt);
123
40.7M
            }
124
40.7M
        }
125
3.89M
#undef ADJUST_BBOX
126
127
3.89M
#define STORE_BBOX(b)\
128
7.78M
  (b).p.x = px, (b).p.y = py, (b).q.x = qx, (b).q.y = qy;
129
3.89M
        STORE_BBOX(*pbox);
130
3.89M
        STORE_BBOX(ppath->bbox);
131
3.89M
#undef STORE_BBOX
132
133
3.89M
        ppath->box_last = ppath->current_subpath->last;
134
3.89M
    }
135
4.77M
    return 0;
136
4.77M
}
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
1.76k
{
142
1.76k
    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
1.76k
        return gx_path_bbox(ppath, pbox);
148
1.76k
}
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
8.80k
  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
8.80k
{
174
8.80k
    return gx_path_is_null_inline(ppath);
175
8.80k
}
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
254k
{
191
254k
    const segment *pseg1, *pseg2, *pseg3, *pseg4;
192
254k
    gx_path_rectangular_type type = prt_none;
193
254k
    fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y;
194
254k
    fixed x1, y1, x2, y2, x3, y3;
195
196
254k
    pseg1 = (const segment *)pseg0;
197
256k
    do {
198
256k
        pseg1 = pseg1->next;
199
256k
        if (pseg1 == NULL)
200
8
            return prt_none;
201
256k
        x1 = pseg1->pt.x;
202
256k
        y1 = pseg1->pt.y;
203
256k
        if (pseg1->type == s_curve) {
204
1.80k
            if (gx_curve_is_really_point(x0, y0, pseg1))
205
0
                continue; /* Ignore this one and try again */
206
1.80k
            if (gx_curve_is_really_line(x0, y0, pseg1))
207
0
                break; /* That'll do! */
208
1.80k
            return prt_none;
209
254k
        } else if (pseg1->type != s_line && pseg1->type != s_gap)
210
520
            return prt_none;
211
256k
    } while (x1 == x0 && y1 == y0);
212
213
251k
    pseg2 = pseg1;
214
252k
    do {
215
252k
        pseg2 = pseg2->next;
216
252k
        if (pseg2 == NULL)
217
116
            return prt_none;
218
252k
        x2 = pseg2->pt.x;
219
252k
        y2 = pseg2->pt.y;
220
252k
        if (pseg2->type == s_curve) {
221
361
            if (gx_curve_is_really_point(x1, y1, pseg2))
222
0
                continue; /* Ignore this one and try again */
223
361
            if (gx_curve_is_really_line(x1, y1, pseg2))
224
0
                break; /* That'll do! */
225
361
            return prt_none;
226
251k
        } else if (pseg2->type != s_line && pseg2->type != s_gap)
227
140
            return prt_none;
228
252k
    } while (x2 == x1 && y2 == y1);
229
230
251k
    pseg3 = pseg2;
231
251k
    do {
232
251k
        pseg3 = pseg3->next;
233
251k
        if (pseg3 == NULL)
234
60
            return prt_none;
235
251k
        x3 = pseg3->pt.x;
236
251k
        y3 = pseg3->pt.y;
237
251k
        if (pseg3->type == s_curve) {
238
74
            if (gx_curve_is_really_point(x2, y2, pseg3))
239
0
                continue; /* Ignore this one and try again */
240
74
            if (gx_curve_is_really_line(x2, y2, pseg3))
241
0
                break; /* That'll do! */
242
74
            return prt_none;
243
251k
        } else if (pseg3->type != s_line && pseg3->type != s_gap)
244
1.62k
            return prt_none;
245
251k
    } while (x3 == x2 && y3 == y2);
246
247
249k
    pseg4 = pseg3;
248
249k
    do {
249
249k
        pseg4 = pseg4->next;
250
249k
        if (pseg4 == NULL || pseg4->type == s_start) {
251
33.3k
            type = prt_open; /* M, L, L, L */
252
33.3k
            goto type_known;
253
33.3k
        }
254
216k
        if (pseg4->type == s_curve) {
255
5
            if (gx_curve_is_really_point(x3, y3, pseg4))
256
0
                continue; /* Ignore this one and try again */
257
5
            if (gx_curve_is_really_line(x3, y3, pseg4))
258
0
                break; /* That'll do! */
259
5
            return prt_none;
260
216k
        } else if (pseg4->type == s_line_close) {
261
193k
            type = prt_closed; /* M, L, L, L, C */
262
193k
            goto type_known;
263
193k
        }
264
216k
    } while (pseg4->pt.x == x3 && pseg4->pt.y == y3);
265
266
22.8k
    if (pseg4->pt.x != pseg0->pt.x || pseg4->pt.y != pseg0->pt.y)
267
22.5k
        return prt_none;
268
361
    else if (pseg4->next == NULL || pseg4->next->type == s_start)
269
57
        type = prt_fake_closed;  /* Mo, L, L, L, L, Mo */
270
304
    else
271
304
        return prt_none;
272
273
226k
type_known:
274
275
226k
    if ((x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) ||
276
226k
        (x0 == x3 && y3 == y2 && x2 == x1 && y1 == y0)) {
277
        /* Path is a rectangle.  Return the bounding box. */
278
222k
        if (x0 < x2)
279
186k
            pbox->p.x = x0, pbox->q.x = x2;
280
36.5k
        else
281
36.5k
            pbox->p.x = x2, pbox->q.x = x0;
282
222k
        if (y0 < y2)
283
145k
            pbox->p.y = y0, pbox->q.y = y2;
284
77.0k
        else
285
77.0k
            pbox->p.y = y2, pbox->q.y = y0;
286
411k
        while (pseg4 != 0 && pseg4->type != s_start)
287
189k
            pseg4 = pseg4->next;
288
222k
        *ppnext = (const subpath *)pseg4;
289
222k
        return type;
290
222k
    }
291
3.99k
    return prt_none;
292
226k
}
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
262k
{
297
262k
    const subpath *pnext;
298
299
262k
    return
300
262k
        (gx_path_subpath_count(ppath) == 1 ?
301
254k
         gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) :
302
262k
         prt_none);
303
262k
}
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
1.24M
{
310
1.24M
    segment *pseg;
311
312
1.24M
#define update_xy(pt)\
313
1.24M
  pt.x += dx, pt.y += dy
314
1.24M
    if (ppath->box_last != 0) {
315
0
        update_xy(ppath->bbox.p);
316
0
        update_xy(ppath->bbox.q);
317
0
    }
318
1.24M
    if (path_position_valid(ppath))
319
0
        update_xy(ppath->position);
320
1.24M
    for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
321
1.24M
         pseg = pseg->next
322
1.24M
        )
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
1.24M
#undef update_xy
334
1.24M
    return 0;
335
1.24M
}
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
3
{
413
3
    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
3
 nsp:
420
3
    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
3
#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
3
    if (ppath_old->first_subpath == 0 &&
498
3
        path_last_is_moveto(ppath_old)
499
3
        ) {
500
0
        int code = gx_path_add_point(ppath, ppath_old->position.x,
501
0
                                     ppath_old->position.y);
502
503
0
        if (code < 0)
504
0
            return code;
505
0
    }
506
#ifdef DEBUG
507
    if (gs_debug_c('P'))
508
        gx_dump_path(ppath, "after reversepath");
509
#endif
510
3
    return 0;
511
3
}
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.31M
{
629
1.31M
    penum->memory = 0;    /* path not copied */
630
1.31M
    penum->path = ppath;
631
1.31M
    penum->copied_path = 0; /* not copied */
632
1.31M
    penum->pseg = (const segment *)ppath->first_subpath;
633
1.31M
    penum->moveto_done = false;
634
1.31M
    penum->notes = sn_none;
635
1.31M
    return 0;
636
1.31M
}
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
28.7M
{
644
28.7M
    const segment *pseg = penum->pseg;
645
646
28.7M
    if (pseg == 0) {   /* We've enumerated all the segments, but there might be */
647
        /* a trailing moveto. */
648
1.23M
        const gx_path *ppath = penum->path;
649
650
1.23M
        if (path_last_is_moveto(ppath) && !penum->moveto_done) { /* Handle a trailing moveto */
651
154k
            penum->moveto_done = true;
652
154k
            penum->notes = sn_none;
653
154k
            ppts[0] = ppath->position;
654
154k
            return gs_pe_moveto;
655
154k
        }
656
1.08M
        return 0;
657
1.23M
    }
658
27.4M
    penum->pseg = pseg->next;
659
27.4M
    penum->notes = pseg->notes;
660
27.4M
    switch (pseg->type) {
661
11.3M
        case s_start:
662
11.3M
            ppts[0] = pseg->pt;
663
11.3M
            return gs_pe_moveto;
664
14.4M
        case s_line:
665
14.4M
            ppts[0] = pseg->pt;
666
14.4M
            return gs_pe_lineto;
667
0
        case s_gap:
668
0
            ppts[0] = pseg->pt;
669
0
            return gs_pe_gapto;
670
1.11M
        case s_line_close:
671
1.11M
            ppts[0] = pseg->pt;
672
1.11M
            return gs_pe_closepath;
673
560k
        case s_curve:
674
1.12M
#define pcseg ((const curve_segment *)pseg)
675
560k
            ppts[0] = pcseg->p1;
676
560k
            ppts[1] = pcseg->p2;
677
560k
            ppts[2] = pseg->pt;
678
560k
            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
27.4M
    }
684
27.4M
}
685
686
/* Return the notes from the last-enumerated segment. */
687
segment_notes
688
gx_path_enum_notes(const gs_path_enum * penum)
689
14.9M
{
690
14.9M
    return penum->notes;
691
14.9M
}
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
14.0k
{
700
14.0k
    const segment *pseg = penum->pseg;
701
702
14.0k
    if (pseg != 0) {
703
13.4k
        if ((pseg = pseg->prev) == 0)
704
0
            return false;
705
13.4k
        penum->pseg = pseg;
706
13.4k
        return true;
707
13.4k
    }
708
    /* We're at the end of the path.  Check to see whether */
709
    /* we need to back up over a trailing moveto. */
710
593
    {
711
593
        const gx_path *ppath = penum->path;
712
713
593
        if (path_last_is_moveto(ppath) && penum->moveto_done) {   /* Back up over the trailing moveto. */
714
593
            penum->moveto_done = false;
715
593
            return true;
716
593
        } {
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
}