Coverage Report

Created: 2025-06-24 07:01

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