Coverage Report

Created: 2025-06-10 07:06

/src/ghostpdl/base/gxpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 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
/* Internal path management routines for Ghostscript library */
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gsstruct.h"
21
#include "gxfixed.h"
22
#include "gzpath.h"
23
24
/* These routines all assume that all points are */
25
/* already in device coordinates, and in fixed representation. */
26
/* As usual, they return either 0 or a (negative) error code. */
27
28
/* Forward references */
29
static int path_alloc_copy(gx_path *);
30
static int gx_path_new_subpath(gx_path *);
31
32
#ifdef DEBUG
33
static void gx_print_segment(const gs_memory_t *,const segment *);
34
35
#  define trace_segment(msg, mem, pseg)\
36
     if ( gs_debug_c('P') ) dmlprintf(mem, msg), gx_print_segment(mem,pseg);
37
#else
38
44.3M
#  define trace_segment(msg, mem, pseg) DO_NOTHING
39
#endif
40
41
/* Check a point against a preset bounding box. */
42
#define outside_bbox(ppath, px, py)\
43
0
 (px < ppath->bbox.p.x || px > ppath->bbox.q.x ||\
44
0
  py < ppath->bbox.p.y || py > ppath->bbox.q.y)
45
#define check_in_bbox(ppath, px, py)\
46
0
  if ( outside_bbox(ppath, px, py) )\
47
0
        return_error(gs_error_rangecheck)
48
49
/* Structure descriptors for paths and path segment types. */
50
public_st_path();
51
private_st_path_segments();
52
private_st_segment();
53
private_st_line();
54
private_st_dash();
55
private_st_line_close();
56
private_st_curve();
57
private_st_subpath();
58
59
/* ------ Initialize/free paths ------ */
60
61
static rc_free_proc(rc_free_path_segments);
62
static rc_free_proc(rc_free_path_segments_local);
63
64
/*
65
 * Define the default virtual path interface implementation.
66
 */
67
static int
68
    gz_path_add_point(gx_path *, fixed, fixed),
69
    gz_path_add_line_notes(gx_path *, fixed, fixed, segment_notes),
70
    gz_path_add_gap_notes(gx_path *, fixed, fixed, segment_notes),
71
    gz_path_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes),
72
    gz_path_close_subpath_notes(gx_path *, segment_notes);
73
static byte gz_path_state_flags(gx_path *ppath, byte flags);
74
75
static gx_path_procs default_path_procs = {
76
    gz_path_add_point,
77
    gz_path_add_line_notes,
78
    gz_path_add_gap_notes,
79
    gz_path_add_curve_notes,
80
    gz_path_close_subpath_notes,
81
    gz_path_state_flags
82
};
83
84
/*
85
 * Define virtual path interface implementation for computing a path bbox.
86
 */
87
static int
88
    gz_path_bbox_add_point(gx_path *, fixed, fixed),
89
    gz_path_bbox_add_line_notes(gx_path *, fixed, fixed, segment_notes),
90
    gz_path_bbox_add_gap_notes(gx_path *, fixed, fixed, segment_notes),
91
    gz_path_bbox_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes),
92
    gz_path_bbox_close_subpath_notes(gx_path *, segment_notes);
93
94
static gx_path_procs path_bbox_procs = {
95
    gz_path_bbox_add_point,
96
    gz_path_bbox_add_line_notes,
97
    gz_path_bbox_add_gap_notes,
98
    gz_path_bbox_add_curve_notes,
99
    gz_path_bbox_close_subpath_notes,
100
    gz_path_state_flags
101
};
102
103
static void
104
gx_path_init_contents(gx_path * ppath)
105
6.25M
{
106
6.25M
    ppath->box_last = 0;
107
6.25M
    ppath->first_subpath = ppath->current_subpath = 0;
108
6.25M
    ppath->subpath_count = 0;
109
6.25M
    ppath->curve_count = 0;
110
6.25M
    path_update_newpath(ppath);
111
6.25M
    ppath->bbox_set = 0;
112
6.25M
    ppath->bbox_accurate = 0;
113
6.25M
    ppath->last_charpath_segment = 0;
114
6.25M
    ppath->bbox.p.x = max_fixed;
115
6.25M
    ppath->bbox.p.y = max_fixed;
116
6.25M
    ppath->bbox.q.x = min_fixed;
117
6.25M
    ppath->bbox.q.y = min_fixed;
118
6.25M
}
119
120
/*
121
 * Initialize a path contained in an already-heap-allocated object,
122
 * optionally allocating its segments.
123
 */
124
static int
125
path_alloc_segments(gx_path_segments ** ppsegs, gs_memory_t * mem,
126
                    client_name_t cname)
127
1.25M
{
128
1.25M
    mem = gs_memory_stable(mem);
129
1.25M
    rc_alloc_struct_1(*ppsegs, gx_path_segments, &st_path_segments,
130
1.25M
                      mem, return_error(gs_error_VMerror), cname);
131
1.25M
    (*ppsegs)->rc.free = rc_free_path_segments;
132
1.25M
    return 0;
133
1.25M
}
134
int
135
gx_path_init_contained_shared(gx_path * ppath, const gx_path * shared,
136
                              gs_memory_t * mem, client_name_t cname)
137
176k
{
138
176k
    if (shared) {
139
0
        if (shared->segments == &shared->local_segments) {
140
#ifdef DEBUG
141
            lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n",
142
                     (intptr_t)shared);
143
#endif
144
0
            return_error(gs_error_Fatal);
145
0
        }
146
0
        *ppath = *shared;
147
0
        rc_increment(ppath->segments);
148
176k
    } else {
149
176k
        int code = path_alloc_segments(&ppath->segments, mem, cname);
150
151
176k
        if (code < 0)
152
0
            return code;
153
176k
        gx_path_init_contents(ppath);
154
176k
    }
155
176k
    ppath->memory = mem;
156
176k
    ppath->allocation = path_allocated_contained;
157
176k
    ppath->procs = &default_path_procs;
158
176k
    return 0;
