Coverage Report

Created: 2022-04-16 11:23

/src/ghostpdl/base/gxcpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 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
/* Implementation of clipping paths, other than actual clipping */
18
#include "gx.h"
19
#include "string_.h"
20
#include "gserrors.h"
21
#include "gsstruct.h"
22
#include "gsutil.h"
23
#include "gsline.h"
24
#include "gxdevice.h"
25
#include "gxfixed.h"
26
#include "gxpaint.h"
27
#include "gscoord.h"    /* needs gsmatrix.h */
28
#include "gxgstate.h"
29
#include "gzpath.h"
30
#include "gzcpath.h"
31
#include "gzacpath.h"
32
33
/* Forward references */
34
static void gx_clip_list_from_rectangle(gx_clip_list *, gs_fixed_rect *);
35
36
/* Other structure types */
37
public_st_clip_rect();
38
public_st_clip_list();
39
public_st_clip_path();
40
private_st_clip_rect_list();
41
public_st_device_clip();
42
private_st_cpath_path_list();
43
44
/* GC procedures for gx_clip_path */
45
static
46
4.04M
ENUM_PTRS_WITH(clip_path_enum_ptrs, gx_clip_path *cptr) return ENUM_USING(st_path, &cptr->path, sizeof(cptr->path), index - 3);
47
48
578k
case 0:
49
578k
return ENUM_OBJ((cptr->rect_list == &cptr->local_list ? 0 :
50
0
             cptr->rect_list));
51
578k
case 1:
52
578k
return ENUM_OBJ(cptr->path_list);
53
578k
case 2:
54
578k
return ENUM_OBJ((cptr->cached == &cptr->rect_list->list.single ? 0 :
55
4.04M
             cptr->cached));
56
4.04M
ENUM_PTRS_END
57
static
58
578k
RELOC_PTRS_WITH(clip_path_reloc_ptrs, gx_clip_path *cptr)
59
578k
{
60
578k
    if (cptr->rect_list != &cptr->local_list)
61
578k
        RELOC_VAR(cptr->rect_list);
62
578k
    RELOC_VAR(cptr->path_list);
63
578k
    if (cptr->cached != &cptr->rect_list->list.single)
64
578k
        RELOC_VAR(cptr->cached);
65
578k
    RELOC_USING(st_path, &cptr->path, sizeof(gx_path));
66
578k
}
67
578k
RELOC_PTRS_END
68
69
/* GC procedures for gx_device_clip */
70
static
71
0
ENUM_PTRS_WITH(device_clip_enum_ptrs, gx_device_clip *cptr)
72
0
{
73
0
    if (index < st_clip_list_max_ptrs + 2)
74
0
        return ENUM_USING(st_clip_list, &cptr->list,
75
0
                          sizeof(gx_clip_list), index - 2);
76
0
    return ENUM_USING(st_device_forward, vptr,
77
0
                      sizeof(gx_device_forward),
78
0
                      index - (st_clip_list_max_ptrs + 2));
79
0
}
80
0
case 0:
81
0
ENUM_RETURN((cptr->current == &cptr->list.single ? NULL :
82
0
             (void *)cptr->current));
83
0
case 1:
84
0
ENUM_RETURN((cptr->cpath));
85
0
ENUM_PTRS_END
86
static
87
0
RELOC_PTRS_WITH(device_clip_reloc_ptrs, gx_device_clip *cptr)
88
0
{
89
0
    if (cptr->current == &cptr->list.single)
90
0
        cptr->current = &((gx_device_clip *)RELOC_OBJ(vptr))->list.single;
91
0
    else
92
0
        RELOC_PTR(gx_device_clip, current);
93
0
    RELOC_PTR(gx_device_clip, cpath);
94
0
    RELOC_USING(st_clip_list, &cptr->list, sizeof(gx_clip_list));
95
0
    RELOC_USING(st_device_forward, vptr, sizeof(gx_device_forward));
96
0
}
97
0
RELOC_PTRS_END
98
99
/* Define an empty clip list. */
100
static const gx_clip_list clip_list_empty = {
101
    {
102
        0,       /* next */
103
        0,       /* prev */
104
        min_int, /* ymin */
105
        max_int, /* ymax */
106
        0,       /* xmin */
107
        0,       /* xmax */
108
        0        /* to_visit */
109
    }, /* single */
110
    0, /* head */
111
    0, /* tail */
112
    0, /* insert */
113
    0, /* xmin */
114
    0, /* xmax */
115
    0, /* count */
116
    0  /* transpose = false */
117
};
118
119
/* ------ Clipping path memory management ------ */
120
121
static rc_free_proc(rc_free_cpath_list);
122
static rc_free_proc(rc_free_cpath_list_local);
123
static rc_free_proc(rc_free_cpath_path_list);
124
125
/*
126
 * Initialize those parts of the contents of a clip path that aren't
127
 * part of the path.
128
 */
129
static void
130
cpath_init_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
131
738k
{
132
738k
    gx_clip_list_from_rectangle(&pcpath->rect_list->list, pbox);
133
738k
    pcpath->inner_box = *pbox;
134
738k
    pcpath->path_valid = false;
135
738k
    pcpath->path_fill_adjust.x = 0;
136
738k
    pcpath->path_fill_adjust.y = 0;
137
738k
    pcpath->path.bbox = *pbox;
138
738k
    gx_cpath_set_outer_box(pcpath);
139
738k
    pcpath->id = gs_next_ids(pcpath->path.memory, 1); /* path changed => change id */
140
738k
    pcpath->cached = NULL;
141
738k
}
142
static void
143
cpath_init_own_contents(gx_clip_path * pcpath)
144
323k
{    /* We could make null_rect static, but then it couldn't be const. */
145
323k
    gs_fixed_rect null_rect;
146
147
323k
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
148
323k
    cpath_init_rectangle(pcpath, &null_rect);
149
323k
    pcpath->path_list = NULL;
150
323k
}
151
static void
152
cpath_share_own_contents(gx_clip_path * pcpath, const gx_clip_path * shared)
153
3.25k
{
154
3.25k
    pcpath->inner_box = shared->inner_box;
155
3.25k
    pcpath->path_valid = shared->path_valid;
156
3.25k
    pcpath->path_fill_adjust = shared->path_fill_adjust;
157
3.25k
    pcpath->outer_box = shared->outer_box;
158
3.25k
    pcpath->id = shared->id;
159
3.25k
    pcpath->cached = NULL;
160
3.25k
}
161
162
/* Allocate only the segments of a clipping path on the heap. */
163
static int
164
cpath_alloc_list(gx_clip_rect_list ** prlist, gs_memory_t * mem,
165
                 client_name_t cname)
166
246k
{
167
246k
    rc_alloc_struct_1(*prlist, gx_clip_rect_list, &st_clip_rect_list, mem,
168
246k
                      return_error(gs_error_VMerror), cname);
169
246k
    (*prlist)->rc.free = rc_free_cpath_list;
170
246k
    return 0;
171
246k
}
172
int
173
gx_cpath_init_contained_shared(gx_clip_path * pcpath,
174
        const gx_clip_path * shared, gs_memory_t * mem, client_name_t cname)
