Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gxpcopy.c
Line
Count
Source
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 copying and flattening */
18
#include "math_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gxfixed.h"
22
#include "gxfarith.h"
23
#include "gxgstate.h"   /* for access to line params */
24
#include "gzpath.h"
25
26
/* Forward declarations */
27
static void adjust_point_to_tangent(segment *, const segment *,
28
                                     const gs_fixed_point *);
29
30
static inline int
31
break_line_if_long(gx_path *ppath, const segment *pseg)
32
70.1M
{
33
70.1M
    fixed x0 = ppath->position.x;
34
70.1M
    fixed y0 = ppath->position.y;
35
36
70.1M
    if (gx_check_fixed_diff_overflow(pseg->pt.x, x0) ||
37
70.1M
        gx_check_fixed_diff_overflow(pseg->pt.y, y0)) {
38
31.4k
        fixed x, y;
39
40
31.4k
        if (gx_check_fixed_sum_overflow(pseg->pt.x, x0))
41
1.76k
            x = (pseg->pt.x >> 1) + (x0 >> 1);
42
29.6k
        else
43
29.6k
            x = (pseg->pt.x + x0) >> 1;
44
31.4k
        if (gx_check_fixed_sum_overflow(pseg->pt.y, y0))
45
863
            y = (pseg->pt.y >> 1) + (y0 >> 1);
46
30.5k
        else
47
30.5k
            y = (pseg->pt.y + y0) >> 1;
48
31.4k
        return gx_path_add_line_notes(ppath, x, y, pseg->notes);
49
        /* WARNING: Stringly speaking, the next half segment must get
50
           the sn_not_first flag. We don't bother, because that flag
51
           has no important meaning with colinear segments.
52
         */
53
31.4k
    }
54
70.1M
    return 0;
55
70.1M
}
56
static inline int
57
break_gap_if_long(gx_path *ppath, const segment *pseg)
58
95.8k
{
59
95.8k
    fixed x0 = ppath->position.x;
60
95.8k
    fixed y0 = ppath->position.y;
61
62
95.8k
    if (gx_check_fixed_diff_overflow(pseg->pt.x, x0) ||
63
95.8k
        gx_check_fixed_diff_overflow(pseg->pt.y, y0)) {
64
0
        fixed x, y;
65
66
0
        if (gx_check_fixed_sum_overflow(pseg->pt.x, x0))
67
0
            x = (pseg->pt.x >> 1) + (x0 >> 1);
68
0
        else
69
0
            x = (pseg->pt.x + x0) >> 1;
70
0
        if (gx_check_fixed_sum_overflow(pseg->pt.y, y0))
71
0
            y = (pseg->pt.y >> 1) + (y0 >> 1);
72
0
        else
73
0
            y = (pseg->pt.y + y0) >> 1;
74
0
        return gx_path_add_gap_notes(ppath, x, y, pseg->notes);
75
        /* WARNING: Stringly speaking, the next half segment must get
76
           the sn_not_first flag. We don't bother, because that flag
77
           has no important meaning with colinear segments.
78
         */
79
0
    }
80
95.8k
    return 0;
81
95.8k
}
82
83
/* Copy a path, optionally flattening or monotonizing it. */
84
/* If the copy fails, free the new path. */
85
int
86
gx_path_copy_reducing(const gx_path *ppath_old, gx_path *ppath,
87
                      fixed fixed_flatness, const gs_gstate *pgs,
88
                      gx_path_copy_options options)