159
176k
}
160
161
/*
162
 * Allocate a path on the heap, and initialize it.  If shared is NULL,
163
 * allocate a segments object; if shared is an existing path, share its
164
 * segments.
165
 */
166
gx_path *
167
gx_path_alloc_shared(const gx_path * shared, gs_memory_t * mem,
168
                     client_name_t cname)
169
1.09M
{
170
1.09M
    gx_path *ppath = gs_alloc_struct(mem, gx_path, &st_path, cname);
171
172
1.09M
    if (ppath == 0)
173
0
        return 0;
174
1.09M
    ppath->procs = &default_path_procs;
175
1.09M
    if (shared) {
176
1.07M
        if (shared->segments == &shared->local_segments) {
177
#ifdef DEBUG
178
            lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n",
179
                     (intptr_t)shared);
180
#endif
181
0
            gs_free_object(mem, ppath, cname);
182
0
            return 0;
183
0
        }
184
1.07M
        *ppath = *shared;
185
1.07M
        rc_increment(ppath->segments);
186
1.07M
    } else {
187
14.6k
        int code = path_alloc_segments(&ppath->segments, mem, cname);
188
189
14.6k
        if (code < 0) {
190
0
            gs_free_object(mem, ppath, cname);
191
0
            return 0;
192
0
        }
193
14.6k
        gx_path_init_contents(ppath);
194
14.6k
    }
195
1.09M
    ppath->memory = mem;
196
1.09M
    ppath->allocation = path_allocated_on_heap;
197
1.09M
    return ppath;
198
1.09M
}
199
200
/*
201
 * Initialize a stack-allocated path.  This doesn't allocate anything,
202
 * but may still share the segments.
203
 */
204
int
205
gx_path_init_local_shared(gx_path * ppath, const gx_path * shared,
206
                          gs_memory_t * mem)
207
4.17M
{
208
4.17M
    if (shared) {
209
0
        if (shared->segments == &shared->local_segments) {
210
#ifdef DEBUG
211
            lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n",
212
                     (intptr_t)shared);
213
#endif
214
0
            return_error(gs_error_Fatal);
215
0
        }
216
0
        *ppath = *shared;
217
0
        rc_increment(ppath->segments);
218
4.17M
    } else {
219
4.17M
        rc_init_free(&ppath->local_segments, mem, 1,
220
4.17M
                     rc_free_path_segments_local);
221
4.17M
        ppath->segments = &ppath->local_segments;
222
4.17M
        gx_path_init_contents(ppath);
223
4.17M
    }
224
4.17M
    ppath->memory = mem;
225
4.17M
    ppath->allocation = path_allocated_on_stack;
226
4.17M
    ppath->procs = &default_path_procs;
227
4.17M
    return 0;
228
4.17M
}
229
230
void gx_path_preinit_local_rectangle(gx_path *ppath, gs_memory_t *mem)
231
79.8k
{
232
79.8k
    rc_init_free(&ppath->local_segments, mem, 1, NULL);
233
79.8k
    ppath->segments = &ppath->local_segments;
234
79.8k
    ppath->box_last = 0;
235
79.8k
    ppath->first_subpath = ppath->current_subpath = 0;
236
79.8k
    ppath->subpath_count = 0;
237
79.8k
    ppath->curve_count = 0;
238
79.8k
    path_update_newpath(ppath);
239
79.8k
    ppath->bbox_set = 1;
240
79.8k
    ppath->bbox_accurate = 1;
241
79.8k
    ppath->last_charpath_segment = 0;
242
79.8k
    ppath->memory = mem;
243
79.8k
    ppath->allocation = path_allocated_on_stack;
244
79.8k
    ppath->procs = &default_path_procs;
245
79.8k
}
246
247
void gx_path_init_local_rectangle(gx_path *ppath, gs_fixed_rect *rect)
248
0
{
249
0
    ppath->bbox = *rect;
250
0
}
251
252
/*
253
 * Initialize a stack-allocated pseudo-path for computing a bbox
254
 * for a dynamic path.
255
 */
256
void
257
gx_path_init_bbox_accumulator(gx_path * ppath)
258
12.0k
{
259
12.0k
    ppath->box_last = 0;
260
12.0k
    ppath->subpath_count = 0;
261
12.0k
    ppath->curve_count = 0;
262
12.0k
    ppath->local_segments.contents.subpath_first = 0;
263
12.0k
    ppath->local_segments.contents.subpath_current = 0;
264
12.0k
    ppath->segments = 0;
265
12.0k
    path_update_newpath(ppath);
266
12.0k
    ppath->bbox_set = 0;
267
12.0k
    ppath->bbox.p.x = ppath->bbox.p.y = ppath->bbox.q.x = ppath->bbox.q.y = 0;
268
12.0k
    ppath->bbox_accurate = 1;
269
12.0k
    ppath->memory = NULL; /* Won't allocate anything. */
270
12.0k
    ppath->allocation = path_allocated_on_stack;
271
12.0k
    ppath->procs = &path_bbox_procs;
272
12.0k
}
273
274
/* Guarantee that a path's segments are not shared with any other path. */
275
static inline int path_unshare(gx_path *ppath)
276
15.4M
{
277
15.4M
    int code = 0;
278
279
15.4M
    if (gx_path_is_shared(ppath))
280
41.7k
        code = path_alloc_copy(ppath);
281
15.4M
    return code;
282
15.4M
}
283
284
/*
285
 * Ensure that a path owns its segments, by copying the segments if
286
 * they currently have multiple references.
287
 */
288
int
289
gx_path_unshare(gx_path * ppath)
290
384k
{
291
384k
    return path_unshare(ppath);
292
384k
}
293
294
/*
295
 * Free a path by releasing its segments if they have no more references.
296
 * This also frees the path object iff it was allocated by gx_path_alloc.
297
 */
