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