89
13.2M
{
90
13.2M
    const segment *pseg;
91
13.2M
    fixed flat = fixed_flatness;
92
13.2M
    gs_fixed_point expansion;
93
    /*
94
     * Since we're going to be adding to the path, unshare it
95
     * before we start.
96
     */
97
13.2M
    int code = gx_path_unshare(ppath);
98
99
13.2M
    if (code < 0)
100
0
        return code;
101
#ifdef DEBUG
102
    if (gs_debug_c('P'))
103
        gx_dump_path(ppath_old, "before reducing");
104
#endif
105
13.2M
    if (options & pco_for_stroke) {
106
        /* Precompute the maximum expansion of the bounding box. */
107
268k
        double width = pgs->line_params.half_width;
108
109
268k
        expansion.x =
110
268k
            float2fixed((fabs(pgs->ctm.xx) + fabs(pgs->ctm.yx)) * width) * 2;
111
268k
        expansion.y =
112
268k
            float2fixed((fabs(pgs->ctm.xy) + fabs(pgs->ctm.yy)) * width) * 2;
113
268k
    } else
114
12.9M
        expansion.x = expansion.y = 0; /* Quiet gcc warning. */
115
13.2M
    pseg = (const segment *)(ppath_old->first_subpath);
116
154M
    while (pseg) {
117
141M
        switch (pseg->type) {
118
14.7M
            case s_start:
119
14.7M
                code = gx_path_add_point(ppath,
120
14.7M
                                         pseg->pt.x, pseg->pt.y);
121
14.7M
                break;
122
56.3M
            case s_curve:
123
56.3M
                {
124
56.3M
                    const curve_segment *pc = (const curve_segment *)pseg;
125
126
56.3M
                    if (fixed_flatness == max_fixed) { /* don't flatten */
127
28.6M
                        if (options & pco_monotonize)
128
0
                            code = gx_curve_monotonize(ppath, pc);
129
28.6M
                        else
130
28.6M
                            code = gx_path_add_curve_notes(ppath,
131
28.6M
                                     pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y,
132
28.6M
                                           pc->pt.x, pc->pt.y, pseg->notes);
133
28.6M
                    } else {
134
27.7M
                        fixed x0 = ppath->position.x;
135
27.7M
                        fixed y0 = ppath->position.y;
136
27.7M
                        segment_notes notes = pseg->notes;
137
27.7M
                        curve_segment cseg;
138
27.7M
                        int k;
139
140
27.7M
                        if (options & pco_for_stroke) {
141
                            /*
142
                             * When flattening for stroking, the flatness
143
                             * must apply to the outside of the resulting
144
                             * stroked region.  We approximate this by
145
                             * dividing the flatness by the ratio of the
146
                             * expanded bounding box to the original
147
                             * bounding box.  This is crude, but pretty
148
                             * simple to calculate, and produces reasonably
149
                             * good results.
150
                             */
151
2.34M
                            fixed min01, max01, min23, max23;
152
2.34M
                            fixed ex, ey, flat_x, flat_y;
153
154
2.34M
#define SET_EXTENT(r, c0, c1, c2, c3)\
155
4.69M
    BEGIN\
156
4.69M
        if (c0 < c1) min01 = c0, max01 = c1;\
157
4.69M
        else         min01 = c1, max01 = c0;\
158
4.69M
        if (c2 < c3) min23 = c2, max23 = c3;\
159
4.69M
        else         min23 = c3, max23 = c2;\
160
4.69M
        r = max(max01, max23) - min(min01, min23);\
161
4.69M
    END
162
2.34M
                            SET_EXTENT(ex, x0, pc->p1.x, pc->p2.x, pc->pt.x);
163
2.34M
                            SET_EXTENT(ey, y0, pc->p1.y, pc->p2.y, pc->pt.y);
164
2.34M
#undef SET_EXTENT
165
                            /*
166
                             * We check for the degenerate case specially
167
                             * to avoid a division by zero.
168
                             */
169
2.34M
                            if (ex == 0 || ey == 0)
170
73.5k
                                if (ex != 0) {
171
49.5k
                                    flat = fixed_mult_quo(fixed_flatness, ex,
172
49.5k
                                                          ex + expansion.x);
173
49.5k
                                    k = gx_curve_log2_samples(x0,y0,pc,flat);
174
49.5k
                                } else if (ey != 0) {
175
3.24k
                                    flat = fixed_mult_quo(fixed_flatness, ey,
176
3.24k
                                                          ey + expansion.y);
177
3.24k
                                    k = gx_curve_log2_samples(x0,y0,pc,flat);
178
3.24k
                                } else
179
20.7k
                                    k = 0;
180
2.27M
                            else {
181
2.27M
                                flat_x =
182
2.27M
                                    fixed_mult_quo(fixed_flatness, ex,
183
2.27M
                                                   ex + expansion.x);
184
2.27M
                                flat_y =
185
2.27M
                                    fixed_mult_quo(fixed_flatness, ey,
186
2.27M
                                                   ey + expansion.y);
187
2.27M
                                flat = min(flat_x, flat_y);
188
2.27M
                                k = gx_curve_log2_samples(x0, y0, pc, flat);
189
2.27M
                            }
190
2.34M
                        } else
191
25.3M
                            k = gx_curve_log2_samples(x0, y0, pc, flat);
192
27.7M
                        if (options & pco_accurate) {
193
27.7M
                            segment *start;
194
27.7M
                            segment *end;
195
196
                            /*
197
                             * Add an extra line, which will become
198
                             * the tangent segment.
199
                             */
200
27.7M
                            code = gx_path_add_line_notes(ppath, x0, y0,
201
27.7M
                                                          notes);
202
27.7M
                            if (code < 0)
203
0
                                break;
204
27.7M
                            start = ppath->current_subpath->last;
205
27.7M
                            notes |= sn_not_first;
206
27.7M
                            cseg = *pc;
207
27.7M
                            code = gx_subdivide_curve(ppath, k, &cseg, notes);
208
27.7M
                            if (code < 0)
209
0
                                break;
210
                            /*
211
                             * Adjust the first and last segments so that
212
                             * they line up with the tangents.
213
                             */
214
27.7M
                            end = ppath->current_subpath->last;
215
27.7M
                            if ((code = gx_path_add_line_notes(ppath,
216
27.7M
                                                          ppath->position.x,
217
27.7M
                                                          ppath->position.y,
218
27.7M
                                            pseg->notes | sn_not_first)) < 0)
219
0
                                break;
220
27.7M
                            if (start->next->pt.x != pc->p1.x || start->next->pt.y != pc->p1.y)
221
27.6M
                                adjust_point_to_tangent(start, start->next, &pc->p1);
222
29.3k
                            else if (start->next->pt.x != pc->p2.x || start->next->pt.y != pc->p2.y)
223
704
                                adjust_point_to_tangent(start, start->next, &pc->p2);
224
28.6k
                            else
225
28.6k
                                adjust_point_to_tangent(start, start->next, &end->prev->pt);
226
27.7M
                            if (end->prev->pt.x != pc->p2.x || end->prev->pt.y != pc->p2.y)
227
27.6M
                                adjust_point_to_tangent(end, end->prev, &pc->p2);
228
41.1k
                            else if (end->prev->pt.x != pc->p1.x || end->prev->pt.y != pc->p1.y)
229
573
                                adjust_point_to_tangent(end, end->prev, &pc->p1);
230
40.6k
                            else
231
40.6k
                                adjust_point_to_tangent(end, end->prev, &start->pt);
232
27.7M
                        } else {
233
0
                            cseg = *pc;
234
0
                            code = gx_subdivide_curve(ppath, k, &cseg, notes);
235
0
                        }
236
27.7M
                    }
237
56.3M
                    break;
238
56.3M
                }
239
57.5M
            case s_line:
240
57.5M
                code = break_line_if_long(ppath, pseg);
241
57.5M
                if (code < 0)
242
0
                    break;
243
57.5M
                code = gx_path_add_line_notes(ppath,
244
57.5M
                                       pseg->pt.x, pseg->pt.y, pseg->notes);
245
57.5M
                break;
246
95.8k
            case s_gap:
247
95.8k
                code = break_gap_if_long(ppath, pseg);
248
95.8k
                if (code < 0)
249
0
                    break;
250
95.8k
                code = gx_path_add_gap_notes(ppath,
251
95.8k
                                       pseg->pt.x, pseg->pt.y, pseg->notes);
252
95.8k
                break;
253
0
            case s_dash:
254
0
                {
255
0
                    const dash_segment *pd = (const dash_segment *)pseg;
256
257
0
                    code = gx_path_add_dash_notes(ppath,
258
0
                                       pd->pt.x, pd->pt.y, pd->tangent.x, pd->tangent.y, pseg->notes);
259
0
                    break;
260
95.8k
                }
261
12.6M
            case s_line_close:
262
12.6M
                code = break_line_if_long(ppath, pseg);
263
12.6M
                if (code < 0)
264
0
                    break;
265
12.6M
                code = gx_path_close_subpath(ppath);
266
12.6M
                break;
267
0
            default:    /* can't happen */
268
0
                code = gs_note_error(gs_error_unregistered);
269
141M
        }
270
141M
        if (code < 0) {
271
0
            gx_path_new(ppath);
272
0
            return code;
273
0
        }
274
141M
        pseg = pseg->next;
275
141M
    }
276
13.2M
    if (path_last_is_moveto(ppath_old)) {
277
1.21M
        code = gx_path_add_point(ppath, ppath_old->position.x,
278
1.21M
                          ppath_old->position.y);
279
1.21M
        if (code < 0) {
280
0
            gx_path_new(ppath);
281
0
            return code;
282
0
        }
283
1.21M
    }
284
13.2M
    if (ppath_old->bbox_set) {
285
0
        if (ppath->bbox_set) {
286
0
            ppath->bbox.p.x = min(ppath_old->bbox.p.x, ppath->bbox.p.x);
287
0
            ppath->bbox.p.y = min(ppath_old->bbox.p.y, ppath->bbox.p.y);
288
0
            ppath->bbox.q.x = max(ppath_old->bbox.q.x, ppath->bbox.q.x);
289
0
            ppath->bbox.q.y = max(ppath_old->bbox.q.y, ppath->bbox.q.y);
290
0
        } else {
291
0
            ppath->bbox_set = true;
292
0
            ppath->bbox = ppath_old->bbox;
293
0
        }
294
0
    }
295
#ifdef DEBUG
296
    if (gs_debug_c('P'))
297
        gx_dump_path(ppath, "after reducing");
298
#endif
299
13.2M
    return 0;
300
13.2M
}
301
302
/*
303
 * Adjust one end of a line (the first or last line of a flattened curve)
304
 * so it falls on the curve tangent.  The closest point on the line from
305
 * (0,0) to (C,D) to a point (U,V) -- i.e., the point on the line at which
306
 * a perpendicular line from the point intersects it -- is given by
307
 *      T = (C*U + D*V) / (C^2 + D^2)
308
 *      (X,Y) = (C*T,D*T)
309
 * However, any smaller value of T will also work: the one we actually
310
 * use is 0.25 * the value we just derived.  We must check that
311
 * numerical instabilities don't lead to a negative value of T.
312
 */
