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