Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gxcpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* 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
34.0M
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
4.86M
case 0:
49
4.86M
return ENUM_OBJ((cptr->rect_list == &cptr->local_list ? 0 :
50
0
             cptr->rect_list));
51
4.86M
case 1:
52
4.86M
return ENUM_OBJ(cptr->path_list);
53
4.86M
case 2:
54
4.86M
return ENUM_OBJ((cptr->cached == &cptr->rect_list->list.single ? 0 :
55
34.0M
             cptr->cached));
56
34.0M
ENUM_PTRS_END
57
static
58
4.85M
RELOC_PTRS_WITH(clip_path_reloc_ptrs, gx_clip_path *cptr)
59
4.85M
{
60
4.85M
    if (cptr->rect_list != &cptr->local_list)
61
4.85M
        RELOC_VAR(cptr->rect_list);
62
4.85M
    RELOC_VAR(cptr->path_list);
63
4.85M
    if (cptr->cached != &cptr->rect_list->list.single)
64
4.85M
        RELOC_VAR(cptr->cached);
65
4.85M
    RELOC_USING(st_path, &cptr->path, sizeof(gx_path));
66
4.85M
}
67
4.85M
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
3.16M
{
135
3.16M
    gx_clip_list_from_rectangle(&pcpath->rect_list->list, pbox);
136
3.16M
    pcpath->inner_box = *pbox;
137
3.16M
    pcpath->path_valid = false;
138
3.16M
    pcpath->path_fill_adjust.x = 0;
139
3.16M
    pcpath->path_fill_adjust.y = 0;
140
3.16M
    pcpath->path.bbox = *pbox;
141
3.16M
    gx_cpath_set_outer_box(pcpath);
142
3.16M
    pcpath->id = gs_next_ids(pcpath->path.memory, 1); /* path changed => change id */
143
3.16M
    pcpath->cached = NULL;
144
3.16M
}
145
static void
146
cpath_init_own_contents(gx_clip_path * pcpath)
147
1.43M
{    /* We could make null_rect static, but then it couldn't be const. */
148
1.43M
    gs_fixed_rect null_rect;
149
150
1.43M
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
151
1.43M
    cpath_init_rectangle(pcpath, &null_rect);
152
1.43M
    pcpath->path_list = NULL;
153
1.43M
}
154
static void
155
cpath_share_own_contents(gx_clip_path * pcpath, const gx_clip_path * shared)
156
40.3k
{
157
40.3k
    pcpath->inner_box = shared->inner_box;
158
40.3k
    pcpath->path_valid = shared->path_valid;
159
40.3k
    pcpath->path_fill_adjust = shared->path_fill_adjust;
160
40.3k
    pcpath->outer_box = shared->outer_box;
161
40.3k
    pcpath->id = shared->id;
162
40.3k
    pcpath->cached = NULL;
163
40.3k
}
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
1.28M
{
170
1.28M
    rc_alloc_struct_1(*prlist, gx_clip_rect_list, &st_clip_rect_list, mem,
171
1.28M
                      return_error(gs_error_VMerror), cname);
172
1.28M
    (*prlist)->rc.free = rc_free_cpath_list;
173
1.28M
    return 0;
174
1.28M
}
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
9.42M
{
179
9.42M
    if (shared) {
180
9.17M
        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
9.17M
        *pcpath = *shared;
188
9.17M
        pcpath->path.memory = mem;
189
9.17M
        pcpath->path.allocation = path_allocated_contained;
190
9.17M
        rc_increment(pcpath->path.segments);
191
9.17M
        rc_increment(pcpath->rect_list);
192
9.17M
        rc_increment(pcpath->path_list);
193
9.17M
    } else {
194
254k
        int code = cpath_alloc_list(&pcpath->rect_list, mem, cname);
195
196
254k
        if (code < 0)
197
0
            return code;
198
254k
        code = gx_path_alloc_contained(&pcpath->path, mem, cname);
199
254k
        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
254k
        cpath_init_own_contents(pcpath);
205
254k
    }
206
9.42M
    return 0;
207
9.42M
}
208
#define gx_cpath_alloc_contents(pcpath, shared, mem, cname)\
209
9.42M
  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
