Coverage Report

Created: 2025-11-16 07:40

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