Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxacpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* Accumulator for clipping paths */
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gsrop.h"
21
#include "gsstruct.h"
22
#include "gsutil.h"
23
#include "gsdcolor.h"
24
#include "gsstate.h"
25
#include "gxdevice.h"
26
#include "gxfixed.h"
27
#include "gxgstate.h"
28
#include "gzpath.h"
29
#include "gxpaint.h"
30
#include "gzcpath.h"
31
#include "gzacpath.h"
32
#include "gxdevsop.h"
33
34
/* Device procedures */
35
static dev_proc_open_device(accum_open_device);
36
static dev_proc_close_device(accum_close);
37
static dev_proc_fill_rectangle(accum_fill_rectangle);
38
static dev_proc_dev_spec_op(accum_dev_spec_op);
39
static dev_proc_get_clipping_box(accum_get_clipping_box);
40
41
/* GC information */
42
extern_st(st_clip_list);
43
static
44
0
ENUM_PTRS_WITH(device_cpath_accum_enum_ptrs, gx_device_cpath_accum *pdev)
45
0
    if (index >= st_device_max_ptrs)
46
0
        return ENUM_USING(st_clip_list, &pdev->list, sizeof(gx_clip_list), index - st_device_max_ptrs);
47
0
    ENUM_PREFIX(st_device, 0);
48
0
ENUM_PTRS_END
49
static
50
0
RELOC_PTRS_WITH(device_cpath_accum_reloc_ptrs, gx_device_cpath_accum *pdev)
51
0
{   RELOC_PREFIX(st_device);
52
0
    RELOC_USING(st_clip_list, &pdev->list, size);
53
0
} RELOC_PTRS_END
54
55
public_st_device_cpath_accum();
56
57
/* The device descriptor */
58
static void
59
cpath_accum_initialize_device_procs(gx_device *dev)
60
6.31M
{
61
6.31M
    set_dev_proc(dev, open_device, accum_open_device);
62
6.31M
    set_dev_proc(dev, close_device, accum_close);
63
6.31M
    set_dev_proc(dev, fill_rectangle, accum_fill_rectangle);
64
6.31M
    set_dev_proc(dev, get_clipping_box, accum_get_clipping_box);
65
6.31M
    set_dev_proc(dev, get_color_mapping_procs, gx_default_DevGray_get_color_mapping_procs);
66
6.31M
    set_dev_proc(dev, dev_spec_op, accum_dev_spec_op);
67
6.31M
}
68
69
70
/* Many of these procedures won't be called; they are set to NULL. */
71
static const gx_device_cpath_accum gs_cpath_accum_device =
72
{std_device_std_body(gx_device_cpath_accum,
73
                     cpath_accum_initialize_device_procs,
74
                     "clip list accumulator",
75
                     0, 0, 1, 1)
76
};
77
78
/* Start accumulating a clipping path. */
79
void
80
gx_cpath_accum_begin(gx_device_cpath_accum * padev, gs_memory_t * mem, bool transpose)
81
6.31M
{
82
6.31M
    gx_device_init_on_stack((gx_device *) padev,
83
6.31M
                            (const gx_device *) & gs_cpath_accum_device, mem);
84
6.31M
    padev->list_memory = mem;
85
6.31M
    set_dev_proc(padev, encode_color, gx_default_gray_encode);
86
6.31M
    set_dev_proc(padev, decode_color, gx_default_decode_color);
87
6.31M
    (*dev_proc(padev, open_device)) ((gx_device *) padev);
88
6.31M
    padev->list.transpose = transpose;
89
6.31M
}
90
91
void
92
gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,
93
                        const gs_fixed_rect * pbox)