175
793k
{
176
793k
    if (shared) {
177
789k
        if (shared->path.segments == &shared->path.local_segments) {
178
0
            lprintf1("Attempt to share (local) segments of clip path "PRI_INTPTR"!\n",
179
0
                     (intptr_t)shared);
180
0
            return_error(gs_error_Fatal);
181
0
        }
182
789k
        *pcpath = *shared;
183
789k
        pcpath->path.memory = mem;
184
789k
        pcpath->path.allocation = path_allocated_contained;
185
789k
        rc_increment(pcpath->path.segments);
186
789k
        rc_increment(pcpath->rect_list);
187
789k
        rc_increment(pcpath->path_list);
188
789k
    } else {
189
4.69k
        int code = cpath_alloc_list(&pcpath->rect_list, mem, cname);
190
191
4.69k
        if (code < 0)
192
0
            return code;
193
4.69k
        code = gx_path_alloc_contained(&pcpath->path, mem, cname);
194
4.69k
        if (code < 0) {
195
0
            gs_free_object(mem, pcpath->rect_list, cname);
196
0
            pcpath->rect_list = 0;
197
0
            return code;
198
0
        }
199
4.69k
        cpath_init_own_contents(pcpath);
200
4.69k
    }
201
793k
    return 0;
202
793k
}
203
#define gx_cpath_alloc_contents(pcpath, shared, mem, cname)\
204
793k
  gx_cpath_init_contained_shared(pcpath, shared, mem, cname)
205
206
/* Allocate all of a clipping path on the heap. */
207
gx_clip_path *
208
gx_cpath_alloc_shared(const gx_clip_path * shared, gs_memory_t * mem,
209
                      client_name_t cname)
210
793k
{
211
793k
    gx_clip_path *pcpath =
212
793k
    gs_alloc_struct(mem, gx_clip_path, &st_clip_path, cname);
213
793k
    int code;
214
215
793k
    if (pcpath == 0)
216
0
        return 0;
217
793k
    code = gx_cpath_alloc_contents(pcpath, shared, mem, cname);
218
793k
    if (code < 0) {
219
0
        gs_free_object(mem, pcpath, cname);
220
0
        return 0;
221
0
    }
222
793k
    pcpath->path.allocation = path_allocated_on_heap;
223
793k
    return pcpath;
224
793k
}
225
226
/* Initialize a stack-allocated clipping path. */
227
int
228
gx_cpath_init_local_shared_nested(gx_clip_path * pcpath,
229
                            const gx_clip_path * shared,
230
                                  gs_memory_t  * mem,
231
                                  bool           safely_nested)
232
322k
{
233
322k
    if (shared) {
234
3.25k
        if ((shared->path.segments == &shared->path.local_segments) &&
235
3.25k
            !safely_nested) {
236
0
            lprintf1("Attempt to share (local) segments of clip path "PRI_INTPTR"!\n",
237
0
                     (intptr_t)shared);
238
0
            return_error(gs_error_Fatal);
239
0
        }
240
3.25k
        pcpath->path = shared->path;
241
3.25k
        pcpath->path.allocation = path_allocated_on_stack;
242
3.25k
        rc_increment(pcpath->path.segments);
243
3.25k
        pcpath->rect_list = shared->rect_list;
244
3.25k
        rc_increment(pcpath->rect_list);
245
3.25k
        pcpath->path_list = shared->path_list;
246
3.25k
        rc_increment(pcpath->path_list);
247
3.25k
        cpath_share_own_contents(pcpath, shared);
248
3.25k
        pcpath->rule = shared->rule;
249
318k
    } else {
250
318k
        gx_path_init_local(&pcpath->path, mem);
251
318k
        rc_init_free(&pcpath->local_list, mem, 1, rc_free_cpath_list_local);
252
318k
        pcpath->rect_list = &pcpath->local_list;
253
318k
        cpath_init_own_contents(pcpath);
254
318k
    }
255
322k
    return 0;
256
322k
}
257
258
int
259
gx_cpath_init_local_shared(gx_clip_path * pcpath, const gx_clip_path * shared,
260
                           gs_memory_t * mem)
261
281k
{
262
281k
    return gx_cpath_init_local_shared_nested(pcpath, shared, mem, 0);
263
281k
}
264
265
/* Unshare a clipping path. */
266
int
267
gx_cpath_unshare(gx_clip_path * pcpath)
268
0
{
269
0
    int code = gx_path_unshare(&pcpath->path);
270
0
    gx_clip_rect_list *rlist = pcpath->rect_list;
271
272
0
    if (code < 0)
273
0
        return code;
274
0
    if (rlist->rc.ref_count > 1) {
275
0
        int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
276
0
                                    "gx_cpath_unshare");
277
278
0
        if (code < 0)
279
0
            return code;
280
        /* Copy the rectangle list. */
281
/**************** NYI ****************/
282
        /* Until/Unless we implement this, NULL out the list */
283
0
        memset(&pcpath->rect_list->list, 0x00, sizeof(gx_clip_list));
284
0
        rc_decrement(rlist, "gx_cpath_unshare");
285
0
    }
286
0
    return code;
