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