94
5.64M
{
95
    /* fixed2int_var_ceiling(x) overflows for anything larger
96
     * than max_fixed - fixed_scale - 1. So to protect against
97
     * us doing bad things when passed a min_fixed/max_fixed box,
98
     * clip appropriately. */
99
5.64M
    fixed upperx = pbox->q.x;
100
5.64M
    fixed uppery = pbox->q.y;
101
5.64M
    if (upperx > max_fixed - fixed_scale - 1)
102
102
        upperx = max_fixed - fixed_scale - 1;
103
5.64M
    if (uppery > max_fixed - fixed_scale - 1)
104
102
        uppery = max_fixed - fixed_scale - 1;
105
5.64M
    if (padev->list.transpose) {
106
0
        padev->clip_box.p.x = fixed2int_var(pbox->p.y);
107
0
        padev->clip_box.p.y = fixed2int_var(pbox->p.x);
108
0
        padev->clip_box.q.x = fixed2int_var_ceiling(uppery);
109
0
        padev->clip_box.q.y = fixed2int_var_ceiling(upperx);
110
5.64M
    } else {
111
5.64M
        padev->clip_box.p.x = fixed2int_var(pbox->p.x);
112
5.64M
        padev->clip_box.p.y = fixed2int_var(pbox->p.y);
113
5.64M
        padev->clip_box.q.x = fixed2int_var_ceiling(upperx);
114
5.64M
        padev->clip_box.q.y = fixed2int_var_ceiling(uppery);
115
5.64M
    }
116
5.64M
}
117
118
static void
119
accum_get_clipping_box(gx_device *dev, gs_fixed_rect *pbox)
120
23.7M
{
121
23.7M
    gx_device_cpath_accum * padev = (gx_device_cpath_accum *)dev;
122
123
23.7M
    if (padev->list.transpose) {
124
0
        pbox->p.x = int2fixed(padev->clip_box.p.y);
125
0
        pbox->p.y = int2fixed(padev->clip_box.p.x);
126
0
        pbox->q.x = int2fixed(padev->clip_box.q.y+1)-1;
127
0
        pbox->q.y = int2fixed(padev->clip_box.q.x+1)-1;
128
23.7M
    } else {
129
23.7M
        pbox->p.x = int2fixed(padev->clip_box.p.x);
130
23.7M
        pbox->p.y = int2fixed(padev->clip_box.p.y);
131
23.7M
        pbox->q.x = int2fixed(padev->clip_box.q.x+1)-1;
132
23.7M
        pbox->q.y = int2fixed(padev->clip_box.q.y+1)-1;
133
23.7M
    }
134
23.7M
}
135
136
/* Finish accumulating a clipping path. */
137
/* NB: After this the padev bbox will be restored to "normal" untransposed */
138
int
139
gx_cpath_accum_end(gx_device_cpath_accum * padev, gx_clip_path * pcpath)
140
6.31M
{
141
6.31M
    int code = (*dev_proc(padev, close_device)) ((gx_device *) padev);
142
    /* Make an entire clipping path so we can use cpath_assign. */
143
6.31M
    gx_clip_path apath;
144
145
6.31M
    if (code < 0)
146
0
        return code;
147
6.31M
    gx_cpath_init_local(&apath, padev->list_memory);
148
6.31M
    apath.rect_list->list = padev->list;
149
6.31M
    if (padev->list.count == 0)
150
2.01M
        apath.path.bbox.p.x = apath.path.bbox.p.y =
151
2.01M
        apath.path.bbox.q.x = apath.path.bbox.q.y = 0;
152
4.29M
    else {
153
4.29M
        if (padev->list.transpose) {
154
0
            int tmp;
155
156
0
            tmp = padev->bbox.p.x;
157
0
            padev->bbox.p.x = padev->bbox.p.y;
158
0
            padev->bbox.p.y = tmp;
159
0
            tmp = padev->bbox.q.x;
160
0
            padev->bbox.q.x = padev->bbox.q.y;
161
0
            padev->bbox.q.y = tmp;
162
0
        }
163
4.29M
        apath.path.bbox.p.x = int2fixed(padev->bbox.p.x);
164
4.29M
        apath.path.bbox.p.y = int2fixed(padev->bbox.p.y);
165
4.29M
        apath.path.bbox.q.x = int2fixed(padev->bbox.q.x);
166
4.29M
        apath.path.bbox.q.y = int2fixed(padev->bbox.q.y);
167
4.29M
    }
168
    /* indicate that the bbox is accurate */
169
6.31M
    apath.path.bbox_accurate = 1;
170
    /* Note that the result of the intersection might be */
171
    /* a single rectangle.  This will cause clip_path_is_rect.. */
172
    /* to return true.  This, in turn, requires that */
173
    /* we set apath.inner_box correctly. */
174
6.31M
    if (clip_list_is_rectangle(&padev->list))
175
5.90M
        apath.inner_box = apath.path.bbox;
176
404k
    else {
177
        /* The quick check must fail. */
178
404k
        apath.inner_box.p.x = apath.inner_box.p.y = 0;
179
404k
        apath.inner_box.q.x = apath.inner_box.q.y = 0;
180
404k
    }
181
6.31M
    gx_cpath_set_outer_box(&apath);
182
6.31M
    apath.path_valid = false;
183
6.31M
    apath.id = gs_next_ids(padev->list_memory, 1);  /* path changed => change id */
184
6.31M
    apath.cached = NULL;
185
6.31M
    code = gx_cpath_assign_free(pcpath, &apath);
186
6.31M
    return code;
187
6.31M
}
188
189
/* Discard an accumulator in case of error. */
190
void
191
gx_cpath_accum_discard(gx_device_cpath_accum * padev)
192
0
{
193
0
    gx_clip_list_free(&padev->list, padev->list_memory);
194
0
}
195
196
/* Intersect two clipping paths using an accumulator. */
197
int
198
gx_cpath_intersect_path_slow(gx_clip_path * pcpath, gx_path * ppath,
199
                             int rule, gs_gstate *pgs,
200
                             const gx_fill_params * params0)