298
void
299
gx_path_free(gx_path * ppath, client_name_t cname)
300
6.94M
{
301
6.94M
    rc_decrement(ppath->segments, cname);
302
    /* Clean up pointers for GC. */
303
6.94M
    ppath->box_last = 0;
304
6.94M
    ppath->segments = 0;  /* Nota bene */
305
6.94M
    if (ppath->allocation == path_allocated_on_heap)
306
1.09M
        gs_free_object(ppath->memory, ppath, cname);
307
6.94M
}
308
309
/*
310
 * Assign one path to another, adjusting reference counts appropriately.
311
 * Note that this requires that segments of the two paths (but not the path
312
 * objects themselves) were allocated with the same allocator.  Note also
313
 * that since it does the equivalent of a gx_path_new(ppto), it may allocate
314
 * a new segments object for ppto.
315
 */
316
int
317
gx_path_assign_preserve(gx_path * ppto, gx_path * ppfrom)
318
580k
{
319
580k
    gx_path_segments *fromsegs = ppfrom->segments;
320
580k
    gx_path_segments *tosegs = ppto->segments;
321
580k
    gs_memory_t *mem = ppto->memory;
322
580k
    gx_path_allocation_t allocation = ppto->allocation;
323
324
580k
    if (fromsegs == &ppfrom->local_segments) {
325
        /* We can't use ppfrom's segments object. */
326
415k
        if (tosegs == &ppto->local_segments || gx_path_is_shared(ppto)) {
327
            /* We can't use ppto's segments either.  Allocate a new one. */
328
138k
            int code = path_alloc_segments(&tosegs, ppto->memory,
329
138k
                                           "gx_path_assign");
330
331
138k
            if (code < 0)
332
0
                return code;
333
138k
            rc_decrement(ppto->segments, "gx_path_assign");
334
277k
        } else {
335
            /* Use ppto's segments object. */
336
277k
            rc_free_path_segments_local(tosegs->rc.memory, tosegs,
337
277k
                                        "gx_path_assign");
338
277k
        }
339
415k
        tosegs->contents = fromsegs->contents;
340
415k
        ppfrom->segments = tosegs;
341
415k
        rc_increment(tosegs); /* for reference from ppfrom */
342
415k
    } else {
343
        /* We can use ppfrom's segments object. */
344
165k
        rc_increment(fromsegs);
345
165k
        rc_decrement(tosegs, "gx_path_assign");
346
165k
    }
347
580k
    *ppto = *ppfrom;
348
580k
    ppto->memory = mem;
349
580k
    ppto->allocation = allocation;
350
580k
    return 0;
351
580k
}
352
353
/*
354
 * Assign one path to another and free the first path at the same time.
355
 * (This may do less work than assign_preserve + free.)
356
 */
357
int
358
gx_path_assign_free(gx_path * ppto, gx_path * ppfrom)
359
180k
{
360
180k
    int code = 0;
361
    /*
362
     * Detect the special case where both paths have non-shared local
363
     * segments, since we can avoid allocating new segments in this case.
364
     */
365
180k
    if (ppto->segments == &ppto->local_segments &&
366
180k
        ppfrom->segments == &ppfrom->local_segments &&
367
180k
        !gx_path_is_shared(ppto)
368
180k
        ) {
369
0
#define fromsegs (&ppfrom->local_segments)
370
0
#define tosegs (&ppto->local_segments)
371
0
        gs_memory_t *mem = ppto->memory;
372
0
        gx_path_allocation_t allocation = ppto->allocation;
373
374
0
        rc_free_path_segments_local(tosegs->rc.memory, tosegs,
375
0
                                    "gx_path_assign_free");
376
        /* We record a bogus reference to fromsegs, which */
377
        /* gx_path_free will undo. */
378
0
        *ppto = *ppfrom;
379
0
        rc_increment(fromsegs);
380
0
        ppto->segments = tosegs;
381
0
        ppto->memory = mem;
382
0
        ppto->allocation = allocation;
383
0
#undef fromsegs
384
0
#undef tosegs
385
180k
    } else {
386
        /* In all other cases, just do assign + free. */
387
180k
        code = gx_path_assign_preserve(ppto, ppfrom);
388
180k
    }
389
180k
    gx_path_free(ppfrom, "gx_path_assign_free");
390
180k
    return code;
391
180k
}
392
393
/*
394
 * Free the segments of a path when their reference count goes to zero.
395
 * We do this in reverse order so as to maximize LIFO allocator behavior.
396
 * We don't have to worry about cleaning up pointers, because we're about
397
 * to free the segments object.
398
 */
399
static void
400
rc_free_path_segments_local(gs_memory_t * mem, void *vpsegs,
401
                            client_name_t cname)
