Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/base/gxacpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2022 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, 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
961k
{
61
961k
    set_dev_proc(dev, open_device, accum_open_device);
62
961k
    set_dev_proc(dev, close_device, accum_close);
63
961k
    set_dev_proc(dev, fill_rectangle, accum_fill_rectangle);
64
961k
    set_dev_proc(dev, get_clipping_box, accum_get_clipping_box);
65
961k
    set_dev_proc(dev, get_color_mapping_procs, gx_default_DevGray_get_color_mapping_procs);
66
961k
    set_dev_proc(dev, dev_spec_op, accum_dev_spec_op);
67
961k
}
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
961k
{
82
961k
    gx_device_init_on_stack((gx_device *) padev,
83
961k
                            (const gx_device *) & gs_cpath_accum_device, mem);
84
961k
    padev->list_memory = mem;
85
961k
    set_dev_proc(padev, encode_color, gx_default_gray_encode);
86
961k
    set_dev_proc(padev, decode_color, gx_default_decode_color);
87
961k
    (*dev_proc(padev, open_device)) ((gx_device *) padev);
88
961k
    padev->list.transpose = transpose;
89
961k
}
90
91
void
92
gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,
93
                        const gs_fixed_rect * pbox)
94
863k
{
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
863k
    fixed upperx = pbox->q.x;
100
863k
    fixed uppery = pbox->q.y;
101
863k
    if (upperx > max_fixed - fixed_scale - 1)
102
0
        upperx = max_fixed - fixed_scale - 1;
103
863k
    if (uppery > max_fixed - fixed_scale - 1)
104
0
        uppery = max_fixed - fixed_scale - 1;
105
863k
    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
863k
    } else {
111
863k
        padev->clip_box.p.x = fixed2int_var(pbox->p.x);
112
863k
        padev->clip_box.p.y = fixed2int_var(pbox->p.y);
113
863k
        padev->clip_box.q.x = fixed2int_var_ceiling(upperx);
114
863k
        padev->clip_box.q.y = fixed2int_var_ceiling(uppery);
115
863k
    }
116
863k
}
117
118
static void
119
accum_get_clipping_box(gx_device *dev, gs_fixed_rect *pbox)
120
2.57M
{
121
2.57M
    gx_device_cpath_accum * padev = (gx_device_cpath_accum *)dev;
122
123
2.57M
    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
2.57M
    } else {
129
2.57M
        pbox->p.x = int2fixed(padev->clip_box.p.x);
130
2.57M
        pbox->p.y = int2fixed(padev->clip_box.p.y);
131
2.57M
        pbox->q.x = int2fixed(padev->clip_box.q.x+1)-1;
132
2.57M
        pbox->q.y = int2fixed(padev->clip_box.q.y+1)-1;
133
2.57M
    }