313
static void
314
adjust_point_to_tangent(segment * pseg, const segment * next,
315
                        const gs_fixed_point * p1)
316
55.4M
{
317
55.4M
    const fixed x0 = pseg->pt.x, y0 = pseg->pt.y;
318
55.4M
    const fixed fC = p1->x - x0, fD = p1->y - y0;
319
320
    /*
321
     * By far the commonest case is that the end of the curve is
322
     * horizontal or vertical.  Check for this specially, because
323
     * we can handle it with far less work (and no floating point).
324
     */
325
55.4M
    if (fC == 0) {
326
        /* Vertical tangent. */
327
8.67M
        const fixed DT = arith_rshift(next->pt.y - y0, 2);
328
329
8.67M
        if (fD == 0)
330
522k
            return;    /* anomalous case */
331
8.67M
        if_debug1('2', "[2]adjusting vertical: DT = %g\n",
332
8.14M
                  fixed2float(DT));
333
8.14M
        if ((DT ^ fD) > 0) /* lgtm [cpp/bitwise-sign-check] */
334
8.10M
            pseg->pt.y = DT + y0;
335
46.7M
    } else if (fD == 0) {
336
        /* Horizontal tangent. */
337
9.85M
        const fixed CT = arith_rshift(next->pt.x - x0, 2);
338
339
9.85M
        if_debug1('2', "[2]adjusting horizontal: CT = %g\n",
340
9.85M
                  fixed2float(CT));
341
9.85M
        if ((CT ^ fC) > 0) /* lgtm [cpp/bitwise-sign-check] */
342
9.72M
            pseg->pt.x = CT + x0;
343
36.8M
    } else {
344
        /* General case. */
345
36.8M
        const double C = fC, D = fD;
346
36.8M
        double T = (C * (next->pt.x - x0) + D * (next->pt.y - y0)) /
347
36.8M
        (C * C + D * D);
348
349
36.8M
        if_debug3('2', "[2]adjusting: C = %g, D = %g, T = %g\n",
350
36.8M
                  C, D, T);
351
36.8M
        if (T > 0) {
352
36.8M
            if (T > 1) {
353
                /* Don't go outside the curve bounding box. */
354
18.1M
                T = 1;
355
18.1M
            }
356
36.8M
            pseg->pt.x = arith_rshift((fixed) (C * T), 2) + x0;
357
36.8M
            pseg->pt.y = arith_rshift((fixed) (D * T), 2) + y0;
358
36.8M
        }
359
36.8M
    }
360
55.4M
}
361
362
/* ---------------- Monotonic curves ---------------- */
363
364
/* Test whether a path is free of non-monotonic curves. */
365
bool
366
gx_path__check_curves(const gx_path * ppath, gx_path_copy_options options, fixed fixed_flat)
367
0
{
368
0
    const segment *pseg = (const segment *)(ppath->first_subpath);
369
0
    gs_fixed_point pt0;
370
371
0
    pt0.x = pt0.y = 0; /* Quiet gcc warning. */
372
0
    while (pseg) {
373
0
        switch (pseg->type) {
374
0
            case s_start:
375
0
                {
376
0
                    const subpath *psub = (const subpath *)pseg;
377
378
                    /* Skip subpaths without curves. */
379
0
                    if (!psub->curve_count)
380
0
                        pseg = psub->last;
381
0
                }
382
0
                break;
383
0
            case s_line:
384
0
            case s_gap:
385
0
                if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) ||
386
0
                    gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y))
387
0
                    return false;
388
0
                break;
389
0
            case s_curve:
390
0
                {
391
0
                    const curve_segment *pc = (const curve_segment *)pseg;
392
393
0
                    if (options & pco_monotonize) {
394
0
                        double t[2];
395
0
                        int nz = gx_curve_monotonic_points(pt0.y,
396
0
                                               pc->p1.y, pc->p2.y, pc->pt.y, t);
397
398
0
                        if (nz != 0)
399
0
                            return false;
400
0
                        nz = gx_curve_monotonic_points(pt0.x,
401
0
                                               pc->p1.x, pc->p2.x, pc->pt.x, t);
402
0
                        if (nz != 0)
403
0
                            return false;
404
0
                    }
405
0
                    if (options & pco_small_curves) {
406
0
                        fixed ax, bx, cx, ay, by, cy;
407
0
                        int k = gx_curve_log2_samples(pt0.x, pt0.y, pc, fixed_flat);
408
409
0
                        if(!curve_coeffs_ranged(pt0.x, pc->p1.x, pc->p2.x, pc->pt.x,
410
0
                                pt0.y, pc->p1.y, pc->p2.y, pc->pt.y,
411
0
                                &ax, &bx, &cx, &ay, &by, &cy, k))
412
0
                            return false;
413
0
                    if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) ||
414
0
                        gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y))
