Coverage Report

Created: 2026-02-14 07:09

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