134
2.57M
}
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
961k
{
141
961k
    int code = (*dev_proc(padev, close_device)) ((gx_device *) padev);
142
    /* Make an entire clipping path so we can use cpath_assign. */
143
961k
    gx_clip_path apath;
144
145
961k
    if (code < 0)
146
0
        return code;
147
961k
    gx_cpath_init_local(&apath, padev->list_memory);
148
961k
    apath.rect_list->list = padev->list;
149
961k
    if (padev->list.count == 0)
150
193k
        apath.path.bbox.p.x = apath.path.bbox.p.y =
151
193k
        apath.path.bbox.q.x = apath.path.bbox.q.y = 0;
152
768k
    else {
153
768k
        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
768k
        apath.path.bbox.p.x = int2fixed(padev->bbox.p.x);
164
768k
        apath.path.bbox.p.y = int2fixed(padev->bbox.p.y);
165
768k
        apath.path.bbox.q.x = int2fixed(padev->bbox.q.x);
166
768k
        apath.path.bbox.q.y = int2fixed(padev->bbox.q.y);
167
768k
    }
168
    /* indicate that the bbox is accurate */
169
961k
    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
961k
    if (clip_list_is_rectangle(&padev->list))
175
899k
        apath.inner_box = apath.path.bbox;
176
62.4k
    else {
177
        /* The quick check must fail. */
178
62.4k
        apath.inner_box.p.x = apath.inner_box.p.y = 0;
179
62.4k
        apath.inner_box.q.x = apath.inner_box.q.y = 0;
180
62.4k
    }
181
961k
    gx_cpath_set_outer_box(&apath);
182
961k
    apath.path_valid = false;
183
961k
    apath.id = gs_next_ids(padev->list_memory, 1);  /* path changed => change id */
184
961k
    apath.cached = NULL;
185
961k
    code = gx_cpath_assign_free(pcpath, &apath);
186
961k
    return code;
187
961k
}
188
189
/* Discard an accumulator in case of error. */
190
void
191
gx_cpath_accum_discard(gx_device_cpath_accum * padev)
192
2
{
193
2
    gx_clip_list_free(&padev->list, padev->list_memory);
194
2
}
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
98.4k
{
202
98.4k
    gs_logical_operation_t save_lop = gs_current_logical_op_inline(pgs);
203
98.4k
    gx_device_cpath_accum adev;
204
98.4k
    gx_device_color devc;
205
98.4k
    gx_fill_params params;
206
98.4k
    int code;
207
208
98.4k
    gx_cpath_accum_begin(&adev, pcpath->path.memory, false);
209
98.4k
    set_nonclient_dev_color(&devc, 0); /* arbitrary, but not transparent */
210
98.4k
    gs_set_logical_op_inline(pgs, lop_default);
211
98.4k
    if (params0 != 0)
212
42.5k
        params = *params0;
213
55.8k
    else {
214
55.8k
        gs_point fadjust;
215
55.8k
        params.rule = rule;
216
55.8k
        gs_currentfilladjust(pgs, &fadjust);
217
55.8k
        params.adjust.x = float2fixed(fadjust.x);
218
55.8k
        params.adjust.y = float2fixed(fadjust.y);
219
55.8k
        params.flatness = gs_currentflat_inline(pgs);
220
55.8k
    }
221
98.4k
    code = gx_fill_path_only(ppath, (gx_device *)&adev, pgs,
222
98.4k
                             &params, &devc, pcpath);
223
98.4k
    if (code < 0 || (code = gx_cpath_accum_end(&adev, pcpath)) < 0)
224
2
        gx_cpath_accum_discard(&adev);
225
98.4k
    gs_set_logical_op_inline(pgs, save_lop);
226
98.4k
    return code;
227
98.4k
}
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
961k
{
266
961k
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
267
268
961k
    gx_clip_list_init(&adev->list);
269
961k
    adev->bbox.p.x = adev->bbox.p.y = fixed2int(max_fixed);
270
961k
    adev->bbox.q.x = adev->bbox.q.y = fixed2int(min_fixed);
271
961k
    adev->clip_box.p.x = adev->clip_box.p.y = fixed2int(min_fixed);
272
961k
    adev->clip_box.q.x = adev->clip_box.q.y = fixed2int(max_fixed);
273
961k
    return 0;
274
961k
}
275
276
/* Close the accumulation device. */
277
static int
278
accum_close(gx_device * dev)
279
961k
{
280
961k
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
281
282
961k
    if (adev->list.transpose) {
283
0
        adev->list.xmin = adev->bbox.p.y;
284
0
        adev->list.xmax = adev->bbox.q.y;
285
961k
    } else {
286
961k
        adev->list.xmin = adev->bbox.p.x;
287
961k
        adev->list.xmax = adev->bbox.q.x;
288
961k
    }
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
961k
    return 0;
310
961k
}
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
270
{
320
270
    switch (dev_spec_op) {
321
90
        case gxdso_pattern_is_cpath_accum:
322
90
            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
270
    }
332
180
    return  gx_default_dev_spec_op(pdev1, dev_spec_op, data, size);
333
270
}
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
13.4M
{
346
13.4M
    gs_memory_t *mem = adev->list_memory;
347
13.4M
    gx_clip_rect *ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
348
13.4M
                                       "accum_alloc_rect");
349
350
13.4M
    if (ar == 0)
351
0
        return 0;
352
13.4M
    if (adev->list.count == 2) {
353
        /* We're switching from a single rectangle to a list. */
354
        /* Allocate the head and tail entries. */
355
63.7k
        gx_clip_rect *head = ar;
356
63.7k
        gx_clip_rect *tail =
357
63.7k
            gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
358
63.7k
                            "accum_alloc_rect(tail)");
