1 /* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /*$Id: gxpath2.c,v 1.6 2002/08/30 02:38:24 dan Exp $ */
18 /* Path tracing procedures for Ghostscript library */
19 #include "math_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gspath.h" /* for gs_path_enum_alloc prototype */
23 #include "gsstruct.h"
24 #include "gxfixed.h"
25 #include "gxarith.h"
26 #include "gzpath.h"
27
28 /* Define the enumeration structure. */
29 public_st_path_enum();
30
31 /* Read the current point of a path. */
32 int
gx_path_current_point(const gx_path * ppath,gs_fixed_point * ppt)33 gx_path_current_point(const gx_path * ppath, gs_fixed_point * ppt)
34 {
35 if (!path_position_valid(ppath))
36 return_error(gs_error_nocurrentpoint);
37 /* Copying the coordinates individually */
38 /* is much faster on a PC, and almost as fast on other machines.... */
39 ppt->x = ppath->position.x, ppt->y = ppath->position.y;
40 return 0;
41 }
42
43 /* Read the start point of the current subpath. */
44 int
gx_path_subpath_start_point(const gx_path * ppath,gs_fixed_point * ppt)45 gx_path_subpath_start_point(const gx_path * ppath, gs_fixed_point * ppt)
46 {
47 const subpath *psub = ppath->current_subpath;
48
49 if (!psub)
50 return_error(gs_error_nocurrentpoint);
51 *ppt = psub->pt;
52 return 0;
53 }
54
55 /* Read the bounding box of a path. */
56 /* Note that if the last element of the path is a moveto, */
57 /* the bounding box does not include this point, */
58 /* unless this is the only element of the path. */
59 int
gx_path_bbox(gx_path * ppath,gs_fixed_rect * pbox)60 gx_path_bbox(gx_path * ppath, gs_fixed_rect * pbox)
61 {
62 if (ppath->bbox_accurate) {
63 /* The bounding box was set by setbbox. */
64 *pbox = ppath->bbox;
65 return 0;
66 }
67 if (ppath->first_subpath == 0) {
68 /* The path is empty, use the current point if any. */
69 int code = gx_path_current_point(ppath, &pbox->p);
70
71 if (code < 0) {
72 /*
73 * Don't return garbage, in case the caller doesn't
74 * check the return code.
75 */
76 pbox->p.x = pbox->p.y = 0;
77 }
78 pbox->q = pbox->p;
79 return code;
80 }
81 /* The stored bounding box may not be up to date. */
82 /* Correct it now if necessary. */
83 if (ppath->box_last == ppath->current_subpath->last) {
84 /* Box is up to date */
85 *pbox = ppath->bbox;
86 } else {
87 fixed px, py, qx, qy;
88 const segment *pseg = ppath->box_last;
89
90 if (pseg == 0) { /* box is uninitialized */
91 pseg = (const segment *)ppath->first_subpath;
92 px = qx = pseg->pt.x;
93 py = qy = pseg->pt.y;
94 } else {
95 px = ppath->bbox.p.x, py = ppath->bbox.p.y;
96 qx = ppath->bbox.q.x, qy = ppath->bbox.q.y;
97 }
98
99 /* Macro for adjusting the bounding box when adding a point */
100 #define ADJUST_BBOX(pt)\
101 if ((pt).x < px) px = (pt).x;\
102 else if ((pt).x > qx) qx = (pt).x;\
103 if ((pt).y < py) py = (pt).y;\
104 else if ((pt).y > qy) qy = (pt).y
105
106 while ((pseg = pseg->next) != 0) {
107 switch (pseg->type) {
108 case s_curve:
109 ADJUST_BBOX(((const curve_segment *)pseg)->p1);
110 ADJUST_BBOX(((const curve_segment *)pseg)->p2);
111 /* falls through */
112 default:
113 ADJUST_BBOX(pseg->pt);
114 }
115 }
116 #undef ADJUST_BBOX
117
118 #define STORE_BBOX(b)\
119 (b).p.x = px, (b).p.y = py, (b).q.x = qx, (b).q.y = qy;
120 STORE_BBOX(*pbox);
121 STORE_BBOX(ppath->bbox);
122 #undef STORE_BBOX
123
124 ppath->box_last = ppath->current_subpath->last;
125 }
126 return 0;
127 }
128
129 /* A variation of gs_path_bbox, to be used by the patbbox operator */
130 int
gx_path_bbox_set(gx_path * ppath,gs_fixed_rect * pbox)131 gx_path_bbox_set(gx_path * ppath, gs_fixed_rect * pbox)
132 {
133 if (ppath->bbox_set) {
134 /* The bounding box was set by setbbox. */
135 *pbox = ppath->bbox;
136 return 0;
137 } else
138 return gx_path_bbox(ppath, pbox);
139 }
140
141
142 /* Test if a path has any curves. */
143 #undef gx_path_has_curves
144 bool
gx_path_has_curves(const gx_path * ppath)145 gx_path_has_curves(const gx_path * ppath)
146 {
147 return gx_path_has_curves_inline(ppath);
148 }
149 #define gx_path_has_curves(ppath)\
150 gx_path_has_curves_inline(ppath)
151
152 /* Test if a path has no segments. */
153 #undef gx_path_is_void
154 bool
gx_path_is_void(const gx_path * ppath)155 gx_path_is_void(const gx_path * ppath)
156 {
157 return gx_path_is_void_inline(ppath);
158 }
159 #define gx_path_is_void(ppath)\
160 gx_path_is_void_inline(ppath)
161
162 /* Test if a path has no elements at all. */
163 bool
gx_path_is_null(const gx_path * ppath)164 gx_path_is_null(const gx_path * ppath)
165 {
166 return gx_path_is_null_inline(ppath);
167 }
168
169 /*
170 * Test if a subpath is a rectangle; if so, return its bounding box
171 * and the start of the next subpath.
172 * Note that this must recognize:
173 * ordinary closed rectangles (M, L, L, L, C);
174 * open rectangles (M, L, L, L);
175 * rectangles closed with lineto (Mo, L, L, L, Lo);
176 * rectangles closed with *both* lineto and closepath (bad PostScript,
177 * but unfortunately not rare) (Mo, L, L, L, Lo, C).
178 */
179 gx_path_rectangular_type
gx_subpath_is_rectangular(const subpath * pseg0,gs_fixed_rect * pbox,const subpath ** ppnext)180 gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox,
181 const subpath ** ppnext)
182 {
183 const segment *pseg1, *pseg2, *pseg3, *pseg4;
184 gx_path_rectangular_type type;
185
186 if (pseg0->curve_count == 0 &&
187 (pseg1 = pseg0->next) != 0 &&
188 (pseg2 = pseg1->next) != 0 &&
189 (pseg3 = pseg2->next) != 0
190 ) {
191 if ((pseg4 = pseg3->next) == 0 || pseg4->type == s_start)
192 type = prt_open; /* M, L, L, L */
193 else if (pseg4->type != s_line) /* must be s_line_close */
194 type = prt_closed; /* M, L, L, L, C */
195 else if (pseg4->pt.x != pseg0->pt.x ||
196 pseg4->pt.y != pseg0->pt.y
197 )
198 return prt_none;
199 else if (pseg4->next == 0 || pseg4->next->type == s_start)
200 type = prt_fake_closed; /* Mo, L, L, L, Lo */
201 else if (pseg4->next->type != s_line) /* must be s_line_close */
202 type = prt_closed; /* Mo, L, L, L, Lo, C */
203 else
204 return prt_none;
205 {
206 fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y;
207 fixed x2 = pseg2->pt.x, y2 = pseg2->pt.y;
208
209 if ((x0 == pseg1->pt.x && pseg1->pt.y == y2 &&
210 x2 == pseg3->pt.x && pseg3->pt.y == y0) ||
211 (x0 == pseg3->pt.x && pseg3->pt.y == y2 &&
212 x2 == pseg1->pt.x && pseg1->pt.y == y0)
213 ) { /* Path is a rectangle. Return the bounding box. */
214 if (x0 < x2)
215 pbox->p.x = x0, pbox->q.x = x2;
216 else
217 pbox->p.x = x2, pbox->q.x = x0;
218 if (y0 < y2)
219 pbox->p.y = y0, pbox->q.y = y2;
220 else
221 pbox->p.y = y2, pbox->q.y = y0;
222 while (pseg4 != 0 && pseg4->type != s_start)
223 pseg4 = pseg4->next;
224 *ppnext = (const subpath *)pseg4;
225 return type;
226 }
227 }
228 }
229 return prt_none;
230 }
231 /* Test if an entire path to be filled is a rectangle. */
232 gx_path_rectangular_type
gx_path_is_rectangular(const gx_path * ppath,gs_fixed_rect * pbox)233 gx_path_is_rectangular(const gx_path * ppath, gs_fixed_rect * pbox)
234 {
235 const subpath *pnext;
236
237 return
238 (gx_path_subpath_count(ppath) == 1 ?
239 gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) :
240 prt_none);
241 }
242
243 /* Translate an already-constructed path (in device space). */
244 /* Don't bother to update the cbox. */
245 int
gx_path_translate(gx_path * ppath,fixed dx,fixed dy)246 gx_path_translate(gx_path * ppath, fixed dx, fixed dy)
247 {
248 segment *pseg;
249
250 #define update_xy(pt)\
251 pt.x += dx, pt.y += dy
252 if (ppath->box_last != 0) {
253 update_xy(ppath->bbox.p);
254 update_xy(ppath->bbox.q);
255 }
256 if (path_position_valid(ppath))
257 update_xy(ppath->position);
258 for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
259 pseg = pseg->next
260 )
261 switch (pseg->type) {
262 case s_curve:
263 #define pcseg ((curve_segment *)pseg)
264 update_xy(pcseg->p1);
265 update_xy(pcseg->p2);
266 #undef pcseg
267 default:
268 update_xy(pseg->pt);
269 }
270 #undef update_xy
271 return 0;
272 }
273
274 /* Scale an existing path by a power of 2 (positive or negative). */
275 void
gx_point_scale_exp2(gs_fixed_point * pt,int sx,int sy)276 gx_point_scale_exp2(gs_fixed_point * pt, int sx, int sy)
277 {
278 if (sx >= 0)
279 pt->x <<= sx;
280 else
281 pt->x >>= -sx;
282 if (sy >= 0)
283 pt->y <<= sy;
284 else
285 pt->y >>= -sy;
286 }
287 void
gx_rect_scale_exp2(gs_fixed_rect * pr,int sx,int sy)288 gx_rect_scale_exp2(gs_fixed_rect * pr, int sx, int sy)
289 {
290 gx_point_scale_exp2(&pr->p, sx, sy);
291 gx_point_scale_exp2(&pr->q, sx, sy);
292 }
293 int
gx_path_scale_exp2_shared(gx_path * ppath,int log2_scale_x,int log2_scale_y,bool segments_shared)294 gx_path_scale_exp2_shared(gx_path * ppath, int log2_scale_x, int log2_scale_y,
295 bool segments_shared)
296 {
297 segment *pseg;
298
299 gx_rect_scale_exp2(&ppath->bbox, log2_scale_x, log2_scale_y);
300 #define SCALE_XY(pt) gx_point_scale_exp2(&pt, log2_scale_x, log2_scale_y)
301 SCALE_XY(ppath->position);
302 if (!segments_shared) {
303 for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
304 pseg = pseg->next
305 )
306 switch (pseg->type) {
307 case s_curve:
308 SCALE_XY(((curve_segment *)pseg)->p1);
309 SCALE_XY(((curve_segment *)pseg)->p2);
310 default:
311 SCALE_XY(pseg->pt);
312 }
313 }
314 #undef SCALE_XY
315 return 0;
316 }
317
318 /*
319 * Reverse a path. We know ppath != ppath_old.
320 * NOTE: in releases 5.01 and earlier, the implicit line added by closepath
321 * became the first segment of the reversed path. Starting in release
322 * 5.02, the code follows the Adobe implementation (and LanguageLevel 3
323 * specification), in which this line becomes the *last* segment of the
324 * reversed path. This can produce some quite unintuitive results.
325 */
326 int
gx_path_copy_reversed(const gx_path * ppath_old,gx_path * ppath)327 gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath)
328 {
329 const subpath *psub = ppath_old->first_subpath;
330
331 #ifdef DEBUG
332 if (gs_debug_c('P'))
333 gx_dump_path(ppath_old, "before reversepath");
334 #endif
335 nsp:
336 if (psub) {
337 const segment *prev = psub->last;
338 const segment *pseg;
339 segment_notes notes =
340 (prev == (const segment *)psub ? sn_none :
341 psub->next->notes);
342 segment_notes prev_notes;
343 int code;
344
345 if (!psub->is_closed) {
346 code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y);
347 if (code < 0)
348 return code;
349 }
350 /*
351 * The do ... while structure of this loop is artificial,
352 * designed solely to keep compilers from complaining about
353 * 'statement not reached' or 'end-of-loop code not reached'.
354 * The normal exit from this loop is the goto statement in
355 * the s_start arm of the switch.
356 */
357 do {
358 pseg = prev;
359 prev_notes = notes;
360 prev = pseg->prev;
361 notes = pseg->notes;
362 prev_notes = (prev_notes & sn_not_first) |
363 (notes & ~sn_not_first);
364 switch (pseg->type) {
365 case s_start:
366 /* Finished subpath */
367 if (psub->is_closed) {
368 code =
369 gx_path_close_subpath_notes(ppath, prev_notes);
370 if (code < 0)
371 return code;
372 }
373 psub = (const subpath *)psub->last->next;
374 goto nsp;
375 case s_curve:
376 {
377 const curve_segment *pc =
378 (const curve_segment *)pseg;
379
380 code = gx_path_add_curve_notes(ppath,
381 pc->p2.x, pc->p2.y,
382 pc->p1.x, pc->p1.y,
383 prev->pt.x, prev->pt.y, prev_notes);
384 break;
385 }
386 case s_line:
387 code = gx_path_add_line_notes(ppath,
388 prev->pt.x, prev->pt.y, prev_notes);
389 break;
390 case s_line_close:
391 /* Skip the closing line. */
392 code = gx_path_add_point(ppath, prev->pt.x,
393 prev->pt.y);
394 break;
395 default: /* not possible */
396 return_error(gs_error_Fatal);
397 }
398 } while (code >= 0);
399 return code; /* only reached if code < 0 */
400 }
401 #undef sn_not_end
402 /*
403 * In the Adobe implementations, reversepath discards a trailing
404 * moveto unless the path consists only of a moveto. We reproduce
405 * this behavior here, even though we consider it a bug.
406 */
407 if (ppath_old->first_subpath == 0 &&
408 path_last_is_moveto(ppath_old)
409 ) {
410 int code = gx_path_add_point(ppath, ppath_old->position.x,
411 ppath_old->position.y);
412
413 if (code < 0)
414 return code;
415 }
416 #ifdef DEBUG
417 if (gs_debug_c('P'))
418 gx_dump_path(ppath, "after reversepath");
419 #endif
420 return 0;
421 }
422
423 /* ------ Path enumeration ------ */
424
425 /* Allocate a path enumerator. */
426 gs_path_enum *
gs_path_enum_alloc(gs_memory_t * mem,client_name_t cname)427 gs_path_enum_alloc(gs_memory_t * mem, client_name_t cname)
428 {
429 return gs_alloc_struct(mem, gs_path_enum, &st_path_enum, cname);
430 }
431
432 /* Start enumerating a path. */
433 int
gx_path_enum_init(gs_path_enum * penum,const gx_path * ppath)434 gx_path_enum_init(gs_path_enum * penum, const gx_path * ppath)
435 {
436 penum->memory = 0; /* path not copied */
437 penum->path = ppath;
438 penum->copied_path = 0; /* not copied */
439 penum->pseg = (const segment *)ppath->first_subpath;
440 penum->moveto_done = false;
441 penum->notes = sn_none;
442 return 0;
443 }
444
445 /* Enumerate the next element of a path. */
446 /* If the path is finished, return 0; */
447 /* otherwise, return the element type. */
448 int
gx_path_enum_next(gs_path_enum * penum,gs_fixed_point ppts[3])449 gx_path_enum_next(gs_path_enum * penum, gs_fixed_point ppts[3])
450 {
451 const segment *pseg = penum->pseg;
452
453 if (pseg == 0) { /* We've enumerated all the segments, but there might be */
454 /* a trailing moveto. */
455 const gx_path *ppath = penum->path;
456
457 if (path_last_is_moveto(ppath) && !penum->moveto_done) { /* Handle a trailing moveto */
458 penum->moveto_done = true;
459 penum->notes = sn_none;
460 ppts[0] = ppath->position;
461 return gs_pe_moveto;
462 }
463 return 0;
464 }
465 penum->pseg = pseg->next;
466 penum->notes = pseg->notes;
467 switch (pseg->type) {
468 case s_start:
469 ppts[0] = pseg->pt;
470 return gs_pe_moveto;
471 case s_line:
472 ppts[0] = pseg->pt;
473 return gs_pe_lineto;
474 case s_line_close:
475 ppts[0] = pseg->pt;
476 return gs_pe_closepath;
477 case s_curve:
478 #define pcseg ((const curve_segment *)pseg)
479 ppts[0] = pcseg->p1;
480 ppts[1] = pcseg->p2;
481 ppts[2] = pseg->pt;
482 return gs_pe_curveto;
483 #undef pcseg
484 default:
485 lprintf1("bad type %x in gx_path_enum_next!\n", pseg->type);
486 return_error(gs_error_Fatal);
487 }
488 }
489
490 /* Return the notes from the last-enumerated segment. */
491 segment_notes
gx_path_enum_notes(const gs_path_enum * penum)492 gx_path_enum_notes(const gs_path_enum * penum)
493 {
494 return penum->notes;
495 }
496
497 /* Back up 1 element in the path being enumerated. */
498 /* Return true if successful, false if we are at the beginning of the path. */
499 /* This implementation allows backing up multiple times, */
500 /* but no client currently relies on this. */
501 bool
gx_path_enum_backup(gs_path_enum * penum)502 gx_path_enum_backup(gs_path_enum * penum)
503 {
504 const segment *pseg = penum->pseg;
505
506 if (pseg != 0) {
507 if ((pseg = pseg->prev) == 0)
508 return false;
509 penum->pseg = pseg;
510 return true;
511 }
512 /* We're at the end of the path. Check to see whether */
513 /* we need to back up over a trailing moveto. */
514 {
515 const gx_path *ppath = penum->path;
516
517 if (path_last_is_moveto(ppath) && penum->moveto_done) { /* Back up over the trailing moveto. */
518 penum->moveto_done = false;
519 return true;
520 } {
521 const subpath *psub = ppath->current_subpath;
522
523 if (psub == 0) /* empty path */
524 return false;
525 /* Back up to the last segment of the last subpath. */
526 penum->pseg = psub->last;
527 return true;
528 }
529 }
530 }
531