Coverage Report

Created: 2022-10-31 07:00

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