359
63.7k
        gx_clip_rect *single =
360
63.7k
            gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
361
63.7k
                            "accum_alloc_rect(single)");
362
363
63.7k
        ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
364
63.7k
                             "accum_alloc_rect(head)");
365
63.7k
        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
63.7k
        *head = clip_head_rect;
373
63.7k
        head->next = single;
374
63.7k
        *single = adev->list.single;
375
63.7k
        single->prev = head;
376
63.7k
        single->next = tail;
377
63.7k
        *tail = clip_tail_rect;
378
63.7k
        tail->prev = single;
379
63.7k
        adev->list.head = head;
380
63.7k
        adev->list.tail = tail;
381
63.7k
        adev->list.insert = tail;
382
63.7k
    }
383
13.4M
    return ar;
384
13.4M
}
385
#define ACCUM_ALLOC(s, ar, px, py, qx, qy)\
386
13.4M
        if (++(adev->list.count) == 1)\
387
13.4M
          ar = &adev->list.single;\
388
13.4M
        ar = accum_alloc_rect(adev);\
389
13.4M
        if (ar) ACCUM_SET(s, ar, px, py, qx, qy)
390
#define ACCUM_SET(s, ar, px, py, qx, qy)\
391
13.4M
        (ar)->xmin = px, (ar)->ymin = py, (ar)->xmax = qx, (ar)->ymax = qy;\
392
21.4M
        clip_rect_print('Q', s, ar)
393
/* Link or unlink a rectangle in the list. */
394
#define ACCUM_ADD_LAST(ar)\
395
5.66M
        ACCUM_ADD_BEFORE(ar, adev->list.tail)
396
#define ACCUM_ADD_AFTER(ar, rprev)\
397
2.77M
        ar->prev = (rprev), (ar->next = (rprev)->next)->prev = ar,\
398
2.77M
          (rprev)->next = ar
399
#define ACCUM_ADD_BEFORE(ar, rnext)\
400
5.67M
        (ar->prev = (rnext)->prev)->next = ar, ar->next = (rnext),\
401
5.67M
          (rnext)->prev = ar
402
#define ACCUM_REMOVE(ar)\
403
924k
        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
