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