287
0
}
288
289
/* Free a clipping path. */
290
void
291
gx_cpath_free(gx_clip_path * pcpath, client_name_t cname)
292
1.99M
{
293
1.99M
    if (pcpath == 0L)
294
820k
        return;
295
296
1.17M
    rc_decrement(pcpath->rect_list, cname);
297
1.17M
    rc_decrement(pcpath->path_list, cname);
298
    /* Clean up pointers for GC. */
299
1.17M
    pcpath->rect_list = 0;
300
1.17M
    pcpath->path_list = 0;
301
1.17M
    {
302
1.17M
        gx_path_allocation_t alloc = pcpath->path.allocation;
303
304
1.17M
        if (alloc == path_allocated_on_heap) {
305
793k
            pcpath->path.allocation = path_allocated_contained;
306
793k
            gx_path_free(&pcpath->path, cname);
307
793k
            gs_free_object(pcpath->path.memory, pcpath, cname);
308
793k
        } else
309
378k
            gx_path_free(&pcpath->path, cname);
310
1.17M
    }
311
1.17M
}
312
313
/* Assign a clipping path, preserving the source. */
314
int
315
gx_cpath_assign_preserve(gx_clip_path * pcpto, gx_clip_path * pcpfrom)
316
226k
{
317
226k
    int code = gx_path_assign_preserve(&pcpto->path, &pcpfrom->path);
318
226k
    gx_clip_rect_list *fromlist = pcpfrom->rect_list;
319
226k
    gx_clip_rect_list *tolist = pcpto->rect_list;
320
226k
    gx_path path;
321
322
226k
    if (code < 0)
323
0
        return 0;
324
226k
    if (fromlist == &pcpfrom->local_list) {
325
        /* We can't use pcpfrom's list object. */
326
225k
        if (tolist == &pcpto->local_list || tolist->rc.ref_count > 1) {
327
            /* We can't use pcpto's list either.  Allocate a new one. */
328
84.5k
            int code = cpath_alloc_list(&tolist, tolist->rc.memory,
329
84.5k
                                        "gx_cpath_assign");
330
331
84.5k
            if (code < 0) {
332
0
                rc_decrement(pcpto->path.segments, "gx_path_assign");
333
0
                return code;
334
0
            }
335
84.5k
            rc_decrement(pcpto->rect_list, "gx_cpath_assign");
336
140k
        } else {
337
            /* Use pcpto's list object. */
338
140k
            rc_free_cpath_list_local(tolist->rc.memory, tolist,
339
140k
                                     "gx_cpath_assign");
340
140k
        }
341
225k
        tolist->list = fromlist->list;
342
225k
        pcpfrom->rect_list = tolist;
343
225k
        rc_increment(tolist);
344
225k
    } else {
345
        /* We can use pcpfrom's list object. */
346
1.49k
        rc_increment(fromlist);
347
1.49k
        rc_decrement(pcpto->rect_list, "gx_cpath_assign");
348
1.49k
    }
349
226k
    rc_increment(pcpfrom->path_list);
350
226k
    rc_decrement(pcpto->path_list, "gx_cpath_assign");
351
226k
    path = pcpto->path, *pcpto = *pcpfrom, pcpto->path = path;
352
226k
    return 0;
353
226k
}
354
355
/* Assign a clipping path, releasing the source. */
356
int
357
gx_cpath_assign_free(gx_clip_path * pcpto, gx_clip_path * pcpfrom)
358
225k
{       /* For right now, just do assign + free. */
359
225k
    int code = gx_cpath_assign_preserve(pcpto, pcpfrom);
360
361
225k
    if (code < 0)
362
0
        return code;
363
225k
    gx_cpath_free(pcpfrom, "gx_cpath_assign_free");
364
225k
    return 0;
365
225k
}
366
367
/* Free the clipping list when its reference count goes to zero. */
368
static void
369
rc_free_cpath_list_local(gs_memory_t * mem, void *vrlist,
370
                         client_name_t cname)
371
480k
{
372
480k
    gx_clip_rect_list *rlist = (gx_clip_rect_list *) vrlist;
373
374
480k
    gx_clip_list_free(&rlist->list, mem);
375
480k
}
376
static void
377
rc_free_cpath_list(gs_memory_t * mem, void *vrlist, client_name_t cname)
378
246k
{
379
246k
    rc_free_cpath_list_local(mem, vrlist, cname);
380
246k
    gs_free_object(mem, vrlist, cname);
381
246k
}
382
383
static void
384
rc_free_cpath_path_list(gs_memory_t * mem, void *vplist, client_name_t cname)
385
63.5k
{
386
63.5k
    gx_cpath_path_list *plist = (gx_cpath_path_list *)vplist;
387
63.5k
    rc_decrement(plist->next, cname);
388
63.5k
    gx_path_free(&plist->path, cname);
389
63.5k
    gs_free_object(plist->path.memory, plist, cname);
390
63.5k
}
391
392
/* Allocate a new clip path list node. The created node has a ref count
393
   of 1, and "steals" the reference to next (i.e. does not increment
394
   its reference count). */
395
static int
396
gx_cpath_path_list_new(gs_memory_t *mem, gx_clip_path *pcpath, int rule,
397
                        gx_path *ppfrom, gx_cpath_path_list *next, gx_cpath_path_list **pnew)
398
63.5k
{
399
63.5k
    int code;
400
63.5k
    client_name_t cname = "gx_cpath_path_list_new";
401
63.5k
    gx_cpath_path_list *pcplist = gs_alloc_struct(mem, gx_cpath_path_list,
402
63.5k
                                                  &st_cpath_path_list, cname);
403
404
63.5k
    if (pcplist == 0)
405
0
        return_error(gs_error_VMerror);
406
63.5k
    rc_init_free(pcplist, mem, 1, rc_free_cpath_path_list);
407
63.5k
    if (pcpath!=NULL && !pcpath->path_valid) {
408
55.5k
        code = gx_path_init_contained_shared(&pcplist->path, NULL, mem, cname);
409
55.5k
        if (code < 0) {
410
0
            gs_free_object(mem, pcplist, "gx_cpath_path_list_new");
411
0
            return code;
412
0
        }
413
55.5k
        code = gx_cpath_to_path(pcpath, &pcplist->path);
414
55.5k
    } else {
415
8.04k
        gx_path_init_local(&pcplist->path, mem);
416
8.04k
        code = gx_path_assign_preserve(&pcplist->path, ppfrom);
417
8.04k
    }
418
63.5k
    if (code < 0)
419
0
        return code;
420
63.5k
    pcplist->next = next;
421
63.5k
    rc_increment(next);
422
63.5k
    pcplist->rule = rule;
423
63.5k
    *pnew = pcplist;
424
63.5k
    return 0;
425
63.5k
}
426
427
/* ------ Clipping path accessing ------ */
428
429
/* Synthesize a path from a clipping path. */
430
int
431
gx_cpath_to_path_synthesize(const gx_clip_path * pcpath, gx_path * ppath)
432
56.0k
{
433
    /* Synthesize a path. */
434
56.0k
    gs_cpath_enum cenum;
435
56.0k
    gs_fixed_point pts[3];
436
56.0k
    int code;
437
438
56.0k
    gx_cpath_enum_init(&cenum, pcpath);
439
306k
    while ((code = gx_cpath_enum_next(&cenum, pts)) != 0) {
440
250k
        switch (code) {
441
56.4k
            case gs_pe_moveto:
442
56.4k
                code = gx_path_add_point(ppath, pts[0].x, pts[0].y);
443
56.4k
                break;
444
152k
            case gs_pe_lineto:
445
152k
                code = gx_path_add_line_notes(ppath, pts[0].x, pts[0].y,
446
152k
                                           gx_cpath_enum_notes(&cenum));
447
152k
                break;
448
0
            case gs_pe_gapto:
449
0
                code = gx_path_add_gap_notes(ppath, pts[0].x, pts[0].y,
450
0
                                           gx_cpath_enum_notes(&cenum));
451
0
                break;
452
0
            case gs_pe_curveto:
453
0
                code = gx_path_add_curve_notes(ppath, pts[0].x, pts[0].y,
454
0
                                               pts[1].x, pts[1].y,
455
0
                                               pts[2].x, pts[2].y,
456
0
                                           gx_cpath_enum_notes(&cenum));
457
0
                break;
458
41.8k
            case gs_pe_closepath:
459
41.8k
                code = gx_path_close_subpath_notes(ppath,
460
41.8k
                                           gx_cpath_enum_notes(&cenum));
461
41.8k
                break;
462
0
            default:
463
0
                if (code >= 0)
464
0
                    code = gs_note_error(gs_error_unregistered);
465
250k
        }
466
250k
        if (code < 0)
467
0
            break;
468
250k
    }
469
56.0k
    return 0;
470
56.0k
}
471
472
/* Return the path of a clipping path. */
473
int
474
gx_cpath_to_path(gx_clip_path * pcpath, gx_path * ppath)
475
56.1k
{
476
56.1k
    if (!pcpath->path_valid) {
477
56.0k
        gx_path rpath;
478
56.0k
        int code;
479
480
56.0k
        gx_path_init_local(&rpath, pcpath->path.memory);
481
56.0k
        code = gx_cpath_to_path_synthesize(pcpath, &rpath);
482
56.0k
        if (code < 0) {
483
0
            gx_path_free(&rpath, "gx_cpath_to_path error");
484
0
            return code;
485
0
        }
486
56.0k
        code = gx_path_assign_free(&pcpath->path, &rpath);
487
56.0k
        if (code < 0)
488
0
            return code;
489
56.0k
        pcpath->path_valid = true;
490
56.0k
        pcpath->path_fill_adjust.x = 0;
491
56.0k
        pcpath->path_fill_adjust.y = 0;
492
56.0k
    }
493
56.1k
    return gx_path_assign_preserve(ppath, &pcpath->path);
494
56.1k
}
495
/* Return the inner and outer check rectangles for a clipping path. */
496
/* Return true iff the path is a rectangle. */
497
bool
498
gx_cpath_inner_box(const gx_clip_path * pcpath, gs_fixed_rect * pbox)
499
1.41M
{
500
1.41M
    *pbox = pcpath->inner_box;
501
1.41M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
502
1.41M
}
503
bool
504
gx_cpath_outer_box(const gx_clip_path * pcpath, gs_fixed_rect * pbox)
505
1.17M
{
506
1.17M
    *pbox = pcpath->outer_box;
507
1.17M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
508
1.17M
}
509
510
/* Test if a clipping path includes a rectangle. */
511
/* The rectangle need not be oriented correctly, i.e. x0 > x1 is OK. */
512
bool
513
gx_cpath_includes_rectangle(register const gx_clip_path * pcpath,
514
                            fixed x0, fixed y0, fixed x1, fixed y1)