402
6.11M
{
403
6.11M
    gx_path_segments *psegs = (gx_path_segments *) vpsegs;
404
6.11M
    segment *pseg;
405
406
6.11M
    mem = gs_memory_stable(mem);
407
6.11M
    if (psegs->contents.subpath_first == 0)
408
3.63M
        return;      /* empty path */
409
2.47M
    pseg = (segment *) psegs->contents.subpath_current->last;
410
24.6M
    while (pseg) {
411
22.1M
        segment *prev = pseg->prev;
412
413
22.1M
        trace_segment("[P]release", mem, pseg);
414
22.1M
        gs_free_object(mem, pseg, cname);
415
22.1M
        pseg = prev;
416
22.1M
    }
417
2.47M
}
418
static void
419
rc_free_path_segments(gs_memory_t * mem, void *vpsegs, client_name_t cname)
420
1.25M
{
421
1.25M
    rc_free_path_segments_local(mem, vpsegs, cname);
422
1.25M
    gs_free_object(mem, vpsegs, cname);
423
1.25M
}
424
425
/* ------ Incremental path building ------ */
426
427
/* Macro for opening the current subpath. */
428
/* ppath points to the path; sets psub to ppath->current_subpath. */
429
static inline int path_open(gx_path *ppath)
430
10.3M
{
431
10.3M
    int code = 0;
432
10.3M
    if ( !path_is_drawing(ppath) ) {
433
2.81M
      if ( path_position_valid(ppath) ) {
434
2.81M
          code = gx_path_new_subpath(ppath);
435
2.81M
      }
436
720
      else {
437
720
          code = gs_note_error(gs_error_nocurrentpoint);
438
720
      }
439
2.81M
    }
440
10.3M
    return code;
441
10.3M
}
442
443
/* Macros for allocating path segments. */
444
/* Note that they assume that ppath points to the path. */
445
/* We have to split the macro into two because of limitations */
446
/* on the size of a single statement (sigh). */
447
static inline int path_alloc_segment(gx_path *ppath, segment **ppseg, subpath **ppsub, gs_memory_type_ptr_t pstype, segment_type seg_type, ushort notes, client_name_t cname)
448
13.3M
{
449
13.3M
    int code;
450
451
13.3M
    code = path_unshare(ppath);
452
13.3M
    if (code < 0) return code;
453
13.3M
    if (ppsub)
454
13.3M
        *ppsub = ppath->current_subpath;
455
456
13.3M
    if( !(*ppseg = gs_alloc_struct(gs_memory_stable(ppath->memory), segment, pstype, cname)) )
457
0
      return_error(gs_error_VMerror);
458
13.3M
    (*ppseg)->type = seg_type;
459
13.3M
    (*ppseg)->notes = notes;
460
13.3M
    (*ppseg)->next = 0;
461
462
13.3M
    return 0;
463
13.3M
}
464
465
static inline void path_alloc_link(segment *pseg, subpath *psub)
466
10.5M
{
467
10.5M
    segment *prev = psub->last;
468
10.5M
    prev->next = (segment *)pseg;
469
10.5M
    pseg->prev = prev;
470
10.5M
    psub->last = (segment *)pseg;
471
10.5M
}
472
473
/* Make a new path (newpath). */
474
int
475
gx_path_new(gx_path * ppath)
476
1.88M
{
477
1.88M
    gx_path_segments *psegs = ppath->segments;
478
479
1.88M
    if (gx_path_is_shared(ppath)) {
480
925k
        int code = path_alloc_segments(&ppath->segments, ppath->memory,
481
925k
                                       "gx_path_new");
482
483
925k
        if (code < 0) {
484
            /* Leave the path in a valid state, despite the error */
485
0
            ppath->segments = psegs;
486
0
            return code;
487
0
        }
488
925k
        rc_decrement(psegs, "gx_path_new");
489
961k
    } else {
490
961k
        rc_free_path_segments_local(psegs->rc.memory, psegs, "gx_path_new");
491
961k
    }
492
1.88M
    gx_path_init_contents(ppath);
493
1.88M
    return 0;
494
1.88M
}
495
496
/* Open a new subpath. */
497
/* The client must invoke path_update_xxx. */
498
static int
499
gx_path_new_subpath(gx_path * ppath)
500
2.81M
{
501
2.81M
    subpath *psub;
502
2.81M
    subpath *spp;
503
2.81M
    int code;
504
505
2.81M
    code = path_alloc_segment(ppath, (segment **)&spp, &psub, &st_subpath, s_start, sn_none, "gx_path_new_subpath");
506
2.81M
    if (code < 0)
507
0
        return code;
508
509
2.81M
    spp->last = (segment *) spp;
510
2.81M
    spp->curve_count = 0;
511
2.81M
    spp->is_closed = 0;
512
2.81M
    spp->pt = ppath->position;
513
2.81M
    if (!psub) {   /* first subpath */
514
2.48M
        ppath->first_subpath = spp;
515
2.48M
        spp->prev = 0;
516
2.48M
    } else {
517
334k
        segment *prev = psub->last;
518
519
334k
        prev->next = (segment *) spp;
520
334k
        spp->prev = prev;
521
334k
    }
522
2.81M
    ppath->current_subpath = spp;
523
2.81M
    ppath->subpath_count++;
524
2.81M
    trace_segment("[P]", ppath->memory, (const segment *)spp);
525
2.81M
    return 0;
526
2.81M
}
527
528
static inline void
529
gz_path_bbox_add(gx_path * ppath, fixed x, fixed y)
530
0
{
531
0
    if (!ppath->bbox_set) {
532
0
        ppath->bbox.p.x = ppath->bbox.q.x = x;
533
0
        ppath->bbox.p.y = ppath->bbox.q.y = y;
534
0
        ppath->bbox_set = 1;
535
0
    } else {
536
0
        if (ppath->bbox.p.x > x)
537
0
            ppath->bbox.p.x = x;
538
0
        if (ppath->bbox.p.y > y)
539
0
            ppath->bbox.p.y = y;
540
0
        if (ppath->bbox.q.x < x)
541
0
            ppath->bbox.q.x = x;
542
0
        if (ppath->bbox.q.y < y)
543
0
            ppath->bbox.q.y = y;
544
0
    }
545
0
}
546
547
static inline void
548
gz_path_bbox_move(gx_path * ppath, fixed x, fixed y)
549
0
{
550
    /* a trick : we store 'fixed' into 'double'. */
551
0
    ppath->position.x = x;
552
0
    ppath->position.y = y;
553
0
    ppath->state_flags |= psf_position_valid;
554
0
}
555
556
/* Add a point to the current path (moveto). */
557
int
558
gx_path_add_point(gx_path * ppath, fixed x, fixed y)
559
4.81M
{
560
4.81M
    return ppath->procs->add_point(ppath, x, y);
561
4.81M
}
562
static int
563
gz_path_add_point(gx_path * ppath, fixed x, fixed y)
564
4.81M
{
565
4.81M
    if (ppath->bbox_set)
566
0
        check_in_bbox(ppath, x, y);
567
4.81M
    ppath->position.x = x;
568
4.81M
    ppath->position.y = y;
569
4.81M
    path_update_moveto(ppath);
570
4.81M
    return 0;
571
4.81M
}
572
static int
573
gz_path_bbox_add_point(gx_path * ppath, fixed x, fixed y)
574
0
{
575
0
    gz_path_bbox_move(ppath, x, y);
576
0
    return 0;
577
0
}
578
579
/* Add a relative point to the current path (rmoveto). */
580
int
581
gx_path_add_relative_point(gx_path * ppath, fixed dx, fixed dy)
582
0
{
583
0
    if (!path_position_in_range(ppath))
584
0
        return_error((path_position_valid(ppath) ? gs_error_limitcheck :
585
0
                      gs_error_nocurrentpoint));
586
0
    {
587
0
        fixed nx = ppath->position.x + dx, ny = ppath->position.y + dy;
588
589
        /* Check for overflow in addition. */
590
0
        if (((nx ^ dx) < 0 && (ppath->position.x ^ dx) >= 0) ||
591
0
            ((ny ^ dy) < 0 && (ppath->position.y ^ dy) >= 0)
592
0
            )
593
0
            return_error(gs_error_limitcheck);
594
0
        if (ppath->bbox_set)
595
0
            check_in_bbox(ppath, nx, ny);
596
0
        ppath->position.x = nx;
597
0
        ppath->position.y = ny;
598
0
    }
599
0
    path_update_moveto(ppath);
600
0
    return 0;
601
0
}
602
603
/* Set the segment point and the current point in the path. */
604
/* Assumes ppath points to the path. */
605
#define path_set_point(pseg, fx, fy)\
606
10.5M
        (pseg)->pt.x = ppath->position.x = (fx),\