415
0
                        return false;
416
0
                    }
417
0
                }
418
0
                break;
419
0
            default:
420
0
                ;
421
0
        }
422
0
        pt0 = pseg->pt;
423
0
        pseg = pseg->next;
424
0
    }
425
0
    return true;
426
0
}
427
428
/* Test whether a path is free of long segments. */
429
/* WARNING : This function checks the distance between
430
 * the starting point and the ending point of a segment.
431
 * When they are not too far, a curve nevertheless may be too long.
432
 * Don't worry about it here, because we assume
433
 * this function is never called with paths which have curves.
434
 */
435
bool
436
gx_path_has_long_segments(const gx_path * ppath)
437
4.94M
{
438
4.94M
    const segment *pseg = (const segment *)(ppath->first_subpath);
439
4.94M
    gs_fixed_point pt0;
440
441
4.94M
    pt0.x = pt0.y = 0; /* Quiet gcc warning. */
442
16.8M
    while (pseg) {
443
11.9M
        switch (pseg->type) {
444
3.03M
            case s_start:
445
3.03M
                break;
446
8.89M
            default:
447
8.89M
                if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) ||
448
8.89M
                    gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y))
449
8.96k
                    return true;
450
8.88M
                break;
451
11.9M
        }
452
11.9M
        pt0 = pseg->pt;
453
11.9M
        pseg = pseg->next;
454
11.9M
    }
455
4.94M
    return false;
456
4.94M
}
457
458
/* Monotonize a curve, by splitting it if necessary. */
459
/* In the worst case, this could split the curve into 9 pieces. */
460
int
461
gx_curve_monotonize(gx_path * ppath, const curve_segment * pc)
462
975k
{
463
975k
    fixed x0 = ppath->position.x, y0 = ppath->position.y;
464
975k
    segment_notes notes = pc->notes;
465
975k
    double t[5], tt = 1, tp;
466
975k
    int c[5];
467
975k
    int n0, n1, n, i, j, k = 0;
468
975k
    fixed ax, bx, cx, ay, by, cy, v01, v12;
469
975k
    fixed px, py, qx, qy, rx, ry, sx, sy;
470
975k
    const double delta = 0.0000001;
471
472
    /* Roots of the derivative : */
473
975k
    n0 = gx_curve_monotonic_points(x0, pc->p1.x, pc->p2.x, pc->pt.x, t);
474
975k
    n1 = gx_curve_monotonic_points(y0, pc->p1.y, pc->p2.y, pc->pt.y, t + n0);
475
975k
    n = n0 + n1;
476
975k
    if (n == 0)
477
948k
        return gx_path_add_curve_notes(ppath, pc->p1.x, pc->p1.y,
478
948k
                pc->p2.x, pc->p2.y, pc->pt.x, pc->pt.y, notes);
479
26.8k
    if (n0 > 0)
480
15.3k
        c[0] = 1;
481
26.8k
    if (n0 > 1)
482
2
        c[1] = 1;
483
26.8k
    if (n1 > 0)
484
12.5k
        c[n0] = 2;
485
26.8k
    if (n1 > 1)
486
0
        c[n0 + 1] = 2;
487
    /* Order roots : */
488
54.8k
    for (i = 0; i < n; i++)
489
28.9k
        for (j = i + 1; j < n; j++)
490
1.03k
            if (t[i] > t[j]) {
491
422
                int w;
492
422
                double v = t[i]; t[i] = t[j]; t[j] = v;
493
422
                w = c[i]; c[i] = c[j]; c[j] = w;
494
422
            }
495
    /* Drop roots near zero : */
496
26.8k
    for (k = 0; k < n; k++)
497
26.8k
        if (t[k] >= delta)
498
26.8k
            break;
499
    /* Merge close roots, and drop roots at 1 : */
500
26.8k
    if (t[n - 1] > 1 - delta)
501
0
        n--;
502
27.9k
    for (i = k + 1, j = k; i < n && t[k] < 1 - delta; i++)
503
1.03k
        if (any_abs(t[i] - t[j]) < delta) {
504
0
            t[j] = (t[j] + t[i]) / 2; /* Unlikely 3 roots are close. */
505
0
            c[j] |= c[i];
506
1.03k
        } else {
507
1.03k
            j++;
508
1.03k
            t[j] = t[i];
509
1.03k
            c[j] = c[i];
510
1.03k
        }
511
26.8k
    n = j + 1;
512
    /* Do split : */
513
26.8k
    curve_points_to_coefficients(x0, pc->p1.x, pc->p2.x, pc->pt.x, ax, bx, cx, v01, v12);
514
26.8k
    curve_points_to_coefficients(y0, pc->p1.y, pc->p2.y, pc->pt.y, ay, by, cy, v01, v12);
515
26.8k
    ax *= 3, bx *= 2; /* Coefficients of the derivative. */
516
26.8k
    ay *= 3, by *= 2;
517
26.8k
    px = x0;
518
26.8k
    py = y0;
519
26.8k
    qx = (fixed)((pc->p1.x - px) * t[0] + 0.5);
520
26.8k
    qy = (fixed)((pc->p1.y - py) * t[0] + 0.5);
521
26.8k
    tp = 0;
522
54.8k
    for (i = k; i < n; i++) {
523
27.9k
        double ti = t[i];
524
27.9k
        double t2 = ti * ti, t3 = t2 * ti;
525
27.9k
        double omt = 1 - ti, omt2 = omt * omt, omt3 = omt2 * omt;
526
27.9k
        double x = x0 * omt3 + 3 * pc->p1.x * omt2 * ti + 3 * pc->p2.x * omt * t2 + pc->pt.x * t3;
527
27.9k
        double y = y0 * omt3 + 3 * pc->p1.y * omt2 * ti + 3 * pc->p2.y * omt * t2 + pc->pt.y * t3;
528
27.9k
        double ddx = (c[i] & 1 ? 0 : ax * t2 + bx * ti + cx); /* Suppress noise. */
529
27.9k
        double ddy = (c[i] & 2 ? 0 : ay * t2 + by * ti + cy);
530
27.9k
        fixed dx = (fixed)(ddx + 0.5);
531
27.9k
        fixed dy = (fixed)(ddy + 0.5);
532
27.9k
        int code;
533
534
27.9k
        tt = (i + 1 < n ? t[i + 1] : 1) - ti;
535
27.9k
        rx = (fixed)(dx * (t[i] - tp) / 3 + 0.5);
536
27.9k
        ry = (fixed)(dy * (t[i] - tp) / 3 + 0.5);
537
27.9k
        sx = (fixed)(x + 0.5);
538
27.9k
        sy = (fixed)(y + 0.5);
539
        /* Suppress the derivative sign noise near a peak : */
540
27.9k
        if ((double)(sx - px) * qx + (double)(sy - py) * qy < 0)
541
0
            qx = -qx, qy = -qy;
542
27.9k
        if ((double)(sx - px) * rx + (double)(sy - py) * ry < 0)
543
0
            rx = -rx, ry = -qy;
544
        /* Do add : */
545
27.9k
        code = gx_path_add_curve_notes(ppath, px + qx, py + qy, sx - rx, sy - ry, sx, sy, notes);
546
27.9k
        if (code < 0)
547
0
            return code;
548
27.9k
        notes |= sn_not_first;
549
27.9k
        px = sx;
550
27.9k
        py = sy;
551
27.9k
        qx = (fixed)(dx * tt / 3 + 0.5);
552
27.9k
        qy = (fixed)(dy * tt / 3 + 0.5);
553
27.9k
        tp = t[i];
554
27.9k
    }
555
26.8k
    sx = pc->pt.x;
556
26.8k
    sy = pc->pt.y;
557
26.8k
    rx = (fixed)((pc->pt.x - pc->p2.x) * tt + 0.5);
558
26.8k
    ry = (fixed)((pc->pt.y - pc->p2.y) * tt + 0.5);
559
    /* Suppress the derivative sign noise near peaks : */
560
26.8k
    if ((double)(sx - px) * qx + (double)(sy - py) * qy < 0)
561
0
        qx = -qx, qy = -qy;
562
26.8k
    if ((double)(sx - px) * rx + (double)(sy - py) * ry < 0)
563
0
        rx = -rx, ry = -qy;
564
26.8k
    return gx_path_add_curve_notes(ppath, px + qx, py + qy, sx - rx, sy - ry, sx, sy, notes);
565
26.8k
}
566
567
/*
568
 * Split a curve if necessary into pieces that are monotonic in X or Y as a
569
 * function of the curve parameter t.  This allows us to rasterize curves
570
 * directly without pre-flattening.  This takes a fair amount of analysis....
571
 * Store the values of t of the split points in pst[0] and pst[1].  Return
572
 * the number of split points (0, 1, or 2).
573
 */