515
10.0k
{
516
10.0k
    return
517
10.0k
        (x0 <= x1 ?
518
10.0k
         (pcpath->inner_box.p.x <= x0 && x1 <= pcpath->inner_box.q.x) :
519
10.0k
         (pcpath->inner_box.p.x <= x1 && x0 <= pcpath->inner_box.q.x)) &&
520
10.0k
        (y0 <= y1 ?
521
8.37k
         (pcpath->inner_box.p.y <= y0 && y1 <= pcpath->inner_box.q.y) :
522
8.37k
         (pcpath->inner_box.p.y <= y1 && y0 <= pcpath->inner_box.q.y));
523
10.0k
}
524
525
/* Set the outer clipping box to the path bounding box, */
526
/* expanded to pixel boundaries. */
527
void
528
gx_cpath_set_outer_box(gx_clip_path * pcpath)
529
963k
{
530
963k
    pcpath->outer_box.p.x = fixed_floor(pcpath->path.bbox.p.x);
531
963k
    pcpath->outer_box.p.y = fixed_floor(pcpath->path.bbox.p.y);
532
963k
    pcpath->outer_box.q.x = fixed_ceiling(pcpath->path.bbox.q.x);
533
963k
    pcpath->outer_box.q.y = fixed_ceiling(pcpath->path.bbox.q.y);
534
963k
}
535
536
/* Return the rectangle list of a clipping path (for local use only). */
537
const gx_clip_list *
538
gx_cpath_list(const gx_clip_path *pcpath)
539
3.29M
{
540
3.29M
    return &pcpath->rect_list->list;
541
3.29M
}
542
/* Internal non-const version of the same accessor. */
543
static inline gx_clip_list *
544
gx_cpath_list_private(const gx_clip_path *pcpath)
545
56.0k
{
546
56.0k
    return &pcpath->rect_list->list;
547
56.0k
}
548
549
/* ------ Clipping path setting ------ */
550
551
/* Create a rectangular clipping path. */
552
/* The supplied rectangle may not be oriented correctly, */
553
/* but it will be oriented correctly upon return. */
554
static int
555
cpath_set_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
556
415k
{
557
415k
    gx_clip_rect_list *rlist = pcpath->rect_list;
558
559
415k
    if (rlist->rc.ref_count <= 1)
560
257k
        gx_clip_list_free(&rlist->list, rlist->rc.memory);
561
157k
    else {
562
157k
        int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
563
157k
                                    "gx_cpath_from_rectangle");
564
565
157k
        rc_decrement(rlist, "gx_cpath_from_rectangle");
566
157k
        if (code < 0)
567
0
            return code;
568
157k
        rlist = pcpath->rect_list;
569
157k
    }
570
415k
    cpath_init_rectangle(pcpath, pbox);
571
415k
    return 0;
572
415k
}
573
int
574
gx_cpath_from_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
575
390k
{
576
390k
    int code = gx_path_new(&pcpath->path);
577
578
390k
    if (code < 0)
579
0
        return code;
580
390k
    return cpath_set_rectangle(pcpath, pbox);
581
390k
}
582
int
583
gx_cpath_reset(gx_clip_path * pcpath)
584
180k
{
585
180k
    gs_fixed_rect null_rect;
586
587
180k
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
588
180k
    rc_decrement(pcpath->path_list, "gx_cpath_reset");
589
180k
    return gx_cpath_from_rectangle(pcpath, &null_rect);
590
180k
}
591
592
/* If a clipping path is a rectangle, return the rectangle.
593
 * If its a rectangular path, also return the rectangle.
594
 */
595
gx_path_rectangular_type cpath_is_rectangle(const gx_clip_path * pcpath, gs_fixed_rect *rect)
596
0
{
597
0
    if (pcpath->path_valid) {
598
0
        return gx_path_is_rectangle((const gx_path *)&pcpath->path, rect);
599
0
    }
600
0
    if (pcpath->inner_box.p.x != pcpath->path.bbox.p.x ||
601
0
        pcpath->inner_box.p.y != pcpath->path.bbox.p.y ||
602
0
        pcpath->inner_box.q.x != pcpath->path.bbox.q.x ||
603
0
        pcpath->inner_box.q.y != pcpath->path.bbox.q.y)
604
0
        return prt_none;
605
0
    rect->p.x = pcpath->inner_box.p.x;
606
0
    rect->p.y = pcpath->inner_box.p.y;
607
0
    rect->q.x = pcpath->inner_box.q.x;
608
0
    rect->q.y = pcpath->inner_box.q.y;
609
0
    return prt_closed;
610
0
}
611
612
/* Intersect a new clipping path with an old one. */
613
/* Flatten the new path first (in a copy) if necessary. */
614
int
615
gx_cpath_clip(gs_gstate *pgs, gx_clip_path *pcpath,
616
              /*const*/ gx_path *ppath_orig, int rule)