201
671k
{
202
671k
    gs_logical_operation_t save_lop = gs_current_logical_op_inline(pgs);
203
671k
    gx_device_cpath_accum adev;
204
671k
    gx_device_color devc;
205
671k
    gx_fill_params params;
206
671k
    int code;
207
208
671k
    gx_cpath_accum_begin(&adev, pcpath->path.memory, false);
209
671k
    set_nonclient_dev_color(&devc, 0); /* arbitrary, but not transparent */
210
671k
    gs_set_logical_op_inline(pgs, lop_default);
211
671k
    if (params0 != 0)
212
288k
        params = *params0;
213
383k
    else {
214
383k
        gs_point fadjust;
215
383k
        params.rule = rule;
216
383k
        gs_currentfilladjust(pgs, &fadjust);
217
383k
        params.adjust.x = float2fixed(fadjust.x);
218
383k
        params.adjust.y = float2fixed(fadjust.y);
219
383k
        params.flatness = gs_currentflat_inline(pgs);
220
383k
    }
221
671k
    code = gx_fill_path_only(ppath, (gx_device *)&adev, pgs,
222
671k
                             &params, &devc, pcpath);
223
671k
    if (code < 0 || (code = gx_cpath_accum_end(&adev, pcpath)) < 0)
224
0
        gx_cpath_accum_discard(&adev);
225
671k
    gs_set_logical_op_inline(pgs, save_lop);
226
671k
    return code;
227
671k
}
228
229
/* ------ Device implementation ------ */
230
231
#ifdef DEBUG
232
/* Validate a clipping path after accumulation. */
233
static bool
234
clip_list_validate(const gx_clip_list * clp)
235
{
236
    if (clp->count <= 1)
237
        return (clp->head == 0 && clp->tail == 0 &&
238
                clp->single.next == 0 && clp->single.prev == 0);
239
    else {
240
        const gx_clip_rect *prev = clp->head;
241
        const gx_clip_rect *ptr;
242
        bool ok = true;
243
244
        while ((ptr = prev->next) != 0) {
245
            if (ptr->ymin > ptr->ymax || ptr->xmin > ptr->xmax ||
246
                !(ptr->ymin >= prev->ymax ||
247
                  (ptr->ymin == prev->ymin &&
248
                   ptr->ymax == prev->ymax &&
249
                   ptr->xmin >= prev->xmax)) ||
250
                ptr->prev != prev
251
                ) {
252
                clip_rect_print('q', "WRONG:", ptr);
253
                ok = false;
254
            }
255
            prev = ptr;
256
        }
257
        return ok && prev == clp->tail;
258
    }
259
}
260
#endif /* DEBUG */
261
262
/* Initialize the accumulation device. */
263
int
264
accum_open_device(register gx_device * dev)
265
6.31M
{
266
6.31M
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
267
268
6.31M
    gx_clip_list_init(&adev->list);
269
6.31M
    adev->bbox.p.x = adev->bbox.p.y = fixed2int(max_fixed);
270
6.31M
    adev->bbox.q.x = adev->bbox.q.y = fixed2int(min_fixed);
271
6.31M
    adev->clip_box.p.x = adev->clip_box.p.y = fixed2int(min_fixed);
272
6.31M
    adev->clip_box.q.x = adev->clip_box.q.y = fixed2int(max_fixed);
273
6.31M
    return 0;
274
6.31M
}
275
276
/* Close the accumulation device. */
277
static int
278
accum_close(gx_device * dev)
279
6.31M
{
280
6.31M
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
281
282
6.31M
    if (adev->list.transpose) {
283
0
        adev->list.xmin = adev->bbox.p.y;
284
0
        adev->list.xmax = adev->bbox.q.y;
285
6.31M
    } else {
286
6.31M
        adev->list.xmin = adev->bbox.p.x;
287
6.31M
        adev->list.xmax = adev->bbox.q.x;
288
6.31M
    }
289
#ifdef DEBUG
290
    if (gs_debug_c('q')) {
291
        gx_clip_rect *rp =
292
            (adev->list.count <= 1 ? &adev->list.single : adev->list.head);
293
294
        dmlprintf6(dev->memory,
295
                   "[q]list at "PRI_INTPTR", count=%d, head="PRI_INTPTR", tail="PRI_INTPTR", xrange=(%d,%d):\n",
296
                   (intptr_t)&adev->list, adev->list.count,
297
                   (intptr_t)adev->list.head, (intptr_t)adev->list.tail,
298
                   adev->list.xmin, adev->list.xmax);
299
        while (rp != 0) {
300
            clip_rect_print('q', "   ", rp);
301
            rp = rp->next;
302
        }
303
    }
304
    if (!clip_list_validate(&adev->list)) {
305
        mlprintf1(dev->memory, "[q]Bad clip list "PRI_INTPTR"!\n", (intptr_t)&adev->list);
306
        return_error(gs_error_Fatal);
307
    }
308
#endif
309
6.31M
    return 0;
310
6.31M
}
311
312
/*
313
   The pattern management device method.
314
   See gxdevcli.h about return codes.
315
 */