574
int
575
gx_curve_monotonic_points(fixed v0, fixed v1, fixed v2, fixed v3,
576
                          double pst[2])
577
1.95M
{
578
    /*
579
       Let
580
       v(t) = a*t^3 + b*t^2 + c*t + d, 0 <= t <= 1.
581
       Then
582
       dv(t) = 3*a*t^2 + 2*b*t + c.
583
       v(t) has a local minimum or maximum (or inflection point)
584
       precisely where dv(t) = 0.  Now the roots of dv(t) = 0 (i.e.,
585
       the zeros of dv(t)) are at
586
       t =  ( -2*b +/- sqrt(4*b^2 - 12*a*c) ) / 6*a
587
       =    ( -b +/- sqrt(b^2 - 3*a*c) ) / 3*a
588
       (Note that real roots exist iff b^2 >= 3*a*c.)
589
       We want to know if these lie in the range (0..1).
590
       (The endpoints don't count.)  Call such a root a "valid zero."
591
       Since computing the roots is expensive, we would like to have
592
       some cheap tests to filter out cases where they don't exist
593
       (i.e., where the curve is already monotonic).
594
     */
595
1.95M
    fixed v01, v12, a, b, c, b2, a3;
596
1.95M
    fixed dv_end, b2abs, a3abs;
597
598
1.95M
    curve_points_to_coefficients(v0, v1, v2, v3, a, b, c, v01, v12);
599
1.95M
    b2 = b << 1;
600
1.95M
    a3 = (a << 1) + a;
601
    /*
602
       If a = 0, the only possible zero is t = -c / 2*b.
603
       This zero is valid iff sign(c) != sign(b) and 0 < |c| < 2*|b|.
604
     */
605
1.95M
    if (a == 0) {
606
407k
        if ((b ^ c) < 0 && any_abs(c) < any_abs(b2) && c != 0) {
607
8.04k
            *pst = (double)(-c) / b2;
608
8.04k
            return 1;
609
8.04k
        } else
610
398k
            return 0;
611
407k
    }
612
    /*
613
       Iff a curve is horizontal at t = 0, c = 0.  In this case,
614
       there can be at most one other zero, at -2*b / 3*a.
615
       This zero is valid iff sign(a) != sign(b) and 0 < 2*|b| < 3*|a|.
616
     */
617
1.54M
    if (c == 0) {
618
214k
        if ((a ^ b) < 0 && any_abs(b2) < any_abs(a3) && b != 0) {
619
0
            *pst = (double)(-b2) / a3;
620
0
            return 1;
621
0
        } else
622
214k
            return 0;
623
214k
    }
624
    /*
625
       Similarly, iff a curve is horizontal at t = 1, 3*a + 2*b + c = 0.
626
       In this case, there can be at most one other zero,
627
       at -1 - 2*b / 3*a, iff sign(a) != sign(b) and 1 < -2*b / 3*a < 2,
628
       i.e., 3*|a| < 2*|b| < 6*|a|.
629
     */
630
1.33M
    else if ((dv_end = a3 + b2 + c) == 0) {
631
214k
        if ((a ^ b) < 0 &&
632
85.6k
            (b2abs = any_abs(b2)) > (a3abs = any_abs(a3)) &&
633
85.6k
            b2abs < a3abs << 1
634
214k
            ) {
635
0
            *pst = (double)(-b2 - a3) / a3;
636
0
            return 1;
637
0
        } else
638
214k
            return 0;
639
214k
    }
640
    /*
641
       If sign(dv_end) != sign(c), at least one valid zero exists,
642
       since dv(0) and dv(1) have opposite signs and hence
643
       dv(t) must be zero somewhere in the interval [0..1].
644
     */
645
1.11M
    else if ((dv_end ^ c) < 0);
646
    /*
647
       If sign(a) = sign(b), no valid zero exists,
648
       since dv is monotonic on [0..1] and has the same sign
649
       at both endpoints.
650
     */
651
1.09M
    else if ((a ^ b) >= 0)
652
547k
        return 0;
653
    /*
654
       Otherwise, dv(t) may be non-monotonic on [0..1]; it has valid zeros
655
       iff its sign anywhere in this interval is different from its sign
656
       at the endpoints, which occurs iff it has an extremum in this
657
       interval and the extremum is of the opposite sign from c.
658
       To find this out, we look for the local extremum of dv(t)
659
       by observing
660
       d2v(t) = 6*a*t + 2*b
661
       which has a zero only at
662
       t1 = -b / 3*a
663
       Now if t1 <= 0 or t1 >= 1, no valid zero exists.
664
       Note that we just determined that sign(a) != sign(b), so we know t1 > 0.
665
     */
666
548k
    else if (any_abs(b) >= any_abs(a3))
667
546k
        return 0;
668
    /*
669
       Otherwise, we just go ahead with the computation of the roots,
670
       and test them for being in the correct range.  Note that a valid
671
       zero is an inflection point of v(t) iff d2v(t) = 0; we don't
672
       bother to check for this case, since it's rare.
673
     */
674
22.2k
    {
675
22.2k
        double nbf = (double)(-b);
676
22.2k
        double a3f = (double)a3;
677
22.2k
        double radicand = nbf * nbf - a3f * c;
678
679
22.2k
        if (radicand < 0) {
680
1.40k
            if_debug1('2', "[2]negative radicand = %g\n", radicand);
681
1.40k
            return 0;
682
20.8k
        } {
683
20.8k
            double root = sqrt(radicand);
684
20.8k
            int nzeros = 0;
685
20.8k
            double z = (nbf - root) / a3f;
686
687
            /*
688
             * We need to return the zeros in the correct order.
689
             * We know that root is non-negative, but a3f may be either
690
             * positive or negative, so we need to check the ordering
691
             * explicitly.
692
             */
693
20.8k
            if_debug2('2', "[2]zeros at %g, %g\n", z, (nbf + root) / a3f);
694
20.8k
            if (z > 0 && z < 1)
695
9.93k
                *pst = z, nzeros = 1;
696
20.8k
            if (root != 0) {
697
20.8k
                z = (nbf + root) / a3f;
698
20.8k
                if (z > 0 && z < 1) {
699
9.94k
                    if (nzeros && a3f < 0)  /* order is reversed */
700
2
                        pst[1] = *pst, *pst = z;
701
9.94k
                    else
702
9.94k
                        pst[nzeros] = z;
703
9.94k
                    nzeros++;
704
9.94k
                }
705
20.8k
            }
706
20.8k
            return nzeros;
707
22.2k
        }
708
22.2k
    }
709
22.2k
}
710
711
/* ---------------- Path optimization for the filling algorithm. ---------------- */
712
713
static bool
714
find_contacting_segments(const subpath *sp0, segment *sp0last,
715
                         const subpath *sp1, segment *sp1last,
716
                         segment **sc0, segment **sc1)