617
30.3k
{
618
30.3k
    return gx_cpath_intersect(pcpath, ppath_orig, rule, pgs);
619
30.3k
}
620
621
int
622
gx_cpath_ensure_path_list(gx_clip_path *pcpath)
623
59.5k
{
624
59.5k
    if (pcpath == NULL || pcpath->path_list)
625
1.00k
        return 0;
626
58.5k
    return gx_cpath_path_list_new(pcpath->path.memory, pcpath, pcpath->rule,
627
58.5k
                                  &pcpath->path, NULL, &pcpath->path_list);
628
59.5k
}
629
630
int
631
gx_cpath_intersect_with_params(gx_clip_path *pcpath, /*const*/ gx_path *ppath_orig,
632
                   int rule, gs_gstate *pgs, const gx_fill_params * params)
633
38.6k
{
634
38.6k
    gx_path fpath;
635
38.6k
    /*const*/ gx_path *ppath = ppath_orig;
636
38.6k
    gs_fixed_rect old_box, new_box;
637
38.6k
    int code;
638
38.6k
    int pcpath_is_rect;
639
640
38.6k
    pcpath->cached = NULL;
641
    /* Flatten the path if necessary. */
642
38.6k
    if (gx_path_has_curves_inline(ppath)) {
643
5.08k
        gx_path_init_local(&fpath, pgs->memory);
644
5.08k
        code = gx_path_add_flattened_accurate(ppath, &fpath,
645
5.08k
                                              gs_currentflat_inline(pgs),
646
5.08k
                                              pgs->accurate_curves);
647
5.08k
        if (code < 0)
648
0
            return code;
649
5.08k
        ppath = &fpath;
650
5.08k
    }
651
652
38.6k
    pcpath_is_rect = gx_cpath_inner_box(pcpath, &old_box);
653
38.6k
    if (pcpath_is_rect &&
654
38.6k
        ((code = gx_path_is_rectangle(ppath, &new_box)) ||
655
35.8k
         gx_path_is_void(ppath))
656
38.6k
        ) {
657
25.7k
        int changed = 0;
658
659
25.7k
        if (!code) {
660
            /* The new path is void. */
661
472
            if (gx_path_current_point(ppath, &new_box.p) < 0) {
662
                /* Use the user space origin (arbitrarily). */
663
272
                new_box.p.x = float2fixed(pgs->ctm.tx);
664
272
                new_box.p.y = float2fixed(pgs->ctm.ty);
665
272
            }
666
472
            new_box.q = new_box.p;
667
472
            changed = 1;
668
25.2k
        } else {
669
25.2k
            {   /* Apply same adjustment as for filling the path. */
670
25.2k
                gs_fixed_point adjust = params != NULL ? params->adjust : pgs->fill_adjust;
671
25.2k
                fixed adjust_xl, adjust_xu, adjust_yl, adjust_yu;
672
673
25.2k
                if (adjust.x == -1)
674
0
                    adjust_xl = adjust_xu = adjust_yl = adjust_yu = 0;
675
25.2k
                else {
676
25.2k
                    adjust_xl = (adjust.x == fixed_half ? fixed_half - fixed_epsilon : adjust.x);
677
25.2k
                    adjust_yl = (adjust.y == fixed_half ? fixed_half - fixed_epsilon : adjust.y);
678
25.2k
                    adjust_xu = adjust.x;
679
25.2k
                    adjust_yu = adjust.y;
680
25.2k
                }
681
25.2k
                new_box.p.x = int2fixed(fixed2int_pixround(new_box.p.x - adjust_xl));
682
25.2k
                new_box.p.y = int2fixed(fixed2int_pixround(new_box.p.y - adjust_yl));
683
25.2k
                new_box.q.x = int2fixed(fixed2int_pixround(new_box.q.x + adjust_xu));
684
25.2k
                new_box.q.y = int2fixed(fixed2int_pixround(new_box.q.y + adjust_yu));
685
25.2k
            }
686
            /* Intersect the two rectangles if necessary. */
687
25.2k
            if (old_box.p.x > new_box.p.x)
688
3.51k
                new_box.p.x = old_box.p.x, ++changed;
689
25.2k
            if (old_box.p.y > new_box.p.y)
690
5.07k
                new_box.p.y = old_box.p.y, ++changed;
691
25.2k
            if (old_box.q.x < new_box.q.x)
692
3.89k
                new_box.q.x = old_box.q.x, ++changed;
693
25.2k
            if (old_box.q.y < new_box.q.y)
694
5.50k
                new_box.q.y = old_box.q.y, ++changed;
695
            /* Check for a degenerate rectangle. */
696
25.2k
            if (new_box.q.x < new_box.p.x || new_box.q.y < new_box.p.y)
697
838
                new_box.p = new_box.q, changed = 1;
698
25.2k
        }
699
25.7k
        if (changed == 4) {
700
            /* The new box/path is the same as the old. */
701
1.18k
            return 0;
702
1.18k
        }
703
        /* Release the existing path. */
704
24.5k
        rc_decrement(pcpath->path_list, "gx_cpath_intersect");
705
24.5k
        pcpath->path_list = NULL;
706
24.5k
        gx_path_new(&pcpath->path);
707
24.5k
        ppath->bbox = new_box;
708
24.5k
        cpath_set_rectangle(pcpath, &new_box);
709
24.5k
        if (changed == 0) {
710
            /* The path is valid; otherwise, defer constructing it. */
711
15.8k
            gx_path_assign_preserve(&pcpath->path, ppath);
712
15.8k
            pcpath->path_valid = true;
713
15.8k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
714
15.8k
        }
715
24.5k
    } else {
716
        /* New clip path is nontrivial.  Intersect the slow way. */
717
12.9k
        gx_cpath_path_list *next = NULL;
718
12.9k
        bool path_valid =
719
12.9k
            pcpath_is_rect &&
720
12.9k
            gx_path_bbox(ppath, &new_box) >= 0 &&
721
12.9k
            gx_cpath_includes_rectangle(pcpath,
722
10.0k
                                        new_box.p.x, new_box.p.y,
723
10.0k
                                        new_box.q.x, new_box.q.y);
724
725
12.9k
        if (!path_valid && next == NULL) {
726
            /* gx_cpaths should generally have a path_list set within
727
             * them. In some cases (filled images), they may not. Ensure
728
             * that they do, and remember the path_list */
729
5.06k
            code = gx_cpath_ensure_path_list(pcpath);
730
5.06k
            if (code < 0)
731
0
                goto ex;
732
            /* gx_cpath_intersect_path_slow NULLs pcpath->path_list, so
733
             * remember it here. */
734
5.06k
            next = pcpath->path_list;
735
5.06k
            rc_increment(next);
736
5.06k
        }
737
12.9k
        code = gx_cpath_intersect_path_slow(pcpath, (params != NULL ? ppath_orig : ppath),
738
12.9k
                            rule, pgs, params);
739
12.9k
        if (code < 0) {
740
1
            rc_decrement(next, "gx_cpath_clip");
741
1
            goto ex;
742
1
        }
743
12.9k
        if (path_valid) {
744
7.86k
            gx_path_assign_preserve(&pcpath->path, ppath_orig);
745
7.86k
            pcpath->path_valid = true;
746
7.86k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
747
7.86k
            pcpath->rule = rule;
748
7.86k
        } else {
749
5.06k
            code = gx_cpath_path_list_new(pcpath->path.memory, NULL, rule,
750
5.06k
                                          ppath_orig, next, &pcpath->path_list);
751
5.06k
        }
752
12.9k
        rc_decrement(next, "gx_cpath_clip");
753
12.9k
    }
754
37.4k
ex:
755
37.4k
    if (ppath != ppath_orig)
756
5.08k
        gx_path_free(ppath, "gx_cpath_clip");
757
37.4k
    return code;
758
38.6k
}
759
int
760
gx_cpath_intersect(gx_clip_path *pcpath, /*const*/ gx_path *ppath_orig,
761
                   int rule, gs_gstate *pgs)
