/src/ghostpdl/base/gspath1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Additional PostScript Level 1 path routines for Ghostscript library */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gxfixed.h" |
23 | | #include "gxfarith.h" |
24 | | #include "gxmatrix.h" |
25 | | #include "gzstate.h" |
26 | | #include "gspath.h" |
27 | | #include "gzpath.h" |
28 | | #include "gscoord.h" /* gs_itransform prototype */ |
29 | | |
30 | | /* ------ Arcs ------ */ |
31 | | |
32 | | /* Conversion parameters */ |
33 | 38 | #define degrees_to_radians (M_PI / 180.0) |
34 | | |
35 | | typedef enum { |
36 | | arc_nothing, |
37 | | arc_moveto, |
38 | | arc_lineto |
39 | | } arc_action; |
40 | | |
41 | | typedef struct arc_curve_params_s { |
42 | | /* The following are set once. */ |
43 | | gx_path *ppath; |
44 | | gs_gstate *pgs; |
45 | | gs_point center; /* (not used by arc_add) */ |
46 | | double radius; |
47 | | /* The following may be updated dynamically. */ |
48 | | arc_action action; |
49 | | segment_notes notes; |
50 | | gs_point p0, p3, pt; |
51 | | gs_sincos_t sincos; /* (not used by arc_add) */ |
52 | | double angle; /* (not used by arc_add) */ |
53 | | int fast_quadrant; /* 0 = not calculated, -1 = not fast, */ |
54 | | /* 1 = fast (only used for quadrants) */ |
55 | | /* The following are set once iff fast_quadrant > 0. */ |
56 | | fixed scaled_radius; /* radius * CTM scale */ |
57 | | fixed quadrant_delta; /* scaled_radius * quarter_arc_fraction */ |
58 | | } arc_curve_params_t; |
59 | | |
60 | | /* Forward declarations */ |
61 | | static int arc_add(const arc_curve_params_t *arc, bool is_quadrant); |
62 | | static int gs_gstate_arc_add(gx_path * ppath, gs_gstate * pgs, bool clockwise, |
63 | | double axc, double ayc, double arad, double aang1, double aang2, |
64 | | bool add_line, gs_point *p3); |
65 | | |
66 | | int |
67 | | gx_setcurrentpoint_from_path(gs_gstate *pgs, gx_path *path) |
68 | 814k | { |
69 | 814k | gs_point pt; |
70 | | |
71 | 814k | pt.x = fixed2float(path->position.x); |
72 | 814k | pt.y = fixed2float(path->position.y); |
73 | 814k | gx_setcurrentpoint(pgs, pt.x, pt.y); |
74 | 814k | pgs->current_point_valid = true; |
75 | 814k | return 0; |
76 | 814k | } |
77 | | |
78 | | static inline int |
79 | | gs_arc_add_inline(gs_gstate *pgs, bool cw, double xc, double yc, double rad, |
80 | | double a1, double a2, bool add) |
81 | 2.12k | { |
82 | 2.12k | gs_point p3; |
83 | 2.12k | int code = gs_gstate_arc_add(pgs->path, pgs, cw, xc, yc, rad, a1, a2, add, &p3); |
84 | | |
85 | 2.12k | if (code < 0) |
86 | 6 | return code; |
87 | | |
88 | | #if !PRECISE_CURRENTPOINT |
89 | | return gx_setcurrentpoint_from_path(pgs, pgs->path); |
90 | | #else |
91 | 2.11k | pgs->current_point_valid = true; |
92 | 2.11k | return gs_point_transform(p3.x, p3.y, &ctm_only(pgs), &pgs->current_point); |
93 | 2.12k | #endif |
94 | | |
95 | 2.12k | } |
96 | | |
97 | | int |
98 | | gs_arc(gs_gstate * pgs, |
99 | | double xc, double yc, double r, double ang1, double ang2) |
100 | 2.11k | { |
101 | 2.11k | return gs_arc_add_inline(pgs, false, xc, yc, r, ang1, ang2, true); |
102 | 2.11k | } |
103 | | |
104 | | int |
105 | | gs_arcn(gs_gstate * pgs, |
106 | | double xc, double yc, double r, double ang1, double ang2) |
107 | 10 | { |
108 | 10 | return gs_arc_add_inline(pgs, true, xc, yc, r, ang1, ang2, true); |
109 | 10 | } |
110 | | |
111 | | int |
112 | | gs_arc_add(gs_gstate * pgs, bool clockwise, double axc, double ayc, |
113 | | double arad, double aang1, double aang2, bool add_line) |
114 | 0 | { |
115 | 0 | return gs_arc_add_inline(pgs, clockwise, axc, ayc, arad, |
116 | 0 | aang1, aang2, add_line); |
117 | 0 | } |
118 | | |
119 | | /* Compute the next curve as part of an arc. */ |
120 | | static int |
121 | | next_arc_curve(arc_curve_params_t * arc, double anext) |
122 | 38 | { |
123 | 38 | double x0 = arc->p0.x = arc->p3.x; |
124 | 38 | double y0 = arc->p0.y = arc->p3.y; |
125 | 38 | double trad = arc->radius * |
126 | 38 | tan((anext - arc->angle) * |
127 | 38 | (degrees_to_radians / 2)); |
128 | | |
129 | 38 | arc->pt.x = x0 - trad * arc->sincos.sin; |
130 | 38 | arc->pt.y = y0 + trad * arc->sincos.cos; |
131 | 38 | gs_sincos_degrees(anext, &arc->sincos); |
132 | 38 | arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos; |
133 | 38 | arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin; |
134 | 38 | arc->angle = anext; |
135 | 38 | return arc_add(arc, false); |
136 | 38 | } |
137 | | /* |
138 | | * Use this when both arc.angle and anext are multiples of 90 degrees, |
139 | | * and anext = arc.angle +/- 90. |
140 | | */ |
141 | | static int |
142 | | next_arc_quadrant(arc_curve_params_t * arc, double anext) |
143 | 9.20k | { |
144 | 9.20k | double x0 = arc->p0.x = arc->p3.x; |
145 | 9.20k | double y0 = arc->p0.y = arc->p3.y; |
146 | | |
147 | 9.20k | if (!arc->fast_quadrant) { |
148 | | /* |
149 | | * If the CTM is well-behaved, we can pre-calculate the delta |
150 | | * from the arc points to the control points. |
151 | | */ |
152 | 2.11k | const gs_gstate *pgs = arc->pgs; |
153 | 2.11k | double scale = 0; /* Quiet gcc warning. */ |
154 | | |
155 | 2.11k | if (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ? |
156 | 17 | (scale = fabs(pgs->ctm.xx)) == fabs(pgs->ctm.yy) : |
157 | 2.11k | is_fzero2(pgs->ctm.xx, pgs->ctm.yy) ? |
158 | 0 | (scale = fabs(pgs->ctm.xy)) == fabs(pgs->ctm.yx) : |
159 | 2.09k | 0 |
160 | 2.11k | ) { |
161 | 17 | double scaled_radius = arc->radius * scale; |
162 | | |
163 | 17 | arc->scaled_radius = float2fixed(scaled_radius); |
164 | 17 | arc->quadrant_delta = |
165 | 17 | float2fixed(scaled_radius * quarter_arc_fraction); |
166 | 17 | arc->fast_quadrant = 1; |
167 | 2.09k | } else { |
168 | 2.09k | arc->fast_quadrant = -1; |
169 | 2.09k | } |
170 | 2.11k | } |
171 | | /* |
172 | | * We know that anext is a multiple of 90 (as a fixed); we want |
173 | | * (anext / 90) & 3. The following is much faster than a division. |
174 | | */ |
175 | 9.20k | switch (((int)anext >> 1) & 3) { |
176 | 2.30k | case 0: |
177 | 2.30k | arc->sincos.sin = 0, arc->sincos.cos = 1; |
178 | 2.30k | arc->p3.x = x0 = arc->center.x + arc->radius; |
179 | 2.30k | arc->p3.y = arc->center.y; |
180 | 2.30k | break; |
181 | 2.30k | case 1: |
182 | 2.30k | arc->sincos.sin = 1, arc->sincos.cos = 0; |
183 | 2.30k | arc->p3.x = arc->center.x; |
184 | 2.30k | arc->p3.y = y0 = arc->center.y + arc->radius; |
185 | 2.30k | break; |
186 | 2.30k | case 2: |
187 | 2.30k | arc->sincos.sin = 0, arc->sincos.cos = -1; |
188 | 2.30k | arc->p3.x = x0 = arc->center.x - arc->radius; |
189 | 2.30k | arc->p3.y = arc->center.y; |
190 | 2.30k | break; |
191 | 2.30k | case 3: |
192 | 2.30k | arc->sincos.sin = -1, arc->sincos.cos = 0; |
193 | 2.30k | arc->p3.x = arc->center.x; |
194 | 2.30k | arc->p3.y = y0 = arc->center.y - arc->radius; |
195 | 2.30k | break; |
196 | 9.20k | } |
197 | 9.20k | arc->pt.x = x0, arc->pt.y = y0; |
198 | 9.20k | arc->angle = anext; |
199 | 9.20k | return arc_add(arc, true); |
200 | 9.20k | } |
201 | | |
202 | | static int |
203 | | gs_gstate_arc_add(gx_path * ppath, gs_gstate * pgs, bool clockwise, |
204 | | double axc, double ayc, double arad, double aang1, double aang2, |
205 | | bool add_line, gs_point *p3) |
206 | 2.12k | { |
207 | 2.12k | double ar = arad; |
208 | 2.12k | double ang1 = aang1, ang2 = aang2, anext; |
209 | 2.12k | double ang1r; /* reduced angle */ |
210 | 2.12k | arc_curve_params_t arc; |
211 | 2.12k | int code; |
212 | | |
213 | 2.12k | arc.ppath = ppath; |
214 | 2.12k | arc.pgs = pgs; |
215 | 2.12k | arc.center.x = axc; |
216 | 2.12k | arc.center.y = ayc; |
217 | 2.12k | if (ar < 0) { |
218 | 7 | ang1 += 180; |
219 | 7 | ang2 += 180; |
220 | 7 | ar = -ar; |
221 | 7 | } |
222 | 2.12k | if (ang1 > (max_int - 360) || ang2 > (max_int - 360)) |
223 | 2 | return_error(gs_error_limitcheck); |
224 | | |
225 | 2.12k | arc.radius = ar; |
226 | 2.12k | arc.action = (add_line ? arc_lineto : arc_moveto); |
227 | 2.12k | arc.notes = sn_none; |
228 | 2.12k | arc.fast_quadrant = 0; |
229 | 2.12k | ang1r = fmod(ang1, 360); |
230 | 2.12k | gs_sincos_degrees(ang1r, &arc.sincos); |
231 | 2.12k | arc.p3.x = axc + ar * arc.sincos.cos; |
232 | 2.12k | arc.p3.y = ayc + ar * arc.sincos.sin; |
233 | 2.12k | if (clockwise) { |
234 | 9 | if (ang1 < ang2) { |
235 | 4 | ang2 -= ceil((ang2 - ang1) / 360) * 360; |
236 | 4 | } |
237 | 9 | if (ang2 < 0) { |
238 | 7 | double adjust = ceil(-ang2 / 360) * 360; |
239 | | |
240 | 7 | ang1 += adjust, ang2 += adjust; |
241 | 7 | } |
242 | 9 | arc.angle = ang1; |
243 | 9 | if (ang1 == ang2) |
244 | 4 | goto last; |
245 | | /* Do the first part, up to a multiple of 90 degrees. */ |
246 | 5 | if (!arc.sincos.orthogonal) { |
247 | 4 | anext = floor(arc.angle / 90) * 90; |
248 | 4 | if (anext < ang2) |
249 | 0 | goto last; |
250 | 4 | code = next_arc_curve(&arc, anext); |
251 | 4 | if (code < 0) |
252 | 0 | return code; |
253 | 4 | arc.action = arc_nothing; |
254 | 4 | arc.notes = sn_not_first; |
255 | 4 | } |
256 | | /* Do multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0. */ |
257 | 155 | while ((anext = arc.angle - 90) >= ang2) { |
258 | 151 | code = next_arc_quadrant(&arc, anext); |
259 | 151 | if (code < 0) |
260 | 1 | return code; |
261 | 150 | arc.action = arc_nothing; |
262 | 150 | arc.notes = sn_not_first; |
263 | 150 | } |
264 | 2.11k | } else { |
265 | 2.11k | if (ang2 < ang1) { |
266 | 7 | ang2 += ceil((ang1 - ang2) / 360) * 360; |
267 | 7 | } |
268 | 2.11k | if (ang1 < 0) { |
269 | 1 | double adjust = ceil(-ang1 / 360) * 360; |
270 | | |
271 | 1 | ang1 += adjust, ang2 += adjust; |
272 | 1 | } |
273 | 2.11k | arc.angle = ang1; |
274 | 2.11k | if (ang1 == ang2) { |
275 | 4 | code = next_arc_curve(&arc, ang2); |
276 | 4 | if (code < 0) |
277 | 1 | return code; |
278 | 3 | *p3 = arc.p3; |
279 | 3 | } |
280 | | /* Do the first part, up to a multiple of 90 degrees. */ |
281 | 2.11k | if (!arc.sincos.orthogonal) { |
282 | 11 | anext = ceil(arc.angle / 90) * 90; |
283 | 11 | if (anext > ang2) |
284 | 0 | goto last; |
285 | 11 | code = next_arc_curve(&arc, anext); |
286 | 11 | if (code < 0) |
287 | 1 | return code; |
288 | 10 | arc.action = arc_nothing; |
289 | 10 | arc.notes = sn_not_first; |
290 | 10 | } |
291 | | /* Do multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2. */ |
292 | 11.1k | while ((anext = arc.angle + 90) <= ang2) { |
293 | 9.05k | code = next_arc_quadrant(&arc, anext); |
294 | 9.05k | if (code < 0) |
295 | 1 | return code; |
296 | 9.05k | arc.action = arc_nothing; |
297 | 9.05k | arc.notes = sn_not_first; |
298 | 9.05k | } |
299 | 2.11k | } |
300 | | /* |
301 | | * Do the last curve of the arc, if any. |
302 | | */ |
303 | 2.11k | if (arc.angle == ang2) { |
304 | 2.10k | *p3 = arc.p3; |
305 | 2.10k | return 0; |
306 | 2.10k | } |
307 | 19 | last: |
308 | 19 | code = next_arc_curve(&arc, ang2); |
309 | 19 | if (code < 0) |
310 | 0 | return code; |
311 | 19 | *p3 = arc.p3; |
312 | 19 | return 0; |
313 | 19 | } |
314 | | |
315 | | int |
316 | | gs_arcto(gs_gstate * pgs, |
317 | | double ax1, double ay1, double ax2, double ay2, double arad, float retxy[4]) |
318 | 1 | { |
319 | 1 | double xt0, yt0, xt2, yt2; |
320 | 1 | gs_point up0; |
321 | | |
322 | 1 | #define ax0 up0.x |
323 | 1 | #define ay0 up0.y |
324 | | /* Transform the current point back into user coordinates. */ |
325 | 1 | int code = gs_currentpoint(pgs, &up0); |
326 | | |
327 | 1 | if (code < 0) |
328 | 1 | return code; |
329 | 0 | { |
330 | 0 | double dx0, dy0, dx2, dy2, sql0, sql2; |
331 | | |
332 | | /* Now we have to compute the tangent points. */ |
333 | | /* Basically, the idea is to compute the tangent */ |
334 | | /* of the bisector by using tan(x+y) and tan(z/2) */ |
335 | | /* formulas, without ever using any trig. */ |
336 | 0 | dx0 = ax0 - ax1; dy0 = ay0 - ay1; |
337 | 0 | dx2 = ax2 - ax1; dy2 = ay2 - ay1; |
338 | | |
339 | | /* Compute the squared lengths from p1 to p0 and p2. */ |
340 | 0 | sql0 = dx0 * dx0 + dy0 * dy0; |
341 | 0 | sql2 = dx2 * dx2 + dy2 * dy2; |
342 | |
|
343 | 0 | if (sql0 == 0. || sql2 == 0.) |
344 | 0 | return_error(gs_error_undefinedresult); /* for CET 11-04 */ |
345 | | |
346 | | /* Check for collinear points. */ |
347 | 0 | if (dx0*dy2 == dy0*dx2) { |
348 | 0 | code = gs_lineto(pgs, ax1, ay1); |
349 | 0 | xt0 = xt2 = ax1; |
350 | 0 | yt0 = yt2 = ay1; |
351 | 0 | } else { /* not collinear */ |
352 | | /* Compute the distance from p1 to the tangent points. */ |
353 | | /* This is the only messy part. */ |
354 | 0 | double num = dy0 * dx2 - dy2 * dx0; |
355 | 0 | double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2); |
356 | |
|
357 | 0 | double dist = fabs(arad * num / denom); |
358 | 0 | double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2); |
359 | 0 | arc_curve_params_t arc; |
360 | |
|
361 | 0 | arc.ppath = pgs->path; |
362 | 0 | arc.pgs = pgs; |
363 | 0 | arc.radius = arad; |
364 | 0 | arc.action = arc_lineto; |
365 | 0 | arc.notes = sn_none; |
366 | 0 | if (arad < 0) |
367 | 0 | l0 = -l0, l2 = -l2; |
368 | 0 | arc.p0.x = xt0 = ax1 + dx0 * l0; |
369 | 0 | arc.p0.y = yt0 = ay1 + dy0 * l0; |
370 | 0 | arc.p3.x = xt2 = ax1 + dx2 * l2; |
371 | 0 | arc.p3.y = yt2 = ay1 + dy2 * l2; |
372 | 0 | arc.pt.x = ax1; |
373 | 0 | arc.pt.y = ay1; |
374 | 0 | code = arc_add(&arc, false); |
375 | 0 | if (code == 0) |
376 | 0 | code = gx_setcurrentpoint_from_path(pgs, pgs->path); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | if (retxy != 0) { |
380 | 0 | retxy[0] = xt0; |
381 | 0 | retxy[1] = yt0; |
382 | 0 | retxy[2] = xt2; |
383 | 0 | retxy[3] = yt2; |
384 | 0 | } |
385 | 0 | return code; |
386 | 0 | } |
387 | | |
388 | | /* Internal routine for adding an arc to the path. */ |
389 | | static int |
390 | | arc_add(const arc_curve_params_t * arc, bool is_quadrant) |
391 | 9.24k | { |
392 | 9.24k | gx_path *path = arc->ppath; |
393 | 9.24k | gs_gstate *pgs = arc->pgs; |
394 | 9.24k | double x0 = arc->p0.x, y0 = arc->p0.y; |
395 | 9.24k | double xt = arc->pt.x, yt = arc->pt.y; |
396 | 9.24k | double fraction; |
397 | 9.24k | gs_fixed_point p0, p2, p3, pt; |
398 | 9.24k | int code; |
399 | | |
400 | 9.24k | if ((arc->action != arc_nothing && |
401 | | #if !PRECISE_CURRENTPOINT |
402 | | (code = gs_point_transform2fixed(&pgs->ctm, x0, y0, &p0)) < 0) || |
403 | | (code = gs_point_transform2fixed(&pgs->ctm, xt, yt, &pt)) < 0 || |
404 | | (code = gs_point_transform2fixed(&pgs->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 |
405 | | #else |
406 | 9.24k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, x0, y0, &p0)) < 0) || |
407 | 9.24k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, xt, yt, &pt)) < 0 || |
408 | 9.24k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 |
409 | 9.24k | #endif |
410 | 9.24k | ) |
411 | 4 | return code; |
412 | 9.24k | #if PRECISE_CURRENTPOINT |
413 | 9.24k | if (!path_position_valid(path)) |
414 | 17 | gs_point_transform(arc->p0.x, arc->p0.y, &ctm_only(arc->pgs), &pgs->subpath_start); |
415 | 9.24k | #endif |
416 | 9.24k | code = (arc->action == arc_nothing ? |
417 | 7.12k | (p0.x = path->position.x, p0.y = path->position.y, 0) : |
418 | 9.24k | arc->action == arc_lineto && path_position_valid(path) ? |
419 | 2.10k | gx_path_add_line(path, p0.x, p0.y) : |
420 | | /* action == arc_moveto, or lineto with no current point */ |
421 | 2.12k | gx_path_add_point(path, p0.x, p0.y)); |
422 | 9.24k | if (code < 0) |
423 | 0 | return code; |
424 | | /* Compute the fraction coefficient for the curve. */ |
425 | | /* See gx_path_add_partial_arc for details. */ |
426 | 9.24k | if (is_quadrant) { |
427 | | /* one of |dx| and |dy| is r, the other is zero */ |
428 | 9.20k | fraction = quarter_arc_fraction; |
429 | 9.20k | if (arc->fast_quadrant > 0) { |
430 | | /* |
431 | | * The CTM is well-behaved, and we have pre-calculated the delta |
432 | | * from the circumference points to the control points. |
433 | | */ |
434 | 821 | fixed delta = arc->quadrant_delta; |
435 | | |
436 | 821 | if (pt.x != p0.x) |
437 | 407 | p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta); |
438 | 821 | if (pt.y != p0.y) |
439 | 404 | p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta); |
440 | 821 | p2.x = (pt.x == p3.x ? p3.x : |
441 | 821 | pt.x > p3.x ? p3.x + delta : p3.x - delta); |
442 | 821 | p2.y = (pt.y == p3.y ? p3.y : |
443 | 821 | pt.y > p3.y ? p3.y + delta : p3.y - delta); |
444 | 821 | goto add; |
445 | 821 | } |
446 | 9.20k | } else { |
447 | 36 | double r = arc->radius; |
448 | 36 | double dx = xt - x0, dy = yt - y0; |
449 | 36 | double dist = dx * dx + dy * dy; |
450 | 36 | double r2 = r * r; |
451 | | |
452 | 36 | if (dist >= r2 * 1.0e8) /* almost zero radius; */ |
453 | | /* the >= catches dist == r == 0 */ |
454 | 9 | fraction = 0.0; |
455 | 27 | else |
456 | 27 | fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2)); |
457 | 36 | } |
458 | 8.42k | p0.x += (fixed)((pt.x - p0.x) * fraction); |
459 | 8.42k | p0.y += (fixed)((pt.y - p0.y) * fraction); |
460 | 8.42k | p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction); |
461 | 8.42k | p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction); |
462 | 9.24k | add: |
463 | 9.24k | if_debug8m('r', path->memory, |
464 | 9.24k | "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n", |
465 | 9.24k | fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y, |
466 | 9.24k | (int)arc->action); |
467 | | |
468 | | /* Open-code gx_path_add_partial_arc_notes */ |
469 | 9.24k | return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y, |
470 | 9.24k | arc->notes | sn_from_arc); |
471 | 8.42k | } |
472 | | |
473 | | void |
474 | | make_quadrant_arc(gs_point *p, const gs_point *c, |
475 | | const gs_point *p0, const gs_point *p1, double r) |
476 | 0 | { |
477 | 0 | p[0].x = c->x + p0->x * r; |
478 | 0 | p[0].y = c->y + p0->y * r; |
479 | 0 | p[1].x = c->x + p0->x * r + p1->x * r * quarter_arc_fraction; |
480 | 0 | p[1].y = c->y + p0->y * r + p1->y * r * quarter_arc_fraction; |
481 | 0 | p[2].x = c->x + p0->x * r * quarter_arc_fraction + p1->x * r; |
482 | 0 | p[2].y = c->y + p0->y * r * quarter_arc_fraction + p1->y * r; |
483 | 0 | p[3].x = c->x + p1->x * r; |
484 | 0 | p[3].y = c->y + p1->y * r; |
485 | 0 | } |
486 | | |
487 | | /* ------ Path transformers ------ */ |
488 | | |
489 | | int |
490 | | gs_dashpath(gs_gstate * pgs) |
491 | 0 | { |
492 | 0 | gx_path *ppath; |
493 | 0 | gx_path fpath; |
494 | 0 | int code; |
495 | |
|
496 | 0 | if (gs_currentdash_length(pgs) == 0) |
497 | 0 | return 0; /* no dash pattern */ |
498 | 0 | code = gs_flattenpath(pgs); |
499 | 0 | if (code < 0) |
500 | 0 | return code; |
501 | 0 | ppath = pgs->path; |
502 | 0 | gx_path_init_local(&fpath, ppath->memory); |
503 | 0 | code = gx_path_add_dash_expansion(ppath, &fpath, pgs); |
504 | 0 | if (code < 0) { |
505 | 0 | gx_path_free(&fpath, "gs_dashpath"); |
506 | 0 | return code; |
507 | 0 | } |
508 | 0 | gx_path_assign_free(pgs->path, &fpath); |
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | | int |
513 | | gs_flattenpath(gs_gstate * pgs) |
514 | 48 | { |
515 | 48 | gx_path *ppath = pgs->path; |
516 | 48 | gx_path fpath; |
517 | 48 | int code; |
518 | | |
519 | 48 | if (!gx_path_has_curves(ppath)) |
520 | 48 | return 0; /* nothing to do */ |
521 | 0 | gx_path_init_local(&fpath, ppath->memory); |
522 | 0 | code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness, |
523 | 0 | pgs->accurate_curves); |
524 | 0 | if (code < 0) { |
525 | 0 | gx_path_free(&fpath, "gs_flattenpath"); |
526 | 0 | return code; |
527 | 0 | } |
528 | 0 | gx_path_assign_free(ppath, &fpath); |
529 | 0 | return 0; |
530 | 0 | } |
531 | | |
532 | | int |
533 | | gs_reversepath(gs_gstate * pgs) |
534 | 3 | { |
535 | 3 | gx_path *ppath = pgs->path; |
536 | 3 | gx_path rpath; |
537 | 3 | int code; |
538 | | |
539 | 3 | gx_path_init_local(&rpath, ppath->memory); |
540 | 3 | code = gx_path_copy_reversed(ppath, &rpath); |
541 | 3 | if (code < 0) { |
542 | 0 | gx_path_free(&rpath, "gs_reversepath"); |
543 | 0 | return code; |
544 | 0 | } |
545 | 3 | if (pgs->current_point_valid) { |
546 | | /* Not empty. */ |
547 | 0 | gx_setcurrentpoint(pgs, fixed2float(rpath.position.x), |
548 | 0 | fixed2float(rpath.position.y)); |
549 | 0 | if (rpath.first_subpath != 0) { |
550 | 0 | pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x); |
551 | 0 | pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y); |
552 | 0 | } |
553 | 0 | } |
554 | 3 | gx_path_assign_free(ppath, &rpath); |
555 | 3 | return 0; |
556 | 3 | } |
557 | | |
558 | | /* ------ Accessors ------ */ |
559 | | |
560 | | int |
561 | | gs_upathbbox(gs_gstate * pgs, gs_rect * pbox, bool include_moveto) |
562 | 2.58k | { |
563 | 2.58k | gs_fixed_rect fbox; /* box in device coordinates */ |
564 | 2.58k | gs_rect dbox; |
565 | 2.58k | int code = gx_path_bbox_set(pgs->path, &fbox); |
566 | | |
567 | 2.58k | if (code < 0) |
568 | 152 | return code; |
569 | | /* If the path ends with a moveto and include_moveto is true, */ |
570 | | /* include the moveto in the bounding box. */ |
571 | 2.42k | if (path_last_is_moveto(pgs->path) && include_moveto) { |
572 | 0 | gs_fixed_point pt; |
573 | |
|
574 | 0 | code = gx_path_current_point_inline(pgs, &pt); |
575 | 0 | if (code < 0) |
576 | 0 | return code; |
577 | 0 | if (pt.x < fbox.p.x) |
578 | 0 | fbox.p.x = pt.x; |
579 | 0 | if (pt.y < fbox.p.y) |
580 | 0 | fbox.p.y = pt.y; |
581 | 0 | if (pt.x > fbox.q.x) |
582 | 0 | fbox.q.x = pt.x; |
583 | 0 | if (pt.y > fbox.q.y) |
584 | 0 | fbox.q.y = pt.y; |
585 | 0 | } |
586 | | /* Transform the result back to user coordinates. */ |
587 | 2.42k | dbox.p.x = fixed2float(fbox.p.x); |
588 | 2.42k | dbox.p.y = fixed2float(fbox.p.y); |
589 | 2.42k | dbox.q.x = fixed2float(fbox.q.x); |
590 | 2.42k | dbox.q.y = fixed2float(fbox.q.y); |
591 | 2.42k | return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox); |
592 | 2.42k | } |
593 | | |
594 | | /* ------ Enumerators ------ */ |
595 | | |
596 | | /* Start enumerating a path */ |
597 | | int |
598 | | gs_path_enum_copy_init(gs_memory_t *mem, gs_path_enum * penum, const gs_gstate * pgs, bool copy) |
599 | 0 | { |
600 | 0 | if (copy) { |
601 | 0 | gx_path *copied_path = |
602 | 0 | gx_path_alloc(mem, "gs_path_enum_init"); |
603 | 0 | int code; |
604 | |
|
605 | 0 | if (copied_path == 0) |
606 | 0 | return_error(gs_error_VMerror); |
607 | 0 | code = gx_path_copy(pgs->path, copied_path); |
608 | 0 | if (code < 0) { |
609 | 0 | gx_path_free(copied_path, "gs_path_enum_init"); |
610 | 0 | return code; |
611 | 0 | } |
612 | 0 | gx_path_enum_init(penum, copied_path); |
613 | 0 | penum->copied_path = copied_path; |
614 | 0 | } else { |
615 | 0 | gx_path_enum_init(penum, pgs->path); |
616 | 0 | } |
617 | 0 | penum->memory = mem; |
618 | 0 | gs_currentmatrix(pgs, &penum->mat); |
619 | 0 | return 0; |
620 | 0 | } |
621 | | |
622 | | /* Enumerate the next element of a path. */ |
623 | | /* If the path is finished, return 0; */ |
624 | | /* otherwise, return the element type. */ |
625 | | int |
626 | | gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3]) |
627 | 0 | { |
628 | 0 | gs_fixed_point fpts[3]; |
629 | 0 | int pe_op = gx_path_enum_next(penum, fpts); |
630 | 0 | int code; |
631 | |
|
632 | 0 | switch (pe_op) { |
633 | 0 | case 0: /* all done */ |
634 | 0 | case gs_pe_closepath: |
635 | 0 | break; |
636 | 0 | case gs_pe_curveto: |
637 | 0 | if ((code = gs_point_transform_inverse( |
638 | 0 | fixed2float(fpts[1].x), |
639 | 0 | fixed2float(fpts[1].y), |
640 | 0 | &penum->mat, &ppts[1])) < 0 || |
641 | 0 | (code = gs_point_transform_inverse( |
642 | 0 | fixed2float(fpts[2].x), |
643 | 0 | fixed2float(fpts[2].y), |
644 | 0 | &penum->mat, &ppts[2])) < 0) |
645 | 0 | return code; |
646 | | /* falls through */ |
647 | 0 | case gs_pe_moveto: |
648 | 0 | case gs_pe_lineto: |
649 | 0 | case gs_pe_gapto: |
650 | 0 | if ((code = gs_point_transform_inverse( |
651 | 0 | fixed2float(fpts[0].x), |
652 | 0 | fixed2float(fpts[0].y), |
653 | 0 | &penum->mat, &ppts[0])) < 0) |
654 | 0 | return code; |
655 | 0 | default: /* error */ |
656 | 0 | break; |
657 | 0 | } |
658 | 0 | return pe_op; |
659 | 0 | } |
660 | | |
661 | | /* Clean up after a pathforall. */ |
662 | | void |
663 | | gs_path_enum_cleanup(gs_path_enum * penum) |
664 | 0 | { |
665 | 0 | if (penum->copied_path != 0) { |
666 | 0 | gx_path_free(penum->copied_path, "gs_path_enum_cleanup"); |
667 | 0 | penum->path = 0; |
668 | 0 | penum->copied_path = 0; |
669 | 0 | } |
670 | 0 | } |