xref: /plan9/sys/src/cmd/gs/src/gxpath.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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