717
0
{
718
0
    segment *s0, *s1;
719
0
    const segment *s0s, *s1s;
720
0
    int count0, count1, search_limit = 50;
721
0
    int min_length = fixed_1 * 1;
722
723
    /* This is a simplified algorithm, which only checks for quazi-colinear vertical lines.
724
       "Quazi-vertical" means dx <= 1 && dy >= min_length . */
725
    /* To avoid a big unuseful expence of the processor time,
726
       we search the first subpath from the end
727
       (assuming that it was recently merged near the end),
728
       and restrict the search with search_limit segments
729
       against a quadratic scanning of two long subpaths.
730
       Thus algorithm is not necessary finds anything contacting.
731
       Instead it either quickly finds something, or maybe not. */
732
0
    for (s0 = sp0last, count0 = 0; count0 < search_limit && s0 != (segment *)sp0; s0 = s0->prev, count0++) {
733
0
        s0s = s0->prev;
734
0
        if ((s0->type == s_line || s0->type == s_gap) &&
735
0
            (s0s->pt.x == s0->pt.x ||
736
0
             (any_abs(s0s->pt.x - s0->pt.x) == 1 &&
737
0
              any_abs(s0s->pt.y - s0->pt.y) > min_length))) {
738
0
            for (s1 = sp1last, count1 = 0; count1 < search_limit && s1 != (segment *)sp1; s1 = s1->prev, count1++) {
739
0
                s1s = s1->prev;
740
0
                if ((s1->type == s_line || s1->type == s_gap) &&
741
0
                    (s1s->pt.x == s1->pt.x ||
742
0
                     (any_abs(s1s->pt.x - s1->pt.x) == 1 && any_abs(s1s->pt.y - s1->pt.y) > min_length))) {
743
0
                    if (s0s->pt.x == s1s->pt.x || s0->pt.x == s1->pt.x || s0->pt.x == s1s->pt.x || s0s->pt.x == s1->pt.x) {
744
0
                        if (s0s->pt.y < s0->pt.y && s1s->pt.y > s1->pt.y) {
745
0
                            fixed y0 = max(s0s->pt.y, s1->pt.y);
746
0
                            fixed y1 = min(s0->pt.y, s1s->pt.y);
747
748
0
                            if (y0 <= y1) {
749
0
                                *sc0 = s0;
750
0
                                *sc1 = s1;
751
0
                                return true;
752
0
                            }
753
0
                        }
754
0
                        if (s0s->pt.y > s0->pt.y && s1s->pt.y < s1->pt.y) {
755
0
                            fixed y0 = max(s0->pt.y, s1s->pt.y);
756
0
                            fixed y1 = min(s0s->pt.y, s1->pt.y);
757
758
0
                            if (y0 <= y1) {
759
0
                                *sc0 = s0;
760
0
                                *sc1 = s1;
761
0
                                return true;
762
0
                            }
763
0
                        }
764
0
                    }
765
0
                }
766
0
            }
767
0
        }
768
0
    }
769
0
    return false;
770
0
}
771
772
int
773
gx_path_merge_contacting_contours(gx_path *ppath)
774
0
{
775
    /* Now this is a simplified algorithm,
776
       which merge only contours by a common quazi-vertical line. */
777
    /* Note the merged contour is not equivalent to sum of original contours,
778
       because we ignore small coordinate differences within fixed_epsilon. */
779
0
    int window = 5/* max spot holes */ * 6/* segments per subpath */;
780
0
    subpath *sp0 = ppath->segments->contents.subpath_first;
781
782
0
    for (; sp0 != NULL; sp0 = (subpath *)sp0->last->next) {
783
0
        segment *sp0last = sp0->last;
784
0
        subpath *sp1 = (subpath *)sp0last->next, *spnext;
785
0
        subpath *sp1p = sp0;
786
0
        int count;
787
788
0
        for (count = 0; sp1 != NULL && count < window; sp1 = spnext, count++) {
789
0
            segment *sp1last = sp1->last;
790
0
            segment *sc0, *sc1, *old_first;
791
792
0
            spnext = (subpath *)sp1last->next;
793
0
            if (find_contacting_segments(sp0, sp0last, sp1, sp1last, &sc0, &sc1)) {
794
                /* Detach the subpath 1 from the path: */
795
0
                sp1->prev->next = sp1last->next;
796
0
                if (sp1last->next != NULL)
797
0
                    sp1last->next->prev = sp1->prev;
798
0
                sp1->prev = 0;
799
0
                sp1last->next = 0;
800
0
                old_first = sp1->next;
801
                /* sp1 is not longer in use. Move subpath_current from it for safe removing : */
802
0
                if (ppath->segments->contents.subpath_current == sp1) {
803
0
                    ppath->segments->contents.subpath_current = sp1p;
804
0
                }
805
0
                if (sp1last->type == s_line_close) {
806
                    /* Change 'closepath' of the subpath 1 to a line (maybe degenerate) : */
807
0
                    sp1last->type = s_line;
808
                    /* sp1 is not longer in use. Free it : */
809
0
                    gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours");
810
0
                } else if (sp1last->pt.x == sp1->pt.x && sp1last->pt.y == sp1->pt.y) {
811
                    /* Implicit closepath with zero length. Don't need a new segment. */
812
                    /* sp1 is not longer in use. Free it : */
813
0
                    gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours");
814
0
                } else {
815
                    /* Insert the closing line segment. */
816
                    /* sp1 is not longer in use. Convert it to the line segment : */
817
0
                    sp1->type = s_line;
818
0
                    sp1last->next = (segment *)sp1;
819
0
                    sp1->next = NULL;
820
0
                    sp1->prev = sp1last;
821
0
                    sp1->last = NULL; /* Safety for garbager. */
822
0
                    sp1last = (segment *)sp1;
823
0
                }
824
0
                sp1 = 0; /* Safety. */
825
                /* Rotate the subpath 1 to sc1 : */
826
0
                {   /* Detach s_start and make a loop : */
827
0
                    sp1last->next = old_first;
828
0
                    old_first->prev = sp1last;
829
                    /* Unlink before sc1 : */
830
0
                    sp1last = sc1->prev;
831
0
                    sc1->prev->next = 0;
832
0
                    sc1->prev = 0; /* Safety. */
833
                    /* sp1 is not longer in use. Free it : */
834
0
                    if (ppath->segments->contents.subpath_current == sp1) {
835
0
                        ppath->segments->contents.subpath_current = sp1p;
836
0
                    }
837
0
                    gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours");
838
0
                    sp1 = 0; /* Safety. */
839
0
                }
840
                /* Insert the subpath 1 into the subpath 0 before sc0 :*/
841
0
                sc0->prev->next = sc1;
842
0
                sc1->prev = sc0->prev;
843
0
                sp1last->next = sc0;
844
0
                sc0->prev = sp1last;
845
                /* Remove degenearte "bridge" segments : (fixme: Not done due to low importance). */
846
                /* Edit the subpath count : */
847
0
                ppath->subpath_count--;
848
0
            } else
849
0
                sp1p = sp1;
850
0
        }
851
0
    }
852
0
    return 0;
853
0
}
854
855
static int
856
is_colinear(gs_fixed_rect *rect, fixed x, fixed y)
857
24.0k
{
858
24.0k
    fixed x0 = rect->p.x;
859
24.0k
    fixed y0 = rect->p.y;
860
24.0k
    fixed x1 = rect->q.x;
861
24.0k
    fixed y1 = rect->q.y;
862
863
24.0k
    if (x0 == x1) {
864
11.2k
        if (y0 == y1) {
865
            /* Initial case */
866
            /* Still counts as colinear */
867
9.63k
        } else if (x == x0) {
868
            /* OK! */
869
1.39k
        } else {
870
1.39k
            return 0; /* Not colinear */
871
1.39k
        }
872
12.7k
    } else if (rect->p.y == rect->q.y) {
873
5.44k
        if (y == rect->p.y) {
874
            /* OK */
875
5.20k
        } else {
876
246
            return 0; /* Not colinear */
877
246
        }
878
7.34k
    } else {
879
        /* Need to do hairy maths */
880
        /* The distance of a point (x,y) from the line passing through
881
         * (x0,y0) and (x1,y1) is:
882
         * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2)
883
         *
884
         * We want d <= epsilon to count as colinear.
885
         *
886
         * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2) <= epsilon
887
         *
888
         * |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| <= epsilon * SQR((y1-y0)^2 + (x1-x0)^2)
889
         *
890
         * ((y1-y0)x - (x1-x0)y + x1y0 - y1x0)^2 <= epsilon^2 * ((y1-y0)^2 + (x1-x0)^2)
891
         */
892
7.34k
        int64_t ix1 = ((int64_t)x1);
893
7.34k
        int64_t iy1 = ((int64_t)y1);
894
7.34k
        int64_t dx  = ix1 - x0;
895
7.34k
        int64_t dy  = iy1 - y0;
896
7.34k
        int64_t num = dy*x - dx*y + ix1*y0 - iy1*x0;
897
7.34k
        int64_t den = dx*dx + dy*dy;
898
7.34k
        int epsilon_squared = 2;
899
900
7.34k
        if (num < 0)
901
1.08k
            num = -num;
902
7.89k
        while (num > (1<<30)) {
903
548
            num >>= 2;
904
548
            den >>= 1;
905
548
            if (den == 0)
906
0
                return 0; /* Not colinear */
907
548
        }
908
7.34k
        num *= num;
909
7.34k
        if (num > epsilon_squared * den)
910
6.81k
            return 0;
911
7.34k
    }
912
    /* rect is not really a rect. It's just a pair of points. We guarantee that x0 <= x1. */
913
15.6k
    if (x == x0) {
914
3.36k
        if (y < y0)
915
210
            rect->p.y = y;
916
3.15k
        else if (y > y1)
917
1.32k
            rect->q.y = y;
918
12.2k
    } else if (x < x0) {
919
2.53k
        rect->p.x = x;
920
2.53k
        rect->p.y = y;
921
9.71k
    } else {
922
9.71k
        rect->q.x = x;
923
9.71k
        rect->q.y = y;
924
9.71k
    }
925
926
15.6k
    return 1;
927
24.0k
}
928
929
static int
930
gx_path_copy_eliding_1d(const gx_path *ppath_old, gx_path *ppath)
931
7.97k
{
932
7.97k
    const segment *pseg;
933
    /*
934
     * Since we're going to be adding to the path, unshare it
935
     * before we start.
936
     */
937
7.97k
    int code = gx_path_unshare(ppath);
938
939
7.97k
    if (code < 0)
940
0
        return code;
941
#ifdef DEBUG
942
    if (gs_debug_c('P'))
943
        gx_dump_path(ppath_old, "before eliding_1d");
944
#endif
945
946
7.97k
    pseg = (const segment *)(ppath_old->first_subpath);
947
16.6k
    while (pseg != NULL) {
948
8.62k
        const segment *look = pseg;
949
8.62k
        gs_fixed_rect rect;
950
951
8.62k
        rect.p.x = rect.q.x = look->pt.x;
952
8.62k
        rect.p.y = rect.q.y = look->pt.y;
953
954
8.62k
        if (look->type != s_start) {
955
0
            dlprintf("Unlikely?");
956
0
        }
957
958
8.62k
        look = look->next;
959
22.1k
        while (look != NULL && look->type != s_start) {
960
21.9k
            if (look->type == s_curve) {
961
6.63k
                const curve_segment *pc = (const curve_segment *)look;
962
6.63k
                if (!is_colinear(&rect, pc->p1.x, pc->p1.y) ||
963
1.09k
                    !is_colinear(&rect, pc->p2.x, pc->p2.y) ||
964
1.01k
                    !is_colinear(&rect, pc->pt.x, pc->pt.y))
965
6.22k
                    goto not_colinear;
966
15.3k
            } else if (!is_colinear(&rect, look->pt.x, look->pt.y)) {
967
2.22k
                goto not_colinear;
968
2.22k
            }
969
13.5k
            look = look->next;
970
13.5k
        }
971
175
        pseg = look;
972
175
        if (0)
973
0
        {
974
8.44k
not_colinear:
975
            /* Not colinear. We want to keep this section. */
976
151k
            while (look != NULL && look->type != s_start)
977
142k
                look = look->next;
978
168k
            while (pseg != look && code >= 0) {
979
                /* Copy */
980
159k
                switch (pseg->type) {
981
8.44k
                    case s_start:
982
8.44k
                        code = gx_path_add_point(ppath,
983
8.44k
                                                 pseg->pt.x, pseg->pt.y);
984
8.44k
                        break;
985
25.6k
                    case s_curve:
986
25.6k
                        {
987
25.6k
                            const curve_segment *pc = (const curve_segment *)pseg;
988
989
25.6k
                            code = gx_path_add_curve_notes(ppath,
990
25.6k
                                             pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y,
991
25.6k
                                                   pc->pt.x, pc->pt.y, pseg->notes);
992
25.6k
                            break;
993
0
                        }
994
122k
                    case s_line:
995
122k
                        code = gx_path_add_line_notes(ppath,
996
122k
                                               pseg->pt.x, pseg->pt.y, pseg->notes);
997
122k
                        break;
998
0
                    case s_gap:
999
0
                        code = gx_path_add_gap_notes(ppath,
1000
0
                                               pseg->pt.x, pseg->pt.y, pseg->notes);
1001
0
                        break;
1002
0
                    case s_dash:
1003
0
                        {
1004
0
                            const dash_segment *pd = (const dash_segment *)pseg;
1005
1006
0
                            code = gx_path_add_dash_notes(ppath,
1007
0
                                               pd->pt.x, pd->pt.y, pd->tangent.x, pd->tangent.y, pseg->notes);
1008
0
                            break;
1009
0
                        }
1010
2.89k
                    case s_line_close:
1011
2.89k
                        code = gx_path_close_subpath(ppath);
1012
2.89k
                        break;
1013
0
                    default:    /* can't happen */
1014
0
                        code = gs_note_error(gs_error_unregistered);
1015
159k
                }
1016
159k
                pseg = pseg->next;
1017
159k
            }
1018
8.44k
            if (code < 0) {
1019
0
                gx_path_new(ppath);
1020
0
                return code;
1021
0
            }
1022
8.44k
        }
1023
175
    }
1024
7.97k
    ppath->bbox_set = false;
1025
#ifdef DEBUG
1026
    if (gs_debug_c('P'))
1027
        gx_dump_path(ppath, "after eliding_1d");
1028
#endif
1029
7.97k
    return 0;
1030
7.97k
}
1031
1032
int
1033
gx_path_elide_1d(gx_path *ppath)
1034
7.97k
{
1035
7.97k
    int code;
1036
7.97k
    gx_path path;
1037
1038
7.97k
    gx_path_init_local(&path, ppath->memory);
1039
7.97k
    code = gx_path_copy_eliding_1d(ppath, &path);
1040
7.97k
    if (code < 0)
1041
0
        return code;
1042
7.97k
    gx_path_assign_free(ppath, &path);
1043
7.97k
    gx_path_free(&path, "gx_path_elide_1d");
1044
1045
7.97k
    return 0;
1046
7.97k
}