762
30.3k
{
763
30.3k
    return gx_cpath_intersect_with_params(pcpath, ppath_orig,
764
30.3k
                   rule, pgs, NULL);
765
30.3k
}
766
767
/* Scale a clipping path by a power of 2. */
768
int
769
gx_cpath_scale_exp2_shared(gx_clip_path * pcpath, int log2_scale_x,
770
                           int log2_scale_y, bool list_shared,
771
                           bool segments_shared)
772
0
{
773
0
    int code =
774
0
        (pcpath->path_valid ?
775
0
         gx_path_scale_exp2_shared(&pcpath->path, log2_scale_x, log2_scale_y,
776
0
                                   segments_shared) :
777
0
         0);
778
0
    gx_clip_list *list = gx_cpath_list_private(pcpath);
779
0
    gx_clip_rect *pr;
780
781
0
    if (code < 0)
782
0
        return code;
783
    /* Scale the fixed entries. */
784
0
    gx_rect_scale_exp2(&pcpath->inner_box, log2_scale_x, log2_scale_y);
785
0
    gx_rect_scale_exp2(&pcpath->outer_box, log2_scale_x, log2_scale_y);
786
0
    if (!list_shared) {
787
        /* Scale the clipping list. */
788
0
        pr = list->head;
789
0
        if (pr == 0)
790
0
            pr = &list->single;
791
0
        for (; pr != 0; pr = pr->next)
792
0
            if (pr != list->head && pr != list->tail) {
793
794
0
#define SCALE_V(v, s)\
795
0
  if ( pr->v != min_int && pr->v != max_int )\
796
0
    pr->v = (s >= 0 ? pr->v << s : pr->v >> -s)
797
798
0
                SCALE_V(xmin, log2_scale_x);
799
0
                SCALE_V(xmax, log2_scale_x);
800
0
                SCALE_V(ymin, log2_scale_y);
801
0
                SCALE_V(ymax, log2_scale_y);
802
0
#undef SCALE_V
803
0
            }
804
0
        if (log2_scale_x > 0) {
805
0
            list->xmin <<= log2_scale_x;
806
0
            list->xmax <<= log2_scale_x;
807
0
        } else {
808
0
            list->xmin = arith_rshift(list->xmin, -log2_scale_x);
809
0
            list->xmax = arith_rshift(list->xmax, -log2_scale_x);
810
0
        }
811
0
    }
812
0
    pcpath->id = gs_next_ids(pcpath->path.memory, 1); /* path changed => change id */
813
0
    return 0;
814
0
}
815
816
/* ------ Clipping list routines ------ */
817
818
/* Initialize a clip list. */
819
void
820
gx_clip_list_init(gx_clip_list * clp)
821
1.70M
{
822
1.70M
    *clp = clip_list_empty;
823
1.70M
}
824
825
/* Initialize a clip list to a rectangle. */
826
/* The supplied rectangle may not be oriented correctly, */
827
/* but it will be oriented correctly upon return. */
828
static void
829
gx_clip_list_from_rectangle(register gx_clip_list * clp,
830
                            register gs_fixed_rect * rp)
