/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 | ¶ms, &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 | } |