316
int
317
accum_dev_spec_op(gx_device *pdev1, int dev_spec_op,
318
                void *data, int size)
319
2.67k
{
320
2.67k
    switch (dev_spec_op) {
321
892
        case gxdso_pattern_is_cpath_accum:
322
892
            return 1;
323
0
        case gxdso_pattern_can_accum:
324
0
        case gxdso_pattern_start_accum:
325
0
        case gxdso_pattern_finish_accum:
326
0
        case gxdso_pattern_load:
327
0
        case gxdso_pattern_shading_area:
328
0
        case gxdso_pattern_shfill_doesnt_need_path:
329
0
        case gxdso_pattern_handles_clip_path:
330
0
            return 0;
331
2.67k
    }
332
1.78k
    return  gx_default_dev_spec_op(pdev1, dev_spec_op, data, size);
333
2.67k
}
334
335
/* Accumulate one rectangle. */
336
/* Allocate a rectangle to be added to the list. */
337
static const gx_clip_rect clip_head_rect = {
338
    0, 0, min_int, min_int, min_int, min_int
339
};
340
static const gx_clip_rect clip_tail_rect = {
341
    0, 0, max_int, max_int, max_int, max_int
342
};
343
static gx_clip_rect *
344
accum_alloc_rect(gx_device_cpath_accum * adev)
345
58.7M
{
346
58.7M
    gs_memory_t *mem = adev->list_memory;
347
58.7M
    gx_clip_rect *ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
348
58.7M
                                       "accum_alloc_rect");
