Coverage Report

Created: 2025-06-10 07:15

/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
9.20M
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
1.31M
case 0:
49
1.31M
return ENUM_OBJ((cptr->rect_list == &cptr->local_list ? 0 :
50
0
             cptr->rect_list));
51
1.31M
case 1:
52
1.31M
return ENUM_OBJ(cptr->path_list);
53
1.31M
case 2:
54
1.31M
return ENUM_OBJ((cptr->cached == &cptr->rect_list->list.single ? 0 :
55
9.20M
             cptr->cached));
56
9.20M
ENUM_PTRS_END
57
static
58
1.31M
RELOC_PTRS_WITH(clip_path_reloc_ptrs, gx_clip_path *cptr)
59
1.31M
{
60
1.31M
    if (cptr->rect_list != &cptr->local_list)
61
1.31M
        RELOC_VAR(cptr->rect_list);
62
1.31M
    RELOC_VAR(cptr->path_list);
63
1.31M
    if (cptr->cached != &cptr->rect_list->list.single)
64
1.31M
        RELOC_VAR(cptr->cached);
65
1.31M
    RELOC_USING(st_path, &cptr->path, sizeof(gx_path));
66
1.31M
}
67
1.31M
RELOC_PTRS_END
68
69
/* GC procedures for gx_device_clip */
70
static
71
0
ENUM_PTRS_WITH(device_clip_enum_ptrs, gx_device_clip *cptr)
72
0
{
73
0
    if (index < st_clip_list_max_ptrs + 3)
74
0
        return ENUM_USING(st_clip_list, &cptr->list,
75
0
                          sizeof(gx_clip_list), index - 3);
76
0
    return ENUM_USING(st_device_forward, vptr,
77
0
                      sizeof(gx_device_forward),
78
0
                      index - (st_clip_list_max_ptrs + 3));
79
0
}
80
0
case 0:
81
0
ENUM_RETURN((cptr->current == &cptr->list.single ? NULL :
82
0
             (void *)cptr->current));
83
0
case 1:
84
0
ENUM_RETURN((cptr->cpath));
85
0
case 2:
86
0
ENUM_RETURN((cptr->rect_list));
87
0
ENUM_PTRS_END
88
static
89
0
RELOC_PTRS_WITH(device_clip_reloc_ptrs, gx_device_clip *cptr)
90
0
{
91
0
    if (cptr->current == &cptr->list.single)
92
0
        cptr->current = &((gx_device_clip *)RELOC_OBJ(vptr))->list.single;
93
0
    else
94
0
        RELOC_PTR(gx_device_clip, current);
95
0
    RELOC_PTR(gx_device_clip, cpath);
96
0
    RELOC_PTR(gx_device_clip, rect_list);
97
0
    RELOC_USING(st_clip_list, &cptr->list, sizeof(gx_clip_list));
98
0
    RELOC_USING(st_device_forward, vptr, sizeof(gx_device_forward));
99
0
}
100
0
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
2.79M
{
135
2.79M
    gx_clip_list_from_rectangle(&pcpath->rect_list->list, pbox);
136
2.79M
    pcpath->inner_box = *pbox;
137
2.79M
    pcpath->path_valid = false;
138
2.79M
    pcpath->path_fill_adjust.x = 0;
139
2.79M
    pcpath->path_fill_adjust.y = 0;
140
2.79M
    pcpath->path.bbox = *pbox;
141
2.79M
    gx_cpath_set_outer_box(pcpath);
142
2.79M
    pcpath->id = gs_next_ids(pcpath->path.memory, 1); /* path changed => change id */
143
2.79M
    pcpath->cached = NULL;
144
2.79M
}
145
static void
146
cpath_init_own_contents(gx_clip_path * pcpath)
147
1.07M
{    /* We could make null_rect static, but then it couldn't be const. */
148
1.07M
    gs_fixed_rect null_rect;
149
150
1.07M
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
151
1.07M
    cpath_init_rectangle(pcpath, &null_rect);
152
1.07M
    pcpath->path_list = NULL;
153
1.07M
}
154
static void
155
cpath_share_own_contents(gx_clip_path * pcpath, const gx_clip_path * shared)
156
30.6k
{
157
30.6k
    pcpath->inner_box = shared->inner_box;
158
30.6k
    pcpath->path_valid = shared->path_valid;
159
30.6k
    pcpath->path_fill_adjust = shared->path_fill_adjust;
160
30.6k
    pcpath->outer_box = shared->outer_box;
161
30.6k
    pcpath->id = shared->id;
162
30.6k
    pcpath->cached = NULL;
163
30.6k
}
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
882k
{
170
882k
    rc_alloc_struct_1(*prlist, gx_clip_rect_list, &st_clip_rect_list, mem,
171
882k
                      return_error(gs_error_VMerror), cname);
172
882k
    (*prlist)->rc.free = rc_free_cpath_list;
173
882k
    return 0;
174
882k
}
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
8.19M
{
179
8.19M
    if (shared) {
180
8.12M
        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
8.12M
        *pcpath = *shared;
188
8.12M
        pcpath->path.memory = mem;
189
8.12M
        pcpath->path.allocation = path_allocated_contained;
190
8.12M
        rc_increment(pcpath->path.segments);
191
8.12M
        rc_increment(pcpath->rect_list);
192
8.12M
        rc_increment(pcpath->path_list);
193
8.12M
    } else {
194
76.2k
        int code = cpath_alloc_list(&pcpath->rect_list, mem, cname);
195
196
76.2k
        if (code < 0)
197
0
            return code;
198
76.2k
        code = gx_path_alloc_contained(&pcpath->path, mem, cname);
199
76.2k
        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
76.2k
        cpath_init_own_contents(pcpath);
205
76.2k
    }
206
8.19M
    return 0;
207
8.19M
}
208
#define gx_cpath_alloc_contents(pcpath, shared, mem, cname)\
209
8.19M
  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