5.96M
        if (--(adev->list.count)) {\
407
5.96M
          clip_rect_print('Q', s, ar);\
408
5.96M
          gs_free_object(adev->list_memory, ar, "accum_rect");\
409
5.96M
        }
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
31.7M
{
426
31.7M
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
427
31.7M
    int x, y, xe, ye;
428
31.7M
    gx_clip_rect *nr;
429
31.7M
    gx_clip_rect *ar;
430
31.7M
    register gx_clip_rect *rptr;
431
31.7M
    int ymin, ymax;
432
433
31.7M
    if (adev->list.transpose) {
434
0
        x = yi, xe = yi + h;
435
0
        y = xi, ye = xi + w;
436
31.7M
    } else {
437
31.7M
        x = xi, xe = x + w;
438
31.7M
        y = yi, ye = y + h;
439
31.7M
    }
440
441
    /* Clip the rectangle being added. */
442
31.7M
    if (y < adev->clip_box.p.y)
443
17.2M
        y = adev->clip_box.p.y;
444
31.7M
    if (ye > adev->clip_box.q.y)
445
703k
        ye = adev->clip_box.q.y;
446
31.7M
    if (y >= ye)
447
17.0M
        return 0;
448
14.6M
    if (x < adev->clip_box.p.x)
449
2.36k
        x = adev->clip_box.p.x;
450
14.6M
    if (xe > adev->clip_box.q.x)
451
2.73k
        xe = adev->clip_box.q.x;
452
14.6M
    if (x >= xe)
453
3.21k
        return 0;
454
455
    /* Update the bounding box. */
456
14.6M
    if (x < adev->bbox.p.x)
457
1.47M
        adev->bbox.p.x = x;
458
14.6M
    if (y < adev->bbox.p.y)
459
812k
        adev->bbox.p.y = y;
460
14.6M
    if (xe > adev->bbox.q.x)
461
1.40M
        adev->bbox.q.x = xe;
462
14.6M
    if (ye > adev->bbox.q.y)
463
4.00M
        adev->bbox.q.y = ye;
464
465
15.4M
top:
466
15.4M
    if (adev->list.count == 0) { /* very first rectangle */
467
768k
        adev->list.count = 1;
468
768k
        ACCUM_SET("single", &adev->list.single, x, y, xe, ye);
469
768k
        return 0;
470
768k
    }
471
14.7M
    if (adev->list.count == 1) { /* check for Y merging */
472
706k
        rptr = &adev->list.single;
473
706k
        if (x == rptr->xmin && xe == rptr->xmax &&
474
706k
            y <= rptr->ymax && ye >= rptr->ymin
475
706k
            ) {
476
642k
            if (y < rptr->ymin)
477
355
                rptr->ymin = y;
478
642k
            if (ye > rptr->ymax)
479
640k
                rptr->ymax = ye;
480
642k
            return 0;
481
642k
        }
482
706k
    }
483
13.9M
    else
484
13.9M
        rptr = adev->list.tail->prev;
485
14.0M
    if (y >= rptr->ymax) {
486
2.59M
        if (y == rptr->ymax && x == rptr->xmin && xe == rptr->xmax &&
487
2.59M
            (rptr->prev == 0 || y != rptr->prev->ymax)
488
2.59M
            ) {
489
213k
            rptr->ymax = ye;
490
213k
            return 0;
491
213k
        }
492
2.38M
        ACCUM_ALLOC("app.y", nr, x, y, xe, ye);
493
2.38M
        if (!nr) return_error(gs_error_VMerror);
494
2.38M
        ACCUM_ADD_LAST(nr);
495
2.38M
        return 0;
496
11.4M
    } else if (y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmin) {
497
3.75M
        if (x <= rptr->xmax) {
498
473k
            if (xe > rptr->xmax)
499
444k
                rptr->xmax = xe;
500
473k
            return 0;
501
473k
        }
502
3.27M
        ACCUM_ALLOC("app.x", nr, x, y, xe, ye);
503
3.27M
        if (!nr) return_error(gs_error_VMerror);
504
3.27M
        ACCUM_ADD_LAST(nr);
505
3.27M
        return 0;
506
3.27M
    }
507
7.71M
    ACCUM_ALLOC("accum", nr, x, y, xe, ye);
508
7.71M
    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
7.71M
    rptr = adev->list.insert->prev;
513
    /* We want to find the value of rptr nearest the tail, s.t.
514
     * ye > rptr->ymin */
515
7.71M
    if (ye <= rptr->ymin) {
516
        /* Work backwards till we find the insertion point. */
517
49.0M
        do {
518
49.0M
            rptr = rptr->prev;
519
49.0M
        } while (ye <= rptr->ymin);
520
6.23M
    } else {
521
        /* Search forwards */
522
82.8M
        do {
523
82.8M
            rptr = rptr->next;
524
82.8M
        } while (ye > rptr->ymin);
525
        /* And we've gone one too far */
526
6.23M
        rptr = rptr->prev;
527
6.23M
    }
528
7.71M
    ymin = rptr->ymin;
529
7.71M
    ymax = rptr->ymax;
530
7.71M
    if (ye > ymax) {
531
121k
        if (y >= ymax) { /* Insert between two bands. */
532
121k
            ACCUM_ADD_AFTER(nr, rptr);
533
121k
            adev->list.insert = nr;
534
121k
            return 0;
535
121k
        }
536
        /* Split off the top part of the new rectangle. */
537
657
        ACCUM_ALLOC("a.top", ar, x, ymax, xe, ye);
538
657
        if (!ar) {
539
0
            if (nr != &adev->list.single) ACCUM_FREE("free", nr);
540
0
            return_error(gs_error_VMerror);
541
0
        }
542
657
        ACCUM_ADD_AFTER(ar, rptr);
543
657
        ye = nr->ymax = ymax;
544
657
        clip_rect_print('Q', " ymax", nr);
545
657
    }
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
7.59M
    if (ye < ymax) {
551
84.6k
        gx_clip_rect *rsplit = rptr;
552
553
191k
        while (rsplit->ymax == ymax) {
554
106k
            ACCUM_ALLOC("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
555
106k
            if (!ar) {
556
0
                if (nr != &adev->list.single) ACCUM_FREE("free", nr);
557
0
                return_error(gs_error_VMerror);
558
0
            }
559
106k
            ACCUM_ADD_AFTER(ar, rptr);
560
106k
            rsplit->ymax = ye;
561
106k
            rsplit = rsplit->prev;
562
106k
        }
563
84.6k
    }
564
    /* Now ye = ymax.  If necessary, split off the part of the */
565
    /* existing band that is below the new band. */
566
7.59M
    if (y > ymin) {
567
7.65k
        gx_clip_rect *rbot = rptr, *rsplit;
568
569
8.08k
        while (rbot->prev->ymin == ymin)
570
436
            rbot = rbot->prev;
571
8.08k
        for (rsplit = rbot;;) {
572
8.08k
            ACCUM_ALLOC("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
573
8.08k
            if (!ar) {
574
0
                if (nr != &adev->list.single) ACCUM_FREE("free", nr);
575
0
                return_error(gs_error_VMerror);
576
0
            }
577
8.08k
            ACCUM_ADD_BEFORE(ar, rbot);
578
8.08k
            rsplit->ymin = y;
579
8.08k
            if (rsplit == rptr)
580
7.65k
                break;
581
436
            rsplit = rsplit->next;
582
436
        }
583
7.65k
        ymin = y;
584
7.65k
    }
585
    /* Now y <= ymin as well.  (y < ymin is possible.) */
586
7.59M
    nr->ymin = ymin;
587
    /* Search for the X insertion point. */
588
27.9M
    for (; rptr->ymin == ymin; rptr = rptr->prev) {
589
27.5M
        if (xe < rptr->xmin)
590
19.4M
            continue;    /* still too far to right */
591
8.14M
        if (x > rptr->xmax)
592
2.18M
            break;    /* disjoint */
593
        /* The new rectangle overlaps an existing one.  Merge them. */
594
5.96M
        if (xe > rptr->xmax) {
595
959k
            rptr->xmax = nr->xmax;  /* might be > xe if */
596
            /* we already did a merge */
597
959k
            clip_rect_print('Q', "widen", rptr);
598
959k
        }
599
5.96M
        ACCUM_FREE("free", nr);
600
5.96M
        if (x >= rptr->xmin) {
601
5.04M
            adev->list.insert = rptr;
602
5.04M
            goto out;
603
5.04M
        }
604
        /* Might overlap other rectangles to the left. */
605
924k
        rptr->xmin = x;
606
924k
        nr = rptr;
607
924k
        ACCUM_REMOVE(rptr);
608
924k
        clip_rect_print('Q', "merge", nr);
609
924k
    }
610
2.55M
    ACCUM_ADD_AFTER(nr, rptr);
611
2.55M
    adev->list.insert = nr;
612
7.59M
out:
613
    /* Check whether there are only 0 or 1 rectangles left. */
614
7.59M
    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
1.30k
        gs_memory_t *mem = adev->list_memory;
618
1.30k
        gx_clip_rect *single = adev->list.head->next;
619
620
1.30k
        if (single != adev->list.tail) {
621
1.30k
            adev->list.single = *single;
622
1.30k
            gs_free_object(mem, single, "accum_free_rect(single)");
623
1.30k
            adev->list.single.next = adev->list.single.prev = 0;
624
1.30k
        }
625
1.30k
        gs_free_object(mem, adev->list.tail, "accum_free_rect(tail)");
626
1.30k
        gs_free_object(mem, adev->list.head, "accum_free_rect(head)");
627
1.30k
        adev->list.head = 0;
628
1.30k
        adev->list.tail = 0;
629
1.30k
        adev->list.insert = 0;
630
1.30k
    }
631
    /* Check whether there is still more of the new band to process. */
632
7.59M
    if (y < ymin) {
633
        /* Continue with the bottom part of the new rectangle. */
634
805k
        clip_rect_print('Q', " ymin", nr);
635
805k
        ye = ymin;
636
805k
        goto top;
637
805k
    }
638
6.78M
    return 0;
639
7.59M
}