/src/ghostpdl/base/gxpath.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Internal path management routines for Ghostscript library */ |
18 | | #include "gx.h" |
19 | | #include "gserrors.h" |
20 | | #include "gsstruct.h" |
21 | | #include "gxfixed.h" |
22 | | #include "gzpath.h" |
23 | | |
24 | | /* These routines all assume that all points are */ |
25 | | /* already in device coordinates, and in fixed representation. */ |
26 | | /* As usual, they return either 0 or a (negative) error code. */ |
27 | | |
28 | | /* Forward references */ |
29 | | static int path_alloc_copy(gx_path *); |
30 | | static int gx_path_new_subpath(gx_path *); |
31 | | |
32 | | #ifdef DEBUG |
33 | | static void gx_print_segment(const gs_memory_t *,const segment *); |
34 | | |
35 | | # define trace_segment(msg, mem, pseg)\ |
36 | | if ( gs_debug_c('P') ) dmlprintf(mem, msg), gx_print_segment(mem,pseg); |
37 | | #else |
38 | 4.39G | # define trace_segment(msg, mem, pseg) DO_NOTHING |
39 | | #endif |
40 | | |
41 | | /* Check a point against a preset bounding box. */ |
42 | | #define outside_bbox(ppath, px, py)\ |
43 | 0 | (px < ppath->bbox.p.x || px > ppath->bbox.q.x ||\ |
44 | 0 | py < ppath->bbox.p.y || py > ppath->bbox.q.y) |
45 | | #define check_in_bbox(ppath, px, py)\ |
46 | 0 | if ( outside_bbox(ppath, px, py) )\ |
47 | 0 | return_error(gs_error_rangecheck) |
48 | | |
49 | | /* Structure descriptors for paths and path segment types. */ |
50 | | public_st_path(); |
51 | | private_st_path_segments(); |
52 | | private_st_segment(); |
53 | | private_st_line(); |
54 | | private_st_dash(); |
55 | | private_st_line_close(); |
56 | | private_st_curve(); |
57 | | private_st_subpath(); |
58 | | |
59 | | /* ------ Initialize/free paths ------ */ |
60 | | |
61 | | static rc_free_proc(rc_free_path_segments); |
62 | | static rc_free_proc(rc_free_path_segments_local); |
63 | | |
64 | | /* |
65 | | * Define the default virtual path interface implementation. |
66 | | */ |
67 | | static int |
68 | | gz_path_add_point(gx_path *, fixed, fixed), |
69 | | gz_path_add_line_notes(gx_path *, fixed, fixed, segment_notes), |
70 | | gz_path_add_gap_notes(gx_path *, fixed, fixed, segment_notes), |
71 | | gz_path_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes), |
72 | | gz_path_close_subpath_notes(gx_path *, segment_notes); |
73 | | static byte gz_path_state_flags(gx_path *ppath, byte flags); |
74 | | |
75 | | static gx_path_procs default_path_procs = { |
76 | | gz_path_add_point, |
77 | | gz_path_add_line_notes, |
78 | | gz_path_add_gap_notes, |
79 | | gz_path_add_curve_notes, |
80 | | gz_path_close_subpath_notes, |
81 | | gz_path_state_flags |
82 | | }; |
83 | | |
84 | | /* |
85 | | * Define virtual path interface implementation for computing a path bbox. |
86 | | */ |
87 | | static int |
88 | | gz_path_bbox_add_point(gx_path *, fixed, fixed), |
89 | | gz_path_bbox_add_line_notes(gx_path *, fixed, fixed, segment_notes), |
90 | | gz_path_bbox_add_gap_notes(gx_path *, fixed, fixed, segment_notes), |
91 | | gz_path_bbox_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes), |
92 | | gz_path_bbox_close_subpath_notes(gx_path *, segment_notes); |
93 | | |
94 | | static gx_path_procs path_bbox_procs = { |
95 | | gz_path_bbox_add_point, |
96 | | gz_path_bbox_add_line_notes, |
97 | | gz_path_bbox_add_gap_notes, |
98 | | gz_path_bbox_add_curve_notes, |
99 | | gz_path_bbox_close_subpath_notes, |
100 | | gz_path_state_flags |
101 | | }; |
102 | | |
103 | | static void |
104 | | gx_path_init_contents(gx_path * ppath) |
105 | 340M | { |
106 | 340M | ppath->box_last = 0; |
107 | 340M | ppath->first_subpath = ppath->current_subpath = 0; |
108 | 340M | ppath->subpath_count = 0; |
109 | 340M | ppath->curve_count = 0; |
110 | 340M | path_update_newpath(ppath); |
111 | 340M | ppath->bbox_set = 0; |
112 | 340M | ppath->bbox_accurate = 0; |
113 | 340M | ppath->last_charpath_segment = 0; |
114 | 340M | ppath->bbox.p.x = max_fixed; |
115 | 340M | ppath->bbox.p.y = max_fixed; |
116 | 340M | ppath->bbox.q.x = min_fixed; |
117 | 340M | ppath->bbox.q.y = min_fixed; |
118 | 340M | } |
119 | | |
120 | | /* |
121 | | * Initialize a path contained in an already-heap-allocated object, |
122 | | * optionally allocating its segments. |
123 | | */ |
124 | | static int |
125 | | path_alloc_segments(gx_path_segments ** ppsegs, gs_memory_t * mem, |
126 | | client_name_t cname) |
127 | 44.8M | { |
128 | 44.8M | mem = gs_memory_stable(mem); |
129 | 44.8M | rc_alloc_struct_1(*ppsegs, gx_path_segments, &st_path_segments, |
130 | 44.8M | mem, return_error(gs_error_VMerror), cname); |
131 | 44.8M | (*ppsegs)->rc.free = rc_free_path_segments; |
132 | 44.8M | return 0; |
133 | 44.8M | } |
134 | | int |
135 | | gx_path_init_contained_shared(gx_path * ppath, const gx_path * shared, |
136 | | gs_memory_t * mem, client_name_t cname) |
137 | 4.64M | { |
138 | 4.64M | if (shared) { |
139 | 0 | if (shared->segments == &shared->local_segments) { |
140 | | #ifdef DEBUG |
141 | | lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n", |
142 | | (intptr_t)shared); |
143 | | #endif |
144 | 0 | return_error(gs_error_Fatal); |
145 | 0 | } |
146 | 0 | *ppath = *shared; |
147 | 0 | rc_increment(ppath->segments); |
148 | 4.64M | } else { |
149 | 4.64M | int code = path_alloc_segments(&ppath->segments, mem, cname); |
150 | | |
151 | 4.64M | if (code < 0) |
152 | 0 | return code; |
153 | 4.64M | gx_path_init_contents(ppath); |
154 | 4.64M | } |
155 | 4.64M | ppath->memory = mem; |
156 | 4.64M | ppath->allocation = path_allocated_contained; |
157 | 4.64M | ppath->procs = &default_path_procs; |
158 | 4.64M | return 0; |
159 | 4.64M | } |
160 | | |
161 | | /* |
162 | | * Allocate a path on the heap, and initialize it. If shared is NULL, |
163 | | * allocate a segments object; if shared is an existing path, share its |
164 | | * segments. |
165 | | */ |
166 | | gx_path * |
167 | | gx_path_alloc_shared(const gx_path * shared, gs_memory_t * mem, |
168 | | client_name_t cname) |
169 | 60.0M | { |
170 | 60.0M | gx_path *ppath = gs_alloc_struct(mem, gx_path, &st_path, cname); |
171 | | |
172 | 60.0M | if (ppath == 0) |
173 | 0 | return 0; |
174 | 60.0M | ppath->procs = &default_path_procs; |
175 | 60.0M | if (shared) { |
176 | 59.7M | if (shared->segments == &shared->local_segments) { |
177 | | #ifdef DEBUG |
178 | | lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n", |
179 | | (intptr_t)shared); |
180 | | #endif |
181 | 0 | gs_free_object(mem, ppath, cname); |
182 | 0 | return 0; |
183 | 0 | } |
184 | 59.7M | *ppath = *shared; |
185 | 59.7M | rc_increment(ppath->segments); |
186 | 59.7M | } else { |
187 | 328k | int code = path_alloc_segments(&ppath->segments, mem, cname); |
188 | | |
189 | 328k | if (code < 0) { |
190 | 0 | gs_free_object(mem, ppath, cname); |
191 | 0 | return 0; |
192 | 0 | } |
193 | 328k | gx_path_init_contents(ppath); |
194 | 328k | } |
195 | 60.0M | ppath->memory = mem; |
196 | 60.0M | ppath->allocation = path_allocated_on_heap; |
197 | 60.0M | return ppath; |
198 | 60.0M | } |
199 | | |
200 | | /* |
201 | | * Initialize a stack-allocated path. This doesn't allocate anything, |
202 | | * but may still share the segments. |
203 | | */ |
204 | | int |
205 | | gx_path_init_local_shared(gx_path * ppath, const gx_path * shared, |
206 | | gs_memory_t * mem) |
207 | 266M | { |
208 | 266M | if (shared) { |
209 | 0 | if (shared->segments == &shared->local_segments) { |
210 | | #ifdef DEBUG |
211 | | lprintf1("Attempt to share (local) segments of path "PRI_INTPTR"!\n", |
212 | | (intptr_t)shared); |
213 | | #endif |
214 | 0 | return_error(gs_error_Fatal); |
215 | 0 | } |
216 | 0 | *ppath = *shared; |
217 | 0 | rc_increment(ppath->segments); |
218 | 266M | } else { |
219 | 266M | rc_init_free(&ppath->local_segments, mem, 1, |
220 | 266M | rc_free_path_segments_local); |
221 | 266M | ppath->segments = &ppath->local_segments; |
222 | 266M | gx_path_init_contents(ppath); |
223 | 266M | } |
224 | 266M | ppath->memory = mem; |
225 | 266M | ppath->allocation = path_allocated_on_stack; |
226 | 266M | ppath->procs = &default_path_procs; |
227 | 266M | return 0; |
228 | 266M | } |
229 | | |
230 | | void gx_path_preinit_local_rectangle(gx_path *ppath, gs_memory_t *mem) |
231 | 653k | { |
232 | 653k | rc_init_free(&ppath->local_segments, mem, 1, NULL); |
233 | 653k | ppath->segments = &ppath->local_segments; |
234 | 653k | ppath->box_last = 0; |
235 | 653k | ppath->first_subpath = ppath->current_subpath = 0; |
236 | 653k | ppath->subpath_count = 0; |
237 | 653k | ppath->curve_count = 0; |
238 | 653k | path_update_newpath(ppath); |
239 | 653k | ppath->bbox_set = 1; |
240 | 653k | ppath->bbox_accurate = 1; |
241 | 653k | ppath->last_charpath_segment = 0; |
242 | 653k | ppath->memory = mem; |
243 | 653k | ppath->allocation = path_allocated_on_stack; |
244 | 653k | ppath->procs = &default_path_procs; |
245 | 653k | } |
246 | | |
247 | | void gx_path_init_local_rectangle(gx_path *ppath, gs_fixed_rect *rect) |
248 | 0 | { |
249 | 0 | ppath->bbox = *rect; |
250 | 0 | } |
251 | | |
252 | | /* |
253 | | * Initialize a stack-allocated pseudo-path for computing a bbox |
254 | | * for a dynamic path. |
255 | | */ |
256 | | void |
257 | | gx_path_init_bbox_accumulator(gx_path * ppath) |
258 | 25.4M | { |
259 | 25.4M | ppath->box_last = 0; |
260 | 25.4M | ppath->subpath_count = 0; |
261 | 25.4M | ppath->curve_count = 0; |
262 | 25.4M | ppath->local_segments.contents.subpath_first = 0; |
263 | 25.4M | ppath->local_segments.contents.subpath_current = 0; |
264 | 25.4M | ppath->segments = 0; |
265 | 25.4M | path_update_newpath(ppath); |
266 | 25.4M | ppath->bbox_set = 0; |
267 | 25.4M | ppath->bbox.p.x = ppath->bbox.p.y = ppath->bbox.q.x = ppath->bbox.q.y = 0; |
268 | 25.4M | ppath->bbox_accurate = 1; |
269 | 25.4M | ppath->memory = NULL; /* Won't allocate anything. */ |
270 | 25.4M | ppath->allocation = path_allocated_on_stack; |
271 | 25.4M | ppath->procs = &path_bbox_procs; |
272 | 25.4M | } |
273 | | |
274 | | /* Guarantee that a path's segments are not shared with any other path. */ |
275 | | static inline int path_unshare(gx_path *ppath) |
276 | 1.08G | { |
277 | 1.08G | int code = 0; |
278 | | |
279 | 1.08G | if (gx_path_is_shared(ppath)) |
280 | 1.89M | code = path_alloc_copy(ppath); |
281 | 1.08G | return code; |
282 | 1.08G | } |
283 | | |
284 | | /* |
285 | | * Ensure that a path owns its segments, by copying the segments if |
286 | | * they currently have multiple references. |
287 | | */ |
288 | | int |
289 | | gx_path_unshare(gx_path * ppath) |
290 | 18.6M | { |
291 | 18.6M | return path_unshare(ppath); |
292 | 18.6M | } |
293 | | |
294 | | /* |
295 | | * Free a path by releasing its segments if they have no more references. |
296 | | * This also frees the path object iff it was allocated by gx_path_alloc. |
297 | | */ |
298 | | void |
299 | | gx_path_free(gx_path * ppath, client_name_t cname) |
300 | 409M | { |
301 | 409M | rc_decrement(ppath->segments, cname); |
302 | | /* Clean up pointers for GC. */ |
303 | 409M | ppath->box_last = 0; |
304 | 409M | ppath->segments = 0; /* Nota bene */ |
305 | 409M | if (ppath->allocation == path_allocated_on_heap) |
306 | 60.0M | gs_free_object(ppath->memory, ppath, cname); |
307 | 409M | } |
308 | | |
309 | | /* |
310 | | * Assign one path to another, adjusting reference counts appropriately. |
311 | | * Note that this requires that segments of the two paths (but not the path |
312 | | * objects themselves) were allocated with the same allocator. Note also |
313 | | * that since it does the equivalent of a gx_path_new(ppto), it may allocate |
314 | | * a new segments object for ppto. |
315 | | */ |
316 | | int |
317 | | gx_path_assign_preserve(gx_path * ppto, gx_path * ppfrom) |
318 | 16.7M | { |
319 | 16.7M | gx_path_segments *fromsegs = ppfrom->segments; |
320 | 16.7M | gx_path_segments *tosegs = ppto->segments; |
321 | 16.7M | gs_memory_t *mem = ppto->memory; |
322 | 16.7M | gx_path_allocation_t allocation = ppto->allocation; |
323 | | |
324 | 16.7M | if (fromsegs == &ppfrom->local_segments) { |
325 | | /* We can't use ppfrom's segments object. */ |
326 | 12.0M | if (tosegs == &ppto->local_segments || gx_path_is_shared(ppto)) { |
327 | | /* We can't use ppto's segments either. Allocate a new one. */ |
328 | 4.53M | int code = path_alloc_segments(&tosegs, ppto->memory, |
329 | 4.53M | "gx_path_assign"); |
330 | | |
331 | 4.53M | if (code < 0) |
332 | 0 | return code; |
333 | 4.53M | rc_decrement(ppto->segments, "gx_path_assign"); |
334 | 7.55M | } else { |
335 | | /* Use ppto's segments object. */ |
336 | 7.55M | rc_free_path_segments_local(tosegs->rc.memory, tosegs, |
337 | 7.55M | "gx_path_assign"); |
338 | 7.55M | } |
339 | 12.0M | tosegs->contents = fromsegs->contents; |
340 | 12.0M | ppfrom->segments = tosegs; |
341 | 12.0M | rc_increment(tosegs); /* for reference from ppfrom */ |
342 | 12.0M | } else { |
343 | | /* We can use ppfrom's segments object. */ |
344 | 4.63M | rc_increment(fromsegs); |
345 | 4.63M | rc_decrement(tosegs, "gx_path_assign"); |
346 | 4.63M | } |
347 | 16.7M | *ppto = *ppfrom; |
348 | 16.7M | ppto->memory = mem; |
349 | 16.7M | ppto->allocation = allocation; |
350 | 16.7M | return 0; |
351 | 16.7M | } |
352 | | |
353 | | /* |
354 | | * Assign one path to another and free the first path at the same time. |
355 | | * (This may do less work than assign_preserve + free.) |
356 | | */ |
357 | | int |
358 | | gx_path_assign_free(gx_path * ppto, gx_path * ppfrom) |
359 | 5.38M | { |
360 | 5.38M | int code = 0; |
361 | | /* |
362 | | * Detect the special case where both paths have non-shared local |
363 | | * segments, since we can avoid allocating new segments in this case. |
364 | | */ |
365 | 5.38M | if (ppto->segments == &ppto->local_segments && |
366 | 5.38M | ppfrom->segments == &ppfrom->local_segments && |
367 | 5.38M | !gx_path_is_shared(ppto) |
368 | 5.38M | ) { |
369 | 166k | #define fromsegs (&ppfrom->local_segments) |
370 | 499k | #define tosegs (&ppto->local_segments) |
371 | 166k | gs_memory_t *mem = ppto->memory; |
372 | 166k | gx_path_allocation_t allocation = ppto->allocation; |
373 | | |
374 | 166k | rc_free_path_segments_local(tosegs->rc.memory, tosegs, |
375 | 166k | "gx_path_assign_free"); |
376 | | /* We record a bogus reference to fromsegs, which */ |
377 | | /* gx_path_free will undo. */ |
378 | 166k | *ppto = *ppfrom; |
379 | 166k | rc_increment(fromsegs); |
380 | 166k | ppto->segments = tosegs; |
381 | 166k | ppto->memory = mem; |
382 | 166k | ppto->allocation = allocation; |
383 | 166k | #undef fromsegs |
384 | 166k | #undef tosegs |
385 | 5.21M | } else { |
386 | | /* In all other cases, just do assign + free. */ |
387 | 5.21M | code = gx_path_assign_preserve(ppto, ppfrom); |
388 | 5.21M | } |
389 | 5.38M | gx_path_free(ppfrom, "gx_path_assign_free"); |
390 | 5.38M | return code; |
391 | 5.38M | } |
392 | | |
393 | | /* |
394 | | * Free the segments of a path when their reference count goes to zero. |
395 | | * We do this in reverse order so as to maximize LIFO allocator behavior. |
396 | | * We don't have to worry about cleaning up pointers, because we're about |
397 | | * to free the segments object. |
398 | | */ |
399 | | static void |
400 | | rc_free_path_segments_local(gs_memory_t * mem, void *vpsegs, |
401 | | client_name_t cname) |
402 | 331M | { |
403 | 331M | gx_path_segments *psegs = (gx_path_segments *) vpsegs; |
404 | 331M | segment *pseg; |
405 | | |
406 | 331M | mem = gs_memory_stable(mem); |
407 | 331M | if (psegs->contents.subpath_first == 0) |
408 | 179M | return; /* empty path */ |
409 | 152M | pseg = (segment *) psegs->contents.subpath_current->last; |
410 | 2.34G | while (pseg) { |
411 | 2.18G | segment *prev = pseg->prev; |
412 | | |
413 | 2.18G | trace_segment("[P]release", mem, pseg); |
414 | 2.18G | gs_free_object(mem, pseg, cname); |
415 | 2.18G | pseg = prev; |
416 | 2.18G | } |
417 | 152M | } |
418 | | static void |
419 | | rc_free_path_segments(gs_memory_t * mem, void *vpsegs, client_name_t cname) |
420 | 44.8M | { |
421 | 44.8M | rc_free_path_segments_local(mem, vpsegs, cname); |
422 | 44.8M | gs_free_object(mem, vpsegs, cname); |
423 | 44.8M | } |
424 | | |
425 | | /* ------ Incremental path building ------ */ |
426 | | |
427 | | /* Macro for opening the current subpath. */ |
428 | | /* ppath points to the path; sets psub to ppath->current_subpath. */ |
429 | | static inline int path_open(gx_path *ppath) |
430 | 737M | { |
431 | 737M | int code = 0; |
432 | 737M | if ( !path_is_drawing(ppath) ) { |
433 | 177M | if ( path_position_valid(ppath) ) { |
434 | 177M | code = gx_path_new_subpath(ppath); |
435 | 177M | } |
436 | 207k | else { |
437 | 207k | code = gs_note_error(gs_error_nocurrentpoint); |
438 | 207k | } |
439 | 177M | } |
440 | 737M | return code; |
441 | 737M | } |
442 | | |
443 | | /* Macros for allocating path segments. */ |
444 | | /* Note that they assume that ppath points to the path. */ |
445 | | /* We have to split the macro into two because of limitations */ |
446 | | /* on the size of a single statement (sigh). */ |
447 | | static inline int path_alloc_segment(gx_path *ppath, segment **ppseg, subpath **ppsub, gs_memory_type_ptr_t pstype, segment_type seg_type, ushort notes, client_name_t cname) |
448 | 900M | { |
449 | 900M | int code; |
450 | | |
451 | 900M | code = path_unshare(ppath); |
452 | 900M | if (code < 0) return code; |
453 | 900M | if (ppsub) |
454 | 900M | *ppsub = ppath->current_subpath; |
455 | | |
456 | 900M | if( !(*ppseg = gs_alloc_struct(gs_memory_stable(ppath->memory), segment, pstype, cname)) ) |
457 | 0 | return_error(gs_error_VMerror); |
458 | 900M | (*ppseg)->type = seg_type; |
459 | 900M | (*ppseg)->notes = notes; |
460 | 900M | (*ppseg)->next = 0; |
461 | | |
462 | 900M | return 0; |
463 | 900M | } |
464 | | |
465 | | static inline void path_alloc_link(segment *pseg, subpath *psub) |
466 | 722M | { |
467 | 722M | segment *prev = psub->last; |
468 | 722M | prev->next = (segment *)pseg; |
469 | 722M | pseg->prev = prev; |
470 | 722M | psub->last = (segment *)pseg; |
471 | 722M | } |
472 | | |
473 | | /* Make a new path (newpath). */ |
474 | | int |
475 | | gx_path_new(gx_path * ppath) |
476 | 67.6M | { |
477 | 67.6M | gx_path_segments *psegs = ppath->segments; |
478 | | |
479 | 67.6M | if (gx_path_is_shared(ppath)) { |
480 | 35.3M | int code = path_alloc_segments(&ppath->segments, ppath->memory, |
481 | 35.3M | "gx_path_new"); |
482 | | |
483 | 35.3M | if (code < 0) { |
484 | | /* Leave the path in a valid state, despite the error */ |
485 | 0 | ppath->segments = psegs; |
486 | 0 | return code; |
487 | 0 | } |
488 | 35.3M | rc_decrement(psegs, "gx_path_new"); |
489 | 35.3M | } else { |
490 | 32.3M | rc_free_path_segments_local(psegs->rc.memory, psegs, "gx_path_new"); |
491 | 32.3M | } |
492 | 67.6M | gx_path_init_contents(ppath); |
493 | 67.6M | return 0; |
494 | 67.6M | } |
495 | | |
496 | | /* Open a new subpath. */ |
497 | | /* The client must invoke path_update_xxx. */ |
498 | | static int |
499 | | gx_path_new_subpath(gx_path * ppath) |
500 | 177M | { |
501 | 177M | subpath *psub; |
502 | 177M | subpath *spp; |
503 | 177M | int code; |
504 | | |
505 | 177M | code = path_alloc_segment(ppath, (segment **)&spp, &psub, &st_subpath, s_start, sn_none, "gx_path_new_subpath"); |
506 | 177M | if (code < 0) |
507 | 0 | return code; |
508 | | |
509 | 177M | spp->last = (segment *) spp; |
510 | 177M | spp->curve_count = 0; |
511 | 177M | spp->is_closed = 0; |
512 | 177M | spp->pt = ppath->position; |
513 | 177M | if (!psub) { /* first subpath */ |
514 | 157M | ppath->first_subpath = spp; |
515 | 157M | spp->prev = 0; |
516 | 157M | } else { |
517 | 20.6M | segment *prev = psub->last; |
518 | | |
519 | 20.6M | prev->next = (segment *) spp; |
520 | 20.6M | spp->prev = prev; |
521 | 20.6M | } |
522 | 177M | ppath->current_subpath = spp; |
523 | 177M | ppath->subpath_count++; |
524 | 177M | trace_segment("[P]", ppath->memory, (const segment *)spp); |
525 | 177M | return 0; |
526 | 177M | } |
527 | | |
528 | | static inline void |
529 | | gz_path_bbox_add(gx_path * ppath, fixed x, fixed y) |
530 | 48.6M | { |
531 | 48.6M | if (!ppath->bbox_set) { |
532 | 1.50M | ppath->bbox.p.x = ppath->bbox.q.x = x; |
533 | 1.50M | ppath->bbox.p.y = ppath->bbox.q.y = y; |
534 | 1.50M | ppath->bbox_set = 1; |
535 | 47.1M | } else { |
536 | 47.1M | if (ppath->bbox.p.x > x) |
537 | 6.94M | ppath->bbox.p.x = x; |
538 | 47.1M | if (ppath->bbox.p.y > y) |
539 | 7.09M | ppath->bbox.p.y = y; |
540 | 47.1M | if (ppath->bbox.q.x < x) |
541 | 5.16M | ppath->bbox.q.x = x; |
542 | 47.1M | if (ppath->bbox.q.y < y) |
543 | 5.49M | ppath->bbox.q.y = y; |
544 | 47.1M | } |
545 | 48.6M | } |
546 | | |
547 | | static inline void |
548 | | gz_path_bbox_move(gx_path * ppath, fixed x, fixed y) |
549 | 28.0M | { |
550 | | /* a trick : we store 'fixed' into 'double'. */ |
551 | 28.0M | ppath->position.x = x; |
552 | 28.0M | ppath->position.y = y; |
553 | 28.0M | ppath->state_flags |= psf_position_valid; |
554 | 28.0M | } |
555 | | |
556 | | /* Add a point to the current path (moveto). */ |
557 | | int |
558 | | gx_path_add_point(gx_path * ppath, fixed x, fixed y) |
559 | 292M | { |
560 | 292M | return ppath->procs->add_point(ppath, x, y); |
561 | 292M | } |
562 | | static int |
563 | | gz_path_add_point(gx_path * ppath, fixed x, fixed y) |
564 | 286M | { |
565 | 286M | if (ppath->bbox_set) |
566 | 0 | check_in_bbox(ppath, x, y); |
567 | 286M | ppath->position.x = x; |
568 | 286M | ppath->position.y = y; |
569 | 286M | path_update_moveto(ppath); |
570 | 286M | return 0; |
571 | 286M | } |
572 | | static int |
573 | | gz_path_bbox_add_point(gx_path * ppath, fixed x, fixed y) |
574 | 5.93M | { |
575 | 5.93M | gz_path_bbox_move(ppath, x, y); |
576 | 5.93M | return 0; |
577 | 5.93M | } |
578 | | |
579 | | /* Add a relative point to the current path (rmoveto). */ |
580 | | int |
581 | | gx_path_add_relative_point(gx_path * ppath, fixed dx, fixed dy) |
582 | 0 | { |
583 | 0 | if (!path_position_in_range(ppath)) |
584 | 0 | return_error((path_position_valid(ppath) ? gs_error_limitcheck : |
585 | 0 | gs_error_nocurrentpoint)); |
586 | 0 | { |
587 | 0 | fixed nx = ppath->position.x + dx, ny = ppath->position.y + dy; |
588 | | |
589 | | /* Check for overflow in addition. */ |
590 | 0 | if (((nx ^ dx) < 0 && (ppath->position.x ^ dx) >= 0) || |
591 | 0 | ((ny ^ dy) < 0 && (ppath->position.y ^ dy) >= 0) |
592 | 0 | ) |
593 | 0 | return_error(gs_error_limitcheck); |
594 | 0 | if (ppath->bbox_set) |
595 | 0 | check_in_bbox(ppath, nx, ny); |
596 | 0 | ppath->position.x = nx; |
597 | 0 | ppath->position.y = ny; |
598 | 0 | } |
599 | 0 | path_update_moveto(ppath); |
600 | 0 | return 0; |
601 | 0 | } |
602 | | |
603 | | /* Set the segment point and the current point in the path. */ |
604 | | /* Assumes ppath points to the path. */ |
605 | | #define path_set_point(pseg, fx, fy)\ |
606 | 722M | (pseg)->pt.x = ppath->position.x = (fx),\ |
607 | 722M | (pseg)->pt.y = ppath->position.y = (fy) |
608 | | |
609 | | /* Add a line to the current path (lineto). */ |
610 | | int |
611 | | gx_path_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
612 | 419M | { |
613 | 419M | return ppath->procs->add_line(ppath, x, y, notes); |
614 | 419M | } |
615 | | static int |
616 | | gz_path_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
617 | 410M | { |
618 | 410M | subpath *psub; |
619 | 410M | line_segment *lp; |
620 | 410M | int code; |
621 | | |
622 | 410M | if (ppath->bbox_set) |
623 | 0 | check_in_bbox(ppath, x, y); |
624 | 410M | code = path_open(ppath); |
625 | 410M | if (code < 0) return code; |
626 | 410M | code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line, s_line, notes, "gx_path_add_line"); |
627 | 410M | if (code < 0) return code; |
628 | 410M | path_alloc_link((segment *)lp, psub); |
629 | 410M | path_set_point(lp, x, y); |
630 | 410M | path_update_draw(ppath); |
631 | 410M | trace_segment("[P]", ppath->memory, (segment *) lp); |
632 | 410M | return 0; |
633 | 410M | } |
634 | | static int |
635 | | gz_path_bbox_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
636 | 8.81M | { |
637 | 8.81M | gz_path_bbox_add(ppath, x, y); |
638 | 8.81M | gz_path_bbox_move(ppath, x, y); |
639 | 8.81M | return 0; |
640 | 8.81M | } |
641 | | /* Add a gap to the current path (lineto). */ |
642 | | int |
643 | | gx_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
644 | 0 | { |
645 | 0 | return ppath->procs->add_gap(ppath, x, y, notes); |
646 | 0 | } |
647 | | static int |
648 | | gz_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
649 | 0 | { |
650 | 0 | subpath *psub; |
651 | 0 | line_segment *lp; |
652 | 0 | int code; |
653 | |
|
654 | 0 | if (ppath->bbox_set) |
655 | 0 | check_in_bbox(ppath, x, y); |
656 | 0 | code = path_open(ppath); |
657 | 0 | if (code < 0) return code; |
658 | 0 | code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line, s_gap, notes, "gx_path_add_gap"); |
659 | 0 | if (code < 0) return code; |
660 | 0 | path_alloc_link((segment *)lp, psub); |
661 | 0 | path_set_point(lp, x, y); |
662 | 0 | path_update_draw(ppath); |
663 | 0 | trace_segment("[P]", ppath->memory, (segment *) lp); |
664 | 0 | return 0; |
665 | 0 | } |
666 | | static int |
667 | | gz_path_bbox_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) |
668 | 0 | { |
669 | 0 | gz_path_bbox_add(ppath, x, y); |
670 | 0 | gz_path_bbox_move(ppath, x, y); |
671 | 0 | return 0; |
672 | 0 | } |
673 | | |
674 | | /* Add multiple lines to the current path. */ |
675 | | /* Note that all lines have the same notes. */ |
676 | | int |
677 | | gx_path_add_lines_notes(gx_path *ppath, const gs_fixed_point *ppts, int count, |
678 | | segment_notes notes) |
679 | 165M | { |
680 | 165M | subpath *psub; |
681 | 165M | segment *prev; |
682 | 165M | line_segment *lp = 0; |
683 | 165M | int i; |
684 | 165M | int code = 0; |
685 | | |
686 | 165M | if (count <= 0) |
687 | 4.26k | return 0; |
688 | 165M | code = path_unshare(ppath); |
689 | 165M | if (code < 0) |
690 | 0 | return code; |
691 | | |
692 | 165M | code = path_open(ppath); |
693 | 165M | if (code < 0) return code; |
694 | 165M | psub = ppath->current_subpath; |
695 | 165M | prev = psub->last; |
696 | | /* |
697 | | * We could do better than the following, but this is a start. |
698 | | * Note that we don't make any attempt to undo partial additions |
699 | | * if we fail partway through; this is equivalent to what would |
700 | | * happen with multiple calls on gx_path_add_line. |
701 | | */ |
702 | 1.47G | for (i = 0; i < count; i++) { |
703 | 1.30G | fixed x = ppts[i].x; |
704 | 1.30G | fixed y = ppts[i].y; |
705 | 1.30G | line_segment *next; |
706 | | |
707 | 1.30G | if (ppath->bbox_set && outside_bbox(ppath, x, y)) { |
708 | 0 | code = gs_note_error(gs_error_rangecheck); |
709 | 0 | break; |
710 | 0 | } |
711 | 1.30G | if (!(next = gs_alloc_struct(gs_memory_stable(ppath->memory), |
712 | 1.30G | line_segment, &st_line, |
713 | 1.30G | "gx_path_add_lines")) |
714 | 1.30G | ) { |
715 | 0 | code = gs_note_error(gs_error_VMerror); |
716 | 0 | break; |
717 | 0 | } |
718 | 1.30G | lp = next; |
719 | 1.30G | lp->type = s_line; |
720 | 1.30G | lp->notes = notes; |
721 | 1.30G | prev->next = (segment *) lp; |
722 | 1.30G | lp->prev = prev; |
723 | 1.30G | lp->pt.x = x; |
724 | 1.30G | lp->pt.y = y; |
725 | 1.30G | prev = (segment *) lp; |
726 | 1.30G | trace_segment("[P]", ppath->memory, (segment *) lp); |
727 | 1.30G | } |
728 | 165M | if (lp != 0) |
729 | 165M | ppath->position.x = lp->pt.x, |
730 | 165M | ppath->position.y = lp->pt.y, |
731 | 165M | psub->last = (segment *) lp, |
732 | 165M | lp->next = 0, |
733 | 165M | path_update_draw(ppath); |
734 | 165M | return code; |
735 | 165M | } |
736 | | |
737 | | /* Add a dash to the current path (lineto with a small length). */ |
738 | | /* Only for internal use of the stroking algorithm. */ |
739 | | int |
740 | | gx_path_add_dash_notes(gx_path * ppath, fixed x, fixed y, fixed dx, fixed dy, segment_notes notes) |
741 | 771k | { |
742 | 771k | subpath *psub; |
743 | 771k | dash_segment *lp; |
744 | 771k | int code; |
745 | | |
746 | 771k | if (ppath->bbox_set) |
747 | 0 | check_in_bbox(ppath, x, y); |
748 | 771k | code = path_open(ppath); |
749 | 771k | if (code < 0) return code; |
750 | 771k | code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_dash, s_dash, notes, "gx_dash_add_dash"); |
751 | 771k | if (code < 0) return code; |
752 | 771k | path_alloc_link((segment *)lp, psub); |
753 | 771k | path_set_point(lp, x, y); |
754 | 771k | lp->tangent.x = dx; |
755 | 771k | lp->tangent.y = dy; |
756 | 771k | path_update_draw(ppath); |
757 | 771k | trace_segment("[P]", ppath->memory, (segment *) lp); |
758 | 771k | return 0; |
759 | 771k | } |
760 | | |
761 | | /* Add a rectangle to the current path. */ |
762 | | /* This is a special case of adding a closed polygon. */ |
763 | | int |
764 | | gx_path_add_rectangle(gx_path * ppath, fixed x0, fixed y0, fixed x1, fixed y1) |
765 | 281k | { |
766 | 281k | gs_fixed_point pts[3]; |
767 | 281k | int code; |
768 | | |
769 | 281k | pts[0].x = x0; |
770 | 281k | pts[1].x = pts[2].x = x1; |
771 | 281k | pts[2].y = y0; |
772 | 281k | pts[0].y = pts[1].y = y1; |
773 | 281k | if ((code = gx_path_add_point(ppath, x0, y0)) < 0 || |
774 | 281k | (code = gx_path_add_lines(ppath, pts, 3)) < 0 || |
775 | 281k | (code = gx_path_close_subpath(ppath)) < 0 |
776 | 281k | ) |
777 | 0 | return code; |
778 | 281k | return 0; |
779 | 281k | } |
780 | | |
781 | | /* Add a curve to the current path (curveto). */ |
782 | | int |
783 | | gx_path_add_curve_notes(gx_path * ppath, |
784 | | fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3, |
785 | | segment_notes notes) |
786 | 174M | { |
787 | 174M | return ppath->procs->add_curve(ppath, x1, y1, x2, y2, x3, y3, notes); |
788 | 174M | } |
789 | | static int |
790 | | gz_path_add_curve_notes(gx_path * ppath, |
791 | | fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3, |
792 | | segment_notes notes) |
793 | 161M | { |
794 | 161M | subpath *psub; |
795 | 161M | curve_segment *lp; |
796 | 161M | int code; |
797 | | |
798 | 161M | if (ppath->bbox_set) { |
799 | 0 | check_in_bbox(ppath, x1, y1); |
800 | 0 | check_in_bbox(ppath, x2, y2); |
801 | 0 | check_in_bbox(ppath, x3, y3); |
802 | 0 | } |
803 | 161M | code = path_open(ppath); |
804 | 161M | if (code < 0) return code; |
805 | 161M | code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_curve, s_curve, notes, "gx_path_add_curve"); |
806 | 161M | if (code < 0) return code; |
807 | 161M | path_alloc_link((segment *)lp, psub); |
808 | 161M | lp->p1.x = x1; |
809 | 161M | lp->p1.y = y1; |
810 | 161M | lp->p2.x = x2; |
811 | 161M | lp->p2.y = y2; |
812 | 161M | path_set_point(lp, x3, y3); |
813 | 161M | psub->curve_count++; |
814 | 161M | ppath->curve_count++; |
815 | 161M | path_update_draw(ppath); |
816 | 161M | trace_segment("[P]", ppath->memory, (segment *) lp); |
817 | 161M | return 0; |
818 | 161M | } |
819 | | static int |
820 | | gz_path_bbox_add_curve_notes(gx_path * ppath, |
821 | | fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3, |
822 | | segment_notes notes) |
823 | 13.2M | { |
824 | 13.2M | gz_path_bbox_add(ppath, x1, y1); |
825 | 13.2M | gz_path_bbox_add(ppath, x2, y2); |
826 | 13.2M | gz_path_bbox_add(ppath, x3, y3); |
827 | 13.2M | gz_path_bbox_move(ppath, x3, y3); |
828 | 13.2M | return 0; |
829 | 13.2M | } |
830 | | |
831 | | /* |
832 | | * Add an approximation of an arc to the current path. |
833 | | * The current point of the path is the initial point of the arc; |
834 | | * parameters are the final point of the arc |
835 | | * and the point at which the extended tangents meet. |
836 | | * We require that the arc be less than a semicircle. |
837 | | * The arc may go either clockwise or counterclockwise. |
838 | | * The approximation is a very simple one: a single curve |
839 | | * whose other two control points are a fraction F of the way |
840 | | * to the intersection of the tangents, where |
841 | | * F = (4/3)(1 / (1 + sqrt(1+(d/r)^2))) |
842 | | * where r is the radius and d is the distance from either tangent |
843 | | * point to the intersection of the tangents. This produces |
844 | | * a curve whose center point, as well as its ends, lies on |
845 | | * the desired arc. |
846 | | * |
847 | | * Because F has to be computed in user space, we let the client |
848 | | * compute it and pass it in as an argument. |
849 | | */ |
850 | | int |
851 | | gx_path_add_partial_arc_notes(gx_path * ppath, |
852 | | fixed x3, fixed y3, fixed xt, fixed yt, double fraction, segment_notes notes) |
853 | 20.1M | { |
854 | 20.1M | fixed x0 = ppath->position.x, y0 = ppath->position.y; |
855 | | |
856 | 20.1M | return gx_path_add_curve_notes(ppath, |
857 | 20.1M | x0 + (fixed) ((xt - x0) * fraction), |
858 | 20.1M | y0 + (fixed) ((yt - y0) * fraction), |
859 | 20.1M | x3 + (fixed) ((xt - x3) * fraction), |
860 | 20.1M | y3 + (fixed) ((yt - y3) * fraction), |
861 | 20.1M | x3, y3, notes | sn_from_arc); |
862 | 20.1M | } |
863 | | |
864 | | /* Append a path to another path, and reset the first path. */ |
865 | | /* Currently this is only used to append a path to its parent */ |
866 | | /* (the path in the previous graphics context). */ |
867 | | int |
868 | | gx_path_add_path(gx_path * ppath, gx_path * ppfrom) |
869 | 978k | { |
870 | 978k | int code = path_unshare(ppfrom); |
871 | 978k | if (code < 0) |
872 | 0 | return code; |
873 | 978k | code = path_unshare(ppath); |
874 | 978k | if (code < 0) |
875 | 0 | return code; |
876 | 978k | if (ppfrom->first_subpath) { /* i.e. ppfrom not empty */ |
877 | 718k | if (ppath->first_subpath) { /* i.e. ppath not empty */ |
878 | 706k | subpath *psub = ppath->current_subpath; |
879 | 706k | segment *pseg = psub->last; |
880 | 706k | subpath *pfsub = ppfrom->first_subpath; |
881 | | |
882 | 706k | pseg->next = (segment *) pfsub; |
883 | 706k | pfsub->prev = pseg; |
884 | 706k | } else |
885 | 11.5k | ppath->first_subpath = ppfrom->first_subpath; |
886 | 718k | ppath->current_subpath = ppfrom->current_subpath; |
887 | 718k | ppath->subpath_count += ppfrom->subpath_count; |
888 | 718k | ppath->curve_count += ppfrom->curve_count; |
889 | 718k | } |
890 | | /* Transfer the remaining state. */ |
891 | 978k | ppath->position = ppfrom->position; |
892 | 978k | ppath->state_flags = ppfrom->state_flags; |
893 | | /* Reset the source path. */ |
894 | 978k | gx_path_init_contents(ppfrom); |
895 | 978k | return 0; |
896 | 978k | } |
897 | | |
898 | | /* Add a path or its bounding box to the enclosing path, */ |
899 | | /* and reset the first path. Only used for implementing charpath and its */ |
900 | | /* relatives. */ |
901 | | int |
902 | | gx_path_add_char_path(gx_path * to_path, gx_path * from_path, |
903 | | gs_char_path_mode mode) |
904 | 978k | { |
905 | 978k | int code; |
906 | 978k | gs_fixed_rect bbox; |
907 | | |
908 | 978k | switch (mode) { |
909 | 0 | default: /* shouldn't happen! */ |
910 | 0 | gx_path_new(from_path); |
911 | 0 | return 0; |
912 | 0 | case cpm_charwidth: { |
913 | 0 | gs_fixed_point cpt; |
914 | |
|
915 | 0 | code = gx_path_current_point(from_path, &cpt); |
916 | 0 | if (code < 0) |
917 | 0 | break; |
918 | 0 | return gx_path_add_point(to_path, cpt.x, cpt.y); |
919 | 0 | } |
920 | 932k | case cpm_true_charpath: |
921 | 978k | case cpm_false_charpath: |
922 | 978k | return gx_path_add_path(to_path, from_path); |
923 | 0 | case cpm_true_charboxpath: |
924 | 0 | gx_path_bbox(from_path, &bbox); |
925 | 0 | code = gx_path_add_rectangle(to_path, bbox.p.x, bbox.p.y, |
926 | 0 | bbox.q.x, bbox.q.y); |
927 | 0 | break; |
928 | 0 | case cpm_false_charboxpath: |
929 | 0 | gx_path_bbox(from_path, &bbox); |
930 | 0 | code = gx_path_add_point(to_path, bbox.p.x, bbox.p.y); |
931 | 0 | if (code >= 0) |
932 | 0 | code = gx_path_add_line(to_path, bbox.q.x, bbox.q.y); |
933 | 0 | break; |
934 | 978k | } |
935 | 0 | if (code < 0) |
936 | 0 | return code; |
937 | 0 | gx_path_new(from_path); |
938 | 0 | return 0; |
939 | 0 | } |
940 | | |
941 | | /* Close the current subpath. */ |
942 | | int |
943 | | gx_path_close_subpath_notes(gx_path * ppath, segment_notes notes) |
944 | 153M | { |
945 | 153M | return ppath->procs->close_subpath(ppath, notes); |
946 | 153M | } |
947 | | static int |
948 | | gz_path_close_subpath_notes(gx_path * ppath, segment_notes notes) |
949 | 150M | { |
950 | 150M | subpath *psub; |
951 | 150M | line_close_segment *lp; |
952 | 150M | int code; |
953 | | |
954 | 150M | if (!path_subpath_open(ppath)) |
955 | 142k | return 0; |
956 | 150M | if (path_last_is_moveto(ppath)) { |
957 | | /* The last operation was a moveto: create a subpath. */ |
958 | 67.6k | code = gx_path_new_subpath(ppath); |
959 | 67.6k | if (code < 0) |
960 | 0 | return code; |
961 | 67.6k | } |
962 | 150M | code = path_alloc_segment(ppath, (segment **)&lp, &psub, &st_line_close, s_line_close, notes, "gx_path_close_subpath"); |
963 | 150M | if (code < 0) return code; |
964 | 150M | path_alloc_link((segment *)lp, psub); |
965 | 150M | path_set_point(lp, psub->pt.x, psub->pt.y); |
966 | 150M | lp->sub = psub; |
967 | 150M | psub->is_closed = 1; |
968 | 150M | path_update_closepath(ppath); |
969 | 150M | trace_segment("[P]", ppath->memory, (segment *) lp); |
970 | 150M | return 0; |
971 | 150M | } |
972 | | static int |
973 | | gz_path_bbox_close_subpath_notes(gx_path * ppath, segment_notes notes) |
974 | 2.10M | { |
975 | 2.10M | return 0; |
976 | 2.10M | } |
977 | | |
978 | | /* Access path state flags */ |
979 | | byte |
980 | | gz_path_state_flags(gx_path *ppath, byte flags) |
981 | 0 | { |
982 | 0 | byte flags_old = ppath->state_flags; |
983 | 0 | ppath->state_flags = flags; |
984 | 0 | return flags_old; |
985 | 0 | } |
986 | | byte |
987 | | gx_path_get_state_flags(gx_path *ppath) |
988 | 0 | { |
989 | 0 | byte flags = ppath->procs->state_flags(ppath, 0); |
990 | 0 | ppath->procs->state_flags(ppath, flags); |
991 | 0 | return flags; |
992 | 0 | } |
993 | | void |
994 | | gx_path_set_state_flags(gx_path *ppath, byte flags) |
995 | 0 | { |
996 | 0 | ppath->procs->state_flags(ppath, flags); |
997 | 0 | } |
998 | | bool |
999 | | gx_path_is_drawing(gx_path *ppath) |
1000 | 0 | { |
1001 | 0 | return path_is_drawing(ppath); |
1002 | 0 | } |
1003 | | |
1004 | | /* Remove the last line from the current subpath, and then close it. */ |
1005 | | /* The Type 1 font hinting routines use this if a path ends with */ |
1006 | | /* a line to the start followed by a closepath. */ |
1007 | | int |
1008 | | gx_path_pop_close_notes(gx_path * ppath, segment_notes notes) |
1009 | 0 | { |
1010 | 0 | subpath *psub = ppath->current_subpath; |
1011 | 0 | segment *pseg; |
1012 | 0 | segment *prev; |
1013 | |
|
1014 | 0 | if (psub == 0 || (pseg = psub->last) == 0 || |
1015 | 0 | pseg->type != s_line |
1016 | 0 | ) |
1017 | 0 | return_error(gs_error_unknownerror); |
1018 | 0 | prev = pseg->prev; |
1019 | 0 | prev->next = 0; |
1020 | 0 | psub->last = prev; |
1021 | 0 | gs_free_object(ppath->memory, pseg, "gx_path_pop_close_subpath"); |
1022 | 0 | return gx_path_close_subpath_notes(ppath, notes); |
1023 | 0 | } |
1024 | | |
1025 | | /* ------ Internal routines ------ */ |
1026 | | |
1027 | | /* |
1028 | | * Copy the current path, because it was shared. |
1029 | | */ |
1030 | | static int |
1031 | | path_alloc_copy(gx_path * ppath) |
1032 | 1.89M | { |
1033 | 1.89M | gx_path path_new; |
1034 | 1.89M | int code; |
1035 | | |
1036 | 1.89M | gx_path_init_local(&path_new, ppath->memory); |
1037 | 1.89M | code = gx_path_copy(ppath, &path_new); |
1038 | 1.89M | if (code < 0) { |
1039 | 0 | gx_path_free(&path_new, "path_alloc_copy error"); |
1040 | 0 | return code; |
1041 | 0 | } |
1042 | 1.89M | ppath->last_charpath_segment = 0; |
1043 | 1.89M | return gx_path_assign_free(ppath, &path_new); |
1044 | 1.89M | } |
1045 | | |
1046 | | /* ------ Debugging printout ------ */ |
1047 | | |
1048 | | #ifdef DEBUG |
1049 | | |
1050 | | /* Print out a path with a label */ |
1051 | | void |
1052 | | gx_dump_path(const gx_path * ppath, const char *tag) |
1053 | | { |
1054 | | dmlprintf2(ppath->memory, "[P]Path "PRI_INTPTR" %s:\n", (intptr_t)ppath, tag); |
1055 | | gx_path_print(ppath); |
1056 | | } |
1057 | | |
1058 | | /* Print a path */ |
1059 | | void |
1060 | | gx_path_print(const gx_path * ppath) |
1061 | | { |
1062 | | const segment *pseg = (const segment *)ppath->first_subpath; |
1063 | | |
1064 | | dmlprintf5(ppath->memory, |
1065 | | " %% state_flags=%d subpaths=%d, curves=%d, point=(%f,%f)\n", |
1066 | | ppath->state_flags, ppath->subpath_count, ppath->curve_count, |
1067 | | fixed2float(ppath->position.x), |
1068 | | fixed2float(ppath->position.y)); |
1069 | | dmlprintf5(ppath->memory," %% box=(%f,%f),(%f,%f) last="PRI_INTPTR"\n", |
1070 | | fixed2float(ppath->bbox.p.x), fixed2float(ppath->bbox.p.y), |
1071 | | fixed2float(ppath->bbox.q.x), fixed2float(ppath->bbox.q.y), |
1072 | | (intptr_t)ppath->box_last); |
1073 | | dmlprintf4(ppath->memory, |
1074 | | " %% segments="PRI_INTPTR" (refct=%ld, first="PRI_INTPTR", current="PRI_INTPTR")\n", |
1075 | | (intptr_t)ppath->segments, (long)ppath->segments->rc.ref_count, |
1076 | | (intptr_t)ppath->segments->contents.subpath_first, |
1077 | | (intptr_t)ppath->segments->contents.subpath_current); |
1078 | | while (pseg) { |
1079 | | dmlputs(ppath->memory,""); |
1080 | | gx_print_segment(ppath->memory, pseg); |
1081 | | pseg = pseg->next; |
1082 | | } |
1083 | | } |
1084 | | static void |
1085 | | gx_print_segment(const gs_memory_t *mem, const segment * pseg) |
1086 | | { |
1087 | | double px = fixed2float(pseg->pt.x); |
1088 | | double py = fixed2float(pseg->pt.y); |
1089 | | char out[80]; |
1090 | | |
1091 | | gs_snprintf(out, sizeof(out), PRI_INTPTR "<"PRI_INTPTR","PRI_INTPTR">:%u", |
1092 | | (intptr_t)pseg, (intptr_t)pseg->prev, (intptr_t)pseg->next, pseg->notes); |
1093 | | switch (pseg->type) { |
1094 | | case s_start:{ |
1095 | | const subpath *const psub = (const subpath *)pseg; |
1096 | | |
1097 | | dmprintf5(mem, " %1.4f %1.4f moveto\t%% %s #curves=%d last="PRI_INTPTR"\n", |
1098 | | px, py, out, psub->curve_count, (intptr_t)psub->last); |
1099 | | break; |
1100 | | } |
1101 | | case s_curve:{ |
1102 | | const curve_segment *const pcur = (const curve_segment *)pseg; |
1103 | | |
1104 | | dmprintf7(mem, " %1.4f %1.4f %1.4f %1.4f %1.4f %1.4f curveto\t%% %s\n", |
1105 | | fixed2float(pcur->p1.x), fixed2float(pcur->p1.y), |
1106 | | fixed2float(pcur->p2.x), fixed2float(pcur->p2.y), |
1107 | | px, py, out); |
1108 | | break; |
1109 | | } |
1110 | | case s_line: |
1111 | | dmprintf3(mem, " %1.4f %1.4f lineto\t%% %s\n", px, py, out); |
1112 | | break; |
1113 | | case s_gap: |
1114 | | dmprintf3(mem, " %1.4f %1.4f gapto\t%% %s\n", px, py, out); |
1115 | | break; |
1116 | | case s_dash:{ |
1117 | | const dash_segment *const pd = (const dash_segment *)pseg; |
1118 | | |
1119 | | dmprintf5(mem, " %1.4f %1.4f %1.4f %1.4f dash\t%% %s\n", |
1120 | | fixed2float(pd->pt.x), fixed2float(pd->pt.y), |
1121 | | fixed2float(pd->tangent.x),fixed2float(pd->tangent.y), |
1122 | | out); |
1123 | | break; |
1124 | | } |
1125 | | case s_line_close:{ |
1126 | | const line_close_segment *const plc = |
1127 | | (const line_close_segment *)pseg; |
1128 | | |
1129 | | dmprintf4(mem, " closepath\t%% %s %1.4f %1.4f "PRI_INTPTR"\n", |
1130 | | out, px, py, (intptr_t)(plc->sub)); |
1131 | | break; |
1132 | | } |
1133 | | default: |
1134 | | dmprintf4(mem, " %1.4f %1.4f <type "PRI_INTPTR">\t%% %s\n", |
1135 | | px, py, (intptr_t)pseg->type, out); |
1136 | | } |
1137 | | } |
1138 | | |
1139 | | #endif /* DEBUG */ |