/src/ghostpdl/base/gspath1.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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 | 22.2k | #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 | 1.28M | { |
69 | 1.28M | gs_point pt; |
70 | | |
71 | 1.28M | pt.x = fixed2float(path->position.x); |
72 | 1.28M | pt.y = fixed2float(path->position.y); |
73 | 1.28M | gx_setcurrentpoint(pgs, pt.x, pt.y); |
74 | 1.28M | pgs->current_point_valid = true; |
75 | 1.28M | return 0; |
76 | 1.28M | } |
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 | 108k | { |
82 | 108k | gs_point p3; |
83 | 108k | int code = gs_gstate_arc_add(pgs->path, pgs, cw, xc, yc, rad, a1, a2, add, &p3); |
84 | | |
85 | 108k | if (code < 0) |
86 | 74 | return code; |
87 | | |
88 | | #if !PRECISE_CURRENTPOINT |
89 | | return gx_setcurrentpoint_from_path(pgs, pgs->path); |
90 | | #else |
91 | 108k | pgs->current_point_valid = true; |
92 | 108k | return gs_point_transform(p3.x, p3.y, &ctm_only(pgs), &pgs->current_point); |
93 | 108k | #endif |
94 | | |
95 | 108k | } |
96 | | |
97 | | int |
98 | | gs_arc(gs_gstate * pgs, |
99 | | double xc, double yc, double r, double ang1, double ang2) |
100 | 100k | { |
101 | 100k | return gs_arc_add_inline(pgs, false, xc, yc, r, ang1, ang2, true); |
102 | 100k | } |
103 | | |
104 | | int |
105 | | gs_arcn(gs_gstate * pgs, |
106 | | double xc, double yc, double r, double ang1, double ang2) |
107 | 8.57k | { |
108 | 8.57k | return gs_arc_add_inline(pgs, true, xc, yc, r, ang1, ang2, true); |
109 | 8.57k | } |
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 | 22.2k | { |
123 | 22.2k | double x0 = arc->p0.x = arc->p3.x; |
124 | 22.2k | double y0 = arc->p0.y = arc->p3.y; |
125 | 22.2k | double trad = arc->radius * |
126 | 22.2k | tan((anext - arc->angle) * |
127 | 22.2k | (degrees_to_radians / 2)); |
128 | | |
129 | 22.2k | arc->pt.x = x0 - trad * arc->sincos.sin; |
130 | 22.2k | arc->pt.y = y0 + trad * arc->sincos.cos; |
131 | 22.2k | gs_sincos_degrees(anext, &arc->sincos); |
132 | 22.2k | arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos; |
133 | 22.2k | arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin; |
134 | 22.2k | arc->angle = anext; |
135 | 22.2k | return arc_add(arc, false); |
136 | 22.2k | } |
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 | 388k | { |
144 | 388k | double x0 = arc->p0.x = arc->p3.x; |
145 | 388k | double y0 = arc->p0.y = arc->p3.y; |
146 | | |
147 | 388k | 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 | 95.8k | const gs_gstate *pgs = arc->pgs; |
153 | 95.8k | double scale = 0; /* Quiet gcc warning. */ |
154 | | |
155 | 95.8k | if (is_fzero2(pgs->ctm.xy, pgs->ctm.yx) ? |
156 | 267 | (scale = fabs(pgs->ctm.xx)) == fabs(pgs->ctm.yy) : |
157 | 95.8k | is_fzero2(pgs->ctm.xx, pgs->ctm.yy) ? |
158 | 0 | (scale = fabs(pgs->ctm.xy)) == fabs(pgs->ctm.yx) : |
159 | 95.5k | 0 |
160 | 95.8k | ) { |
161 | 267 | double scaled_radius = arc->radius * scale; |
162 | | |
163 | 267 | arc->scaled_radius = float2fixed(scaled_radius); |
164 | 267 | arc->quadrant_delta = |
165 | 267 | float2fixed(scaled_radius * quarter_arc_fraction); |
166 | 267 | arc->fast_quadrant = 1; |
167 | 95.5k | } else { |
168 | 95.5k | arc->fast_quadrant = -1; |
169 | 95.5k | } |
170 | 95.8k | } |
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 | 388k | switch (((int)anext >> 1) & 3) { |
176 | 97.3k | case 0: |
177 | 97.3k | arc->sincos.sin = 0, arc->sincos.cos = 1; |
178 | 97.3k | arc->p3.x = x0 = arc->center.x + arc->radius; |
179 | 97.3k | arc->p3.y = arc->center.y; |
180 | 97.3k | break; |
181 | 96.6k | case 1: |
182 | 96.6k | arc->sincos.sin = 1, arc->sincos.cos = 0; |
183 | 96.6k | arc->p3.x = arc->center.x; |
184 | 96.6k | arc->p3.y = y0 = arc->center.y + arc->radius; |
185 | 96.6k | break; |
186 | 97.3k | case 2: |
187 | 97.3k | arc->sincos.sin = 0, arc->sincos.cos = -1; |
188 | 97.3k | arc->p3.x = x0 = arc->center.x - arc->radius; |
189 | 97.3k | arc->p3.y = arc->center.y; |
190 | 97.3k | break; |
191 | 97.3k | case 3: |
192 | 97.3k | arc->sincos.sin = -1, arc->sincos.cos = 0; |
193 | 97.3k | arc->p3.x = arc->center.x; |
194 | 97.3k | arc->p3.y = y0 = arc->center.y - arc->radius; |
195 | 97.3k | break; |
196 | 388k | } |
197 | 388k | arc->pt.x = x0, arc->pt.y = y0; |
198 | 388k | arc->angle = anext; |
199 | 388k | return arc_add(arc, true); |
200 | 388k | } |
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 | 108k | { |
207 | 108k | double ar = arad; |
208 | 108k | double ang1 = aang1, ang2 = aang2, anext; |
209 | 108k | double ang1r; /* reduced angle */ |
210 | 108k | arc_curve_params_t arc; |
211 | 108k | int code; |
212 | | |
213 | 108k | arc.ppath = ppath; |
214 | 108k | arc.pgs = pgs; |
215 | 108k | arc.center.x = axc; |
216 | 108k | arc.center.y = ayc; |
217 | 108k | if (ar < 0) { |
218 | 76 | ang1 += 180; |
219 | 76 | ang2 += 180; |
220 | 76 | ar = -ar; |
221 | 76 | } |
222 | 108k | if (ang1 > (max_int - 360) || ang2 > (max_int - 360) || |
223 | 108k | ang1 < (min_int + 360) || ang2 < (min_int + 360)) |
224 | 14 | return_error(gs_error_limitcheck); |
225 | | |
226 | 108k | arc.radius = ar; |
227 | 108k | arc.action = (add_line ? arc_lineto : arc_moveto); |
228 | 108k | arc.notes = sn_none; |
229 | 108k | arc.fast_quadrant = 0; |
230 | 108k | ang1r = fmod(ang1, 360); |
231 | 108k | gs_sincos_degrees(ang1r, &arc.sincos); |
232 | 108k | arc.p3.x = axc + ar * arc.sincos.cos; |
233 | 108k | arc.p3.y = ayc + ar * arc.sincos.sin; |
234 | 108k | if (clockwise) { |
235 | 8.56k | if (ang1 < ang2) { |
236 | 1.73k | ang2 -= ceil((ang2 - ang1) / 360) * 360; |
237 | 1.73k | } |
238 | 8.56k | if (ang2 < 0) { |
239 | 1.77k | double adjust = ceil(-ang2 / 360) * 360; |
240 | | |
241 | 1.77k | ang1 += adjust, ang2 += adjust; |
242 | 1.77k | } |
243 | 8.56k | arc.angle = ang1; |
244 | 8.56k | if (ang1 == ang2) |
245 | 88 | goto last; |
246 | | /* Do the first part, up to a multiple of 90 degrees. */ |
247 | 8.48k | if (!arc.sincos.orthogonal) { |
248 | 8.47k | anext = floor(arc.angle / 90) * 90; |
249 | 8.47k | if (anext < ang2) |
250 | 45 | goto last; |
251 | 8.42k | code = next_arc_curve(&arc, anext); |
252 | 8.42k | if (code < 0) |
253 | 40 | return code; |
254 | 8.38k | arc.action = arc_nothing; |
255 | 8.38k | arc.notes = sn_not_first; |
256 | 8.38k | } |
257 | | /* Do multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0. */ |
258 | 10.8k | while ((anext = arc.angle - 90) >= ang2) { |
259 | 2.50k | code = next_arc_quadrant(&arc, anext); |
260 | 2.50k | if (code < 0) |
261 | 3 | return code; |
262 | 2.50k | arc.action = arc_nothing; |
263 | 2.50k | arc.notes = sn_not_first; |
264 | 2.50k | } |
265 | 100k | } else { |
266 | 100k | if (ang2 < ang1) { |
267 | 32 | ang2 += ceil((ang1 - ang2) / 360) * 360; |
268 | 32 | } |
269 | 100k | if (ang1 < 0) { |
270 | 14 | double adjust = ceil(-ang1 / 360) * 360; |
271 | | |
272 | 14 | ang1 += adjust, ang2 += adjust; |
273 | 14 | } |
274 | 100k | arc.angle = ang1; |
275 | 100k | if (ang1 == ang2) { |
276 | 29 | code = next_arc_curve(&arc, ang2); |
277 | 29 | if (code < 0) |
278 | 5 | return code; |
279 | 24 | *p3 = arc.p3; |
280 | 24 | } |
281 | | /* Do the first part, up to a multiple of 90 degrees. */ |
282 | 100k | if (!arc.sincos.orthogonal) { |
283 | 704 | anext = ceil(arc.angle / 90) * 90; |
284 | 704 | if (anext > ang2) |
285 | 7 | goto last; |
286 | 697 | code = next_arc_curve(&arc, anext); |
287 | 697 | if (code < 0) |
288 | 7 | return code; |
289 | 690 | arc.action = arc_nothing; |
290 | 690 | arc.notes = sn_not_first; |
291 | 690 | } |
292 | | /* Do multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2. */ |
293 | 486k | while ((anext = arc.angle + 90) <= ang2) { |
294 | 386k | code = next_arc_quadrant(&arc, anext); |
295 | 386k | if (code < 0) |
296 | 3 | return code; |
297 | 386k | arc.action = arc_nothing; |
298 | 386k | arc.notes = sn_not_first; |
299 | 386k | } |
300 | 100k | } |
301 | | /* |
302 | | * Do the last curve of the arc, if any. |
303 | | */ |
304 | 108k | if (arc.angle == ang2) { |
305 | 95.7k | *p3 = arc.p3; |
306 | 95.7k | return 0; |
307 | 95.7k | } |
308 | 13.0k | last: |
309 | 13.0k | code = next_arc_curve(&arc, ang2); |
310 | 13.0k | if (code < 0) |
311 | 2 | return code; |
312 | 13.0k | *p3 = arc.p3; |
313 | 13.0k | return 0; |
314 | 13.0k | } |
315 | | |
316 | | int |
317 | | gs_arcto(gs_gstate * pgs, |
318 | | double ax1, double ay1, double ax2, double ay2, double arad, float retxy[4]) |
319 | 92 | { |
320 | 92 | double xt0, yt0, xt2, yt2; |
321 | 92 | gs_point up0; |
322 | | |
323 | 92 | #define ax0 up0.x |
324 | 92 | #define ay0 up0.y |
325 | | /* Transform the current point back into user coordinates. */ |
326 | 92 | int code = gs_currentpoint(pgs, &up0); |
327 | | |
328 | 92 | if (code < 0) |
329 | 12 | return code; |
330 | 80 | { |
331 | 80 | double dx0, dy0, dx2, dy2, sql0, sql2; |
332 | | |
333 | | /* Now we have to compute the tangent points. */ |
334 | | /* Basically, the idea is to compute the tangent */ |
335 | | /* of the bisector by using tan(x+y) and tan(z/2) */ |
336 | | /* formulas, without ever using any trig. */ |
337 | 80 | dx0 = ax0 - ax1; dy0 = ay0 - ay1; |
338 | 80 | dx2 = ax2 - ax1; dy2 = ay2 - ay1; |
339 | | |
340 | | /* Compute the squared lengths from p1 to p0 and p2. */ |
341 | 80 | sql0 = dx0 * dx0 + dy0 * dy0; |
342 | 80 | sql2 = dx2 * dx2 + dy2 * dy2; |
343 | | |
344 | 80 | if (sql0 == 0. || sql2 == 0.) |
345 | 0 | return_error(gs_error_undefinedresult); /* for CET 11-04 */ |
346 | | |
347 | | /* Check for collinear points. */ |
348 | 80 | if (dx0*dy2 == dy0*dx2) { |
349 | 0 | code = gs_lineto(pgs, ax1, ay1); |
350 | 0 | xt0 = xt2 = ax1; |
351 | 0 | yt0 = yt2 = ay1; |
352 | 80 | } else { /* not collinear */ |
353 | | /* Compute the distance from p1 to the tangent points. */ |
354 | | /* This is the only messy part. */ |
355 | 80 | double num = dy0 * dx2 - dy2 * dx0; |
356 | 80 | double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2); |
357 | | |
358 | 80 | double dist = fabs(arad * num / denom); |
359 | 80 | double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2); |
360 | 80 | arc_curve_params_t arc; |
361 | | |
362 | 80 | arc.ppath = pgs->path; |
363 | 80 | arc.pgs = pgs; |
364 | 80 | arc.radius = arad; |
365 | 80 | arc.action = arc_lineto; |
366 | 80 | arc.notes = sn_none; |
367 | 80 | if (arad < 0) |
368 | 0 | l0 = -l0, l2 = -l2; |
369 | 80 | arc.p0.x = xt0 = ax1 + dx0 * l0; |
370 | 80 | arc.p0.y = yt0 = ay1 + dy0 * l0; |
371 | 80 | arc.p3.x = xt2 = ax1 + dx2 * l2; |
372 | 80 | arc.p3.y = yt2 = ay1 + dy2 * l2; |
373 | 80 | arc.pt.x = ax1; |
374 | 80 | arc.pt.y = ay1; |
375 | 80 | code = arc_add(&arc, false); |
376 | 80 | if (code == 0) |
377 | 80 | code = gx_setcurrentpoint_from_path(pgs, pgs->path); |
378 | 80 | } |
379 | 80 | } |
380 | 80 | if (retxy != 0) { |
381 | 0 | retxy[0] = xt0; |
382 | 0 | retxy[1] = yt0; |
383 | 0 | retxy[2] = xt2; |
384 | 0 | retxy[3] = yt2; |
385 | 0 | } |
386 | 80 | return code; |
387 | 80 | } |
388 | | |
389 | | /* Internal routine for adding an arc to the path. */ |
390 | | static int |
391 | | arc_add(const arc_curve_params_t * arc, bool is_quadrant) |
392 | 411k | { |
393 | 411k | gx_path *path = arc->ppath; |
394 | 411k | gs_gstate *pgs = arc->pgs; |
395 | 411k | double x0 = arc->p0.x, y0 = arc->p0.y; |
396 | 411k | double xt = arc->pt.x, yt = arc->pt.y; |
397 | 411k | double fraction; |
398 | 411k | gs_fixed_point p0, p2, p3, pt; |
399 | 411k | int code; |
400 | | |
401 | 411k | if ((arc->action != arc_nothing && |
402 | | #if !PRECISE_CURRENTPOINT |
403 | | (code = gs_point_transform2fixed(&pgs->ctm, x0, y0, &p0)) < 0) || |
404 | | (code = gs_point_transform2fixed(&pgs->ctm, xt, yt, &pt)) < 0 || |
405 | | (code = gs_point_transform2fixed(&pgs->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 |
406 | | #else |
407 | 108k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, x0, y0, &p0)) < 0) || |
408 | 411k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, xt, yt, &pt)) < 0 || |
409 | 410k | (code = gs_point_transform2fixed_rounding(&pgs->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 |
410 | 411k | #endif |
411 | 411k | ) |
412 | 60 | return code; |
413 | 410k | #if PRECISE_CURRENTPOINT |
414 | 410k | if (!path_position_valid(path)) |
415 | 122 | gs_point_transform(arc->p0.x, arc->p0.y, &ctm_only(arc->pgs), &pgs->subpath_start); |
416 | 410k | #endif |
417 | 410k | code = (arc->action == arc_nothing ? |
418 | 302k | (p0.x = path->position.x, p0.y = path->position.y, 0) : |
419 | 410k | arc->action == arc_lineto && path_position_valid(path) ? |
420 | 108k | gx_path_add_line(path, p0.x, p0.y) : |
421 | | /* action == arc_moveto, or lineto with no current point */ |
422 | 108k | gx_path_add_point(path, p0.x, p0.y)); |
423 | 410k | if (code < 0) |
424 | 0 | return code; |
425 | | /* Compute the fraction coefficient for the curve. */ |
426 | | /* See gx_path_add_partial_arc for details. */ |
427 | 410k | if (is_quadrant) { |
428 | | /* one of |dx| and |dy| is r, the other is zero */ |
429 | 388k | fraction = quarter_arc_fraction; |
430 | 388k | if (arc->fast_quadrant > 0) { |
431 | | /* |
432 | | * The CTM is well-behaved, and we have pre-calculated the delta |
433 | | * from the circumference points to the control points. |
434 | | */ |
435 | 7.10k | fixed delta = arc->quadrant_delta; |
436 | | |
437 | 7.10k | if (pt.x != p0.x) |
438 | 3.52k | p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta); |
439 | 7.10k | if (pt.y != p0.y) |
440 | 3.50k | p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta); |
441 | 7.10k | p2.x = (pt.x == p3.x ? p3.x : |
442 | 7.10k | pt.x > p3.x ? p3.x + delta : p3.x - delta); |
443 | 7.10k | p2.y = (pt.y == p3.y ? p3.y : |
444 | 7.10k | pt.y > p3.y ? p3.y + delta : p3.y - delta); |
445 | 7.10k | goto add; |
446 | 7.10k | } |
447 | 388k | } else { |
448 | 22.2k | double r = arc->radius; |
449 | 22.2k | double dx = xt - x0, dy = yt - y0; |
450 | 22.2k | double dist = dx * dx + dy * dy; |
451 | 22.2k | double r2 = r * r; |
452 | | |
453 | 22.2k | if (dist >= r2 * 1.0e8) /* almost zero radius; */ |
454 | | /* the >= catches dist == r == 0 */ |
455 | 108 | fraction = 0.0; |
456 | 22.1k | else |
457 | 22.1k | fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2)); |
458 | 22.2k | } |
459 | 403k | p0.x += (fixed)((pt.x - p0.x) * fraction); |
460 | 403k | p0.y += (fixed)((pt.y - p0.y) * fraction); |
461 | 403k | p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction); |
462 | 403k | p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction); |
463 | 410k | add: |
464 | 410k | if_debug8m('r', path->memory, |
465 | 410k | "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n", |
466 | 410k | fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y, |
467 | 410k | (int)arc->action); |
468 | | |
469 | | /* Open-code gx_path_add_partial_arc_notes */ |
470 | 410k | return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y, |
471 | 410k | arc->notes | sn_from_arc); |
472 | 403k | } |
473 | | |
474 | | void |
475 | | make_quadrant_arc(gs_point *p, const gs_point *c, |
476 | | const gs_point *p0, const gs_point *p1, double r) |
477 | 3.86k | { |
478 | 3.86k | p[0].x = c->x + p0->x * r; |
479 | 3.86k | p[0].y = c->y + p0->y * r; |
480 | 3.86k | p[1].x = c->x + p0->x * r + p1->x * r * quarter_arc_fraction; |
481 | 3.86k | p[1].y = c->y + p0->y * r + p1->y * r * quarter_arc_fraction; |
482 | 3.86k | p[2].x = c->x + p0->x * r * quarter_arc_fraction + p1->x * r; |
483 | 3.86k | p[2].y = c->y + p0->y * r * quarter_arc_fraction + p1->y * r; |
484 | 3.86k | p[3].x = c->x + p1->x * r; |
485 | 3.86k | p[3].y = c->y + p1->y * r; |
486 | 3.86k | } |
487 | | |
488 | | /* ------ Path transformers ------ */ |
489 | | |
490 | | int |
491 | | gs_dashpath(gs_gstate * pgs) |
492 | 0 | { |
493 | 0 | gx_path *ppath; |
494 | 0 | gx_path fpath; |
495 | 0 | int code; |
496 | |
|
497 | 0 | if (gs_currentdash_length(pgs) == 0) |
498 | 0 | return 0; /* no dash pattern */ |
499 | 0 | code = gs_flattenpath(pgs); |
500 | 0 | if (code < 0) |
501 | 0 | return code; |
502 | 0 | ppath = pgs->path; |
503 | 0 | gx_path_init_local(&fpath, ppath->memory); |
504 | 0 | code = gx_path_add_dash_expansion(ppath, &fpath, pgs); |
505 | 0 | if (code < 0) { |
506 | 0 | gx_path_free(&fpath, "gs_dashpath"); |
507 | 0 | return code; |
508 | 0 | } |
509 | 0 | gx_path_assign_free(pgs->path, &fpath); |
510 | 0 | return 0; |
511 | 0 | } |
512 | | |
513 | | int |
514 | | gs_flattenpath(gs_gstate * pgs) |
515 | 1.51k | { |
516 | 1.51k | gx_path *ppath = pgs->path; |
517 | 1.51k | gx_path fpath; |
518 | 1.51k | int code; |
519 | | |
520 | 1.51k | if (!gx_path_has_curves(ppath)) |
521 | 892 | return 0; /* nothing to do */ |
522 | 618 | gx_path_init_local(&fpath, ppath->memory); |
523 | 618 | code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness, |
524 | 618 | pgs->accurate_curves); |
525 | 618 | if (code < 0) { |
526 | 0 | gx_path_free(&fpath, "gs_flattenpath"); |
527 | 0 | return code; |
528 | 0 | } |
529 | 618 | gx_path_assign_free(ppath, &fpath); |
530 | 618 | return 0; |
531 | 618 | } |
532 | | |
533 | | int |
534 | | gs_reversepath(gs_gstate * pgs) |
535 | 42 | { |
536 | 42 | gx_path *ppath = pgs->path; |
537 | 42 | gx_path rpath; |
538 | 42 | int code; |
539 | | |
540 | 42 | gx_path_init_local(&rpath, ppath->memory); |
541 | 42 | code = gx_path_copy_reversed(ppath, &rpath); |
542 | 42 | if (code < 0) { |
543 | 0 | gx_path_free(&rpath, "gs_reversepath"); |
544 | 0 | return code; |
545 | 0 | } |
546 | 42 | if (pgs->current_point_valid) { |
547 | | /* Not empty. */ |
548 | 7 | gx_setcurrentpoint(pgs, fixed2float(rpath.position.x), |
549 | 7 | fixed2float(rpath.position.y)); |
550 | 7 | if (rpath.first_subpath != 0) { |
551 | 0 | pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x); |
552 | 0 | pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y); |
553 | 0 | } |
554 | 7 | } |
555 | 42 | gx_path_assign_free(ppath, &rpath); |
556 | 42 | return 0; |
557 | 42 | } |
558 | | |
559 | | /* ------ Accessors ------ */ |
560 | | |
561 | | int |
562 | | gs_upathbbox(gs_gstate * pgs, gs_rect * pbox, bool include_moveto) |
563 | 65.4k | { |
564 | 65.4k | gs_fixed_rect fbox; /* box in device coordinates */ |
565 | 65.4k | gs_rect dbox; |
566 | 65.4k | int code = gx_path_bbox_set(pgs->path, &fbox); |
567 | | |
568 | 65.4k | if (code < 0) |
569 | 556 | return code; |
570 | | /* If the path ends with a moveto and include_moveto is true, */ |
571 | | /* include the moveto in the bounding box. */ |
572 | 64.8k | if (path_last_is_moveto(pgs->path) && include_moveto) { |
573 | 0 | gs_fixed_point pt; |
574 | |
|
575 | 0 | code = gx_path_current_point_inline(pgs, &pt); |
576 | 0 | if (code < 0) |
577 | 0 | return code; |
578 | 0 | if (pt.x < fbox.p.x) |
579 | 0 | fbox.p.x = pt.x; |
580 | 0 | if (pt.y < fbox.p.y) |
581 | 0 | fbox.p.y = pt.y; |
582 | 0 | if (pt.x > fbox.q.x) |
583 | 0 | fbox.q.x = pt.x; |
584 | 0 | if (pt.y > fbox.q.y) |
585 | 0 | fbox.q.y = pt.y; |
586 | 0 | } |
587 | | /* Transform the result back to user coordinates. */ |
588 | 64.8k | dbox.p.x = fixed2float(fbox.p.x); |
589 | 64.8k | dbox.p.y = fixed2float(fbox.p.y); |
590 | 64.8k | dbox.q.x = fixed2float(fbox.q.x); |
591 | 64.8k | dbox.q.y = fixed2float(fbox.q.y); |
592 | 64.8k | return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox); |
593 | 64.8k | } |
594 | | |
595 | | /* ------ Enumerators ------ */ |
596 | | |
597 | | /* Start enumerating a path */ |
598 | | int |
599 | | gs_path_enum_copy_init(gs_memory_t *mem, gs_path_enum * penum, const gs_gstate * pgs, bool copy) |
600 | 1 | { |
601 | 1 | if (copy) { |
602 | 0 | gx_path *copied_path = |
603 | 0 | gx_path_alloc(mem, "gs_path_enum_init"); |
604 | 0 | int code; |
605 | |
|
606 | 0 | if (copied_path == 0) |
607 | 0 | return_error(gs_error_VMerror); |
608 | 0 | code = gx_path_copy(pgs->path, copied_path); |
609 | 0 | if (code < 0) { |
610 | 0 | gx_path_free(copied_path, "gs_path_enum_init"); |
611 | 0 | return code; |
612 | 0 | } |
613 | 0 | gx_path_enum_init(penum, copied_path); |
614 | 0 | penum->copied_path = copied_path; |
615 | 1 | } else { |
616 | 1 | gx_path_enum_init(penum, pgs->path); |
617 | 1 | } |
618 | 1 | penum->memory = mem; |
619 | 1 | gs_currentmatrix(pgs, &penum->mat); |
620 | 1 | return 0; |
621 | 1 | } |
622 | | |
623 | | /* Enumerate the next element of a path. */ |
624 | | /* If the path is finished, return 0; */ |
625 | | /* otherwise, return the element type. */ |
626 | | int |
627 | | gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3]) |
628 | 1 | { |
629 | 1 | gs_fixed_point fpts[3]; |
630 | 1 | int pe_op = gx_path_enum_next(penum, fpts); |
631 | 1 | int code; |
632 | | |
633 | 1 | switch (pe_op) { |
634 | 1 | case 0: /* all done */ |
635 | 1 | case gs_pe_closepath: |
636 | 1 | break; |
637 | 0 | case gs_pe_curveto: |
638 | 0 | if ((code = gs_point_transform_inverse( |
639 | 0 | fixed2float(fpts[1].x), |
640 | 0 | fixed2float(fpts[1].y), |
641 | 0 | &penum->mat, &ppts[1])) < 0 || |
642 | 0 | (code = gs_point_transform_inverse( |
643 | 0 | fixed2float(fpts[2].x), |
644 | 0 | fixed2float(fpts[2].y), |
645 | 0 | &penum->mat, &ppts[2])) < 0) |
646 | 0 | return code; |
647 | | /* falls through */ |
648 | 0 | case gs_pe_moveto: |
649 | 0 | case gs_pe_lineto: |
650 | 0 | case gs_pe_gapto: |
651 | 0 | if ((code = gs_point_transform_inverse( |
652 | 0 | fixed2float(fpts[0].x), |
653 | 0 | fixed2float(fpts[0].y), |
654 | 0 | &penum->mat, &ppts[0])) < 0) |
655 | 0 | return code; |
656 | 0 | default: /* error */ |
657 | 0 | break; |
658 | 1 | } |
659 | 1 | return pe_op; |
660 | 1 | } |
661 | | |
662 | | /* Clean up after a pathforall. */ |
663 | | void |
664 | | gs_path_enum_cleanup(gs_path_enum * penum) |
665 | 0 | { |
666 | 0 | if (penum->copied_path != 0) { |
667 | 0 | gx_path_free(penum->copied_path, "gs_path_enum_cleanup"); |
668 | 0 | penum->path = 0; |
669 | 0 | penum->copied_path = 0; |
670 | 0 | } |
671 | 0 | } |