349
350
58.7M
    if (ar == 0)
351
0
        return 0;
352
58.7M
    if (adev->list.count == 2) {
353
        /* We're switching from a single rectangle to a list. */
354
        /* Allocate the head and tail entries. */
355
408k
        gx_clip_rect *head = ar;
356
408k
        gx_clip_rect *tail =
357
408k
            gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
358
408k
                            "accum_alloc_rect(tail)");
359
408k
        gx_clip_rect *single =
360
408k
            gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
361
408k
                            "accum_alloc_rect(single)");
362
363
408k
        ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
364
408k
                             "accum_alloc_rect(head)");
365
408k
        if (tail == 0 || single == 0 || ar == 0) {
366
0
            gs_free_object(mem, ar, "accum_alloc_rect");
367
0
            gs_free_object(mem, single, "accum_alloc_rect(single)");
368
0
            gs_free_object(mem, tail, "accum_alloc_rect(tail)");
369
0
            gs_free_object(mem, head, "accum_alloc_rect(head)");
370
0
            return 0;
371
0
        }
372
408k
        *head = clip_head_rect;
373
408k
        head->next = single;
374
408k
        *single = adev->list.single;
375
408k
        single->prev = head;
376
408k
        single->next = tail;
377
408k
        *tail = clip_tail_rect;
378
408k
        tail->prev = single;
379
408k
        adev->list.head = head;
380
408k
        adev->list.tail = tail;
381
408k
        adev->list.insert = tail;
382
408k
    }
383
58.7M
    return ar;
384
58.7M
}
385
#define ACCUM_ALLOC(s, ar, px, py, qx, qy)\
386
58.7M
        if (++(adev->list.count) == 1)\
387
58.7M
          ar = &adev->list.single;\
388
58.7M
        ar = accum_alloc_rect(adev);\
389
58.7M
        if (ar) ACCUM_SET(s, ar, px, py, qx, qy)
390
#define ACCUM_SET(s, ar, px, py, qx, qy)\
391
58.7M
        (ar)->xmin = px, (ar)->ymin = py, (ar)->xmax = qx, (ar)->ymax = qy;\
392
102M
        clip_rect_print('Q', s, ar)
393
/* Link or unlink a rectangle in the list. */
394
#define ACCUM_ADD_LAST(ar)\
395
32.6M
        ACCUM_ADD_BEFORE(ar, adev->list.tail)
396
#define ACCUM_ADD_AFTER(ar, rprev)\
397
10.0M
        ar->prev = (rprev), (ar->next = (rprev)->next)->prev = ar,\
398
10.0M
          (rprev)->next = ar
399
#define ACCUM_ADD_BEFORE(ar, rnext)\
400
32.6M
        (ar->prev = (rnext)->prev)->next = ar, ar->next = (rnext),\
401
32.6M
          (rnext)->prev = ar
402
#define ACCUM_REMOVE(ar)\
403
2.78M
        ar->next->prev = ar->prev, ar->prev->next = ar->next