831
738k
{
832
738k
    gx_clip_list_init(clp);
833
738k
    if (rp->p.x > rp->q.x) {
834
0
        fixed t = rp->p.x;
835
836
0
        rp->p.x = rp->q.x;
837
0
        rp->q.x = t;
838
0
    }
839
738k
    if (rp->p.y > rp->q.y) {
840
0
        fixed t = rp->p.y;
841
842
0
        rp->p.y = rp->q.y;
843
0
        rp->q.y = t;
844
0
    }
845
738k
    clp->single.xmin = clp->xmin = fixed2int_var(rp->p.x);
846
738k
    clp->single.ymin = fixed2int_var(rp->p.y);
847
    /* Handle degenerate rectangles specially. */
848
738k
    clp->single.xmax = clp->xmax =
849
738k
        (rp->q.x == rp->p.x ? clp->single.xmin :
850
738k
         fixed2int_var_ceiling(rp->q.x));
851
738k
    clp->single.ymax =
852
738k
        (rp->q.y == rp->p.y ? clp->single.ymin :
853
738k
         fixed2int_var_ceiling(rp->q.y));
854
738k
    clp->count = 1;
855
738k
}
856
857
/* Start enumerating a clipping path. */
858
int
859
gx_cpath_enum_init(gs_cpath_enum * penum, const gx_clip_path * pcpath)
860
56.0k
{
861
56.0k
    if ((penum->using_path = pcpath->path_valid)) {
862
0
        gx_path_enum_init(&penum->path_enum, &pcpath->path);
863
0
        penum->rp = penum->visit = 0;
864
0
        penum->first_visit = visit_left;
865
56.0k
    } else {
866
56.0k
        gx_path empty_path;
867
56.0k
        gx_clip_list *clp = gx_cpath_list_private(pcpath);
868
56.0k
        gx_clip_rect *head = (clp->count <= 1 ? &clp->single : clp->head);
869
56.0k
        gx_clip_rect *rp;
870
871
        /* Initialize the pointers in the path_enum properly. */
872
56.0k
        gx_path_init_local(&empty_path, pcpath->path.memory);
873
56.0k
        gx_path_enum_init(&penum->path_enum, &empty_path);
874
56.0k
        penum->first_visit = visit_left;
875
56.0k
        penum->visit = head;
876
123k
        for (rp = head; rp != 0; rp = rp->next)
877
67.4k
            rp->to_visit =
878
67.4k
                (rp->xmin < rp->xmax && rp->ymin < rp->ymax ?
879
50.6k
                 visit_left | visit_right : 0);
880
56.0k
        penum->rp = 0;    /* scan will initialize */
881
56.0k
        penum->any_rectangles = false;
882
56.0k
        penum->state = cpe_scan;
883
56.0k
        penum->have_line = false;
884
56.0k
    }
885
56.0k
    return 0;
886
56.0k
}
887
888
/* Enumerate the next segment of a clipping path. */
889
/* In general, this produces a path made up of zillions of tiny lines. */
890
int
891
gx_cpath_enum_next(gs_cpath_enum * penum, gs_fixed_point pts[3])
892
306k
{
893
306k
    if (penum->using_path)
894
0
        return gx_path_enum_next(&penum->path_enum, pts);
895
306k
#define set_pt(xi, yi)\
896
306k
  (pts[0].x = int2fixed(xi), pts[0].y = int2fixed(yi))
897
306k
#define set_line(xi, yi)\
898
306k
  (penum->line_end.x = (xi), penum->line_end.y = (yi), penum->have_line = true)
899
306k
    if (penum->have_line) {
900
55.2k
        set_pt(penum->line_end.x, penum->line_end.y);
901
55.2k
        penum->have_line = false;
902
55.2k
        return gs_pe_lineto;
903
251k
    } {
904
251k
        gx_clip_rect *visit = penum->visit;
905
251k
        gx_clip_rect *rp = penum->rp;
906
251k
        cpe_visit_t first_visit = penum->first_visit;
907
251k
        cpe_state_t state = penum->state;
908
251k
        gx_clip_rect *look;
909
251k
        int code;
910
911
251k
        switch (state) {
912
913
97.8k
            case cpe_scan:
914
                /* Look for the start of an edge to trace. */
915
165k
                for (; visit != 0; visit = visit->next) {
916
109k
                    if (visit->to_visit & visit_left) {
917
41.7k
                        set_pt(visit->xmin, visit->ymin);
918
41.7k
                        first_visit = visit_left;
919
41.7k
                        state = cpe_left;
920
67.5k
                    } else if (visit->to_visit & visit_right) {
921
23
                        set_pt(visit->xmax, visit->ymax);
922
23
                        first_visit = visit_right;
923
23
                        state = cpe_right;
924
23
                    } else
925
67.4k
                        continue;
926
41.8k
                    rp = visit;
927
41.8k
                    code = gs_pe_moveto;
928
41.8k
                    penum->any_rectangles = true;
929
41.8k
                    goto out;
930
109k
                }
931
                /* We've enumerated all the edges. */
932
56.0k
                state = cpe_done;
933
56.0k
                if (!penum->any_rectangles) {
934
                    /* We didn't have any rectangles. */
935
14.6k
                    set_pt(fixed_0, fixed_0);
936
14.6k
                    code = gs_pe_moveto;
937
14.6k
                    break;
938
14.6k
                }
939
                /* falls through */
940
941
56.0k
            case cpe_done:
942
                /* All done. */
943
56.0k
                code = 0;
944
56.0k
                break;
945
946
/* We can't use the BEGIN ... END hack here: we need to be able to break. */
947
0
#define return_line(px, py)\
948
97.0k
  set_pt(px, py); code = gs_pe_lineto; break
949
950
48.4k
            case cpe_left:
951
952
50.6k
              left:   /* Trace upward along a left edge. */
953
                /* We're at the lower left corner of rp. */
954
50.6k
                rp->to_visit &= ~visit_left;
955
                /* Look for an adjacent rectangle above rp. */
956
50.6k
                for (look = rp;
957
52.7k
                     (look = look->next) != 0 &&
958
52.7k
                     (look->ymin == rp->ymin ||
959
12.4k
                      (look->ymin == rp->ymax && look->xmax <= rp->xmin));
960
50.6k
                    );
961
                /* Now we know look->ymin >= rp->ymax. */
962
50.6k
                if (look == 0 || look->ymin > rp->ymax ||
963
50.6k
                    look->xmin >= rp->xmax
964
50.6k
                    ) {   /* No adjacent rectangle, switch directions. */
965
41.7k
                    state =
966
41.7k
                        (rp == visit && first_visit == visit_right ? cpe_close :
967
41.7k
                         (set_line(rp->xmax, rp->ymax), cpe_right));
968
41.7k
                    return_line(rp->xmin, rp->ymax);
969
41.7k
                }
970
                /* We found an adjacent rectangle. */
971
                /* See if it also adjoins a rectangle to the left of rp. */
972
8.89k
                {
973
8.89k
                    gx_clip_rect *prev = rp->prev;
974
8.89k
                    gx_clip_rect *cur = rp;
975
976
8.89k
                    if (prev != 0 && prev->ymax == rp->ymax &&
977
8.89k
                        look->xmin < prev->xmax
978
8.89k
                        ) { /* There's an adjoining rectangle as well. */
979
                        /* Switch directions. */
980
35
                        rp = prev;
981
35
                        state =
982
35
                            (rp == visit && first_visit == visit_right ? cpe_close :
983
35
                             (set_line(prev->xmax, prev->ymax), cpe_right));
984
35
                        return_line(cur->xmin, cur->ymax);
985
35
                    }
986
8.85k
                    rp = look;
987
8.85k
                    if (rp == visit && first_visit == visit_left)
988
0
                        state = cpe_close;
989
8.85k
                    else if (rp->xmin == cur->xmin)
990
2.19k
                        goto left;
991
6.66k
                    else
992
6.66k
                        set_line(rp->xmin, rp->ymin);
993
8.85k
                    return_line(cur->xmin, cur->ymax);
994
8.85k
                }
995
996
48.5k
            case cpe_right:
997
998
50.6k
              right:    /* Trace downward along a right edge. */
999
                /* We're at the upper right corner of rp. */
1000
50.6k
                rp->to_visit &= ~visit_right;
1001
                /* Look for an adjacent rectangle below rp. */
1002
50.6k
                for (look = rp;
1003
52.7k
                     (look = look->prev) != 0 &&
1004
52.7k
                     (look->ymax == rp->ymax ||
1005
12.4k
                      (look->ymax == rp->ymin && look->xmin >= rp->xmax));
1006
50.6k
                    );
1007
                /* Now we know look->ymax <= rp->ymin. */
1008
50.6k
                if (look == 0 || look->ymax < rp->ymin ||
1009
50.6k
                    look->xmax <= rp->xmin
1010
50.6k
                    ) {   /* No adjacent rectangle, switch directions. */
1011
41.7k
                    state =
1012
41.7k
                        (rp == visit && first_visit == visit_left ? cpe_close :
1013
41.7k
                         (set_line(rp->xmin, rp->ymin), cpe_left));
1014
41.7k
                    return_line(rp->xmax, rp->ymin);
1015
41.7k
                }
1016
                /* We found an adjacent rectangle. */
1017
                /* See if it also adjoins a rectangle to the right of rp. */
1018
8.89k
                {
1019
8.89k
                    gx_clip_rect *next = rp->next;
1020
8.89k
                    gx_clip_rect *cur = rp;
1021
1022
8.89k
                    if (next != 0 && next->ymin == rp->ymin &&
1023
8.89k
                        look->xmax > next->xmin
1024
8.89k
                        ) { /* There's an adjoining rectangle as well. */
1025
                        /* Switch directions. */
1026
35
                        rp = next;
1027
35
                        state =
1028
35
                            (rp == visit && first_visit == visit_left ? cpe_close :
1029
35
                             (set_line(next->xmin, next->ymin), cpe_left));
1030
35
                        return_line(cur->xmax, cur->ymin);
1031
35
                    }
1032
8.85k
                    rp = look;
1033
8.85k
                    if (rp == visit && first_visit == visit_right)
1034
23
                        state = cpe_close;
1035
8.83k
                    else if (rp->xmax == cur->xmax)
1036
2.14k
                        goto right;
1037
6.68k
                    else
1038
6.68k
                        set_line(rp->xmax, rp->ymax);
1039
8.85k
                    return_line(cur->xmax, cur->ymin);
1040
8.85k
                }
1041
1042
0
#undef return_line
1043
1044
41.8k
            case cpe_close:
1045
                /* We've gone all the way around an edge. */
1046
41.8k
                code = gs_pe_closepath;
1047
41.8k
                state = cpe_scan;
1048
41.8k
                break;
1049
1050
0
            default:
1051
0
                return_error(gs_error_unknownerror);
1052
251k
        }
1053
1054
251k
      out:      /* Store the state before exiting. */
1055
251k
        penum->visit = visit;
1056
251k
        penum->rp = rp;
1057
251k
        penum->first_visit = first_visit;
1058
251k
        penum->state = state;
1059
251k
        return code;
1060
251k
    }
1061
251k
#undef set_pt
1062
251k
#undef set_line
1063
251k
}
1064
segment_notes
1065
gx_cpath_enum_notes(const gs_cpath_enum * penum)
1066
194k
{
1067
194k
    return sn_none;
1068
194k
}
1069
1070
/* Free a clip list. */
1071
void
1072
gx_clip_list_free(gx_clip_list * clp, gs_memory_t * mem)
1073
738k
{
1074
738k
    gx_clip_rect *rp = clp->tail;
1075
1076
1.29M
    while (rp != 0) {
1077
555k
        gx_clip_rect *prev = rp->prev;
1078
1079
555k
        gs_free_object(mem, rp, "gx_clip_list_free");
1080
555k
        rp = prev;
1081
555k
    }
1082
738k
    gx_clip_list_init(clp);
1083
738k
}
1084
1085
/* Check whether a rectangle has a non-empty intersection with a clipping patch. */
1086
bool
1087
gx_cpath_rect_visible(gx_clip_path * pcpath, gs_int_rect *prect)
1088
5.25k
{
1089
5.25k
    const gx_clip_rect *pr;
1090
5.25k
    const gx_clip_list *list = &pcpath->rect_list->list;
1091
1092
5.25k
    switch (list->count) {
1093
1
        case 0:
1094
1
            return false;
1095
5.23k
        case 1:
1096
5.23k
            pr = &list->single;
1097
5.23k
            break;
1098
20
        default:
1099
20
            pr = list->head;
1100
5.25k
    }
1101
5.82k
    for (; pr != 0; pr = pr->next) {
1102
5.82k
        if (pr->xmin > prect->q.x)
1103
0
            continue;
1104
5.82k
        if (pr->xmax < prect->p.x)
1105
20
            continue;
1106
5.80k
        if (pr->ymin > prect->q.y)
1107
0
            continue;
1108
5.80k
        if (pr->ymax < prect->p.y)
1109
555
            continue;
1110
5.25k
        return true;
1111
5.80k
    }
1112
0
    return false;
1113
5.25k
}
1114
1115
int
1116
gx_cpath_copy(const gx_clip_path * from, gx_clip_path * pcpath)
1117
1.99k
{   /* *pcpath must be initialized. */
1118
1.99k
    gx_clip_rect *r, *s;
1119
1.99k
    gx_clip_list *l = &pcpath->rect_list->list;
1120
1121
1.99k
    pcpath->path_valid = false;
1122
    /* NOTE: pcpath->path still contains the old path. */
1123
1.99k
    if (pcpath->path_list)
1124
1.99k
        rc_decrement(pcpath->path_list, "gx_cpath_copy");
1125
1.99k
    pcpath->path_list = NULL;
1126
1.99k
    pcpath->rule = from->rule;
1127
1.99k
    pcpath->outer_box = from->outer_box;
1128
1.99k
    pcpath->inner_box = from->inner_box;
1129
1.99k
    pcpath->cached = NULL;
1130
1.99k
    l->single = from->rect_list->list.single;
1131
5.05k
    for (r = from->rect_list->list.head; r != NULL; r = r->next) {
1132
3.05k
        if (pcpath->rect_list->rc.memory == NULL)
1133
0
            s = gs_alloc_struct(from->rect_list->rc.memory, gx_clip_rect, &st_clip_rect, "gx_cpath_copy");
1134
3.05k
        else
1135
3.05k
            s = gs_alloc_struct(pcpath->rect_list->rc.memory, gx_clip_rect, &st_clip_rect, "gx_cpath_copy");
1136
1137
3.05k
        if (s == NULL)
1138
0
            return_error(gs_error_VMerror);
1139
3.05k
        *s = *r;
1140
3.05k
        s->next = NULL;
1141
3.05k
        if (l->tail) {
1142
3.04k
            s->prev = l->tail;
1143
3.04k
            l->tail->next = s;
1144
3.04k
        } else {
1145
19
            l->head = s;
1146
19
            s->prev = NULL;
1147
19
        }
1148
3.05k
        l->tail = s;
1149
3.05k
    }
1150
1.99k
    l->count = from->rect_list->list.count;
1151
1.99k
    return 0;
1152
1.99k
}
1153
1154
/* ------ Debugging printout ------ */
1155
1156
#ifdef DEBUG
1157
1158
/* Print a clipping list. */
1159
static void
1160
gx_clip_list_print(const gs_memory_t *mem, const gx_clip_list *list)
1161
{
1162
    const gx_clip_rect *pr;
1163
1164
    dmlprintf3(mem, "   list count=%d xmin=%d xmax=%d\n",
1165
               list->count, list->xmin, list->xmax);
1166
    switch (list->count) {
1167
        case 0:
1168
            pr = 0;
1169
            break;
1170
        case 1:
1171
            pr = &list->single;
1172
            break;
1173
        default:
1174
            pr = list->head;
1175
    }
1176
    for (; pr != 0; pr = pr->next)
1177
        dmlprintf4(mem, "   rect: (%d,%d),(%d,%d)\n",
1178
                   pr->xmin, pr->ymin, pr->xmax, pr->ymax);
1179
}
1180
1181
/* Print a clipping path */
1182
void
1183
gx_cpath_print(const gs_memory_t *mem, const gx_clip_path * pcpath)
1184
{
1185
    if (pcpath->path_valid)
1186
        gx_path_print(&pcpath->path);
1187
    else
1188
        dmlputs(mem, "   (path not valid)\n");
1189
    dmlprintf4(mem, "   inner_box=(%g,%g),(%g,%g)\n",
1190
               fixed2float(pcpath->inner_box.p.x),
1191
               fixed2float(pcpath->inner_box.p.y),
1192
               fixed2float(pcpath->inner_box.q.x),
1193
               fixed2float(pcpath->inner_box.q.y));
1194
    dmlprintf4(mem, "     outer_box=(%g,%g),(%g,%g)",
1195
               fixed2float(pcpath->outer_box.p.x),
1196
               fixed2float(pcpath->outer_box.p.y),
1197
               fixed2float(pcpath->outer_box.q.x),
1198
               fixed2float(pcpath->outer_box.q.y));
1199
    dmprintf2(mem, "     rule=%d list.refct=%ld\n",
1200
              pcpath->rule, pcpath->rect_list->rc.ref_count);
1201
    gx_clip_list_print(mem, gx_cpath_list(pcpath));
1202
}
1203
1204
#endif /* DEBUG */