607
10.5M
        (pseg)->pt.y = ppath->position.y = (fy)
608
609
/* Add a line to the current path (lineto). */
610
int
611
gx_path_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
612
6.86M
{
613
6.86M
    return ppath->procs->add_line(ppath, x, y, notes);
614
6.86M
}
615
static int
616
gz_path_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
617
6.86M
{
618
6.86M
    subpath *psub;
619
6.86M
    line_segment *lp;
620
6.86M
    int code;
621
622
6.86M
    if (ppath->bbox_set)
623
0
        check_in_bbox(ppath, x, y);
624
6.86M
    code = path_open(ppath);
625
6.86M
    if (code < 0) return code;
626
6.86M
    code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line, s_line, notes, "gx_path_add_line");
627
6.86M
    if (code < 0) return code;
628
6.86M
    path_alloc_link((segment *)lp, psub);
629
6.86M
    path_set_point(lp, x, y);
630
6.86M
    path_update_draw(ppath);
631
6.86M
    trace_segment("[P]", ppath->memory, (segment *) lp);
632
6.86M
    return 0;
633
6.86M
}
634
static int
635
gz_path_bbox_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
636
0
{
637
0
    gz_path_bbox_add(ppath, x, y);
638
0
    gz_path_bbox_move(ppath, x, y);
639
0
    return 0;
640
0
}
641
/* Add a gap to the current path (lineto). */
642
int
643
gx_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
644
0
{
645
0
    return ppath->procs->add_gap(ppath, x, y, notes);
646
0
}
647
static int
648
gz_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
649
0
{
650
0
    subpath *psub;
651
0
    line_segment *lp;
652
0
    int code;
653
654
0
    if (ppath->bbox_set)
655
0
        check_in_bbox(ppath, x, y);
656
0
    code = path_open(ppath);
657
0
    if (code < 0) return code;
658
0
    code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line, s_gap, notes, "gx_path_add_gap");
659
0
    if (code < 0) return code;
660
0
    path_alloc_link((segment *)lp, psub);
661
0
    path_set_point(lp, x, y);
662
0
    path_update_draw(ppath);
663
0
    trace_segment("[P]", ppath->memory, (segment *) lp);
664
0
    return 0;
665
0
}
666
static int
667
gz_path_bbox_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes)
668
0
{
669
0
    gz_path_bbox_add(ppath, x, y);
670
0
    gz_path_bbox_move(ppath, x, y);
671
0
    return 0;
672
0
}
673
674
/* Add multiple lines to the current path. */
675
/* Note that all lines have the same notes. */
676
int
677
gx_path_add_lines_notes(gx_path *ppath, const gs_fixed_point *ppts, int count,
678
                        segment_notes notes)
679
1.68M
{
680
1.68M
    subpath *psub;
681
1.68M
    segment *prev;
682
1.68M
    line_segment *lp = 0;
683
1.68M
    int i;
684
1.68M
    int code = 0;
685
686
1.68M
    if (count <= 0)
687
76
        return 0;
688
1.68M
    code = path_unshare(ppath);
689
1.68M
    if (code < 0)
690
0
        return code;
691
692
1.68M
    code = path_open(ppath);
693
1.68M
    if (code < 0) return code;
694
1.68M
    psub = ppath->current_subpath;
695
1.68M
    prev = psub->last;
696
    /*
697
     * We could do better than the following, but this is a start.
698
     * Note that we don't make any attempt to undo partial additions
699
     * if we fail partway through; this is equivalent to what would
700
     * happen with multiple calls on gx_path_add_line.
701
     */
702
10.4M
    for (i = 0; i < count; i++) {
703
8.80M
        fixed x = ppts[i].x;
704
8.80M
        fixed y = ppts[i].y;
705
8.80M
        line_segment *next;
706
707
8.80M
        if (ppath->bbox_set && outside_bbox(ppath, x, y)) {
708
0
            code = gs_note_error(gs_error_rangecheck);
709
0
            break;
710
0
        }
711
8.80M
        if (!(next = gs_alloc_struct(gs_memory_stable(ppath->memory),
712
8.80M
                                     line_segment, &st_line,
713
8.80M
                                     "gx_path_add_lines"))
714
8.80M
            ) {
715
0
            code = gs_note_error(gs_error_VMerror);
716
0
            break;
717
0
        }
718
8.80M
        lp = next;
719
8.80M
        lp->type = s_line;
720
8.80M
        lp->notes = notes;
721
8.80M
        prev->next = (segment *) lp;
722
8.80M
        lp->prev = prev;
723
8.80M
        lp->pt.x = x;
724
8.80M
        lp->pt.y = y;
725
8.80M
        prev = (segment *) lp;
726
8.80M
        trace_segment("[P]", ppath->memory, (segment *) lp);
727
8.80M
    }
728
1.68M
    if (lp != 0)
729
1.68M
        ppath->position.x = lp->pt.x,
730
1.68M
            ppath->position.y = lp->pt.y,
731
1.68M
            psub->last = (segment *) lp,
732
1.68M
            lp->next = 0,
733
1.68M
            path_update_draw(ppath);
734
1.68M
    return code;
735
1.68M
}
736
737
/* Add a dash to the current path (lineto with a small length). */
738
/* Only for internal use of the stroking algorithm. */
739
int
740
gx_path_add_dash_notes(gx_path * ppath, fixed x, fixed y, fixed dx, fixed dy, segment_notes notes)
741
14.0k
{
742
14.0k
    subpath *psub;
743
14.0k
    dash_segment *lp;
744
14.0k
    int code;
745
746
14.0k
    if (ppath->bbox_set)
747
0
        check_in_bbox(ppath, x, y);
748
14.0k
    code = path_open(ppath);
749
14.0k
    if (code < 0) return code;
750
14.0k
    code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_dash, s_dash, notes, "gx_dash_add_dash");