404
/* Free a rectangle that was removed from the list. */
405
#define ACCUM_FREE(s, ar)\
406
18.7M
        if (--(adev->list.count)) {\
407
18.7M
          clip_rect_print('Q', s, ar);\
408
18.7M
          gs_free_object(adev->list_memory, ar, "accum_rect");\
409
18.7M
        }
410
/*
411
 * Add a rectangle to the list.  It would be wonderful if rectangles
412
 * were always disjoint and always presented in the correct order,
413
 * but they aren't: the fill loop works by trapezoids, not by scan lines,
414
 * and may produce slightly overlapping rectangles because of "fattening".
415
 * All we can count on is that they are approximately disjoint and
416
 * approximately in order.
417
 *
418
 * Because of the way the fill loop handles a path that is just a single
419
 * rectangle, we take special care to merge Y-adjacent rectangles when
420
 * this is possible.
421
 */
422
static int
423
accum_fill_rectangle(gx_device * dev, int xi, int yi, int w, int h,
424
                     gx_color_index color)
425
82.6M
{
426
82.6M
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
427
82.6M
    int x, y, xe, ye;
428
82.6M
    gx_clip_rect *nr;
429
82.6M
    gx_clip_rect *ar;
430
82.6M
    register gx_clip_rect *rptr;
431
82.6M
    int ymin, ymax;
432
433
82.6M
    if (adev->list.transpose) {
434
0
        x = yi, xe = yi + h;
435
0
        y = xi, ye = xi + w;
436
82.6M
    } else {
437
82.6M
        x = xi, xe = x + w;
438
82.6M
        y = yi, ye = y + h;
439
82.6M
    }
440
441
    /* Clip the rectangle being added. */
442
82.6M
    if (y < adev->clip_box.p.y)
443
15.8M
        y = adev->clip_box.p.y;
444
82.6M
    if (ye > adev->clip_box.q.y)
445
5.23M
        ye = adev->clip_box.q.y;
446
82.6M
    if (y >= ye)
447
15.8M
        return 0;
448
66.7M
    if (x < adev->clip_box.p.x)
449
1.19M
        x = adev->clip_box.p.x;
450
66.7M
    if (xe > adev->clip_box.q.x)
451
178k
        xe = adev->clip_box.q.x;
452
66.7M
    if (x >= xe)
453
1.09M
        return 0;
454
455
    /* Update the bounding box. */
456
65.6M
    if (x < adev->bbox.p.x)
457
10.8M
        adev->bbox.p.x = x;
458
65.6M
    if (y < adev->bbox.p.y)
459
4.46M
        adev->bbox.p.y = y;
460
65.6M
    if (xe > adev->bbox.q.x)
461
12.0M
        adev->bbox.q.x = xe;
462
65.6M
    if (ye > adev->bbox.q.y)
463
35.6M
        adev->bbox.q.y = ye;
464
465
69.5M
top:
466
69.5M
    if (adev->list.count == 0) { /* very first rectangle */
467
4.29M
        adev->list.count = 1;
468
4.29M
        ACCUM_SET("single", &adev->list.single, x, y, xe, ye);
469
4.29M
        return 0;
470
4.29M
    }
471
65.2M
    if (adev->list.count == 1) { /* check for Y merging */
472
4.51M
        rptr = &adev->list.single;
473
4.51M
        if (x == rptr->xmin && xe == rptr->xmax &&
474
4.51M
            y <= rptr->ymax && ye >= rptr->ymin
475
4.51M
            ) {
476
4.10M
            if (y < rptr->ymin)
477
1.13k
                rptr->ymin = y;
478
4.10M
            if (ye > rptr->ymax)
479
4.09M
                rptr->ymax = ye;
480
4.10M
            return 0;
481
4.10M
        }
482
4.51M
    }
483
60.6M
    else
484
60.6M
        rptr = adev->list.tail->prev;
485
61.1M
    if (y >= rptr->ymax) {
486
27.2M
        if (y == rptr->ymax && x == rptr->xmin && xe == rptr->xmax &&
487
27.2M
            (rptr->prev == 0 || y != rptr->prev->ymax)
488
27.2M
            ) {
489
2.32M
            rptr->ymax = ye;
490
2.32M
            return 0;
491
2.32M
        }
492
24.8M
        ACCUM_ALLOC("app.y", nr, x, y, xe, ye);
493
24.8M
        if (!nr) return_error(gs_error_VMerror);
494
24.8M
        ACCUM_ADD_LAST(nr);
495
24.8M
        return 0;
496
33.8M
    } else if (y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmin) {
497
8.70M
        if (x <= rptr->xmax) {
498
956k
            if (xe > rptr->xmax)
499
810k
                rptr->xmax = xe;
500
956k
            return 0;
501
956k
        }
502
7.74M
        ACCUM_ALLOC("app.x", nr, x, y, xe, ye);
503
7.74M
        if (!nr) return_error(gs_error_VMerror);
504
7.74M
        ACCUM_ADD_LAST(nr);
505
7.74M
        return 0;
506
7.74M
    }
507
25.1M
    ACCUM_ALLOC("accum", nr, x, y, xe, ye);
508
25.1M
    if (!nr) return_error(gs_error_VMerror);
509
    /* Previously we used to always search back from the tail here. Now we
510
     * base our search on the previous insertion point, in the hopes that
511
     * locality of reference will save us time. */
512
25.1M
    rptr = adev->list.insert->prev;
513
    /* We want to find the value of rptr nearest the tail, s.t.
514
     * ye > rptr->ymin */
515
25.1M
    if (ye <= rptr->ymin) {
516
        /* Work backwards till we find the insertion point. */
517
101M
        do {
518
101M
            rptr = rptr->prev;
519
101M
        } while (ye <= rptr->ymin);
520
21.7M
    } else {
521
        /* Search forwards */
522
179M
        do {
523
179M
            rptr = rptr->next;
524
179M
        } while (ye > rptr->ymin);
525
        /* And we've gone one too far */
526
21.7M
        rptr = rptr->prev;
527
21.7M
    }
528
25.1M
    ymin = rptr->ymin;
529
25.1M
    ymax = rptr->ymax;
530
25.1M
    if (ye > ymax) {
531
545k
        if (y >= ymax) { /* Insert between two bands. */
532
542k
            ACCUM_ADD_AFTER(nr, rptr);
533
542k
            adev->list.insert = nr;
534
542k
            return 0;
535
542k
        }
536
        /* Split off the top part of the new rectangle. */
537
3.19k
        ACCUM_ALLOC("a.top", ar, x, ymax, xe, ye);
538
3.19k
        if (!ar) {
539
0
            if (nr != &adev->list.single) ACCUM_FREE("free", nr);
540
0
            return_error(gs_error_VMerror);
541
0
        }
542
3.19k
        ACCUM_ADD_AFTER(ar, rptr);
543
3.19k
        ye = nr->ymax = ymax;
544
3.19k
        clip_rect_print('Q', " ymax", nr);
545
3.19k
    }
546
    /* Here we know ymin < ye <= ymax; */
547
    /* rptr points to the last node with this value of ymin/ymax. */
548
    /* If necessary, split off the part of the existing band */
549
    /* that is above the new band. */
550
24.6M
    if (ye < ymax) {
551
625k
        gx_clip_rect *rsplit = rptr;
552
553
1.44M
        while (rsplit->ymax == ymax) {
554
817k
            ACCUM_ALLOC("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
555
817k
            if (!ar) {
556
0
                if (nr != &adev->list.single) ACCUM_FREE("free", nr);
557
0
                return_error(gs_error_VMerror);
558
0
            }
559
817k
            ACCUM_ADD_AFTER(ar, rptr);
560
817k
            rsplit->ymax = ye;
561
817k
            rsplit = rsplit->prev;
562
817k
        }
563
625k
    }
564
    /* Now ye = ymax.  If necessary, split off the part of the */
565
    /* existing band that is below the new band. */
566
24.6M
    if (y > ymin) {
567
41.2k
        gx_clip_rect *rbot = rptr, *rsplit;
568
569
51.7k
        while (rbot->prev->ymin == ymin)
570
10.4k
            rbot = rbot->prev;
571
51.7k
        for (rsplit = rbot;;) {
572
51.7k
            ACCUM_ALLOC("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
573
51.7k
            if (!ar) {
574
0
                if (nr != &adev->list.single) ACCUM_FREE("free", nr);
575
0
                return_error(gs_error_VMerror);
576
0
            }
577
51.7k
            ACCUM_ADD_BEFORE(ar, rbot);
578
51.7k
            rsplit->ymin = y;
579
51.7k
            if (rsplit == rptr)
580
41.2k
                break;
581
10.4k
            rsplit = rsplit->next;
582
10.4k
        }
583
41.2k
        ymin = y;
584
41.2k
    }
585
    /* Now y <= ymin as well.  (y < ymin is possible.) */
586
24.6M
    nr->ymin = ymin;
587
    /* Search for the X insertion point. */
588
59.5M
    for (; rptr->ymin == ymin; rptr = rptr->prev) {
589
57.9M
        if (xe < rptr->xmin)
590
32.1M
            continue;    /* still too far to right */
591
25.7M
        if (x > rptr->xmax)
592
7.01M
            break;    /* disjoint */
593
        /* The new rectangle overlaps an existing one.  Merge them. */
594
18.7M
        if (xe > rptr->xmax) {
595
3.54M
            rptr->xmax = nr->xmax;  /* might be > xe if */
596
            /* we already did a merge */
597
3.54M
            clip_rect_print('Q', "widen", rptr);
598
3.54M
        }
599
18.7M
        ACCUM_FREE("free", nr);
600
18.7M
        if (x >= rptr->xmin) {
601
15.9M
            adev->list.insert = rptr;
602
15.9M
            goto out;
603
15.9M
        }
604
        /* Might overlap other rectangles to the left. */
605
2.78M
        rptr->xmin = x;
606
2.78M
        nr = rptr;
607
2.78M
        ACCUM_REMOVE(rptr);
608
2.78M
        clip_rect_print('Q', "merge", nr);
609
2.78M
    }
610
8.67M
    ACCUM_ADD_AFTER(nr, rptr);
611
8.67M
    adev->list.insert = nr;
612
24.6M
out:
613
    /* Check whether there are only 0 or 1 rectangles left. */
614
24.6M
    if (adev->list.count <= 1) {
615
        /* We're switching from a list to at most 1 rectangle. */
616
        /* Free the head and tail entries. */
617
4.33k
        gs_memory_t *mem = adev->list_memory;
618
4.33k
        gx_clip_rect *single = adev->list.head->next;
619
620
4.33k
        if (single != adev->list.tail) {
621
4.33k
            adev->list.single = *single;
622
4.33k
            gs_free_object(mem, single, "accum_free_rect(single)");
623
4.33k
            adev->list.single.next = adev->list.single.prev = 0;
624
4.33k
        }
625
4.33k
        gs_free_object(mem, adev->list.tail, "accum_free_rect(tail)");
626
4.33k
        gs_free_object(mem, adev->list.head, "accum_free_rect(head)");
627
4.33k
        adev->list.head = 0;
628
4.33k
        adev->list.tail = 0;
629
4.33k
        adev->list.insert = 0;
630
4.33k
    }
631
    /* Check whether there is still more of the new band to process. */
632
24.6M
    if (y < ymin) {
633
        /* Continue with the bottom part of the new rectangle. */
634
3.86M
        clip_rect_print('Q', " ymin", nr);
635
3.86M
        ye = ymin;
636
3.86M
        goto top;
637
3.86M
    }
638
20.7M
    return 0;
639
24.6M
}