2 |
- |
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
|
|
|
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
|
|
|
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
|
|
|
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 *
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
880 |
gx_path_set_state_flags(gx_path *ppath, byte flags)
|
|
|
881 |
{
|
|
|
882 |
ppath->procs->state_flags(ppath, flags);
|
|
|
883 |
}
|
|
|
884 |
bool
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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 */
|