751
14.0k
    if (code < 0) return code;
752
14.0k
    path_alloc_link((segment *)lp, psub);
753
14.0k
    path_set_point(lp, x, y);
754
14.0k
    lp->tangent.x = dx;
755
14.0k
    lp->tangent.y = dy;
756
14.0k
    path_update_draw(ppath);
757
14.0k
    trace_segment("[P]", ppath->memory, (segment *) lp);
758
14.0k
    return 0;
759
14.0k
}
760
761
/* Add a rectangle to the current path. */
762
/* This is a special case of adding a closed polygon. */
763
int
764
gx_path_add_rectangle(gx_path * ppath, fixed x0, fixed y0, fixed x1, fixed y1)
765
515
{
766
515
    gs_fixed_point pts[3];
767
515
    int code;
768
769
515
    pts[0].x = x0;
770
515
    pts[1].x = pts[2].x = x1;
771
515
    pts[2].y = y0;
772
515
    pts[0].y = pts[1].y = y1;
773
515
    if ((code = gx_path_add_point(ppath, x0, y0)) < 0 ||
774
515
        (code = gx_path_add_lines(ppath, pts, 3)) < 0 ||
775
515
        (code = gx_path_close_subpath(ppath)) < 0
776
515
        )
777
0
        return code;
778
515
    return 0;
779
515
}
780
781
/* Add a curve to the current path (curveto). */
782
int
783
gx_path_add_curve_notes(gx_path * ppath,
784
                 fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3,
785
                        segment_notes notes)
786
1.75M
{
787
1.75M
    return ppath->procs->add_curve(ppath, x1, y1, x2, y2, x3, y3, notes);
788
1.75M
}
789
static int
790
gz_path_add_curve_notes(gx_path * ppath,
791
                 fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3,
792
                        segment_notes notes)
793
1.75M
{
794
1.75M
    subpath *psub;
795
1.75M
    curve_segment *lp;
796
1.75M
    int code;
797
798
1.75M
    if (ppath->bbox_set) {
799
0
        check_in_bbox(ppath, x1, y1);
800
0
        check_in_bbox(ppath, x2, y2);
801
0
        check_in_bbox(ppath, x3, y3);
802
0
    }
803
1.75M
    code = path_open(ppath);
804
1.75M
    if (code < 0) return code;
805
1.75M
    code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_curve, s_curve, notes, "gx_path_add_curve");
806
1.75M
    if (code < 0) return code;
807
1.75M
    path_alloc_link((segment *)lp, psub);
808
1.75M
    lp->p1.x = x1;
809
1.75M
    lp->p1.y = y1;
810
1.75M
    lp->p2.x = x2;
811
1.75M
    lp->p2.y = y2;
812
1.75M
    path_set_point(lp, x3, y3);
813
1.75M
    psub->curve_count++;
814
1.75M
    ppath->curve_count++;
815
1.75M
    path_update_draw(ppath);
816
1.75M
    trace_segment("[P]", ppath->memory, (segment *) lp);
817
1.75M
    return 0;
818
1.75M
}
819
static int
820
gz_path_bbox_add_curve_notes(gx_path * ppath,
821
                 fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3,
822
                        segment_notes notes)
823
0
{
824
0
    gz_path_bbox_add(ppath, x1, y1);
825
0
    gz_path_bbox_add(ppath, x2, y2);
826
0
    gz_path_bbox_add(ppath, x3, y3);
827
0
    gz_path_bbox_move(ppath, x3, y3);
828
0
    return 0;
829
0
}
830
831
/*
832
 * Add an approximation of an arc to the current path.
833
 * The current point of the path is the initial point of the arc;
834
 * parameters are the final point of the arc
835
 * and the point at which the extended tangents meet.
836
 * We require that the arc be less than a semicircle.
837
 * The arc may go either clockwise or counterclockwise.
838
 * The approximation is a very simple one: a single curve
839
 * whose other two control points are a fraction F of the way
840
 * to the intersection of the tangents, where
841
 *      F = (4/3)(1 / (1 + sqrt(1+(d/r)^2)))
842
 * where r is the radius and d is the distance from either tangent
843
 * point to the intersection of the tangents.  This produces
844
 * a curve whose center point, as well as its ends, lies on
845
 * the desired arc.
846
 *
847
 * Because F has to be computed in user space, we let the client
848
 * compute it and pass it in as an argument.
849
 */