9.42M
{
216
9.42M
    gx_clip_path *pcpath =
217
9.42M
    gs_alloc_struct(mem, gx_clip_path, &st_clip_path, cname);
218
9.42M
    int code;
219
220
9.42M
    if (pcpath == 0)
221
0
        return 0;
222
9.42M
    code = gx_cpath_alloc_contents(pcpath, shared, mem, cname);
223
9.42M
    if (code < 0) {
224
0
        gs_free_object(mem, pcpath, cname);
225
0
        return 0;
226
0
    }
227
9.42M
    pcpath->path.allocation = path_allocated_on_heap;
228
9.42M
    return pcpath;
229
9.42M
}
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
1.22M
{
238
1.22M
    if (shared) {
239
40.3k
        if ((shared->path.segments == &shared->path.local_segments) &&
240
40.3k
            !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
40.3k
        pcpath->path = shared->path;
248
40.3k
        pcpath->path.allocation = path_allocated_on_stack;
249
40.3k
        rc_increment(pcpath->path.segments);
250
40.3k
        pcpath->rect_list = shared->rect_list;
251
40.3k
        rc_increment(pcpath->rect_list);
252
40.3k
        pcpath->path_list = shared->path_list;
253
40.3k
        rc_increment(pcpath->path_list);
254
40.3k
        cpath_share_own_contents(pcpath, shared);
255
40.3k
        pcpath->rule = shared->rule;
256
1.18M
    } else {
257
1.18M
        gx_path_init_local(&pcpath->path, mem);
258
1.18M
        rc_init_free(&pcpath->local_list, mem, 1, rc_free_cpath_list_local);
259
1.18M
        pcpath->rect_list = &pcpath->local_list;
260
1.18M
        cpath_init_own_contents(pcpath);
261
1.18M
    }
262
1.22M
    return 0;
263
1.22M
}
264
265
int
266
gx_cpath_init_local_shared(gx_clip_path * pcpath, const gx_clip_path * shared,
267
                           gs_memory_t * mem)
268
870k
{
269
870k
    return gx_cpath_init_local_shared_nested(pcpath, shared, mem, 0);
270
870k
}
271
272
void gx_cpath_preinit_local_rectangle(gx_clip_path *pcpath, gs_memory_t *mem)
273
6.34k
{
274
6.34k
    gx_clip_list *clp = &pcpath->local_list.list;
275
6.34k
    gx_path_preinit_local_rectangle(&pcpath->path, mem);
276
6.34k
    rc_init_free(&pcpath->local_list, mem, 1, NULL);
277
6.34k
    pcpath->rect_list = &pcpath->local_list;
278
6.34k
    gx_clip_list_init(clp);
279
6.34k
    clp->count = 1;
280
6.34k
    pcpath->path_valid = false;
281
6.34k
    pcpath->path_fill_adjust.x = 0;
282
6.34k
    pcpath->path_fill_adjust.y = 0;
283
6.34k
    pcpath->cached = NULL;
284
6.34k
    pcpath->path_list = NULL;
285
6.34k
}
286
287
void gx_cpath_init_local_rectangle(gx_clip_path *pcpath, gs_fixed_rect *r, gs_id id)
288
6.34k
{
289
6.34k
    gx_clip_list *clp = &pcpath->local_list.list;
290
6.34k
    clp->single.xmin = clp->xmin = fixed2int_var(r->p.x);
291
6.34k
    clp->single.ymin = fixed2int_var(r->p.y);
292
6.34k
    clp->single.xmax = clp->xmax = fixed2int_var_ceiling(r->q.x);
293
6.34k
    clp->single.ymax = fixed2int_var_ceiling(r->q.y);
294
6.34k
    pcpath->inner_box = *r;
295
6.34k
    pcpath->path.bbox = *r;
296
6.34k
    gx_cpath_set_outer_box(pcpath);
297
6.34k
    pcpath->id = id;
298
6.34k
}
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
18.3M
{
328
18.3M
    if (pcpath == 0L)
329
7.41M
        return;
330
331
10.8M
    rc_decrement(pcpath->rect_list, cname);
332
10.8M
    rc_decrement(pcpath->path_list, cname);
333
    /* Clean up pointers for GC. */
334
10.8M
    pcpath->rect_list = 0;
335
10.8M
    pcpath->path_list = 0;
336
10.8M
    {
337
10.8M
        gx_path_allocation_t alloc = pcpath->path.allocation;
338
339
10.8M
        if (alloc == path_allocated_on_heap) {
340
9.42M
            pcpath->path.allocation = path_allocated_contained;
341
9.42M
            gx_path_free(&pcpath->path, cname);
342
9.42M
            gs_free_object(pcpath->path.memory, pcpath, cname);
343
9.42M
        } else
344
1.46M
            gx_path_free(&pcpath->path, cname);
345
10.8M
    }
346
10.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
628k
{
352
628k
    int code = gx_path_assign_preserve(&pcpto->path, &pcpfrom->path);
353
628k
    gx_clip_rect_list *fromlist = pcpfrom->rect_list;
354
628k
    gx_clip_rect_list *tolist = pcpto->rect_list;
355
628k
    gx_path path;
356
357
628k
    if (code < 0)
358
0
        return 0;
359
628k
    if (fromlist == &pcpfrom->local_list) {
360
        /* We can't use pcpfrom's list object. */
361
625k
        if (tolist == &pcpto->local_list || tolist->rc.ref_count > 1) {
362
            /* We can't use pcpto's list either.  Allocate a new one. */
363
382k
            int code = cpath_alloc_list(&tolist, tolist->rc.memory,
364
382k
                                        "gx_cpath_assign");
365
366
382k
            if (code < 0) {
367
0
                rc_decrement(pcpto->path.segments, "gx_path_assign");
368
0
                return code;
369
0
            }
370
382k
            rc_decrement(pcpto->rect_list, "gx_cpath_assign");
371
382k
        } else {
372
            /* Use pcpto's list object. */
373
243k
            rc_free_cpath_list_local(tolist->rc.memory, tolist,
374
243k
                                     "gx_cpath_assign");
375
243k
        }
376
625k
        tolist->list = fromlist->list;
377
625k
        pcpfrom->rect_list = tolist;
378
625k
        rc_increment(tolist);
379
625k
    } else {
380
        /* We can use pcpfrom's list object. */
381
3.27k
        rc_increment(fromlist);
382
3.27k
        rc_decrement(pcpto->rect_list, "gx_cpath_assign");
383
3.27k
    }
384
628k
    rc_increment(pcpfrom->path_list);
385
628k
    rc_decrement(pcpto->path_list, "gx_cpath_assign");
386
628k
    path = pcpto->path, *pcpto = *pcpfrom, pcpto->path = path;
387
628k
    return 0;
388
628k
}
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
625k
{       /* For right now, just do assign + free. */
394
625k
    int code = gx_cpath_assign_preserve(pcpto, pcpfrom);
395
396
625k
    if (code < 0)
397
0
        return code;
398
625k
    gx_cpath_free(pcpfrom, "gx_cpath_assign_free");
399
625k
    return 0;
400
625k
}
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
2.08M
{
407
2.08M
    gx_clip_rect_list *rlist = (gx_clip_rect_list *) vrlist;
408
409
2.08M
    gx_clip_list_free(&rlist->list, mem);
410
2.08M
}
411
static void
412
rc_free_cpath_list(gs_memory_t * mem, void *vrlist, client_name_t cname)
413
1.28M
{
414
1.28M
    rc_free_cpath_list_local(mem, vrlist, cname);
415
1.28M
    gs_free_object(mem, vrlist, cname);
416
1.28M
}
417
418
static void
419
rc_free_cpath_path_list(gs_memory_t * mem, void *vplist, client_name_t cname)
420
466k
{
421
466k
    gx_cpath_path_list *plist = (gx_cpath_path_list *)vplist;
422
466k
    rc_decrement(plist->next, cname);
423
466k
    gx_path_free(&plist->path, cname);
424
466k
    gs_free_object(plist->path.memory, plist, cname);
425
466k
}
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
466k
{
434
466k
    int code;
435
466k
    client_name_t cname = "gx_cpath_path_list_new";
436
466k
    gx_cpath_path_list *pcplist = gs_alloc_struct(mem, gx_cpath_path_list,
437
466k
                                                  &st_cpath_path_list, cname);
438
439
466k
    if (pcplist == 0)
440
0
        return_error(gs_error_VMerror);
441
466k
    rc_init_free(pcplist, mem, 1, rc_free_cpath_path_list);
442
466k
    if (pcpath!=NULL && !pcpath->path_valid) {
443
309k
        code = gx_path_init_contained_shared(&pcplist->path, NULL, mem, cname);
444
309k
        if (code < 0) {
445
0
            gs_free_object(mem, pcplist, "gx_cpath_path_list_new");
446
0
            return code;
447
0
        }
448
309k
        code = gx_cpath_to_path(pcpath, &pcplist->path);
449
309k
    } else {
450
157k
        gx_path_init_local(&pcplist->path, mem);
451
157k
        code = gx_path_assign_preserve(&pcplist->path, ppfrom);
452
157k
    }
453
466k
    if (code < 0)
454
0
        return code;
455
466k
    pcplist->next = next;
456
466k
    rc_increment(next);
457
466k
    pcplist->rule = rule;
458
466k
    *pnew = pcplist;
459
466k
    return 0;
460
466k
}
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
310k
{
468
    /* Synthesize a path. */
469
310k
    gs_cpath_enum cenum;
470
310k
    gs_fixed_point pts[3];
471
310k
    int code;
472
473
310k
    gx_cpath_enum_init(&cenum, pcpath);
474
1.76M
    while ((code = gx_cpath_enum_next(&cenum, pts)) != 0) {
475
1.45M
        switch (code) {
476
311k
            case gs_pe_moveto:
477
311k
                code = gx_path_add_point(ppath, pts[0].x, pts[0].y);
478
311k
                break;
479
897k
            case gs_pe_lineto:
480
897k
                code = gx_path_add_line_notes(ppath, pts[0].x, pts[0].y,
481
897k
                                           gx_cpath_enum_notes(&cenum));
482
897k
                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
242k
            case gs_pe_closepath:
494
242k
                code = gx_path_close_subpath_notes(ppath,
495
242k
                                           gx_cpath_enum_notes(&cenum));
496
242k
                break;
497
0
            default:
498
0
                if (code >= 0)
499
0
                    code = gs_note_error(gs_error_unregistered);
500
1.45M
        }
501
1.45M
        if (code < 0)
502
0
            break;
503
1.45M
    }
504
310k
    return 0;
505
310k
}
506
507
/* Return the path of a clipping path. */
508
int
509
gx_cpath_to_path(gx_clip_path * pcpath, gx_path * ppath)
510
310k
{
511
310k
    if (!pcpath->path_valid) {
512
310k
        gx_path rpath;
513
310k
        int code;
514
515
310k
        gx_path_init_local(&rpath, pcpath->path.memory);
516
310k
        code = gx_cpath_to_path_synthesize(pcpath, &rpath);
517
310k
        if (code < 0) {
518
0
            gx_path_free(&rpath, "gx_cpath_to_path error");
519
0
            return code;
520
0
        }
521
310k
        code = gx_path_assign_free(&pcpath->path, &rpath);
522
310k
        if (code < 0)
523
0
            return code;
524
310k
        pcpath->path_valid = true;
525
310k
        pcpath->path_fill_adjust.x = 0;
526
310k
        pcpath->path_fill_adjust.y = 0;
527
310k
    }
528
310k
    return gx_path_assign_preserve(ppath, &pcpath->path);
529
310k
}
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
5.05M
{
535
5.05M
    *pbox = pcpath->inner_box;
536
5.05M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
537
5.05M
}
538
bool
539
gx_cpath_outer_box(const gx_clip_path * pcpath, gs_fixed_rect * pbox)
540
4.60M
{
541
4.60M
    *pbox = pcpath->outer_box;
542
4.60M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
543
4.60M
}
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
179k
{
551
179k
    return
552
179k
        (x0 <= x1 ?
553
179k
         (pcpath->inner_box.p.x <= x0 && x1 <= pcpath->inner_box.q.x) :
554
179k
         (pcpath->inner_box.p.x <= x1 && x0 <= pcpath->inner_box.q.x)) &&
555
179k
        (y0 <= y1 ?
556
38.9k
         (pcpath->inner_box.p.y <= y0 && y1 <= pcpath->inner_box.q.y) :
557
38.9k
         (pcpath->inner_box.p.y <= y1 && y0 <= pcpath->inner_box.q.y));
558
179k
}
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
3.79M
{
565
3.79M
    pcpath->outer_box.p.x = fixed_floor(pcpath->path.bbox.p.x);
566
3.79M
    pcpath->outer_box.p.y = fixed_floor(pcpath->path.bbox.p.y);
567
3.79M
    pcpath->outer_box.q.x = fixed_ceiling(pcpath->path.bbox.q.x);
568
3.79M
    pcpath->outer_box.q.y = fixed_ceiling(pcpath->path.bbox.q.y);
569
3.79M
}
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
11.3M
{
575
11.3M
    return &pcpath->rect_list->list;
576
11.3M
}
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
310k
{
581
310k
    return &pcpath->rect_list->list;
582
310k
}
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
1.72M
{
592
1.72M
    gx_clip_rect_list *rlist = pcpath->rect_list;
593
594
1.72M
    if (rlist->rc.ref_count <= 1)
595
1.07M
        gx_clip_list_free(&rlist->list, rlist->rc.memory);
596
648k
    else {
597
648k
        int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
598
648k
                                    "gx_cpath_from_rectangle");
599
600
648k
        if (code < 0) {
601
0
            pcpath->rect_list = rlist;
602
0
            return code;
603
0
        }
604
648k
        rc_decrement(rlist, "gx_cpath_from_rectangle");
605
648k
        rlist = pcpath->rect_list;
606
648k
    }
607
1.72M
    cpath_init_rectangle(pcpath, pbox);
608
1.72M
    return 0;
609
1.72M
}
610
int
611
gx_cpath_from_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
612
1.54M
{
613
1.54M
    int code = gx_path_new(&pcpath->path);
614
615
1.54M
    if (code < 0)
616
0
        return code;
617
1.54M
    return cpath_set_rectangle(pcpath, pbox);
618
1.54M
}
619
int
620
gx_cpath_reset(gx_clip_path * pcpath)
621
369k
{
622
369k
    gs_fixed_rect null_rect;
623
624
369k
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
625
369k
    rc_decrement(pcpath->path_list, "gx_cpath_reset");
626
369k
    return gx_cpath_from_rectangle(pcpath, &null_rect);
627
369k
}
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
0
{
634
0
    if (pcpath->path_valid) {
635
0
        return gx_path_is_rectangle((const gx_path *)&pcpath->path, rect);
636
0
    }
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
126k
{
655
126k
    return gx_cpath_intersect(pcpath, ppath_orig, rule, pgs);
656
126k
}
657
658
int
659
gx_cpath_ensure_path_list(gx_clip_path *pcpath)
660
315k
{
661
315k
    if (pcpath == NULL || pcpath->path_list)
662
3.09k
        return 0;
663
312k
    return gx_cpath_path_list_new(pcpath->path.memory, pcpath, pcpath->rule,
664
312k
                                  &pcpath->path, NULL, &pcpath->path_list);
665
315k
}
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
409k
{
671
409k
    gx_path fpath;
672
409k
    /*const*/ gx_path *ppath = ppath_orig;
673
409k
    gs_fixed_rect old_box, new_box;
674
409k
    int code;
675
409k
    int pcpath_is_rect;
676
677
409k
    pcpath->cached = NULL;
678
    /* Flatten the path if necessary. */
679
409k
    if (gx_path_has_curves_inline(ppath)) {
680
24.5k
        gx_path_init_local(&fpath, pgs->memory);
681
24.5k
        code = gx_path_add_flattened_accurate(ppath, &fpath,
682
24.5k
                                              gs_currentflat_inline(pgs),
683
24.5k
                                              pgs->accurate_curves);
684
24.5k
        if (code < 0)
685
0
            return code;
686
24.5k
        ppath = &fpath;
687
24.5k
    }
688
689
409k
    pcpath_is_rect = gx_cpath_inner_box(pcpath, &old_box);
690
409k
    if (pcpath_is_rect &&
691
409k
        ((code = gx_path_is_rectangle(ppath, &new_box)) ||
692
405k
         gx_path_is_void(ppath))
693
409k
        ) {
694
228k
        int changed = 0;
695
696
228k
        if (!code) {
697
            /* The new path is void. */
698
74.4k
            if (gx_path_current_point(ppath, &new_box.p) < 0) {
699
                /* Use the user space origin (arbitrarily). */
700
73.9k
                new_box.p.x = float2fixed(pgs->ctm.tx);
701
73.9k
                new_box.p.y = float2fixed(pgs->ctm.ty);
702
73.9k
            }
703
74.4k
            new_box.q = new_box.p;
704
74.4k
            changed = 1;
705
153k
        } else {
706
153k
            {   /* Apply same adjustment as for filling the path. */
707
153k
                gs_fixed_point adjust = params != NULL ? params->adjust : pgs->fill_adjust;
708
153k
                fixed adjust_xl, adjust_xu, adjust_yl, adjust_yu;
709
710
153k
                if (adjust.x == -1)
711
0
                    adjust_xl = adjust_xu = adjust_yl = adjust_yu = 0;
712
153k
                else {
713
153k
                    adjust_xl = (adjust.x == fixed_half ? fixed_half - fixed_epsilon : adjust.x);
714
153k
                    adjust_yl = (adjust.y == fixed_half ? fixed_half - fixed_epsilon : adjust.y);
715
153k
                    adjust_xu = adjust.x;
716
153k
                    adjust_yu = adjust.y;
717
153k
                }
718
153k
                new_box.p.x = int2fixed(fixed2int_pixround(new_box.p.x - adjust_xl));
719
153k
                new_box.p.y = int2fixed(fixed2int_pixround(new_box.p.y - adjust_yl));
720
153k
                new_box.q.x = int2fixed(fixed2int_pixround(new_box.q.x + adjust_xu));
721
153k
                new_box.q.y = int2fixed(fixed2int_pixround(new_box.q.y + adjust_yu));
722
153k
            }
723
            /* Intersect the two rectangles if necessary. */
724
153k
            if (old_box.p.x >= new_box.p.x)
725
50.0k
                new_box.p.x = old_box.p.x, ++changed;
726
153k
            if (old_box.p.y >= new_box.p.y)
727
72.3k
                new_box.p.y = old_box.p.y, ++changed;
728
153k
            if (old_box.q.x <= new_box.q.x)
729
116k
                new_box.q.x = old_box.q.x, ++changed;
730
153k
            if (old_box.q.y <= new_box.q.y)
731
89.8k
                new_box.q.y = old_box.q.y, ++changed;
732
            /* Check for a degenerate rectangle. */
733
153k
            if (new_box.q.x < new_box.p.x || new_box.q.y < new_box.p.y)
734
70.7k
                new_box.p = new_box.q, changed = 1;
735
153k
        }
736
228k
        if (changed == 4) {
737
            /* The new box/path is the same as the old. */
738
46.7k
            return 0;
739
46.7k
        }
740
        /* Release the existing path. */
741
181k
        rc_decrement(pcpath->path_list, "gx_cpath_intersect");
742
181k
        pcpath->path_list = NULL;
743
181k
        gx_path_new(&pcpath->path);
744
181k
        ppath->bbox = new_box;
745
181k
        cpath_set_rectangle(pcpath, &new_box);
746
181k
        if (changed == 0) {
747
            /* The path is valid; otherwise, defer constructing it. */
748
22.5k
            gx_path_assign_preserve(&pcpath->path, ppath);
749
22.5k
            pcpath->path_valid = true;
750
22.5k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
751
22.5k
        }
752
181k
    } else {
753
        /* New clip path is nontrivial.  Intersect the slow way. */
754
181k
        gx_cpath_path_list *next = NULL;
755
181k
        bool path_valid =
756
181k
            pcpath_is_rect &&
757
181k
            gx_path_bbox(ppath, &new_box) >= 0 &&
758
181k
            gx_cpath_includes_rectangle(pcpath,
759
177k
                                        new_box.p.x, new_box.p.y,
760
177k
                                        new_box.q.x, new_box.q.y);
761
762
181k
        if (!path_valid && next == NULL) {
763
            /* gx_cpaths should generally have a path_list set within
764
             * them. In some cases (filled images), they may not. Ensure
765
             * that they do, and remember the path_list */
766
154k
            code = gx_cpath_ensure_path_list(pcpath);
767
154k
            if (code < 0)
768
0
                goto ex;
769
            /* gx_cpath_intersect_path_slow NULLs pcpath->path_list, so
770
             * remember it here. */
771
154k
            next = pcpath->path_list;
772
154k
            rc_increment(next);
773
154k
        }
774
181k
        code = gx_cpath_intersect_path_slow(pcpath, (params != NULL ? ppath_orig : ppath),
775
181k
                            rule, pgs, params);
776
181k
        if (code < 0) {
777
0
            rc_decrement(next, "gx_cpath_clip");
778
0
            goto ex;
779
0
        }
780
181k
        if (path_valid) {
781
27.1k
            gx_path_assign_preserve(&pcpath->path, ppath_orig);
782
27.1k
            pcpath->path_valid = true;
783
27.1k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
784
27.1k
            pcpath->rule = rule;
785
154k
        } else {
786
154k
            code = gx_cpath_path_list_new(pcpath->path.memory, NULL, rule,
787
154k
                                          ppath_orig, next, &pcpath->path_list);
788
154k
        }
789
181k
        rc_decrement(next, "gx_cpath_clip");
790
181k
    }
791
363k
ex:
792
363k
    if (ppath != ppath_orig)
793
24.5k
        gx_path_free(ppath, "gx_cpath_clip");
794
363k
    return code;
795
409k
}
796
int
797
gx_cpath_intersect(gx_clip_path *pcpath, /*const*/ gx_path *ppath_orig,
798
                   int rule, gs_gstate *pgs)
799
127k
{
800
127k
    return gx_cpath_intersect_with_params(pcpath, ppath_orig,
801
127k
                   rule, pgs, NULL);
802
127k
}
803
804
/* Scale a clipping path by a power of 2. */
805
int
806
gx_cpath_scale_exp2_shared(gx_clip_path * pcpath, int log2_scale_x,
807
                           int log2_scale_y, bool list_shared,
808
                           bool segments_shared)
809
0
{
810
0
    int code =
811
0
        (pcpath->path_valid ?
812
0
         gx_path_scale_exp2_shared(&pcpath->path, log2_scale_x, log2_scale_y,
813
0
                                   segments_shared) :
814
0
         0);
815
0
    gx_clip_list *list = gx_cpath_list_private(pcpath);
816
0
    gx_clip_rect *pr;
817
818
0
    if (code < 0)
819
0
        return code;
820
    /* Scale the fixed entries. */
821
0
    gx_rect_scale_exp2(&pcpath->inner_box, log2_scale_x, log2_scale_y);
822
0
    gx_rect_scale_exp2(&pcpath->outer_box, log2_scale_x, log2_scale_y);
823
0
    if (!list_shared) {
824
        /* Scale the clipping list. */
825
0
        pr = list->head;
826
0
        if (pr == 0)
827
0
            pr = &list->single;
828
0
        for (; pr != 0; pr = pr->next)
829
0
            if (pr != list->head && pr != list->tail) {
830
831
0
#define SCALE_V(v, s)\
832
0
  if ( pr->v != min_int && pr->v != max_int )\
833
0
    pr->v = (s >= 0 ? pr->v << s : pr->v >> -s)
834
835
0
                SCALE_V(xmin, log2_scale_x);
836
0
                SCALE_V(xmax, log2_scale_x);
837
0
                SCALE_V(ymin, log2_scale_y);
838
0
                SCALE_V(ymax, log2_scale_y);
839
0
#undef SCALE_V
840
0
            }
841
0
        if (log2_scale_x > 0) {
842
0
            list->xmin <<= log2_scale_x;
843
0
            list->xmax <<= log2_scale_x;
844
0
        } else {
845
0
            list->xmin = arith_rshift(list->xmin, -log2_scale_x);
846
0
            list->xmax = arith_rshift(list->xmax, -log2_scale_x);
847
0
        }
848
0
    }
849
0
    pcpath->id = gs_next_ids(pcpath->path.memory, 1); /* path changed => change id */
850
0
    return 0;
851
0
}
852
853
/* ------ Clipping list routines ------ */
854
855
/* Initialize a clip list. */
856
void
857
gx_clip_list_init(gx_clip_list * clp)
858
6.96M
{
859
6.96M
    *clp = clip_list_empty;
860
6.96M
}
861
862
/* Initialize a clip list to a rectangle. */
863
/* The supplied rectangle may not be oriented correctly, */
864
/* but it will be oriented correctly upon return. */
865
static void
866
gx_clip_list_from_rectangle(register gx_clip_list * clp,
867
                            register gs_fixed_rect * rp)
868
3.16M
{
869
3.16M
    gx_clip_list_init(clp);
870
3.16M
    if (rp->p.x > rp->q.x) {
871
0
        fixed t = rp->p.x;
872
873
0
        rp->p.x = rp->q.x;
874
0
        rp->q.x = t;
875
0
    }
876
3.16M
    if (rp->p.y > rp->q.y) {
877
0
        fixed t = rp->p.y;
878
879
0
        rp->p.y = rp->q.y;
880
0
        rp->q.y = t;
881
0
    }
882
3.16M
    clp->single.xmin = clp->xmin = fixed2int_var(rp->p.x);
883
3.16M
    clp->single.ymin = fixed2int_var(rp->p.y);
884
    /* Handle degenerate rectangles specially. */
885
3.16M
    clp->single.xmax = clp->xmax =
886
3.16M
        (rp->q.x == rp->p.x ? clp->single.xmin :
887
3.16M
         fixed2int_var_ceiling(rp->q.x));
888
3.16M
    clp->single.ymax =
889
3.16M
        (rp->q.y == rp->p.y ? clp->single.ymin :
890
3.16M
         fixed2int_var_ceiling(rp->q.y));
891
3.16M
    clp->count = 1;
892
3.16M
}
893
894
/* Start enumerating a clipping path. */
895
int
896
gx_cpath_enum_init(gs_cpath_enum * penum, const gx_clip_path * pcpath)
897
310k
{
898
310k
    if ((penum->using_path = pcpath->path_valid)) {
899
0
        gx_path_enum_init(&penum->path_enum, &pcpath->path);
900
0
        penum->rp = penum->visit = 0;
901
0
        penum->first_visit = visit_left;
902
310k
    } else {
903
310k
        gx_path empty_path;
904
310k
        gx_clip_list *clp = gx_cpath_list_private(pcpath);
905
310k
        gx_clip_rect *head = (clp->count <= 1 ? &clp->single : clp->head);
906
310k
        gx_clip_rect *rp;
907
908
        /* Initialize the pointers in the path_enum properly. */
909
310k
        gx_path_init_local(&empty_path, pcpath->path.memory);
910
310k
        gx_path_enum_init(&penum->path_enum, &empty_path);
911
310k
        penum->first_visit = visit_left;
912
310k
        penum->visit = head;
913
701k
        for (rp = head; rp != 0; rp = rp->next)
914
390k
            rp->to_visit =
915
390k
                (rp->xmin < rp->xmax && rp->ymin < rp->ymax ?
916
315k
                 visit_left | visit_right : 0);
917
310k
        penum->rp = 0;    /* scan will initialize */
918
310k
        penum->any_rectangles = false;
919
310k
        penum->state = cpe_scan;
920
310k
        penum->have_line = false;
921
310k
    }
922
310k
    return 0;
923
310k
}
924
925
/* Enumerate the next segment of a clipping path. */
926
/* In general, this produces a path made up of zillions of tiny lines. */
927
int
928
gx_cpath_enum_next(gs_cpath_enum * penum, gs_fixed_point pts[3])
929
1.76M
{
930
1.76M
    if (penum->using_path)
931
0
        return gx_path_enum_next(&penum->path_enum, pts);
932
1.76M
#define set_pt(xi, yi)\
933
1.76M
  (pts[0].x = int2fixed(xi), pts[0].y = int2fixed(yi))
934
1.76M
#define set_line(xi, yi)\
935
1.76M
  (penum->line_end.x = (xi), penum->line_end.y = (yi), penum->have_line = true)
936
1.76M
    if (penum->have_line) {
937
327k
        set_pt(penum->line_end.x, penum->line_end.y);
938
327k
        penum->have_line = false;
939
327k
        return gs_pe_lineto;
940
1.43M
    } {
941
1.43M
        gx_clip_rect *visit = penum->visit;
942
1.43M
        gx_clip_rect *rp = penum->rp;
943
1.43M
        cpe_visit_t first_visit = penum->first_visit;
944
1.43M
        cpe_state_t state = penum->state;
945
1.43M
        gx_clip_rect *look;
946
1.43M
        int code;
947
948
1.43M
        switch (state) {
949
950
553k
            case cpe_scan:
951
                /* Look for the start of an edge to trace. */
952
944k
                for (; visit != 0; visit = visit->next) {
953
633k
                    if (visit->to_visit & visit_left) {
954
242k
                        set_pt(visit->xmin, visit->ymin);
955
242k
                        first_visit = visit_left;
956
242k
                        state = cpe_left;
957
390k
                    } else if (visit->to_visit & visit_right) {
958
60
                        set_pt(visit->xmax, visit->ymax);
959
60
                        first_visit = visit_right;
960
60
                        state = cpe_right;
961
60
                    } else
962
390k
                        continue;
963
242k
                    rp = visit;
964
242k
                    code = gs_pe_moveto;
965
242k
                    penum->any_rectangles = true;
966
242k
                    goto out;
967
633k
                }
968
                /* We've enumerated all the edges. */
969
310k
                state = cpe_done;
970
310k
                if (!penum->any_rectangles) {
971
                    /* We didn't have any rectangles. */
972
68.4k
                    set_pt(fixed_0, fixed_0);
973
68.4k
                    code = gs_pe_moveto;
974
68.4k
                    break;
975
68.4k
                }
976
                /* falls through */
977
978
310k
            case cpe_done:
979
                /* All done. */
980
310k
                code = 0;
981
310k
                break;
982
983
/* We can't use the BEGIN ... END hack here: we need to be able to break. */
984
0
#define return_line(px, py)\
985
570k
  set_pt(px, py); code = gs_pe_lineto; break
986
987
280k
            case cpe_left:
988
989
315k
              left:   /* Trace upward along a left edge. */
990
                /* We're at the lower left corner of rp. */
991
315k
                rp->to_visit &= ~visit_left;
992
                /* Look for an adjacent rectangle above rp. */
993
315k
                for (look = rp;
994
360k
                     (look = look->next) != 0 &&
995
360k
                     (look->ymin == rp->ymin ||
996
122k
                      (look->ymin == rp->ymax && look->xmax <= rp->xmin));
997
315k
                    );
998
                /* Now we know look->ymin >= rp->ymax. */
999
315k
                if (look == 0 || look->ymin > rp->ymax ||
1000
315k
                    look->xmin >= rp->xmax
1001
315k
                    ) {   /* No adjacent rectangle, switch directions. */
1002
242k
                    state =
1003
242k
                        (rp == visit && first_visit == visit_right ? cpe_close :
1004
242k
                         (set_line(rp->xmax, rp->ymax), cpe_right));
1005
242k
                    return_line(rp->xmin, rp->ymax);
1006
242k
                }
1007
                /* We found an adjacent rectangle. */
1008
                /* See if it also adjoins a rectangle to the left of rp. */
1009
72.3k
                {
1010
72.3k
                    gx_clip_rect *prev = rp->prev;
1011
72.3k
                    gx_clip_rect *cur = rp;
1012
1013
72.3k
                    if (prev != 0 && prev->ymax == rp->ymax &&
1014
72.3k
                        look->xmin < prev->xmax
1015
72.3k
                        ) { /* There's an adjoining rectangle as well. */
1016
                        /* Switch directions. */
1017
132
                        rp = prev;
1018
132
                        state =
1019
132
                            (rp == visit && first_visit == visit_right ? cpe_close :
1020
132
                             (set_line(prev->xmax, prev->ymax), cpe_right));
1021
132
                        return_line(cur->xmin, cur->ymax);
1022
132
                    }
1023
72.1k
                    rp = look;
1024
72.1k
                    if (rp == visit && first_visit == visit_left)
1025
0
                        state = cpe_close;
1026
72.1k
                    else if (rp->xmin == cur->xmin)
1027
34.5k
                        goto left;
1028
37.5k
                    else
1029
37.5k
                        set_line(rp->xmin, rp->ymin);
1030
72.1k
                    return_line(cur->xmin, cur->ymax);
1031
72.1k
                }
1032
1033
289k
            case cpe_right:
1034
1035
315k
              right:    /* Trace downward along a right edge. */
1036
                /* We're at the upper right corner of rp. */
1037
315k
                rp->to_visit &= ~visit_right;
1038
                /* Look for an adjacent rectangle below rp. */
1039
315k
                for (look = rp;
1040
360k
                     (look = look->prev) != 0 &&
1041
360k
                     (look->ymax == rp->ymax ||
1042
122k
                      (look->ymax == rp->ymin && look->xmin >= rp->xmax));
1043
315k
                    );
1044
                /* Now we know look->ymax <= rp->ymin. */
1045
315k
                if (look == 0 || look->ymax < rp->ymin ||
1046
315k
                    look->xmax <= rp->xmin
1047
315k
                    ) {   /* No adjacent rectangle, switch directions. */
1048
242k
                    state =
1049
242k
                        (rp == visit && first_visit == visit_left ? cpe_close :
1050
242k
                         (set_line(rp->xmin, rp->ymin), cpe_left));
1051
242k
                    return_line(rp->xmax, rp->ymin);
1052
242k
                }
1053
                /* We found an adjacent rectangle. */
1054
                /* See if it also adjoins a rectangle to the right of rp. */
1055
72.3k
                {
1056
72.3k
                    gx_clip_rect *next = rp->next;
1057
72.3k
                    gx_clip_rect *cur = rp;
1058
1059
72.3k
                    if (next != 0 && next->ymin == rp->ymin &&
1060
72.3k
                        look->xmax > next->xmin
1061
72.3k
                        ) { /* There's an adjoining rectangle as well. */
1062
                        /* Switch directions. */
1063
124
                        rp = next;
1064
124
                        state =
1065
124
                            (rp == visit && first_visit == visit_left ? cpe_close :
1066
124
                             (set_line(next->xmin, next->ymin), cpe_left));
1067
124
                        return_line(cur->xmax, cur->ymin);
1068
124
                    }
1069
72.1k
                    rp = look;
1070
72.1k
                    if (rp == visit && first_visit == visit_right)
1071
56
                        state = cpe_close;
1072
72.1k
                    else if (rp->xmax == cur->xmax)
1073
25.7k
                        goto right;
1074
46.3k
                    else
1075
46.3k
                        set_line(rp->xmax, rp->ymax);
1076
72.1k
                    return_line(cur->xmax, cur->ymin);
1077
72.1k
                }
1078
1079
0
#undef return_line
1080
1081
242k
            case cpe_close:
1082
                /* We've gone all the way around an edge. */
1083
242k
                code = gs_pe_closepath;
1084
242k
                state = cpe_scan;
1085
242k
                break;
1086
1087
0
            default:
1088
0
                return_error(gs_error_unknownerror);
1089
1.43M
        }
1090
1091
1.43M
      out:      /* Store the state before exiting. */
1092
1.43M
        penum->visit = visit;
1093
1.43M
        penum->rp = rp;
1094
1.43M
        penum->first_visit = first_visit;
1095
1.43M
        penum->state = state;
1096
1.43M
        return code;
1097
1.43M
    }
1098
1.43M
#undef set_pt
1099
1.43M
#undef set_line
1100
1.43M
}
1101
segment_notes
1102
gx_cpath_enum_notes(const gs_cpath_enum * penum)
1103
1.14M
{
1104
1.14M
    return sn_none;
1105
1.14M
}
1106
1107
/* Free a clip list. */
1108
void
1109
gx_clip_list_free(gx_clip_list * clp, gs_memory_t * mem)
1110
3.16M
{
1111
3.16M
    gx_clip_rect *rp = clp->tail;
1112
1113
12.7M
    while (rp != 0) {
1114
9.63M
        gx_clip_rect *prev = rp->prev;
1115
1116
9.63M
        gs_free_object(mem, rp, "gx_clip_list_free");
1117
9.63M
        rp = prev;
1118
9.63M
    }
1119
3.16M
    gx_clip_list_init(clp);
1120
3.16M
}
1121
1122
/* Check whether a rectangle has a non-empty intersection with a clipping patch. */
1123
bool
1124
gx_cpath_rect_visible(gx_clip_path * pcpath, gs_int_rect *prect)
1125
240k
{
1126
240k
    const gx_clip_rect *pr;
1127
240k
    const gx_clip_list *list = &pcpath->rect_list->list;
1128
1129
240k
    switch (list->count) {
1130
11
        case 0:
1131
11
            return false;
1132
240k
        case 1:
1133
240k
            pr = &list->single;
1134
240k
            break;
1135
28
        default:
1136
28
            pr = list->head;
1137
240k
    }
1138
452k
    for (; pr != 0; pr = pr->next) {
1139
240k
        if (pr->xmin > prect->q.x)
1140
130k
            continue;
1141
109k
        if (pr->xmax < prect->p.x)
1142
56.7k
            continue;
1143
52.7k
        if (pr->ymin > prect->q.y)
1144
25.1k
            continue;
1145
27.5k
        if (pr->ymax < prect->p.y)
1146
154
            continue;
1147
27.4k
        return true;
1148
27.5k
    }
1149
212k
    return false;
1150
240k
}
1151
1152
int
1153
gx_cpath_copy(const gx_clip_path * from, gx_clip_path * pcpath)
1154
195k
{   /* *pcpath must be initialized. */
1155
195k
    gx_clip_rect *r, *s;
1156
195k
    gx_clip_list *l = &pcpath->rect_list->list;
1157
1158
195k
    pcpath->path_valid = false;
1159
    /* NOTE: pcpath->path still contains the old path. */
1160
195k
    if (pcpath->path_list)
1161
195k
        rc_decrement(pcpath->path_list, "gx_cpath_copy");
1162
195k
    pcpath->path_list = NULL;
1163
195k
    pcpath->rule = from->rule;
1164
195k
    pcpath->outer_box = from->outer_box;
1165
195k
    pcpath->inner_box = from->inner_box;
1166
195k
    pcpath->cached = NULL;
1167
195k
    l->single = from->rect_list->list.single;
1168
198k
    for (r = from->rect_list->list.head; r != NULL; r = r->next) {
1169
2.60k
        if (pcpath->rect_list->rc.memory == NULL)
1170
0
            s = gs_alloc_struct(from->rect_list->rc.memory, gx_clip_rect, &st_clip_rect, "gx_cpath_copy");
1171
2.60k
        else
1172
2.60k
            s = gs_alloc_struct(pcpath->rect_list->rc.memory, gx_clip_rect, &st_clip_rect, "gx_cpath_copy");
1173
1174
2.60k
        if (s == NULL)
1175
0
            return_error(gs_error_VMerror);
1176
2.60k
        *s = *r;
1177
2.60k
        s->next = NULL;
1178
2.60k
        if (l->tail) {
1179
2.58k
            s->prev = l->tail;
1180
2.58k
            l->tail->next = s;
1181
2.58k
        } else {
1182
20
            l->head = s;
1183
20
            s->prev = NULL;
1184
20
        }
1185
2.60k
        l->tail = s;
1186
2.60k
    }
1187
195k
    l->count = from->rect_list->list.count;
1188
195k
    return 0;
1189
195k
}
1190
1191
/* ------ Debugging printout ------ */
1192
1193
#ifdef DEBUG
1194
1195
/* Print a clipping list. */
1196
static void
1197
gx_clip_list_print(const gs_memory_t *mem, const gx_clip_list *list)
1198
{
1199
    const gx_clip_rect *pr;
1200
1201
    dmlprintf3(mem, "   list count=%d xmin=%d xmax=%d\n",
1202
               list->count, list->xmin, list->xmax);
1203
    switch (list->count) {
1204
        case 0:
1205
            pr = 0;
1206
            break;
1207
        case 1:
1208
            pr = &list->single;
1209
            break;
1210
        default:
1211
            pr = list->head;
1212
    }
1213
    for (; pr != 0; pr = pr->next)
1214
        dmlprintf4(mem, "   rect: (%d,%d),(%d,%d)\n",
1215
                   pr->xmin, pr->ymin, pr->xmax, pr->ymax);
1216
}
1217
1218
/* Print a clipping path */
1219
void
1220
gx_cpath_print(const gs_memory_t *mem, const gx_clip_path * pcpath)
1221
{
1222
    if (pcpath->path_valid)
1223
        gx_path_print(&pcpath->path);
1224
    else
1225
        dmlputs(mem, "   (path not valid)\n");
1226
    dmlprintf4(mem, "   inner_box=(%g,%g),(%g,%g)\n",
1227
               fixed2float(pcpath->inner_box.p.x),
1228
               fixed2float(pcpath->inner_box.p.y),
1229
               fixed2float(pcpath->inner_box.q.x),
1230
               fixed2float(pcpath->inner_box.q.y));
1231
    dmlprintf4(mem, "     outer_box=(%g,%g),(%g,%g)",
1232
               fixed2float(pcpath->outer_box.p.x),
1233
               fixed2float(pcpath->outer_box.p.y),
1234
               fixed2float(pcpath->outer_box.q.x),
1235
               fixed2float(pcpath->outer_box.q.y));
1236
    dmprintf2(mem, "     rule=%d list.refct=%ld\n",
1237
              pcpath->rule, pcpath->rect_list->rc.ref_count);
1238
    gx_clip_list_print(mem, gx_cpath_list(pcpath));
1239
}
1240
1241
#endif /* DEBUG */