/src/ghostpdl/base/gxpath2.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Path tracing procedures for Ghostscript library */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gspath.h" /* for gs_path_enum_alloc prototype */ |
22 | | #include "gsstruct.h" |
23 | | #include "gxfixed.h" |
24 | | #include "gxarith.h" |
25 | | #include "gzpath.h" |
26 | | |
27 | | /* Define the enumeration structure. */ |
28 | | public_st_path_enum(); |
29 | | |
30 | | /* Check whether current path has valid point */ |
31 | | bool |
32 | | gx_path_position_valid(const gx_path *ppath) |
33 | 0 | { |
34 | 0 | return path_position_valid(ppath); |
35 | 0 | } |
36 | | |
37 | | /* Read the current point of a path. */ |
38 | | int |
39 | | gx_path_current_point(const gx_path * ppath, gs_fixed_point * ppt) |
40 | 262k | { |
41 | 262k | if (!path_position_valid(ppath)) |
42 | 254k | return_error(gs_error_nocurrentpoint); |
43 | | /* Copying the coordinates individually */ |
44 | | /* is much faster on a PC, and almost as fast on other machines.... */ |
45 | 7.86k | ppt->x = ppath->position.x, ppt->y = ppath->position.y; |
46 | 7.86k | return 0; |
47 | 262k | } |
48 | | |
49 | | /* Read the start point of the current subpath. */ |
50 | | int |
51 | | gx_path_subpath_start_point(const gx_path * ppath, gs_fixed_point * ppt) |
52 | 0 | { |
53 | 0 | const subpath *psub = ppath->current_subpath; |
54 | |
|
55 | 0 | if (!psub) |
56 | 0 | return_error(gs_error_nocurrentpoint); |
57 | 0 | *ppt = psub->pt; |
58 | 0 | return 0; |
59 | 0 | } |
60 | | |
61 | | /* Read the bounding box of a path. */ |
62 | | /* Note that if the last element of the path is a moveto, */ |
63 | | /* the bounding box does not include this point, */ |
64 | | /* unless this is the only element of the path. */ |
65 | | int |
66 | | gx_path_bbox(gx_path * ppath, gs_fixed_rect * pbox) |
67 | 1.46M | { |
68 | 1.46M | if (ppath == NULL) { |
69 | 0 | return_error(gs_error_unknownerror) ; |
70 | 0 | } |
71 | 1.46M | if (ppath->bbox_accurate) { |
72 | | /* The bounding box was set by setbbox. */ |
73 | 0 | *pbox = ppath->bbox; |
74 | 0 | return 0; |
75 | 0 | } |
76 | 1.46M | if (ppath->first_subpath == 0) { |
77 | | /* The path is empty, use the current point if any. */ |
78 | 259k | int code = gx_path_current_point(ppath, &pbox->p); |
79 | | |
80 | 259k | if (code < 0) { |
81 | | /* |
82 | | * Don't return garbage, in case the caller doesn't |
83 | | * check the return code. |
84 | | */ |
85 | 254k | pbox->p.x = pbox->p.y = 0; |
86 | 254k | } |
87 | 259k | pbox->q = pbox->p; |
88 | 259k | return code; |
89 | 259k | } |
90 | | /* The stored bounding box may not be up to date. */ |
91 | | /* Correct it now if necessary. */ |
92 | 1.20M | if (ppath->box_last == ppath->current_subpath->last) { |
93 | | /* Box is up to date */ |
94 | 64.4k | *pbox = ppath->bbox; |
95 | 1.14M | } else { |
96 | 1.14M | fixed px, py, qx, qy; |
97 | 1.14M | const segment *pseg = ppath->box_last; |
98 | | |
99 | 1.14M | if (pseg == 0) { /* box is uninitialized */ |
100 | 1.14M | pseg = (const segment *)ppath->first_subpath; |
101 | 1.14M | px = qx = pseg->pt.x; |
102 | 1.14M | py = qy = pseg->pt.y; |
103 | 1.14M | } else { |
104 | 0 | px = ppath->bbox.p.x, py = ppath->bbox.p.y; |
105 | 0 | qx = ppath->bbox.q.x, qy = ppath->bbox.q.y; |
106 | 0 | } |
107 | | |
108 | | /* Macro for adjusting the bounding box when adding a point */ |
109 | 1.14M | #define ADJUST_BBOX(pt)\ |
110 | 33.4M | if ((pt).x < px) px = (pt).x;\ |
111 | 33.4M | else if ((pt).x > qx) qx = (pt).x;\ |
112 | 33.4M | if ((pt).y < py) py = (pt).y;\ |
113 | 33.4M | else if ((pt).y > qy) qy = (pt).y |
114 | | |
115 | 21.9M | while ((pseg = pseg->next) != 0) { |
116 | 20.7M | switch (pseg->type) { |
117 | 6.31M | case s_curve: |
118 | 6.31M | ADJUST_BBOX(((const curve_segment *)pseg)->p1); |
119 | 6.31M | ADJUST_BBOX(((const curve_segment *)pseg)->p2); |
120 | | /* falls through */ |
121 | 20.7M | default: |
122 | 20.7M | ADJUST_BBOX(pseg->pt); |
123 | 20.7M | } |
124 | 20.7M | } |
125 | 1.14M | #undef ADJUST_BBOX |
126 | | |
127 | 1.14M | #define STORE_BBOX(b)\ |
128 | 2.28M | (b).p.x = px, (b).p.y = py, (b).q.x = qx, (b).q.y = qy; |
129 | 1.14M | STORE_BBOX(*pbox); |
130 | 1.14M | STORE_BBOX(ppath->bbox); |
131 | 1.14M | #undef STORE_BBOX |
132 | | |
133 | 1.14M | ppath->box_last = ppath->current_subpath->last; |
134 | 1.14M | } |
135 | 1.20M | return 0; |
136 | 1.20M | } |
137 | | |
138 | | /* A variation of gs_path_bbox, to be used by the patbbox operator */ |
139 | | int |
140 | | gx_path_bbox_set(gx_path * ppath, gs_fixed_rect * pbox) |
141 | 806 | { |
142 | 806 | if (ppath->bbox_set) { |
143 | | /* The bounding box was set by setbbox. */ |
144 | 0 | *pbox = ppath->bbox; |
145 | 0 | return 0; |
146 | 0 | } else |
147 | 806 | return gx_path_bbox(ppath, pbox); |
148 | 806 | } |
149 | | |
150 | | /* Test if a path has any curves. */ |
151 | | #undef gx_path_has_curves |
152 | | bool |
153 | | gx_path_has_curves(const gx_path * ppath) |
154 | 0 | { |
155 | 0 | return gx_path_has_curves_inline(ppath); |
156 | 0 | } |
157 | | #define gx_path_has_curves(ppath)\ |
158 | | gx_path_has_curves_inline(ppath) |
159 | | |
160 | | /* Test if a path has no segments. */ |
161 | | #undef gx_path_is_void |
162 | | bool |
163 | | gx_path_is_void(const gx_path * ppath) |
164 | 0 | { |
165 | 0 | return gx_path_is_void_inline(ppath); |
166 | 0 | } |
167 | | #define gx_path_is_void(ppath)\ |
168 | 2.24k | gx_path_is_void_inline(ppath) |
169 | | |
170 | | /* Test if a path has no elements at all. */ |
171 | | bool |
172 | | gx_path_is_null(const gx_path * ppath) |
173 | 2.24k | { |
174 | 2.24k | return gx_path_is_null_inline(ppath); |
175 | 2.24k | } |
176 | | |
177 | | /* |
178 | | * Test if a subpath is a rectangle; if so, return its bounding box |
179 | | * and the start of the next subpath. |
180 | | * Note that this must recognize: |
181 | | * ordinary closed rectangles (M, L, L, L, C); |
182 | | * open rectangles (M, L, L, L); |
183 | | * rectangles closed with lineto (Mo, L, L, L, Lo); |
184 | | * rectangles closed with *both* lineto and closepath (bad PostScript, |
185 | | * but unfortunately not rare) (Mo, L, L, L, Lo, C). |
186 | | */ |
187 | | gx_path_rectangular_type |
188 | | gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox, |
189 | | const subpath ** ppnext) |
190 | 220k | { |
191 | 220k | const segment *pseg1, *pseg2, *pseg3, *pseg4; |
192 | 220k | gx_path_rectangular_type type = prt_none; |
193 | 220k | fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y; |
194 | 220k | fixed x1, y1, x2, y2, x3, y3; |
195 | | |
196 | 220k | pseg1 = (const segment *)pseg0; |
197 | 250k | do { |
198 | 250k | pseg1 = pseg1->next; |
199 | 250k | if (pseg1 == NULL) |
200 | 762 | return prt_none; |
201 | 249k | x1 = pseg1->pt.x; |
202 | 249k | y1 = pseg1->pt.y; |
203 | 249k | if (pseg1->type == s_curve) { |
204 | 130 | if (gx_curve_is_really_point(x0, y0, pseg1)) |
205 | 0 | continue; /* Ignore this one and try again */ |
206 | 130 | if (gx_curve_is_really_line(x0, y0, pseg1)) |
207 | 0 | break; /* That'll do! */ |
208 | 130 | return prt_none; |
209 | 249k | } else if (pseg1->type != s_line && pseg1->type != s_gap) |
210 | 5.06k | return prt_none; |
211 | 249k | } while (x1 == x0 && y1 == y0); |
212 | | |
213 | 214k | pseg2 = pseg1; |
214 | 214k | do { |
215 | 214k | pseg2 = pseg2->next; |
216 | 214k | if (pseg2 == NULL) |
217 | 90 | return prt_none; |
218 | 214k | x2 = pseg2->pt.x; |
219 | 214k | y2 = pseg2->pt.y; |
220 | 214k | if (pseg2->type == s_curve) { |
221 | 0 | if (gx_curve_is_really_point(x1, y1, pseg2)) |
222 | 0 | continue; /* Ignore this one and try again */ |
223 | 0 | if (gx_curve_is_really_line(x1, y1, pseg2)) |
224 | 0 | break; /* That'll do! */ |
225 | 0 | return prt_none; |
226 | 214k | } else if (pseg2->type != s_line && pseg2->type != s_gap) |
227 | 346 | return prt_none; |
228 | 214k | } while (x2 == x1 && y2 == y1); |
229 | | |
230 | 213k | pseg3 = pseg2; |
231 | 214k | do { |
232 | 214k | pseg3 = pseg3->next; |
233 | 214k | if (pseg3 == NULL) |
234 | 11 | return prt_none; |
235 | 214k | x3 = pseg3->pt.x; |
236 | 214k | y3 = pseg3->pt.y; |
237 | 214k | if (pseg3->type == s_curve) { |
238 | 0 | if (gx_curve_is_really_point(x2, y2, pseg3)) |
239 | 0 | continue; /* Ignore this one and try again */ |
240 | 0 | if (gx_curve_is_really_line(x2, y2, pseg3)) |
241 | 0 | break; /* That'll do! */ |
242 | 0 | return prt_none; |
243 | 214k | } else if (pseg3->type != s_line && pseg3->type != s_gap) |
244 | 1.10k | return prt_none; |
245 | 214k | } while (x3 == x2 && y3 == y2); |
246 | | |
247 | 212k | pseg4 = pseg3; |
248 | 213k | do { |
249 | 213k | pseg4 = pseg4->next; |
250 | 213k | if (pseg4 == NULL || pseg4->type == s_start) { |
251 | 753 | type = prt_open; /* M, L, L, L */ |
252 | 753 | goto type_known; |
253 | 753 | } |
254 | 212k | if (pseg4->type == s_curve) { |
255 | 0 | if (gx_curve_is_really_point(x3, y3, pseg4)) |
256 | 0 | continue; /* Ignore this one and try again */ |
257 | 0 | if (gx_curve_is_really_line(x3, y3, pseg4)) |
258 | 0 | break; /* That'll do! */ |
259 | 0 | return prt_none; |
260 | 212k | } else if (pseg4->type == s_line_close) { |
261 | 181k | type = prt_closed; /* M, L, L, L, C */ |
262 | 181k | goto type_known; |
263 | 181k | } |
264 | 212k | } while (pseg4->pt.x == x3 && pseg4->pt.y == y3); |
265 | | |
266 | 30.1k | if (pseg4->pt.x != pseg0->pt.x || pseg4->pt.y != pseg0->pt.y) |
267 | 29.7k | return prt_none; |
268 | 385 | else if (pseg4->next == NULL || pseg4->next->type == s_start) |
269 | 6 | type = prt_fake_closed; /* Mo, L, L, L, L, Mo */ |
270 | 379 | else |
271 | 379 | return prt_none; |
272 | | |
273 | 182k | type_known: |
274 | | |
275 | 182k | if ((x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) || |
276 | 182k | (x0 == x3 && y3 == y2 && x2 == x1 && y1 == y0)) { |
277 | | /* Path is a rectangle. Return the bounding box. */ |
278 | 176k | if (x0 < x2) |
279 | 173k | pbox->p.x = x0, pbox->q.x = x2; |
280 | 2.88k | else |
281 | 2.88k | pbox->p.x = x2, pbox->q.x = x0; |
282 | 176k | if (y0 < y2) |
283 | 63.7k | pbox->p.y = y0, pbox->q.y = y2; |
284 | 112k | else |
285 | 112k | pbox->p.y = y2, pbox->q.y = y0; |
286 | 352k | while (pseg4 != 0 && pseg4->type != s_start) |
287 | 175k | pseg4 = pseg4->next; |
288 | 176k | *ppnext = (const subpath *)pseg4; |
289 | 176k | return type; |
290 | 176k | } |
291 | 6.15k | return prt_none; |
292 | 182k | } |
293 | | /* Test if an entire path to be filled is a rectangle. */ |
294 | | gx_path_rectangular_type |
295 | | gx_path_is_rectangular(const gx_path * ppath, gs_fixed_rect * pbox) |
296 | 222k | { |
297 | 222k | const subpath *pnext; |
298 | | |
299 | 222k | return |
300 | 222k | (gx_path_subpath_count(ppath) == 1 ? |
301 | 220k | gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) : |
302 | 222k | prt_none); |
303 | 222k | } |
304 | | |
305 | | /* Translate an already-constructed path (in device space). */ |
306 | | /* Don't bother to update the cbox. */ |
307 | | int |
308 | | gx_path_translate(gx_path * ppath, fixed dx, fixed dy) |
309 | 1.38M | { |
310 | 1.38M | segment *pseg; |
311 | | |
312 | 1.38M | #define update_xy(pt)\ |
313 | 1.38M | pt.x += dx, pt.y += dy |
314 | 1.38M | if (ppath->box_last != 0) { |
315 | 0 | update_xy(ppath->bbox.p); |
316 | 0 | update_xy(ppath->bbox.q); |
317 | 0 | } |
318 | 1.38M | if (path_position_valid(ppath)) |
319 | 0 | update_xy(ppath->position); |
320 | 1.38M | for (pseg = (segment *) (ppath->first_subpath); pseg != 0; |
321 | 1.38M | pseg = pseg->next |
322 | 1.38M | ) |
323 | 0 | switch (pseg->type) { |
324 | 0 | case s_curve: |
325 | 0 | #define pcseg ((curve_segment *)pseg) |
326 | 0 | update_xy(pcseg->p1); |
327 | 0 | update_xy(pcseg->p2); |
328 | 0 | #undef pcseg |
329 | | /* fall through */ |
330 | 0 | default: |
331 | 0 | update_xy(pseg->pt); |
332 | 0 | } |
333 | 1.38M | #undef update_xy |
334 | 1.38M | return 0; |
335 | 1.38M | } |
336 | | |
337 | | /* Scale an existing path by a power of 2 (positive or negative). |
338 | | * Currently the path drawing routines can't handle values |
339 | | * close to the edge of the representable space. |
340 | | * Also see clamp_point() in gspath.c . |
341 | | */ |
342 | | void |
343 | | gx_point_scale_exp2(gs_fixed_point * pt, int sx, int sy) |
344 | 0 | { |
345 | 0 | int v; |
346 | |
|
347 | 0 | if (sx > 0) { |
348 | 0 | v = (max_int - int2fixed(1000)) >> sx; /* arbitrary */ |
349 | 0 | if (pt->x > v) |
350 | 0 | pt->x = v; |
351 | 0 | else if (pt->x < -v) |
352 | 0 | pt->x = -v; |
353 | 0 | pt->x <<= sx; |
354 | 0 | } else |
355 | 0 | pt->x >>= -sx; |
356 | |
|
357 | 0 | if (sy > 0) { |
358 | 0 | v = (max_int - int2fixed(1000)) >> sy; |
359 | 0 | if (pt->y > v) |
360 | 0 | pt->y = v; |
361 | 0 | else if (pt->y < -v) |
362 | 0 | pt->y = -v; |
363 | 0 | pt->y <<= sy; |
364 | 0 | } else |
365 | 0 | pt->y >>= -sy; |
366 | 0 | } |
367 | | void |
368 | | gx_rect_scale_exp2(gs_fixed_rect * pr, int sx, int sy) |
369 | 0 | { |
370 | 0 | gx_point_scale_exp2(&pr->p, sx, sy); |
371 | 0 | gx_point_scale_exp2(&pr->q, sx, sy); |
372 | 0 | } |
373 | | int |
374 | | gx_path_scale_exp2_shared(gx_path * ppath, int log2_scale_x, int log2_scale_y, |
375 | | bool segments_shared) |
376 | 0 | { |
377 | 0 | segment *pseg; |
378 | |
|
379 | 0 | gx_rect_scale_exp2(&ppath->bbox, log2_scale_x, log2_scale_y); |
380 | 0 | #define SCALE_XY(pt) gx_point_scale_exp2(&pt, log2_scale_x, log2_scale_y) |
381 | 0 | SCALE_XY(ppath->position); |
382 | 0 | if (!segments_shared) { |
383 | 0 | for (pseg = (segment *) (ppath->first_subpath); pseg != 0; |
384 | 0 | pseg = pseg->next |
385 | 0 | ) |
386 | 0 | switch (pseg->type) { |
387 | 0 | case s_curve: |
388 | 0 | SCALE_XY(((curve_segment *)pseg)->p1); |
389 | 0 | SCALE_XY(((curve_segment *)pseg)->p2); |
390 | | /* fall through */ |
391 | 0 | default: |
392 | 0 | SCALE_XY(pseg->pt); |
393 | 0 | } |
394 | 0 | } |
395 | 0 | #undef SCALE_XY |
396 | 0 | return 0; |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * Reverse a path. We know ppath != ppath_old. |
401 | | * NOTE: in releases 5.01 and earlier, the implicit line added by closepath |
402 | | * became the first segment of the reversed path. Starting in release |
403 | | * 5.02, the code follows the Adobe implementation (and LanguageLevel 3 |
404 | | * specification), in which this line becomes the *last* segment of the |
405 | | * reversed path. This can produce some quite unintuitive results. |
406 | | * |
407 | | * The order of the subpaths is unspecified in the PLRM, but the CPSI |
408 | | * reverses the subpaths, and the CET (11-05 p6, test 3) tests for it. |
409 | | */ |
410 | | int |
411 | | gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath) |
412 | 0 | { |
413 | 0 | const subpath *psub = ppath_old->current_subpath; |
414 | |
|
415 | | #ifdef DEBUG |
416 | | if (gs_debug_c('P')) |
417 | | gx_dump_path(ppath_old, "before reversepath"); |
418 | | #endif |
419 | 0 | nsp: |
420 | 0 | if (psub) { |
421 | 0 | const segment *prev = psub->last; |
422 | 0 | const segment *pseg; |
423 | 0 | segment_notes notes = |
424 | 0 | (prev == (const segment *)psub ? sn_none : |
425 | 0 | psub->next->notes); |
426 | 0 | segment_notes prev_notes; |
427 | 0 | int code; |
428 | |
|
429 | 0 | if (!psub->is_closed) { |
430 | 0 | code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y); |
431 | 0 | if (code < 0) |
432 | 0 | return code; |
433 | 0 | } |
434 | | /* |
435 | | * The do ... while structure of this loop is artificial, |
436 | | * designed solely to keep compilers from complaining about |
437 | | * 'statement not reached' or 'end-of-loop code not reached'. |
438 | | * The normal exit from this loop is the goto statement in |
439 | | * the s_start arm of the switch. |
440 | | */ |
441 | 0 | do { |
442 | 0 | pseg = prev; |
443 | 0 | prev_notes = notes; |
444 | 0 | prev = pseg->prev; |
445 | 0 | notes = pseg->notes; |
446 | 0 | prev_notes = (prev_notes & sn_not_first) | |
447 | 0 | (notes & ~sn_not_first); |
448 | 0 | switch (pseg->type) { |
449 | 0 | case s_start: |
450 | | /* Finished subpath */ |
451 | 0 | if (psub->is_closed) { |
452 | 0 | code = |
453 | 0 | gx_path_close_subpath_notes(ppath, prev_notes); |
454 | 0 | if (code < 0) |
455 | 0 | return code; |
456 | 0 | } |
457 | 0 | do { |
458 | 0 | psub = (const subpath *)psub->prev; |
459 | 0 | } while (psub && psub->type != s_start); |
460 | 0 | goto nsp; |
461 | 0 | case s_curve: |
462 | 0 | { |
463 | 0 | const curve_segment *pc = |
464 | 0 | (const curve_segment *)pseg; |
465 | |
|
466 | 0 | code = gx_path_add_curve_notes(ppath, |
467 | 0 | pc->p2.x, pc->p2.y, |
468 | 0 | pc->p1.x, pc->p1.y, |
469 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
470 | 0 | break; |
471 | 0 | } |
472 | 0 | case s_line: |
473 | 0 | code = gx_path_add_line_notes(ppath, |
474 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
475 | 0 | break; |
476 | 0 | case s_gap: |
477 | 0 | code = gx_path_add_gap_notes(ppath, |
478 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
479 | 0 | break; |
480 | 0 | case s_line_close: |
481 | | /* Skip the closing line. */ |
482 | 0 | code = gx_path_add_point(ppath, prev->pt.x, |
483 | 0 | prev->pt.y); |
484 | 0 | break; |
485 | 0 | default: /* not possible */ |
486 | 0 | return_error(gs_error_Fatal); |
487 | 0 | } |
488 | 0 | } while (code >= 0); |
489 | 0 | return code; /* only reached if code < 0 */ |
490 | 0 | } |
491 | 0 | #undef sn_not_end |
492 | | /* |
493 | | * In the Adobe implementations, reversepath discards a trailing |
494 | | * moveto unless the path consists only of a moveto. We reproduce |
495 | | * this behavior here, even though we consider it a bug. |
496 | | */ |
497 | 0 | if (ppath_old->first_subpath == 0 && |
498 | 0 | path_last_is_moveto(ppath_old) |
499 | 0 | ) { |
500 | 0 | int code = gx_path_add_point(ppath, ppath_old->position.x, |
501 | 0 | ppath_old->position.y); |
502 | |
|
503 | 0 | if (code < 0) |
504 | 0 | return code; |
505 | 0 | } |
506 | | #ifdef DEBUG |
507 | | if (gs_debug_c('P')) |
508 | | gx_dump_path(ppath, "after reversepath"); |
509 | | #endif |
510 | 0 | return 0; |
511 | 0 | } |
512 | | |
513 | | int |
514 | | gx_path_append_reversed(const gx_path * ppath_old, gx_path * ppath) |
515 | 0 | { |
516 | 0 | const subpath *psub = ppath_old->current_subpath; |
517 | |
|
518 | | #ifdef DEBUG |
519 | | if (gs_debug_c('P')) |
520 | | gx_dump_path(ppath_old, "before reversepath"); |
521 | | #endif |
522 | 0 | nsp: |
523 | 0 | if (psub) { |
524 | 0 | const segment *prev = psub->last; |
525 | 0 | const segment *pseg; |
526 | 0 | segment_notes notes = |
527 | 0 | (prev == (const segment *)psub ? sn_none : |
528 | 0 | psub->next->notes); |
529 | 0 | segment_notes prev_notes; |
530 | 0 | int code; |
531 | |
|
532 | 0 | if (!psub->is_closed) { |
533 | 0 | code = gx_path_add_line(ppath, prev->pt.x, prev->pt.y); |
534 | 0 | if (code < 0) |
535 | 0 | return code; |
536 | 0 | } |
537 | | /* |
538 | | * The do ... while structure of this loop is artificial, |
539 | | * designed solely to keep compilers from complaining about |
540 | | * 'statement not reached' or 'end-of-loop code not reached'. |
541 | | * The normal exit from this loop is the goto statement in |
542 | | * the s_start arm of the switch. |
543 | | */ |
544 | 0 | do { |
545 | 0 | pseg = prev; |
546 | 0 | prev_notes = notes; |
547 | 0 | prev = pseg->prev; |
548 | 0 | notes = pseg->notes; |
549 | 0 | prev_notes = (prev_notes & sn_not_first) | |
550 | 0 | (notes & ~sn_not_first); |
551 | 0 | switch (pseg->type) { |
552 | 0 | case s_start: |
553 | | /* Finished subpath */ |
554 | 0 | if (psub->is_closed) { |
555 | 0 | code = |
556 | 0 | gx_path_close_subpath_notes(ppath, prev_notes); |
557 | 0 | if (code < 0) |
558 | 0 | return code; |
559 | 0 | } |
560 | 0 | do { |
561 | 0 | psub = (const subpath *)psub->prev; |
562 | 0 | } while (psub && psub->type != s_start); |
563 | 0 | goto nsp; |
564 | 0 | case s_curve: |
565 | 0 | { |
566 | 0 | const curve_segment *pc = |
567 | 0 | (const curve_segment *)pseg; |
568 | |
|
569 | 0 | code = gx_path_add_curve_notes(ppath, |
570 | 0 | pc->p2.x, pc->p2.y, |
571 | 0 | pc->p1.x, pc->p1.y, |
572 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
573 | 0 | break; |
574 | 0 | } |
575 | 0 | case s_line: |
576 | 0 | code = gx_path_add_line_notes(ppath, |
577 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
578 | 0 | break; |
579 | 0 | case s_gap: |
580 | 0 | code = gx_path_add_gap_notes(ppath, |
581 | 0 | prev->pt.x, prev->pt.y, prev_notes); |
582 | 0 | break; |
583 | 0 | case s_line_close: |
584 | | /* Skip the closing line. */ |
585 | 0 | code = gx_path_add_point(ppath, prev->pt.x, |
586 | 0 | prev->pt.y); |
587 | 0 | break; |
588 | 0 | default: /* not possible */ |
589 | 0 | return_error(gs_error_Fatal); |
590 | 0 | } |
591 | 0 | } while (code >= 0); |
592 | 0 | return code; /* only reached if code < 0 */ |
593 | 0 | } |
594 | 0 | #undef sn_not_end |
595 | | /* |
596 | | * In the Adobe implementations, reversepath discards a trailing |
597 | | * moveto unless the path consists only of a moveto. We reproduce |
598 | | * this behavior here, even though we consider it a bug. |
599 | | */ |
600 | 0 | if (ppath_old->first_subpath == 0 && |
601 | 0 | path_last_is_moveto(ppath_old) |
602 | 0 | ) { |
603 | 0 | int code = gx_path_add_point(ppath, ppath_old->position.x, |
604 | 0 | ppath_old->position.y); |
605 | |
|
606 | 0 | if (code < 0) |
607 | 0 | return code; |
608 | 0 | } |
609 | | #ifdef DEBUG |
610 | | if (gs_debug_c('P')) |
611 | | gx_dump_path(ppath, "after reversepath"); |
612 | | #endif |
613 | 0 | return 0; |
614 | 0 | } |
615 | | |
616 | | /* ------ Path enumeration ------ */ |
617 | | |
618 | | /* Allocate a path enumerator. */ |
619 | | gs_path_enum * |
620 | | gs_path_enum_alloc(gs_memory_t * mem, client_name_t cname) |
621 | 0 | { |
622 | 0 | return gs_alloc_struct(mem, gs_path_enum, &st_path_enum, cname); |
623 | 0 | } |
624 | | |
625 | | /* Start enumerating a path. */ |
626 | | int |
627 | | gx_path_enum_init(gs_path_enum * penum, const gx_path * ppath) |
628 | 3.61M | { |
629 | 3.61M | penum->memory = 0; /* path not copied */ |
630 | 3.61M | penum->path = ppath; |
631 | 3.61M | penum->copied_path = 0; /* not copied */ |
632 | 3.61M | penum->pseg = (const segment *)ppath->first_subpath; |
633 | 3.61M | penum->moveto_done = false; |
634 | 3.61M | penum->notes = sn_none; |
635 | 3.61M | return 0; |
636 | 3.61M | } |
637 | | |
638 | | /* Enumerate the next element of a path. */ |
639 | | /* If the path is finished, return 0; */ |
640 | | /* otherwise, return the element type. */ |
641 | | int |
642 | | gx_path_enum_next(gs_path_enum * penum, gs_fixed_point ppts[3]) |
643 | 86.3M | { |
644 | 86.3M | const segment *pseg = penum->pseg; |
645 | | |
646 | 86.3M | if (pseg == 0) { /* We've enumerated all the segments, but there might be */ |
647 | | /* a trailing moveto. */ |
648 | 3.89M | const gx_path *ppath = penum->path; |
649 | | |
650 | 3.89M | if (path_last_is_moveto(ppath) && !penum->moveto_done) { /* Handle a trailing moveto */ |
651 | 177k | penum->moveto_done = true; |
652 | 177k | penum->notes = sn_none; |
653 | 177k | ppts[0] = ppath->position; |
654 | 177k | return gs_pe_moveto; |
655 | 177k | } |
656 | 3.71M | return 0; |
657 | 3.89M | } |
658 | 82.4M | penum->pseg = pseg->next; |
659 | 82.4M | penum->notes = pseg->notes; |
660 | 82.4M | switch (pseg->type) { |
661 | 23.0M | case s_start: |
662 | 23.0M | ppts[0] = pseg->pt; |
663 | 23.0M | return gs_pe_moveto; |
664 | 37.4M | case s_line: |
665 | 37.4M | ppts[0] = pseg->pt; |
666 | 37.4M | return gs_pe_lineto; |
667 | 0 | case s_gap: |
668 | 0 | ppts[0] = pseg->pt; |
669 | 0 | return gs_pe_gapto; |
670 | 4.24M | case s_line_close: |
671 | 4.24M | ppts[0] = pseg->pt; |
672 | 4.24M | return gs_pe_closepath; |
673 | 17.6M | case s_curve: |
674 | 35.3M | #define pcseg ((const curve_segment *)pseg) |
675 | 17.6M | ppts[0] = pcseg->p1; |
676 | 17.6M | ppts[1] = pcseg->p2; |
677 | 17.6M | ppts[2] = pseg->pt; |
678 | 17.6M | return gs_pe_curveto; |
679 | 0 | #undef pcseg |
680 | 0 | default: |
681 | 0 | lprintf1("bad type %x in gx_path_enum_next!\n", pseg->type); |
682 | 0 | return_error(gs_error_Fatal); |
683 | 82.4M | } |
684 | 82.4M | } |
685 | | |
686 | | /* Return the notes from the last-enumerated segment. */ |
687 | | segment_notes |
688 | | gx_path_enum_notes(const gs_path_enum * penum) |
689 | 55.1M | { |
690 | 55.1M | return penum->notes; |
691 | 55.1M | } |
692 | | |
693 | | /* Back up 1 element in the path being enumerated. */ |
694 | | /* Return true if successful, false if we are at the beginning of the path. */ |
695 | | /* This implementation allows backing up multiple times, */ |
696 | | /* but no client currently relies on this. */ |
697 | | bool |
698 | | gx_path_enum_backup(gs_path_enum * penum) |
699 | 35.2k | { |
700 | 35.2k | const segment *pseg = penum->pseg; |
701 | | |
702 | 35.2k | if (pseg != 0) { |
703 | 33.5k | if ((pseg = pseg->prev) == 0) |
704 | 0 | return false; |
705 | 33.5k | penum->pseg = pseg; |
706 | 33.5k | return true; |
707 | 33.5k | } |
708 | | /* We're at the end of the path. Check to see whether */ |
709 | | /* we need to back up over a trailing moveto. */ |
710 | 1.71k | { |
711 | 1.71k | const gx_path *ppath = penum->path; |
712 | | |
713 | 1.71k | if (path_last_is_moveto(ppath) && penum->moveto_done) { /* Back up over the trailing moveto. */ |
714 | 1.71k | penum->moveto_done = false; |
715 | 1.71k | return true; |
716 | 1.71k | } { |
717 | 0 | const subpath *psub = ppath->current_subpath; |
718 | |
|
719 | 0 | if (psub == 0) /* empty path */ |
720 | 0 | return false; |
721 | | /* Back up to the last segment of the last subpath. */ |
722 | 0 | penum->pseg = psub->last; |
723 | 0 | return true; |
724 | 0 | } |
725 | 0 | } |
726 | 0 | } |