850
int
851
gx_path_add_partial_arc_notes(gx_path * ppath,
852
fixed x3, fixed y3, fixed xt, fixed yt, double fraction, segment_notes notes)
853
465k
{
854
465k
    fixed x0 = ppath->position.x, y0 = ppath->position.y;
855
856
465k
    return gx_path_add_curve_notes(ppath,
857
465k
                                   x0 + (fixed) ((xt - x0) * fraction),
858
465k
                                   y0 + (fixed) ((yt - y0) * fraction),
859
465k
                                   x3 + (fixed) ((xt - x3) * fraction),
860
465k
                                   y3 + (fixed) ((yt - y3) * fraction),
861
465k
                                   x3, y3, notes | sn_from_arc);
862
465k
}
863
864
/* Append a path to another path, and reset the first path. */
865
/* Currently this is only used to append a path to its parent */
866
/* (the path in the previous graphics context). */
867
int
868
gx_path_add_path(gx_path * ppath, gx_path * ppfrom)
869
6.61k
{
870
6.61k
    int code = path_unshare(ppfrom);
871
6.61k
    if (code < 0)
872
0
        return code;
873
6.61k
    code = path_unshare(ppath);
874
6.61k
    if (code < 0)
875
0
        return code;
876
6.61k
    if (ppfrom->first_subpath) { /* i.e. ppfrom not empty */
877
5.49k
        if (ppath->first_subpath) { /* i.e. ppath not empty */
878
5.10k
            subpath *psub = ppath->current_subpath;
879
5.10k
            segment *pseg = psub->last;
880
5.10k
            subpath *pfsub = ppfrom->first_subpath;
881
882
5.10k
            pseg->next = (segment *) pfsub;
883
5.10k
            pfsub->prev = pseg;
884
5.10k
        } else
885
383
            ppath->first_subpath = ppfrom->first_subpath;
886
5.49k
        ppath->current_subpath = ppfrom->current_subpath;
887
5.49k
        ppath->subpath_count += ppfrom->subpath_count;
888
5.49k
        ppath->curve_count += ppfrom->curve_count;
889
5.49k
    }
890
    /* Transfer the remaining state. */
891
6.61k
    ppath->position = ppfrom->position;
892
6.61k
    ppath->state_flags = ppfrom->state_flags;
893
    /* Reset the source path. */
894
6.61k
    gx_path_init_contents(ppfrom);
895
6.61k
    return 0;
896
6.61k
}
897
898
/* Add a path or its bounding box to the enclosing path, */
899
/* and reset the first path.  Only used for implementing charpath and its */
900
/* relatives. */
901
int
902
gx_path_add_char_path(gx_path * to_path, gx_path * from_path,
903
                      gs_char_path_mode mode)
904
6.61k
{
905
6.61k
    int code;
906
6.61k
    gs_fixed_rect bbox;
907
908
6.61k
    switch (mode) {
909
0
        default:    /* shouldn't happen! */
910
0
            gx_path_new(from_path);
911
0
            return 0;
912
0
        case cpm_charwidth: {
913
0
            gs_fixed_point cpt;
914
915
0
            code = gx_path_current_point(from_path, &cpt);
916
0
            if (code < 0)
917
0
                break;
918
0
            return gx_path_add_point(to_path, cpt.x, cpt.y);
919
0
        }
920
5.24k
        case cpm_true_charpath:
921
6.61k
        case cpm_false_charpath:
922
6.61k
            return gx_path_add_path(to_path, from_path);
923
0
        case cpm_true_charboxpath:
924
0
            gx_path_bbox(from_path, &bbox);
925
0
            code = gx_path_add_rectangle(to_path, bbox.p.x, bbox.p.y,
926
0
                                         bbox.q.x, bbox.q.y);
927
0
            break;
928
0
        case cpm_false_charboxpath:
929
0
            gx_path_bbox(from_path, &bbox);
930
0
            code = gx_path_add_point(to_path, bbox.p.x, bbox.p.y);
931
0
            if (code >= 0)
932
0
                code = gx_path_add_line(to_path, bbox.q.x, bbox.q.y);
933
0
            break;
934
6.61k
    }
935
0
    if (code < 0)
936
0
        return code;
937
0
    gx_path_new(from_path);
938
0
    return 0;
939
0
}
940
941
/* Close the current subpath. */
942
int
943
gx_path_close_subpath_notes(gx_path * ppath, segment_notes notes)
944
1.94M
{
945
1.94M
    return ppath->procs->close_subpath(ppath, notes);
946
1.94M
}
947
static int
948
gz_path_close_subpath_notes(gx_path * ppath, segment_notes notes)
949
1.94M
{
950
1.94M
    subpath *psub;
951
1.94M
    line_close_segment *lp;
952
1.94M
    int code;
953
954
1.94M
    if (!path_subpath_open(ppath))
955
2.77k
        return 0;
956
1.94M
    if (path_last_is_moveto(ppath)) {
957
        /* The last operation was a moveto: create a subpath. */
958
533
        code = gx_path_new_subpath(ppath);
959
533
        if (code < 0)
960
0
            return code;
961
533
    }
962
1.94M
    code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line_close, s_line_close, notes, "gx_path_close_subpath");
963
1.94M
    if (code < 0) return code;
964
1.94M
    path_alloc_link((segment *)lp, psub);
965
1.94M
    path_set_point(lp, psub->pt.x, psub->pt.y);
966
1.94M
    lp->sub = psub;
967
1.94M
    psub->is_closed = 1;
968
1.94M
    path_update_closepath(ppath);
969
1.94M
    trace_segment("[P]", ppath->memory, (segment *) lp);
970
1.94M
    return 0;
971
1.94M
}
972
static int
973
gz_path_bbox_close_subpath_notes(gx_path * ppath, segment_notes notes)
974
0
{
975
0
    return 0;
976
0
}
977
978
/* Access path state flags */
979
byte
980
gz_path_state_flags(gx_path *ppath, byte flags)
981
0
{
982
0
    byte flags_old = ppath->state_flags;
983
0
    ppath->state_flags = flags;
984
0
    return flags_old;
985
0
}
986
byte
987
gx_path_get_state_flags(gx_path *ppath)
988
0
{
989
0
    byte flags = ppath->procs->state_flags(ppath, 0);
990
0
    ppath->procs->state_flags(ppath, flags);
991
0
    return flags;
992
0
}
993
void
994
gx_path_set_state_flags(gx_path *ppath, byte flags)
995
0
{
996
0
    ppath->procs->state_flags(ppath, flags);
997
0
}
998
bool
999
gx_path_is_drawing(gx_path *ppath)
1000
0
{
1001
0
  return path_is_drawing(ppath);
1002
0
}
1003
1004
/* Remove the last line from the current subpath, and then close it. */
1005
/* The Type 1 font hinting routines use this if a path ends with */
1006
/* a line to the start followed by a closepath. */
1007
int
1008
gx_path_pop_close_notes(gx_path * ppath, segment_notes notes)
1009
0
{
1010
0
    subpath *psub = ppath->current_subpath;
1011
0
    segment *pseg;
1012
0
    segment *prev;
1013
1014
0
    if (psub == 0 || (pseg = psub->last) == 0 ||
1015
0
        pseg->type != s_line
1016
0
        )
1017
0
        return_error(gs_error_unknownerror);
1018
0
    prev = pseg->prev;
1019
0
    prev->next = 0;
1020
0
    psub->last = prev;
1021
0
    gs_free_object(ppath->memory, pseg, "gx_path_pop_close_subpath");
1022
0
    return gx_path_close_subpath_notes(ppath, notes);
1023
0
}
1024
1025
/* ------ Internal routines ------ */
1026
1027
/*
1028
 * Copy the current path, because it was shared.
1029
 */