8.19M
{
216
8.19M
    gx_clip_path *pcpath =
217
8.19M
    gs_alloc_struct(mem, gx_clip_path, &st_clip_path, cname);
218
8.19M
    int code;
219
220
8.19M
    if (pcpath == 0)
221
0
        return 0;
222
8.19M
    code = gx_cpath_alloc_contents(pcpath, shared, mem, cname);
223
8.19M
    if (code < 0) {
224
0
        gs_free_object(mem, pcpath, cname);
225
0
        return 0;
226
0
    }
227
8.19M
    pcpath->path.allocation = path_allocated_on_heap;
228
8.19M
    return pcpath;
229
8.19M
}
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.03M
{
238
1.03M
    if (shared) {
239
30.6k
        if ((shared->path.segments == &shared->path.local_segments) &&
240
30.6k
            !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
30.6k
        pcpath->path = shared->path;
248
30.6k
        pcpath->path.allocation = path_allocated_on_stack;
249
30.6k
        rc_increment(pcpath->path.segments);
250
30.6k
        pcpath->rect_list = shared->rect_list;
251
30.6k
        rc_increment(pcpath->rect_list);
252
30.6k
        pcpath->path_list = shared->path_list;
253
30.6k
        rc_increment(pcpath->path_list);
254
30.6k
        cpath_share_own_contents(pcpath, shared);
255
30.6k
        pcpath->rule = shared->rule;
256
1.00M
    } else {
257
1.00M
        gx_path_init_local(&pcpath->path, mem);
258
1.00M
        rc_init_free(&pcpath->local_list, mem, 1, rc_free_cpath_list_local);
259
1.00M
        pcpath->rect_list = &pcpath->local_list;
260
1.00M
        cpath_init_own_contents(pcpath);
261
1.00M
    }
262
1.03M
    return 0;
263
1.03M
}
264
265
int
266
gx_cpath_init_local_shared(gx_clip_path * pcpath, const gx_clip_path * shared,
267
                           gs_memory_t * mem)
268
1.00M
{
269
1.00M
    return gx_cpath_init_local_shared_nested(pcpath, shared, mem, 0);
270
1.00M
}
271
272
void gx_cpath_preinit_local_rectangle(gx_clip_path *pcpath, gs_memory_t *mem)
273
145k
{
274
145k
    gx_clip_list *clp = &pcpath->local_list.list;
275
145k
    gx_path_preinit_local_rectangle(&pcpath->path, mem);
276
145k
    rc_init_free(&pcpath->local_list, mem, 1, NULL);
277
145k
    pcpath->rect_list = &pcpath->local_list;
278
145k
    gx_clip_list_init(clp);
279
145k
    clp->count = 1;
280
145k
    pcpath->path_valid = false;
281
145k
    pcpath->path_fill_adjust.x = 0;
282
145k
    pcpath->path_fill_adjust.y = 0;
283
145k
    pcpath->cached = NULL;
284
145k
    pcpath->path_list = NULL;
285
145k
}
286
287
void gx_cpath_init_local_rectangle(gx_clip_path *pcpath, gs_fixed_rect *r, gs_id id)
288
165k
{
289
165k
    gx_clip_list *clp = &pcpath->local_list.list;
290
165k
    clp->single.xmin = clp->xmin = fixed2int_var(r->p.x);
291
165k
    clp->single.ymin = fixed2int_var(r->p.y);
292
165k
    clp->single.xmax = clp->xmax = fixed2int_var_ceiling(r->q.x);
293
165k
    clp->single.ymax = fixed2int_var_ceiling(r->q.y);
294
165k
    pcpath->inner_box = *r;
295
165k
    pcpath->path.bbox = *r;
296
165k
    gx_cpath_set_outer_box(pcpath);
297
165k
    pcpath->id = id;
298
165k
}
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
14.6M
{
328
14.6M
    if (pcpath == 0L)
329
5.05M
        return;
330
331
9.54M
    rc_decrement(pcpath->rect_list, cname);
332
9.54M
    rc_decrement(pcpath->path_list, cname);
333
    /* Clean up pointers for GC. */
334
9.54M
    pcpath->rect_list = 0;
335
9.54M
    pcpath->path_list = 0;
336
9.54M
    {
337
9.54M
        gx_path_allocation_t alloc = pcpath->path.allocation;
338
339
9.54M
        if (alloc == path_allocated_on_heap) {
340
8.19M
            pcpath->path.allocation = path_allocated_contained;
341
8.19M
            gx_path_free(&pcpath->path, cname);
342
8.19M
            gs_free_object(pcpath->path.memory, pcpath, cname);
343
8.19M
        } else
344
1.35M
            gx_path_free(&pcpath->path, cname);
345
9.54M
    }
346
9.54M
}
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
690k
{
352
690k
    int code = gx_path_assign_preserve(&pcpto->path, &pcpfrom->path);
353
690k
    gx_clip_rect_list *fromlist = pcpfrom->rect_list;
354
690k
    gx_clip_rect_list *tolist = pcpto->rect_list;
355
690k
    gx_path path;
356
357
690k
    if (code < 0)
358
0
        return 0;
359
690k
    if (fromlist == &pcpfrom->local_list) {
360
        /* We can't use pcpfrom's list object. */
361
681k
        if (tolist == &pcpto->local_list || tolist->rc.ref_count > 1) {
362
            /* We can't use pcpto's list either.  Allocate a new one. */
363
229k
            int code = cpath_alloc_list(&tolist, tolist->rc.memory,
364
229k
                                        "gx_cpath_assign");
365
366
229k
            if (code < 0) {
367
0
                rc_decrement(pcpto->path.segments, "gx_path_assign");
368
0
                return code;
369
0
            }
370
229k
            rc_decrement(pcpto->rect_list, "gx_cpath_assign");
371
451k
        } else {
372
            /* Use pcpto's list object. */
373
451k
            rc_free_cpath_list_local(tolist->rc.memory, tolist,
374
451k
                                     "gx_cpath_assign");
375
451k
        }
376
681k
        tolist->list = fromlist->list;
377
681k
        pcpfrom->rect_list = tolist;
378
681k
        rc_increment(tolist);
379
681k
    } else {
380
        /* We can use pcpfrom's list object. */
381
9.39k
        rc_increment(fromlist);
382
9.39k
        rc_decrement(pcpto->rect_list, "gx_cpath_assign");
383
9.39k
    }
384
690k
    rc_increment(pcpfrom->path_list);
385
690k
    rc_decrement(pcpto->path_list, "gx_cpath_assign");
386
690k
    path = pcpto->path, *pcpto = *pcpfrom, pcpto->path = path;
387
690k
    return 0;
388
690k
}
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
681k
{       /* For right now, just do assign + free. */
394
681k
    int code = gx_cpath_assign_preserve(pcpto, pcpfrom);
395
396
681k
    if (code < 0)
397
0
        return code;
398
681k
    gx_cpath_free(pcpfrom, "gx_cpath_assign_free");
399
681k
    return 0;
400
681k
}
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
1.65M
{
407
1.65M
    gx_clip_rect_list *rlist = (gx_clip_rect_list *) vrlist;
408
409
1.65M
    gx_clip_list_free(&rlist->list, mem);
410
1.65M
}
411
static void
412
rc_free_cpath_list(gs_memory_t * mem, void *vrlist, client_name_t cname)
413
882k
{
414
882k
    rc_free_cpath_list_local(mem, vrlist, cname);
415
882k
    gs_free_object(mem, vrlist, cname);
416
882k
}
417
418
static void
419
rc_free_cpath_path_list(gs_memory_t * mem, void *vplist, client_name_t cname)
420
370k
{
421
370k
    gx_cpath_path_list *plist = (gx_cpath_path_list *)vplist;
422
370k
    rc_decrement(plist->next, cname);
423
370k
    gx_path_free(&plist->path, cname);
424
370k
    gs_free_object(plist->path.memory, plist, cname);
425
370k
}
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
370k
{
434
370k
    int code;
435
370k
    client_name_t cname = "gx_cpath_path_list_new";
436
370k
    gx_cpath_path_list *pcplist = gs_alloc_struct(mem, gx_cpath_path_list,
437
370k
                                                  &st_cpath_path_list, cname);
438
439
370k
    if (pcplist == 0)
440
0
        return_error(gs_error_VMerror);
441
370k
    rc_init_free(pcplist, mem, 1, rc_free_cpath_path_list);
442
370k
    if (pcpath!=NULL && !pcpath->path_valid) {
443
346k
        code = gx_path_init_contained_shared(&pcplist->path, NULL, mem, cname);
444
346k
        if (code < 0) {
445
0
            gs_free_object(mem, pcplist, "gx_cpath_path_list_new");
446
0
            return code;
447
0
        }
448
346k
        code = gx_cpath_to_path(pcpath, &pcplist->path);
449
346k
    } else {
450
24.2k
        gx_path_init_local(&pcplist->path, mem);
451
24.2k
        code = gx_path_assign_preserve(&pcplist->path, ppfrom);
452
24.2k
    }
453
370k
    if (code < 0)
454
0
        return code;
455
370k
    pcplist->next = next;
456
370k
    rc_increment(next);
457
370k
    pcplist->rule = rule;
458
370k
    *pnew = pcplist;
459
370k
    return 0;
460
370k
}
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
349k
{
468
    /* Synthesize a path. */
469
349k
    gs_cpath_enum cenum;
470
349k
    gs_fixed_point pts[3];
471
349k
    int code;
472
473
349k
    gx_cpath_enum_init(&cenum, pcpath);
474
1.76M
    while ((code = gx_cpath_enum_next(&cenum, pts)) != 0) {
475
1.41M
        switch (code) {
476
351k
            case gs_pe_moveto:
477
351k
                code = gx_path_add_point(ppath, pts[0].x, pts[0].y);
478
351k
                break;
479
870k
            case gs_pe_lineto:
480
870k
                code = gx_path_add_line_notes(ppath, pts[0].x, pts[0].y,
481
870k
                                           gx_cpath_enum_notes(&cenum));
482
870k
                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
188k
            case gs_pe_closepath:
494
188k
                code = gx_path_close_subpath_notes(ppath,
495
188k
                                           gx_cpath_enum_notes(&cenum));
496
188k
                break;
497
0
            default:
498
0
                if (code >= 0)
499
0
                    code = gs_note_error(gs_error_unregistered);
500
1.41M
        }
501
1.41M
        if (code < 0)
502
0
            break;
503
1.41M
    }
504
349k
    return 0;
505
349k
}
506
507
/* Return the path of a clipping path. */
508
int
509
gx_cpath_to_path(gx_clip_path * pcpath, gx_path * ppath)
510
365k
{
511
365k
    if (!pcpath->path_valid) {
512
349k
        gx_path rpath;
513
349k
        int code;
514
515
349k
        gx_path_init_local(&rpath, pcpath->path.memory);
516
349k
        code = gx_cpath_to_path_synthesize(pcpath, &rpath);
517
349k
        if (code < 0) {
518
0
            gx_path_free(&rpath, "gx_cpath_to_path error");
519
0
            return code;
520
0
        }
521
349k
        code = gx_path_assign_free(&pcpath->path, &rpath);
522
349k
        if (code < 0)
523
0
            return code;
524
349k
        pcpath->path_valid = true;
525
349k
        pcpath->path_fill_adjust.x = 0;
526
349k
        pcpath->path_fill_adjust.y = 0;
527
349k
    }
528
365k
    return gx_path_assign_preserve(ppath, &pcpath->path);
529
365k
}
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.00M
{
535
5.00M
    *pbox = pcpath->inner_box;
536
5.00M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
537
5.00M
}
538
bool
539
gx_cpath_outer_box(const gx_clip_path * pcpath, gs_fixed_rect * pbox)
540
6.29M
{
541
6.29M
    *pbox = pcpath->outer_box;
542
6.29M
    return clip_list_is_rectangle(gx_cpath_list(pcpath));
543
6.29M
}
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
4.27M
{
551
4.27M
    return
552
4.27M
        (x0 <= x1 ?
553
4.27M
         (pcpath->inner_box.p.x <= x0 && x1 <= pcpath->inner_box.q.x) :
554
4.27M
         (pcpath->inner_box.p.x <= x1 && x0 <= pcpath->inner_box.q.x)) &&
555
4.27M
        (y0 <= y1 ?
556
4.21M
         (pcpath->inner_box.p.y <= y0 && y1 <= pcpath->inner_box.q.y) :
557
4.21M
         (pcpath->inner_box.p.y <= y1 && y0 <= pcpath->inner_box.q.y));
558
4.27M
}
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.64M
{
565
3.64M
    pcpath->outer_box.p.x = fixed_floor(pcpath->path.bbox.p.x);
566
3.64M
    pcpath->outer_box.p.y = fixed_floor(pcpath->path.bbox.p.y);
567
3.64M
    pcpath->outer_box.q.x = fixed_ceiling(pcpath->path.bbox.q.x);
568
3.64M
    pcpath->outer_box.q.y = fixed_ceiling(pcpath->path.bbox.q.y);
569
3.64M
}
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
13.2M
{
575
13.2M
    return &pcpath->rect_list->list;
576
13.2M
}
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
355k
{
581
355k
    return &pcpath->rect_list->list;
582
355k
}
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.71M
{
592
1.71M
    gx_clip_rect_list *rlist = pcpath->rect_list;
593
594
1.71M
    if (rlist->rc.ref_count <= 1)
595
1.14M
        gx_clip_list_free(&rlist->list, rlist->rc.memory);
596
576k
    else {
597
576k
        int code = cpath_alloc_list(&pcpath->rect_list, pcpath->path.memory,
598
576k
                                    "gx_cpath_from_rectangle");
599
600
576k
        if (code < 0) {
601
0
            pcpath->rect_list = rlist;
602
0
            return code;
603
0
        }
604
576k
        rc_decrement(rlist, "gx_cpath_from_rectangle");
605
576k
        rlist = pcpath->rect_list;
606
576k
    }
607
1.71M
    cpath_init_rectangle(pcpath, pbox);
608
1.71M
    return 0;
609
1.71M
}
610
int
611
gx_cpath_from_rectangle(gx_clip_path * pcpath, gs_fixed_rect * pbox)
612
1.66M
{
613
1.66M
    int code = gx_path_new(&pcpath->path);
614
615
1.66M
    if (code < 0)
616
0
        return code;
617
1.66M
    return cpath_set_rectangle(pcpath, pbox);
618
1.66M
}
619
int
620
gx_cpath_reset(gx_clip_path * pcpath)
621
630k
{
622
630k
    gs_fixed_rect null_rect;
623
624
630k
    null_rect.p.x = null_rect.p.y = null_rect.q.x = null_rect.q.y = 0;
625
630k
    rc_decrement(pcpath->path_list, "gx_cpath_reset");
626
630k
    return gx_cpath_from_rectangle(pcpath, &null_rect);
627
630k
}
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
14.1k
{
634
14.1k
    if (pcpath->path_valid) {
635
14.1k
        return gx_path_is_rectangle((const gx_path *)&pcpath->path, rect);
636
14.1k
    }
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
135k
{
655
135k
    return gx_cpath_intersect(pcpath, ppath_orig, rule, pgs);
656
135k
}
657
658
int
659
gx_cpath_ensure_path_list(gx_clip_path *pcpath)
660
360k
{
661
360k
    if (pcpath == NULL || pcpath->path_list)
662
5.25k
        return 0;
663
354k
    return gx_cpath_path_list_new(pcpath->path.memory, pcpath, pcpath->rule,
664
354k
                                  &pcpath->path, NULL, &pcpath->path_list);
665
360k
}
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
167k
{
671
167k
    gx_path fpath;
672
167k
    /*const*/ gx_path *ppath = ppath_orig;
673
167k
    gs_fixed_rect old_box, new_box;
674
167k
    int code;
675
167k
    int pcpath_is_rect;
676
677
167k
    pcpath->cached = NULL;
678
    /* Flatten the path if necessary. */
679
167k
    if (gx_path_has_curves_inline(ppath)) {
680
12.6k
        gx_path_init_local(&fpath, pgs->memory);
681
12.6k
        code = gx_path_add_flattened_accurate(ppath, &fpath,
682
12.6k
                                              gs_currentflat_inline(pgs),
683
12.6k
                                              pgs->accurate_curves);
684
12.6k
        if (code < 0)
685
0
            return code;
686
12.6k
        ppath = &fpath;
687
12.6k
    }
688
689
167k
    pcpath_is_rect = gx_cpath_inner_box(pcpath, &old_box);
690
167k
    if (pcpath_is_rect &&
691
167k
        ((code = gx_path_is_rectangle(ppath, &new_box)) ||
692
157k
         gx_path_is_void(ppath))
693
167k
        ) {
694
116k
        int changed = 0;
695
696
116k
        if (!code) {
697
            /* The new path is void. */
698
468
            if (gx_path_current_point(ppath, &new_box.p) < 0) {
699
                /* Use the user space origin (arbitrarily). */
700
86
                new_box.p.x = float2fixed(pgs->ctm.tx);
701
86
                new_box.p.y = float2fixed(pgs->ctm.ty);
702
86
            }
703
468
            new_box.q = new_box.p;
704
468
            changed = 1;
705
116k
        } else {
706
116k
            {   /* Apply same adjustment as for filling the path. */
707
116k
                gs_fixed_point adjust = params != NULL ? params->adjust : pgs->fill_adjust;
708
116k
                fixed adjust_xl, adjust_xu, adjust_yl, adjust_yu;
709
710
116k
                if (adjust.x == -1)
711
0
                    adjust_xl = adjust_xu = adjust_yl = adjust_yu = 0;
712
116k
                else {
713
116k
                    adjust_xl = (adjust.x == fixed_half ? fixed_half - fixed_epsilon : adjust.x);
714
116k
                    adjust_yl = (adjust.y == fixed_half ? fixed_half - fixed_epsilon : adjust.y);
715
116k
                    adjust_xu = adjust.x;
716
116k
                    adjust_yu = adjust.y;
717
116k
                }
718
116k
                new_box.p.x = int2fixed(fixed2int_pixround(new_box.p.x - adjust_xl));
719
116k
                new_box.p.y = int2fixed(fixed2int_pixround(new_box.p.y - adjust_yl));
720
116k
                new_box.q.x = int2fixed(fixed2int_pixround(new_box.q.x + adjust_xu));
721
116k
                new_box.q.y = int2fixed(fixed2int_pixround(new_box.q.y + adjust_yu));
722
116k
            }
723
            /* Intersect the two rectangles if necessary. */
724
116k
            if (old_box.p.x >= new_box.p.x)
725
62.2k
                new_box.p.x = old_box.p.x, ++changed;
726
116k
            if (old_box.p.y >= new_box.p.y)
727
66.6k
                new_box.p.y = old_box.p.y, ++changed;
728
116k
            if (old_box.q.x <= new_box.q.x)
729
63.6k
                new_box.q.x = old_box.q.x, ++changed;
730
116k
            if (old_box.q.y <= new_box.q.y)
731
67.2k
                new_box.q.y = old_box.q.y, ++changed;
732
            /* Check for a degenerate rectangle. */
733
116k
            if (new_box.q.x < new_box.p.x || new_box.q.y < new_box.p.y)
734
1.73k
                new_box.p = new_box.q, changed = 1;
735
116k
        }
736
116k
        if (changed == 4) {
737
            /* The new box/path is the same as the old. */
738
59.6k
            return 0;
739
59.6k
        }
740
        /* Release the existing path. */
741
57.0k
        rc_decrement(pcpath->path_list, "gx_cpath_intersect");
742
57.0k
        pcpath->path_list = NULL;
743
57.0k
        gx_path_new(&pcpath->path);
744
57.0k
        ppath->bbox = new_box;
745
57.0k
        cpath_set_rectangle(pcpath, &new_box);
746
57.0k
        if (changed == 0) {
747
            /* The path is valid; otherwise, defer constructing it. */
748
43.6k
            gx_path_assign_preserve(&pcpath->path, ppath);
749
43.6k
            pcpath->path_valid = true;
750
43.6k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
751
43.6k
        }
752
57.0k
    } else {
753
        /* New clip path is nontrivial.  Intersect the slow way. */
754
50.7k
        gx_cpath_path_list *next = NULL;
755
50.7k
        bool path_valid =
756
50.7k
            pcpath_is_rect &&
757
50.7k
            gx_path_bbox(ppath, &new_box) >= 0 &&
758
50.7k
            gx_cpath_includes_rectangle(pcpath,
759
40.5k
                                        new_box.p.x, new_box.p.y,
760
40.5k
                                        new_box.q.x, new_box.q.y);
761
762
50.7k
        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
15.4k
            code = gx_cpath_ensure_path_list(pcpath);
767
15.4k
            if (code < 0)
768
0
                goto ex;
769
            /* gx_cpath_intersect_path_slow NULLs pcpath->path_list, so
770
             * remember it here. */
771
15.4k
            next = pcpath->path_list;
772
15.4k
            rc_increment(next);
773
15.4k
        }
774
50.7k
        code = gx_cpath_intersect_path_slow(pcpath, (params != NULL ? ppath_orig : ppath),
775
50.7k
                            rule, pgs, params);
776
50.7k
        if (code < 0) {
777
0
            rc_decrement(next, "gx_cpath_clip");
778
0
            goto ex;
779
0
        }
780
50.7k
        if (path_valid) {
781
35.3k
            gx_path_assign_preserve(&pcpath->path, ppath_orig);
782
35.3k
            pcpath->path_valid = true;
783
35.3k
            pcpath->path_fill_adjust = params != NULL ? params->adjust : pgs->fill_adjust;
784
35.3k
            pcpath->rule = rule;
785
35.3k
        } else {
786
15.4k
            code = gx_cpath_path_list_new(pcpath->path.memory, NULL, rule,
787
15.4k
                                          ppath_orig, next, &pcpath->path_list);
788
15.4k
        }
789
50.7k
        rc_decrement(next, "gx_cpath_clip");
790
50.7k
    }
791
107k
ex:
792
107k
    if (ppath != ppath_orig)
793
12.6k
        gx_path_free(ppath, "gx_cpath_clip");
794
107k
    return code;
795
167k
}
796
int
797
gx_cpath_intersect(gx_clip_path *pcpath, /*const*/ gx_path *ppath_orig,
798
                   int rule, gs_gstate *pgs)
799
136k
{
800
136k
    return gx_cpath_intersect_with_params(pcpath, ppath_orig,
801
136k
                   rule, pgs, NULL);
802
136k
}
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.41M
{
859
6.41M
    *clp = clip_list_empty;
860
6.41M
}
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
2.79M
{
869
2.79M
    gx_clip_list_init(clp);
870
2.79M
    if (rp->p.x > rp->q.x) {
871
9
        fixed t = rp->p.x;
872
873
9
        rp->p.x = rp->q.x;
874
9
        rp->q.x = t;
875
9
    }
876
2.79M
    if (rp->p.y > rp->q.y) {
877
13
        fixed t = rp->p.y;
878
879
13
        rp->p.y = rp->q.y;
880
13
        rp->q.y = t;
881
13
    }
882
2.79M
    clp->single.xmin = clp->xmin = fixed2int_var(rp->p.x);
883
2.79M
    clp->single.ymin = fixed2int_var(rp->p.y);
884
    /* Handle degenerate rectangles specially. */
885
2.79M
    clp->single.xmax = clp->xmax =
886
2.79M
        (rp->q.x == rp->p.x ? clp->single.xmin :
887
2.79M
         fixed2int_var_ceiling(rp->q.x));
888
2.79M
    clp->single.ymax =
889
2.79M
        (rp->q.y == rp->p.y ? clp->single.ymin :
890
2.79M
         fixed2int_var_ceiling(rp->q.y));
891
2.79M
    clp->count = 1;
892
2.79M
}
893
894
/* Start enumerating a clipping path. */
895
int
896
gx_cpath_enum_init(gs_cpath_enum * penum, const gx_clip_path * pcpath)
897
369k
{
898
369k
    if ((penum->using_path = pcpath->path_valid)) {
899
14.0k
        gx_path_enum_init(&penum->path_enum, &pcpath->path);
900
14.0k
        penum->rp = penum->visit = 0;
901
14.0k
        penum->first_visit = visit_left;
902
355k
    } else {
903
355k
        gx_path empty_path;
904
355k
        gx_clip_list *clp = gx_cpath_list_private(pcpath);
905
355k
        gx_clip_rect *head = (clp->count <= 1 ? &clp->single : clp->head);
906
355k
        gx_clip_rect *rp;
907
908
        /* Initialize the pointers in the path_enum properly. */
909
355k
        gx_path_init_local(&empty_path, pcpath->path.memory);
910
355k
        gx_path_enum_init(&penum->path_enum, &empty_path);
911
355k
        penum->first_visit = visit_left;
912
355k
        penum->visit = head;
913
930k
        for (rp = head; rp != 0; rp = rp->next)
914
575k
            rp->to_visit =
915
575k
                (rp->xmin < rp->xmax && rp->ymin < rp->ymax ?
916
394k
                 visit_left | visit_right : 0);
917
355k
        penum->rp = 0;    /* scan will initialize */
918
355k
        penum->any_rectangles = false;
919
355k
        penum->state = cpe_scan;
920
355k
        penum->have_line = false;
921
355k
    }
922
369k
    return 0;
923
369k
}
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.81M
{
930
1.81M
    if (penum->using_path)
931
34.5k
        return gx_path_enum_next(&penum->path_enum, pts);
932
1.77M
#define set_pt(xi, yi)\
933
1.77M
  (pts[0].x = int2fixed(xi), pts[0].y = int2fixed(yi))
934
1.77M
#define set_line(xi, yi)\
935
1.77M
  (penum->line_end.x = (xi), penum->line_end.y = (yi), penum->have_line = true)
936
1.77M
    if (penum->have_line) {
937
345k
        set_pt(penum->line_end.x, penum->line_end.y);
938
345k
        penum->have_line = false;
939
345k
        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
545k
            case cpe_scan:
951
                /* Look for the start of an edge to trace. */
952
1.08M
                for (; visit != 0; visit = visit->next) {
953
732k
                    if (visit->to_visit & visit_left) {
954
193k
                        set_pt(visit->xmin, visit->ymin);
955
193k
                        first_visit = visit_left;
956
193k
                        state = cpe_left;
957
538k
                    } else if (visit->to_visit & visit_right) {
958
630
                        set_pt(visit->xmax, visit->ymax);
959
630
                        first_visit = visit_right;
960
630
                        state = cpe_right;
961
630
                    } else
962
537k
                        continue;
963
194k
                    rp = visit;
964
194k
                    code = gs_pe_moveto;
965
194k
                    penum->any_rectangles = true;
966
194k
                    goto out;
967
732k
                }
968
                /* We've enumerated all the edges. */
969
350k
                state = cpe_done;
970
350k
                if (!penum->any_rectangles) {
971
                    /* We didn't have any rectangles. */
972
162k
                    set_pt(fixed_0, fixed_0);
973
162k
                    code = gs_pe_moveto;
974
162k
                    break;
975
162k
                }
976
                /* falls through */
977
978
350k
            case cpe_done:
979
                /* All done. */
980
350k
                code = 0;
981
350k
                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
535k
  set_pt(px, py); code = gs_pe_lineto; break
986
987
265k
            case cpe_left:
988
989
360k
              left:   /* Trace upward along a left edge. */
990
                /* We're at the lower left corner of rp. */
991
360k
                rp->to_visit &= ~visit_left;
992
                /* Look for an adjacent rectangle above rp. */
993
360k
                for (look = rp;
994
652k
                     (look = look->next) != 0 &&
995
652k
                     (look->ymin == rp->ymin ||
996
472k
                      (look->ymin == rp->ymax && look->xmax <= rp->xmin));
997
360k
                    );
998
                /* Now we know look->ymin >= rp->ymax. */
999
360k
                if (look == 0 || look->ymin > rp->ymax ||
1000
360k
                    look->xmin >= rp->xmax
1001
360k
                    ) {   /* No adjacent rectangle, switch directions. */
1002
189k
                    state =
1003
189k
                        (rp == visit && first_visit == visit_right ? cpe_close :
1004
189k
                         (set_line(rp->xmax, rp->ymax), cpe_right));
1005
189k
                    return_line(rp->xmin, rp->ymax);
1006
189k
                }
1007
                /* We found an adjacent rectangle. */
1008
                /* See if it also adjoins a rectangle to the left of rp. */
1009
170k
                {
1010
170k
                    gx_clip_rect *prev = rp->prev;
1011
170k
                    gx_clip_rect *cur = rp;
1012
1013
170k
                    if (prev != 0 && prev->ymax == rp->ymax &&
1014
170k
                        look->xmin < prev->xmax
1015
170k
                        ) { /* There's an adjoining rectangle as well. */
1016
                        /* Switch directions. */
1017
762
                        rp = prev;
1018
762
                        state =
1019
762
                            (rp == visit && first_visit == visit_right ? cpe_close :
1020
762
                             (set_line(prev->xmax, prev->ymax), cpe_right));
1021
762
                        return_line(cur->xmin, cur->ymax);
1022
762
                    }
1023
169k
                    rp = look;
1024
169k
                    if (rp == visit && first_visit == visit_left)
1025
0
                        state = cpe_close;
1026
169k
                    else if (rp->xmin == cur->xmin)
1027
94.4k
                        goto left;
1028
75.2k
                    else
1029
75.2k
                        set_line(rp->xmin, rp->ymin);
1030
169k
                    return_line(cur->xmin, cur->ymax);
1031
169k
                }
1032
1033
269k
            case cpe_right:
1034
1035
357k
              right:    /* Trace downward along a right edge. */
1036
                /* We're at the upper right corner of rp. */
1037
357k
                rp->to_visit &= ~visit_right;
1038
                /* Look for an adjacent rectangle below rp. */
1039
357k
                for (look = rp;
1040
649k
                     (look = look->prev) != 0 &&
1041
649k
                     (look->ymax == rp->ymax ||
1042
469k
                      (look->ymax == rp->ymin && look->xmin >= rp->xmax));
1043
357k
                    );
1044
                /* Now we know look->ymax <= rp->ymin. */
1045
357k
                if (look == 0 || look->ymax < rp->ymin ||
1046
357k
                    look->xmax <= rp->xmin
1047
357k
                    ) {   /* No adjacent rectangle, switch directions. */
1048
189k
                    state =
1049
189k
                        (rp == visit && first_visit == visit_left ? cpe_close :
1050
189k
                         (set_line(rp->xmin, rp->ymin), cpe_left));
1051
189k
                    return_line(rp->xmax, rp->ymin);
1052
189k
                }
1053
                /* We found an adjacent rectangle. */
1054
                /* See if it also adjoins a rectangle to the right of rp. */
1055
168k
                {
1056
168k
                    gx_clip_rect *next = rp->next;
1057
168k
                    gx_clip_rect *cur = rp;
1058
1059
168k
                    if (next != 0 && next->ymin == rp->ymin &&
1060
168k
                        look->xmax > next->xmin
1061
168k
                        ) { /* There's an adjoining rectangle as well. */
1062
                        /* Switch directions. */
1063
807
                        rp = next;
1064
807
                        state =
1065
807
                            (rp == visit && first_visit == visit_left ? cpe_close :
1066
807
                             (set_line(next->xmin, next->ymin), cpe_left));
1067
807
                        return_line(cur->xmax, cur->ymin);
1068
807
                    }
1069
167k
                    rp = look;
1070
167k
                    if (rp == visit && first_visit == visit_right)
1071
543
                        state = cpe_close;
1072
166k
                    else if (rp->xmax == cur->xmax)
1073
87.8k
                        goto right;
1074
78.9k
                    else
1075
78.9k
                        set_line(rp->xmax, rp->ymax);
1076
167k
                    return_line(cur->xmax, cur->ymin);
1077
167k
                }
1078
1079
0
#undef return_line
1080
1081
189k
            case cpe_close:
1082
                /* We've gone all the way around an edge. */
1083
189k
                code = gs_pe_closepath;
1084
189k
                state = cpe_scan;
1085
189k
                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.05M
{
1104
1.05M
    return sn_none;
1105
1.05M
}
1106
1107
/* Free a clip list. */
1108
void
1109
gx_clip_list_free(gx_clip_list * clp, gs_memory_t * mem)
1110
2.79M
{
1111
2.79M
    gx_clip_rect *rp = clp->tail;
1112
1113
4.90M
    while (rp != 0) {
1114
2.11M
        gx_clip_rect *prev = rp->prev;
1115
1116
2.11M
        gs_free_object(mem, rp, "gx_clip_list_free");
1117
2.11M
        rp = prev;
1118
2.11M
    }
1119
2.79M
    gx_clip_list_init(clp);
1120
2.79M
}
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
42.7k
{
1126
42.7k
    const gx_clip_rect *pr;
1127
42.7k
    const gx_clip_list *list = &pcpath->rect_list->list;
1128
1129
42.7k
    switch (list->count) {
1130
21
        case 0:
1131
21
            return false;
1132
42.6k
        case 1:
1133
42.6k
            pr = &list->single;
1134
42.6k
            break;
1135
46
        default:
1136
46
            pr = list->head;
1137
42.7k
    }
1138
51.4k
    for (; pr != 0; pr = pr->next) {
1139
49.7k
        if (pr->xmin > prect->q.x)
1140
975
            continue;
1141
48.7k
        if (pr->xmax < prect->p.x)
1142
7.19k
            continue;
1143
41.5k
        if (pr->ymin > prect->q.y)
1144
27
            continue;
1145
41.5k
        if (pr->ymax < prect->p.y)
1146
581
            continue;
1147
40.9k
        return true;
1148
41.5k
    }
1149
1.74k
    return false;
1150
42.6k
}
1151
1152
int
1153
gx_cpath_copy(const gx_clip_path * from, gx_clip_path * pcpath)
1154
6.22k
{   /* *pcpath must be initialized. */
1155
6.22k
    gx_clip_rect *r, *s;
1156
6.22k
    gx_clip_list *l = &pcpath->rect_list->list;
1157
1158
6.22k
    pcpath->path_valid = false;
1159
    /* NOTE: pcpath->path still contains the old path. */
1160
6.22k
    if (pcpath->path_list)
1161
6.22k
        rc_decrement(pcpath->path_list, "gx_cpath_copy");
1162
6.22k
    pcpath->path_list = NULL;
1163
6.22k
    pcpath->rule = from->rule;
1164
6.22k
    pcpath->outer_box = from->outer_box;
1165
6.22k
    pcpath->inner_box = from->inner_box;
1166
6.22k
    pcpath->cached = NULL;
1167
6.22k
    l->single = from->rect_list->list.single;
1168
8.31k
    for (r = from->rect_list->list.head; r != NULL; r = r->next) {
1169
2.09k
        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.09k
        else
1172
2.09k
            s = gs_alloc_struct(pcpath->rect_list->rc.memory, gx_clip_rect, &st_clip_rect, "gx_cpath_copy");
1173
1174
2.09k
        if (s == NULL)
1175
0
            return_error(gs_error_VMerror);
1176
2.09k
        *s = *r;
1177
2.09k
        s->next = NULL;
1178
2.09k
        if (l->tail) {
1179
2.08k
            s->prev = l->tail;
1180
2.08k
            l->tail->next = s;
1181
2.08k
        } else {
1182
13
            l->head = s;
1183
13
            s->prev = NULL;
1184
13
        }
1185
2.09k
        l->tail = s;
1186
2.09k
    }
1187
6.22k
    l->count = from->rect_list->list.count;
1188
6.22k
    return 0;
1189
6.22k
}
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 */