1030
static int
1031
path_alloc_copy(gx_path * ppath)
1032
41.7k
{
1033
41.7k
    gx_path path_new;
1034
41.7k
    int code;
1035
1036
41.7k
    gx_path_init_local(&path_new, ppath->memory);
1037
41.7k
    code = gx_path_copy(ppath, &path_new);
1038
41.7k
    if (code < 0) {
1039
0
        gx_path_free(&path_new, "path_alloc_copy error");
1040
0
        return code;
1041
0
    }
1042
41.7k
    ppath->last_charpath_segment = 0;
1043
41.7k
    return gx_path_assign_free(ppath, &path_new);
1044
41.7k
}
1045
1046
/* ------ Debugging printout ------ */
1047
1048
#ifdef DEBUG
1049
1050
/* Print out a path with a label */
1051
void
1052
gx_dump_path(const gx_path * ppath, const char *tag)
1053
{
1054
    dmlprintf2(ppath->memory, "[P]Path "PRI_INTPTR" %s:\n", (intptr_t)ppath, tag);
1055
    gx_path_print(ppath);
1056
}
1057
1058
/* Print a path */
1059
void
1060
gx_path_print(const gx_path * ppath)
1061
{
1062
    const segment *pseg = (const segment *)ppath->first_subpath;
1063
1064
    dmlprintf5(ppath->memory,
1065
               " %% state_flags=%d subpaths=%d, curves=%d, point=(%f,%f)\n",
1066
               ppath->state_flags, ppath->subpath_count, ppath->curve_count,
1067
               fixed2float(ppath->position.x),
1068
               fixed2float(ppath->position.y));
1069
    dmlprintf5(ppath->memory," %% box=(%f,%f),(%f,%f) last="PRI_INTPTR"\n",
1070
               fixed2float(ppath->bbox.p.x), fixed2float(ppath->bbox.p.y),
1071
               fixed2float(ppath->bbox.q.x), fixed2float(ppath->bbox.q.y),
1072
               (intptr_t)ppath->box_last);
1073
    dmlprintf4(ppath->memory,
1074
               " %% segments="PRI_INTPTR" (refct=%ld, first="PRI_INTPTR", current="PRI_INTPTR")\n",
1075
               (intptr_t)ppath->segments, (long)ppath->segments->rc.ref_count,
1076
               (intptr_t)ppath->segments->contents.subpath_first,
1077
               (intptr_t)ppath->segments->contents.subpath_current);
1078
    while (pseg) {
1079
        dmlputs(ppath->memory,"");
1080
        gx_print_segment(ppath->memory, pseg);
1081
        pseg = pseg->next;
1082
    }
1083
}
1084
static void
1085
gx_print_segment(const gs_memory_t *mem, const segment * pseg)
1086
{
1087
    double px = fixed2float(pseg->pt.x);
1088
    double py = fixed2float(pseg->pt.y);
1089
    char out[80];
1090
1091
    gs_snprintf(out, sizeof(out), PRI_INTPTR "<"PRI_INTPTR","PRI_INTPTR">:%u",
1092
               (intptr_t)pseg, (intptr_t)pseg->prev, (intptr_t)pseg->next, pseg->notes);
1093
    switch (pseg->type) {
1094
        case s_start:{
1095
                const subpath *const psub = (const subpath *)pseg;
1096
1097
                dmprintf5(mem, "   %1.4f %1.4f moveto\t%% %s #curves=%d last="PRI_INTPTR"\n",
1098
                          px, py, out, psub->curve_count, (intptr_t)psub->last);
1099
                break;
1100
            }
1101
        case s_curve:{
1102
                const curve_segment *const pcur = (const curve_segment *)pseg;
1103
1104
                dmprintf7(mem, "   %1.4f %1.4f %1.4f %1.4f %1.4f %1.4f curveto\t%% %s\n",
1105
                          fixed2float(pcur->p1.x), fixed2float(pcur->p1.y),
1106
                          fixed2float(pcur->p2.x), fixed2float(pcur->p2.y),
1107
                          px, py, out);
1108
                break;
1109
            }
1110
        case s_line:
1111
            dmprintf3(mem, "   %1.4f %1.4f lineto\t%% %s\n", px, py, out);
1112
            break;
1113
        case s_gap:
1114
            dmprintf3(mem, "   %1.4f %1.4f gapto\t%% %s\n", px, py, out);
1115
            break;
1116
        case s_dash:{
1117
                const dash_segment *const pd = (const dash_segment *)pseg;
1118
1119
                dmprintf5(mem, "   %1.4f %1.4f %1.4f  %1.4f dash\t%% %s\n",
1120
                          fixed2float(pd->pt.x), fixed2float(pd->pt.y),
1121
                          fixed2float(pd->tangent.x),fixed2float(pd->tangent.y),
1122
                          out);
1123
                break;
1124
            }
1125
        case s_line_close:{
1126
                const line_close_segment *const plc =
1127
                (const line_close_segment *)pseg;
1128
1129
                dmprintf4(mem, "   closepath\t%% %s %1.4f %1.4f "PRI_INTPTR"\n",
1130
                          out, px, py, (intptr_t)(plc->sub));
1131
                break;
1132
            }
1133
        default:
1134
            dmprintf4(mem, "   %1.4f %1.4f <type "PRI_INTPTR">\t%% %s\n",
1135
                      px, py, (intptr_t)pseg->type, out);
1136
    }
1137
}
1138
1139
#endif /* DEBUG */