/src/ghostpdl/base/gxpcopy.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 | | /* Path copying and flattening */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gxfixed.h" |
22 | | #include "gxfarith.h" |
23 | | #include "gxgstate.h" /* for access to line params */ |
24 | | #include "gzpath.h" |
25 | | |
26 | | /* Forward declarations */ |
27 | | static void adjust_point_to_tangent(segment *, const segment *, |
28 | | const gs_fixed_point *); |
29 | | |
30 | | static inline int |
31 | | break_line_if_long(gx_path *ppath, const segment *pseg) |
32 | 100M | { |
33 | 100M | fixed x0 = ppath->position.x; |
34 | 100M | fixed y0 = ppath->position.y; |
35 | | |
36 | 100M | if (gx_check_fixed_diff_overflow(pseg->pt.x, x0) || |
37 | 100M | gx_check_fixed_diff_overflow(pseg->pt.y, y0)) { |
38 | 34.3k | fixed x, y; |
39 | | |
40 | 34.3k | if (gx_check_fixed_sum_overflow(pseg->pt.x, x0)) |
41 | 2.05k | x = (pseg->pt.x >> 1) + (x0 >> 1); |
42 | 32.3k | else |
43 | 32.3k | x = (pseg->pt.x + x0) >> 1; |
44 | 34.3k | if (gx_check_fixed_sum_overflow(pseg->pt.y, y0)) |
45 | 1.14k | y = (pseg->pt.y >> 1) + (y0 >> 1); |
46 | 33.2k | else |
47 | 33.2k | y = (pseg->pt.y + y0) >> 1; |
48 | 34.3k | return gx_path_add_line_notes(ppath, x, y, pseg->notes); |
49 | | /* WARNING: Stringly speaking, the next half segment must get |
50 | | the sn_not_first flag. We don't bother, because that flag |
51 | | has no important meaning with colinear segments. |
52 | | */ |
53 | 34.3k | } |
54 | 100M | return 0; |
55 | 100M | } |
56 | | static inline int |
57 | | break_gap_if_long(gx_path *ppath, const segment *pseg) |
58 | 0 | { |
59 | 0 | fixed x0 = ppath->position.x; |
60 | 0 | fixed y0 = ppath->position.y; |
61 | |
|
62 | 0 | if (gx_check_fixed_diff_overflow(pseg->pt.x, x0) || |
63 | 0 | gx_check_fixed_diff_overflow(pseg->pt.y, y0)) { |
64 | 0 | fixed x, y; |
65 | |
|
66 | 0 | if (gx_check_fixed_sum_overflow(pseg->pt.x, x0)) |
67 | 0 | x = (pseg->pt.x >> 1) + (x0 >> 1); |
68 | 0 | else |
69 | 0 | x = (pseg->pt.x + x0) >> 1; |
70 | 0 | if (gx_check_fixed_sum_overflow(pseg->pt.y, y0)) |
71 | 0 | y = (pseg->pt.y >> 1) + (y0 >> 1); |
72 | 0 | else |
73 | 0 | y = (pseg->pt.y + y0) >> 1; |
74 | 0 | return gx_path_add_gap_notes(ppath, x, y, pseg->notes); |
75 | | /* WARNING: Stringly speaking, the next half segment must get |
76 | | the sn_not_first flag. We don't bother, because that flag |
77 | | has no important meaning with colinear segments. |
78 | | */ |
79 | 0 | } |
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | | /* Copy a path, optionally flattening or monotonizing it. */ |
84 | | /* If the copy fails, free the new path. */ |
85 | | int |
86 | | gx_path_copy_reducing(const gx_path *ppath_old, gx_path *ppath, |
87 | | fixed fixed_flatness, const gs_gstate *pgs, |
88 | | gx_path_copy_options options) |
89 | 18.5M | { |
90 | 18.5M | const segment *pseg; |
91 | 18.5M | fixed flat = fixed_flatness; |
92 | 18.5M | gs_fixed_point expansion; |
93 | | /* |
94 | | * Since we're going to be adding to the path, unshare it |
95 | | * before we start. |
96 | | */ |
97 | 18.5M | int code = gx_path_unshare(ppath); |
98 | | |
99 | 18.5M | if (code < 0) |
100 | 0 | return code; |
101 | | #ifdef DEBUG |
102 | | if (gs_debug_c('P')) |
103 | | gx_dump_path(ppath_old, "before reducing"); |
104 | | #endif |
105 | 18.5M | if (options & pco_for_stroke) { |
106 | | /* Precompute the maximum expansion of the bounding box. */ |
107 | 254k | double width = pgs->line_params.half_width; |
108 | | |
109 | 254k | expansion.x = |
110 | 254k | float2fixed((fabs(pgs->ctm.xx) + fabs(pgs->ctm.yx)) * width) * 2; |
111 | 254k | expansion.y = |
112 | 254k | float2fixed((fabs(pgs->ctm.xy) + fabs(pgs->ctm.yy)) * width) * 2; |
113 | 254k | } else |
114 | 18.3M | expansion.x = expansion.y = 0; /* Quiet gcc warning. */ |
115 | 18.5M | pseg = (const segment *)(ppath_old->first_subpath); |
116 | 227M | while (pseg) { |
117 | 208M | switch (pseg->type) { |
118 | 19.8M | case s_start: |
119 | 19.8M | code = gx_path_add_point(ppath, |
120 | 19.8M | pseg->pt.x, pseg->pt.y); |
121 | 19.8M | break; |
122 | 88.9M | case s_curve: |
123 | 88.9M | { |
124 | 88.9M | const curve_segment *pc = (const curve_segment *)pseg; |
125 | | |
126 | 88.9M | if (fixed_flatness == max_fixed) { /* don't flatten */ |
127 | 55.2M | if (options & pco_monotonize) |
128 | 0 | code = gx_curve_monotonize(ppath, pc); |
129 | 55.2M | else |
130 | 55.2M | code = gx_path_add_curve_notes(ppath, |
131 | 55.2M | pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y, |
132 | 55.2M | pc->pt.x, pc->pt.y, pseg->notes); |
133 | 55.2M | } else { |
134 | 33.7M | fixed x0 = ppath->position.x; |
135 | 33.7M | fixed y0 = ppath->position.y; |
136 | 33.7M | segment_notes notes = pseg->notes; |
137 | 33.7M | curve_segment cseg; |
138 | 33.7M | int k; |
139 | | |
140 | 33.7M | if (options & pco_for_stroke) { |
141 | | /* |
142 | | * When flattening for stroking, the flatness |
143 | | * must apply to the outside of the resulting |
144 | | * stroked region. We approximate this by |
145 | | * dividing the flatness by the ratio of the |
146 | | * expanded bounding box to the original |
147 | | * bounding box. This is crude, but pretty |
148 | | * simple to calculate, and produces reasonably |
149 | | * good results. |
150 | | */ |
151 | 2.45M | fixed min01, max01, min23, max23; |
152 | 2.45M | fixed ex, ey, flat_x, flat_y; |
153 | | |
154 | 2.45M | #define SET_EXTENT(r, c0, c1, c2, c3)\ |
155 | 4.90M | BEGIN\ |
156 | 4.90M | if (c0 < c1) min01 = c0, max01 = c1;\ |
157 | 4.90M | else min01 = c1, max01 = c0;\ |
158 | 4.90M | if (c2 < c3) min23 = c2, max23 = c3;\ |
159 | 4.90M | else min23 = c3, max23 = c2;\ |
160 | 4.90M | r = max(max01, max23) - min(min01, min23);\ |
161 | 4.90M | END |
162 | 2.45M | SET_EXTENT(ex, x0, pc->p1.x, pc->p2.x, pc->pt.x); |
163 | 2.45M | SET_EXTENT(ey, y0, pc->p1.y, pc->p2.y, pc->pt.y); |
164 | 2.45M | #undef SET_EXTENT |
165 | | /* |
166 | | * We check for the degenerate case specially |
167 | | * to avoid a division by zero. |
168 | | */ |
169 | 2.45M | if (ex == 0 || ey == 0) |
170 | 222k | if (ex != 0) { |
171 | 82.9k | flat = fixed_mult_quo(fixed_flatness, ex, |
172 | 82.9k | ex + expansion.x); |
173 | 82.9k | k = gx_curve_log2_samples(x0,y0,pc,flat); |
174 | 139k | } else if (ey != 0) { |
175 | 8.65k | flat = fixed_mult_quo(fixed_flatness, ey, |
176 | 8.65k | ey + expansion.y); |
177 | 8.65k | k = gx_curve_log2_samples(x0,y0,pc,flat); |
178 | 8.65k | } else |
179 | 131k | k = 0; |
180 | 2.22M | else { |
181 | 2.22M | flat_x = |
182 | 2.22M | fixed_mult_quo(fixed_flatness, ex, |
183 | 2.22M | ex + expansion.x); |
184 | 2.22M | flat_y = |
185 | 2.22M | fixed_mult_quo(fixed_flatness, ey, |
186 | 2.22M | ey + expansion.y); |
187 | 2.22M | flat = min(flat_x, flat_y); |
188 | 2.22M | k = gx_curve_log2_samples(x0, y0, pc, flat); |
189 | 2.22M | } |
190 | 2.45M | } else |
191 | 31.3M | k = gx_curve_log2_samples(x0, y0, pc, flat); |
192 | 33.7M | if (options & pco_accurate) { |
193 | 33.7M | segment *start; |
194 | 33.7M | segment *end; |
195 | | |
196 | | /* |
197 | | * Add an extra line, which will become |
198 | | * the tangent segment. |
199 | | */ |
200 | 33.7M | code = gx_path_add_line_notes(ppath, x0, y0, |
201 | 33.7M | notes); |
202 | 33.7M | if (code < 0) |
203 | 0 | break; |
204 | 33.7M | start = ppath->current_subpath->last; |
205 | 33.7M | notes |= sn_not_first; |
206 | 33.7M | cseg = *pc; |
207 | 33.7M | code = gx_subdivide_curve(ppath, k, &cseg, notes); |
208 | 33.7M | if (code < 0) |
209 | 0 | break; |
210 | | /* |
211 | | * Adjust the first and last segments so that |
212 | | * they line up with the tangents. |
213 | | */ |
214 | 33.7M | end = ppath->current_subpath->last; |
215 | 33.7M | if ((code = gx_path_add_line_notes(ppath, |
216 | 33.7M | ppath->position.x, |
217 | 33.7M | ppath->position.y, |
218 | 33.7M | pseg->notes | sn_not_first)) < 0) |
219 | 0 | break; |
220 | 33.7M | if (start->next->pt.x != pc->p1.x || start->next->pt.y != pc->p1.y) |
221 | 33.6M | adjust_point_to_tangent(start, start->next, &pc->p1); |
222 | 165k | else if (start->next->pt.x != pc->p2.x || start->next->pt.y != pc->p2.y) |
223 | 495 | adjust_point_to_tangent(start, start->next, &pc->p2); |
224 | 164k | else |
225 | 164k | adjust_point_to_tangent(start, start->next, &end->prev->pt); |
226 | 33.7M | if (end->prev->pt.x != pc->p2.x || end->prev->pt.y != pc->p2.y) |
227 | 33.5M | adjust_point_to_tangent(end, end->prev, &pc->p2); |
228 | 174k | else if (end->prev->pt.x != pc->p1.x || end->prev->pt.y != pc->p1.y) |
229 | 2.62k | adjust_point_to_tangent(end, end->prev, &pc->p1); |
230 | 171k | else |
231 | 171k | adjust_point_to_tangent(end, end->prev, &start->pt); |
232 | 33.7M | } else { |
233 | 0 | cseg = *pc; |
234 | 0 | code = gx_subdivide_curve(ppath, k, &cseg, notes); |
235 | 0 | } |
236 | 33.7M | } |
237 | 88.9M | break; |
238 | 88.9M | } |
239 | 88.9M | case s_line: |
240 | 82.6M | code = break_line_if_long(ppath, pseg); |
241 | 82.6M | if (code < 0) |
242 | 0 | break; |
243 | 82.6M | code = gx_path_add_line_notes(ppath, |
244 | 82.6M | pseg->pt.x, pseg->pt.y, pseg->notes); |
245 | 82.6M | break; |
246 | 0 | case s_gap: |
247 | 0 | code = break_gap_if_long(ppath, pseg); |
248 | 0 | if (code < 0) |
249 | 0 | break; |
250 | 0 | code = gx_path_add_gap_notes(ppath, |
251 | 0 | pseg->pt.x, pseg->pt.y, pseg->notes); |
252 | 0 | break; |
253 | 0 | case s_dash: |
254 | 0 | { |
255 | 0 | const dash_segment *pd = (const dash_segment *)pseg; |
256 | |
|
257 | 0 | code = gx_path_add_dash_notes(ppath, |
258 | 0 | pd->pt.x, pd->pt.y, pd->tangent.x, pd->tangent.y, pseg->notes); |
259 | 0 | break; |
260 | 0 | } |
261 | 17.4M | case s_line_close: |
262 | 17.4M | code = break_line_if_long(ppath, pseg); |
263 | 17.4M | if (code < 0) |
264 | 0 | break; |
265 | 17.4M | code = gx_path_close_subpath(ppath); |
266 | 17.4M | break; |
267 | 0 | default: /* can't happen */ |
268 | 0 | code = gs_note_error(gs_error_unregistered); |
269 | 208M | } |
270 | 208M | if (code < 0) { |
271 | 0 | gx_path_new(ppath); |
272 | 0 | return code; |
273 | 0 | } |
274 | 208M | pseg = pseg->next; |
275 | 208M | } |
276 | 18.5M | if (path_last_is_moveto(ppath_old)) { |
277 | 1.95M | code = gx_path_add_point(ppath, ppath_old->position.x, |
278 | 1.95M | ppath_old->position.y); |
279 | 1.95M | if (code < 0) { |
280 | 0 | gx_path_new(ppath); |
281 | 0 | return code; |
282 | 0 | } |
283 | 1.95M | } |
284 | 18.5M | if (ppath_old->bbox_set) { |
285 | 0 | if (ppath->bbox_set) { |
286 | 0 | ppath->bbox.p.x = min(ppath_old->bbox.p.x, ppath->bbox.p.x); |
287 | 0 | ppath->bbox.p.y = min(ppath_old->bbox.p.y, ppath->bbox.p.y); |
288 | 0 | ppath->bbox.q.x = max(ppath_old->bbox.q.x, ppath->bbox.q.x); |
289 | 0 | ppath->bbox.q.y = max(ppath_old->bbox.q.y, ppath->bbox.q.y); |
290 | 0 | } else { |
291 | 0 | ppath->bbox_set = true; |
292 | 0 | ppath->bbox = ppath_old->bbox; |
293 | 0 | } |
294 | 0 | } |
295 | | #ifdef DEBUG |
296 | | if (gs_debug_c('P')) |
297 | | gx_dump_path(ppath, "after reducing"); |
298 | | #endif |
299 | 18.5M | return 0; |
300 | 18.5M | } |
301 | | |
302 | | /* |
303 | | * Adjust one end of a line (the first or last line of a flattened curve) |
304 | | * so it falls on the curve tangent. The closest point on the line from |
305 | | * (0,0) to (C,D) to a point (U,V) -- i.e., the point on the line at which |
306 | | * a perpendicular line from the point intersects it -- is given by |
307 | | * T = (C*U + D*V) / (C^2 + D^2) |
308 | | * (X,Y) = (C*T,D*T) |
309 | | * However, any smaller value of T will also work: the one we actually |
310 | | * use is 0.25 * the value we just derived. We must check that |
311 | | * numerical instabilities don't lead to a negative value of T. |
312 | | */ |
313 | | static void |
314 | | adjust_point_to_tangent(segment * pseg, const segment * next, |
315 | | const gs_fixed_point * p1) |
316 | 67.5M | { |
317 | 67.5M | const fixed x0 = pseg->pt.x, y0 = pseg->pt.y; |
318 | 67.5M | const fixed fC = p1->x - x0, fD = p1->y - y0; |
319 | | |
320 | | /* |
321 | | * By far the commonest case is that the end of the curve is |
322 | | * horizontal or vertical. Check for this specially, because |
323 | | * we can handle it with far less work (and no floating point). |
324 | | */ |
325 | 67.5M | if (fC == 0) { |
326 | | /* Vertical tangent. */ |
327 | 13.9M | const fixed DT = arith_rshift(next->pt.y - y0, 2); |
328 | | |
329 | 13.9M | if (fD == 0) |
330 | 728k | return; /* anomalous case */ |
331 | 13.9M | if_debug1('2', "[2]adjusting vertical: DT = %g\n", |
332 | 13.2M | fixed2float(DT)); |
333 | 13.2M | if ((DT ^ fD) > 0) /* lgtm [cpp/bitwise-sign-check] */ |
334 | 13.1M | pseg->pt.y = DT + y0; |
335 | 53.5M | } else if (fD == 0) { |
336 | | /* Horizontal tangent. */ |
337 | 16.6M | const fixed CT = arith_rshift(next->pt.x - x0, 2); |
338 | | |
339 | 16.6M | if_debug1('2', "[2]adjusting horizontal: CT = %g\n", |
340 | 16.6M | fixed2float(CT)); |
341 | 16.6M | if ((CT ^ fC) > 0) /* lgtm [cpp/bitwise-sign-check] */ |
342 | 16.4M | pseg->pt.x = CT + x0; |
343 | 36.9M | } else { |
344 | | /* General case. */ |
345 | 36.9M | const double C = fC, D = fD; |
346 | 36.9M | double T = (C * (next->pt.x - x0) + D * (next->pt.y - y0)) / |
347 | 36.9M | (C * C + D * D); |
348 | | |
349 | 36.9M | if_debug3('2', "[2]adjusting: C = %g, D = %g, T = %g\n", |
350 | 36.9M | C, D, T); |
351 | 36.9M | if (T > 0) { |
352 | 36.8M | if (T > 1) { |
353 | | /* Don't go outside the curve bounding box. */ |
354 | 16.7M | T = 1; |
355 | 16.7M | } |
356 | 36.8M | pseg->pt.x = arith_rshift((fixed) (C * T), 2) + x0; |
357 | 36.8M | pseg->pt.y = arith_rshift((fixed) (D * T), 2) + y0; |
358 | 36.8M | } |
359 | 36.9M | } |
360 | 67.5M | } |
361 | | |
362 | | /* ---------------- Monotonic curves ---------------- */ |
363 | | |
364 | | /* Test whether a path is free of non-monotonic curves. */ |
365 | | bool |
366 | | gx_path__check_curves(const gx_path * ppath, gx_path_copy_options options, fixed fixed_flat) |
367 | 0 | { |
368 | 0 | const segment *pseg = (const segment *)(ppath->first_subpath); |
369 | 0 | gs_fixed_point pt0; |
370 | |
|
371 | 0 | pt0.x = pt0.y = 0; /* Quiet gcc warning. */ |
372 | 0 | while (pseg) { |
373 | 0 | switch (pseg->type) { |
374 | 0 | case s_start: |
375 | 0 | { |
376 | 0 | const subpath *psub = (const subpath *)pseg; |
377 | | |
378 | | /* Skip subpaths without curves. */ |
379 | 0 | if (!psub->curve_count) |
380 | 0 | pseg = psub->last; |
381 | 0 | } |
382 | 0 | break; |
383 | 0 | case s_line: |
384 | 0 | case s_gap: |
385 | 0 | if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) || |
386 | 0 | gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y)) |
387 | 0 | return false; |
388 | 0 | break; |
389 | 0 | case s_curve: |
390 | 0 | { |
391 | 0 | const curve_segment *pc = (const curve_segment *)pseg; |
392 | |
|
393 | 0 | if (options & pco_monotonize) { |
394 | 0 | double t[2]; |
395 | 0 | int nz = gx_curve_monotonic_points(pt0.y, |
396 | 0 | pc->p1.y, pc->p2.y, pc->pt.y, t); |
397 | |
|
398 | 0 | if (nz != 0) |
399 | 0 | return false; |
400 | 0 | nz = gx_curve_monotonic_points(pt0.x, |
401 | 0 | pc->p1.x, pc->p2.x, pc->pt.x, t); |
402 | 0 | if (nz != 0) |
403 | 0 | return false; |
404 | 0 | } |
405 | 0 | if (options & pco_small_curves) { |
406 | 0 | fixed ax, bx, cx, ay, by, cy; |
407 | 0 | int k = gx_curve_log2_samples(pt0.x, pt0.y, pc, fixed_flat); |
408 | |
|
409 | 0 | if(!curve_coeffs_ranged(pt0.x, pc->p1.x, pc->p2.x, pc->pt.x, |
410 | 0 | pt0.y, pc->p1.y, pc->p2.y, pc->pt.y, |
411 | 0 | &ax, &bx, &cx, &ay, &by, &cy, k)) |
412 | 0 | return false; |
413 | 0 | if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) || |
414 | 0 | gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y)) |
415 | 0 | return false; |
416 | 0 | } |
417 | 0 | } |
418 | 0 | break; |
419 | 0 | default: |
420 | 0 | ; |
421 | 0 | } |
422 | 0 | pt0 = pseg->pt; |
423 | 0 | pseg = pseg->next; |
424 | 0 | } |
425 | 0 | return true; |
426 | 0 | } |
427 | | |
428 | | /* Test whether a path is free of long segments. */ |
429 | | /* WARNING : This function checks the distance between |
430 | | * the starting point and the ending point of a segment. |
431 | | * When they are not too far, a curve nevertheless may be too long. |
432 | | * Don't worry about it here, because we assume |
433 | | * this function is never called with paths which have curves. |
434 | | */ |
435 | | bool |
436 | | gx_path_has_long_segments(const gx_path * ppath) |
437 | 4.01M | { |
438 | 4.01M | const segment *pseg = (const segment *)(ppath->first_subpath); |
439 | 4.01M | gs_fixed_point pt0; |
440 | | |
441 | 4.01M | pt0.x = pt0.y = 0; /* Quiet gcc warning. */ |
442 | 19.4M | while (pseg) { |
443 | 15.3M | switch (pseg->type) { |
444 | 4.34M | case s_start: |
445 | 4.34M | break; |
446 | 11.0M | default: |
447 | 11.0M | if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) || |
448 | 11.0M | gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y)) |
449 | 6.94k | return true; |
450 | 11.0M | break; |
451 | 15.3M | } |
452 | 15.3M | pt0 = pseg->pt; |
453 | 15.3M | pseg = pseg->next; |
454 | 15.3M | } |
455 | 4.00M | return false; |
456 | 4.01M | } |
457 | | |
458 | | /* Monotonize a curve, by splitting it if necessary. */ |
459 | | /* In the worst case, this could split the curve into 9 pieces. */ |
460 | | int |
461 | | gx_curve_monotonize(gx_path * ppath, const curve_segment * pc) |
462 | 0 | { |
463 | 0 | fixed x0 = ppath->position.x, y0 = ppath->position.y; |
464 | 0 | segment_notes notes = pc->notes; |
465 | 0 | double t[5], tt = 1, tp; |
466 | 0 | int c[5]; |
467 | 0 | int n0, n1, n, i, j, k = 0; |
468 | 0 | fixed ax, bx, cx, ay, by, cy, v01, v12; |
469 | 0 | fixed px, py, qx, qy, rx, ry, sx, sy; |
470 | 0 | const double delta = 0.0000001; |
471 | | |
472 | | /* Roots of the derivative : */ |
473 | 0 | n0 = gx_curve_monotonic_points(x0, pc->p1.x, pc->p2.x, pc->pt.x, t); |
474 | 0 | n1 = gx_curve_monotonic_points(y0, pc->p1.y, pc->p2.y, pc->pt.y, t + n0); |
475 | 0 | n = n0 + n1; |
476 | 0 | if (n == 0) |
477 | 0 | return gx_path_add_curve_notes(ppath, pc->p1.x, pc->p1.y, |
478 | 0 | pc->p2.x, pc->p2.y, pc->pt.x, pc->pt.y, notes); |
479 | 0 | if (n0 > 0) |
480 | 0 | c[0] = 1; |
481 | 0 | if (n0 > 1) |
482 | 0 | c[1] = 1; |
483 | 0 | if (n1 > 0) |
484 | 0 | c[n0] = 2; |
485 | 0 | if (n1 > 1) |
486 | 0 | c[n0 + 1] = 2; |
487 | | /* Order roots : */ |
488 | 0 | for (i = 0; i < n; i++) |
489 | 0 | for (j = i + 1; j < n; j++) |
490 | 0 | if (t[i] > t[j]) { |
491 | 0 | int w; |
492 | 0 | double v = t[i]; t[i] = t[j]; t[j] = v; |
493 | 0 | w = c[i]; c[i] = c[j]; c[j] = w; |
494 | 0 | } |
495 | | /* Drop roots near zero : */ |
496 | 0 | for (k = 0; k < n; k++) |
497 | 0 | if (t[k] >= delta) |
498 | 0 | break; |
499 | | /* Merge close roots, and drop roots at 1 : */ |
500 | 0 | if (t[n - 1] > 1 - delta) |
501 | 0 | n--; |
502 | 0 | for (i = k + 1, j = k; i < n && t[k] < 1 - delta; i++) |
503 | 0 | if (any_abs(t[i] - t[j]) < delta) { |
504 | 0 | t[j] = (t[j] + t[i]) / 2; /* Unlikely 3 roots are close. */ |
505 | 0 | c[j] |= c[i]; |
506 | 0 | } else { |
507 | 0 | j++; |
508 | 0 | t[j] = t[i]; |
509 | 0 | c[j] = c[i]; |
510 | 0 | } |
511 | 0 | n = j + 1; |
512 | | /* Do split : */ |
513 | 0 | curve_points_to_coefficients(x0, pc->p1.x, pc->p2.x, pc->pt.x, ax, bx, cx, v01, v12); |
514 | 0 | curve_points_to_coefficients(y0, pc->p1.y, pc->p2.y, pc->pt.y, ay, by, cy, v01, v12); |
515 | 0 | ax *= 3, bx *= 2; /* Coefficients of the derivative. */ |
516 | 0 | ay *= 3, by *= 2; |
517 | 0 | px = x0; |
518 | 0 | py = y0; |
519 | 0 | qx = (fixed)((pc->p1.x - px) * t[0] + 0.5); |
520 | 0 | qy = (fixed)((pc->p1.y - py) * t[0] + 0.5); |
521 | 0 | tp = 0; |
522 | 0 | for (i = k; i < n; i++) { |
523 | 0 | double ti = t[i]; |
524 | 0 | double t2 = ti * ti, t3 = t2 * ti; |
525 | 0 | double omt = 1 - ti, omt2 = omt * omt, omt3 = omt2 * omt; |
526 | 0 | double x = x0 * omt3 + 3 * pc->p1.x * omt2 * ti + 3 * pc->p2.x * omt * t2 + pc->pt.x * t3; |
527 | 0 | double y = y0 * omt3 + 3 * pc->p1.y * omt2 * ti + 3 * pc->p2.y * omt * t2 + pc->pt.y * t3; |
528 | 0 | double ddx = (c[i] & 1 ? 0 : ax * t2 + bx * ti + cx); /* Suppress noise. */ |
529 | 0 | double ddy = (c[i] & 2 ? 0 : ay * t2 + by * ti + cy); |
530 | 0 | fixed dx = (fixed)(ddx + 0.5); |
531 | 0 | fixed dy = (fixed)(ddy + 0.5); |
532 | 0 | int code; |
533 | |
|
534 | 0 | tt = (i + 1 < n ? t[i + 1] : 1) - ti; |
535 | 0 | rx = (fixed)(dx * (t[i] - tp) / 3 + 0.5); |
536 | 0 | ry = (fixed)(dy * (t[i] - tp) / 3 + 0.5); |
537 | 0 | sx = (fixed)(x + 0.5); |
538 | 0 | sy = (fixed)(y + 0.5); |
539 | | /* Suppress the derivative sign noise near a peak : */ |
540 | 0 | if ((double)(sx - px) * qx + (double)(sy - py) * qy < 0) |
541 | 0 | qx = -qx, qy = -qy; |
542 | 0 | if ((double)(sx - px) * rx + (double)(sy - py) * ry < 0) |
543 | 0 | rx = -rx, ry = -qy; |
544 | | /* Do add : */ |
545 | 0 | code = gx_path_add_curve_notes(ppath, px + qx, py + qy, sx - rx, sy - ry, sx, sy, notes); |
546 | 0 | if (code < 0) |
547 | 0 | return code; |
548 | 0 | notes |= sn_not_first; |
549 | 0 | px = sx; |
550 | 0 | py = sy; |
551 | 0 | qx = (fixed)(dx * tt / 3 + 0.5); |
552 | 0 | qy = (fixed)(dy * tt / 3 + 0.5); |
553 | 0 | tp = t[i]; |
554 | 0 | } |
555 | 0 | sx = pc->pt.x; |
556 | 0 | sy = pc->pt.y; |
557 | 0 | rx = (fixed)((pc->pt.x - pc->p2.x) * tt + 0.5); |
558 | 0 | ry = (fixed)((pc->pt.y - pc->p2.y) * tt + 0.5); |
559 | | /* Suppress the derivative sign noise near peaks : */ |
560 | 0 | if ((double)(sx - px) * qx + (double)(sy - py) * qy < 0) |
561 | 0 | qx = -qx, qy = -qy; |
562 | 0 | if ((double)(sx - px) * rx + (double)(sy - py) * ry < 0) |
563 | 0 | rx = -rx, ry = -qy; |
564 | 0 | return gx_path_add_curve_notes(ppath, px + qx, py + qy, sx - rx, sy - ry, sx, sy, notes); |
565 | 0 | } |
566 | | |
567 | | /* |
568 | | * Split a curve if necessary into pieces that are monotonic in X or Y as a |
569 | | * function of the curve parameter t. This allows us to rasterize curves |
570 | | * directly without pre-flattening. This takes a fair amount of analysis.... |
571 | | * Store the values of t of the split points in pst[0] and pst[1]. Return |
572 | | * the number of split points (0, 1, or 2). |
573 | | */ |
574 | | int |
575 | | gx_curve_monotonic_points(fixed v0, fixed v1, fixed v2, fixed v3, |
576 | | double pst[2]) |
577 | 0 | { |
578 | | /* |
579 | | Let |
580 | | v(t) = a*t^3 + b*t^2 + c*t + d, 0 <= t <= 1. |
581 | | Then |
582 | | dv(t) = 3*a*t^2 + 2*b*t + c. |
583 | | v(t) has a local minimum or maximum (or inflection point) |
584 | | precisely where dv(t) = 0. Now the roots of dv(t) = 0 (i.e., |
585 | | the zeros of dv(t)) are at |
586 | | t = ( -2*b +/- sqrt(4*b^2 - 12*a*c) ) / 6*a |
587 | | = ( -b +/- sqrt(b^2 - 3*a*c) ) / 3*a |
588 | | (Note that real roots exist iff b^2 >= 3*a*c.) |
589 | | We want to know if these lie in the range (0..1). |
590 | | (The endpoints don't count.) Call such a root a "valid zero." |
591 | | Since computing the roots is expensive, we would like to have |
592 | | some cheap tests to filter out cases where they don't exist |
593 | | (i.e., where the curve is already monotonic). |
594 | | */ |
595 | 0 | fixed v01, v12, a, b, c, b2, a3; |
596 | 0 | fixed dv_end, b2abs, a3abs; |
597 | |
|
598 | 0 | curve_points_to_coefficients(v0, v1, v2, v3, a, b, c, v01, v12); |
599 | 0 | b2 = b << 1; |
600 | 0 | a3 = (a << 1) + a; |
601 | | /* |
602 | | If a = 0, the only possible zero is t = -c / 2*b. |
603 | | This zero is valid iff sign(c) != sign(b) and 0 < |c| < 2*|b|. |
604 | | */ |
605 | 0 | if (a == 0) { |
606 | 0 | if ((b ^ c) < 0 && any_abs(c) < any_abs(b2) && c != 0) { |
607 | 0 | *pst = (double)(-c) / b2; |
608 | 0 | return 1; |
609 | 0 | } else |
610 | 0 | return 0; |
611 | 0 | } |
612 | | /* |
613 | | Iff a curve is horizontal at t = 0, c = 0. In this case, |
614 | | there can be at most one other zero, at -2*b / 3*a. |
615 | | This zero is valid iff sign(a) != sign(b) and 0 < 2*|b| < 3*|a|. |
616 | | */ |
617 | 0 | if (c == 0) { |
618 | 0 | if ((a ^ b) < 0 && any_abs(b2) < any_abs(a3) && b != 0) { |
619 | 0 | *pst = (double)(-b2) / a3; |
620 | 0 | return 1; |
621 | 0 | } else |
622 | 0 | return 0; |
623 | 0 | } |
624 | | /* |
625 | | Similarly, iff a curve is horizontal at t = 1, 3*a + 2*b + c = 0. |
626 | | In this case, there can be at most one other zero, |
627 | | at -1 - 2*b / 3*a, iff sign(a) != sign(b) and 1 < -2*b / 3*a < 2, |
628 | | i.e., 3*|a| < 2*|b| < 6*|a|. |
629 | | */ |
630 | 0 | else if ((dv_end = a3 + b2 + c) == 0) { |
631 | 0 | if ((a ^ b) < 0 && |
632 | 0 | (b2abs = any_abs(b2)) > (a3abs = any_abs(a3)) && |
633 | 0 | b2abs < a3abs << 1 |
634 | 0 | ) { |
635 | 0 | *pst = (double)(-b2 - a3) / a3; |
636 | 0 | return 1; |
637 | 0 | } else |
638 | 0 | return 0; |
639 | 0 | } |
640 | | /* |
641 | | If sign(dv_end) != sign(c), at least one valid zero exists, |
642 | | since dv(0) and dv(1) have opposite signs and hence |
643 | | dv(t) must be zero somewhere in the interval [0..1]. |
644 | | */ |
645 | 0 | else if ((dv_end ^ c) < 0); |
646 | | /* |
647 | | If sign(a) = sign(b), no valid zero exists, |
648 | | since dv is monotonic on [0..1] and has the same sign |
649 | | at both endpoints. |
650 | | */ |
651 | 0 | else if ((a ^ b) >= 0) |
652 | 0 | return 0; |
653 | | /* |
654 | | Otherwise, dv(t) may be non-monotonic on [0..1]; it has valid zeros |
655 | | iff its sign anywhere in this interval is different from its sign |
656 | | at the endpoints, which occurs iff it has an extremum in this |
657 | | interval and the extremum is of the opposite sign from c. |
658 | | To find this out, we look for the local extremum of dv(t) |
659 | | by observing |
660 | | d2v(t) = 6*a*t + 2*b |
661 | | which has a zero only at |
662 | | t1 = -b / 3*a |
663 | | Now if t1 <= 0 or t1 >= 1, no valid zero exists. |
664 | | Note that we just determined that sign(a) != sign(b), so we know t1 > 0. |
665 | | */ |
666 | 0 | else if (any_abs(b) >= any_abs(a3)) |
667 | 0 | return 0; |
668 | | /* |
669 | | Otherwise, we just go ahead with the computation of the roots, |
670 | | and test them for being in the correct range. Note that a valid |
671 | | zero is an inflection point of v(t) iff d2v(t) = 0; we don't |
672 | | bother to check for this case, since it's rare. |
673 | | */ |
674 | 0 | { |
675 | 0 | double nbf = (double)(-b); |
676 | 0 | double a3f = (double)a3; |
677 | 0 | double radicand = nbf * nbf - a3f * c; |
678 | |
|
679 | 0 | if (radicand < 0) { |
680 | 0 | if_debug1('2', "[2]negative radicand = %g\n", radicand); |
681 | 0 | return 0; |
682 | 0 | } { |
683 | 0 | double root = sqrt(radicand); |
684 | 0 | int nzeros = 0; |
685 | 0 | double z = (nbf - root) / a3f; |
686 | | |
687 | | /* |
688 | | * We need to return the zeros in the correct order. |
689 | | * We know that root is non-negative, but a3f may be either |
690 | | * positive or negative, so we need to check the ordering |
691 | | * explicitly. |
692 | | */ |
693 | 0 | if_debug2('2', "[2]zeros at %g, %g\n", z, (nbf + root) / a3f); |
694 | 0 | if (z > 0 && z < 1) |
695 | 0 | *pst = z, nzeros = 1; |
696 | 0 | if (root != 0) { |
697 | 0 | z = (nbf + root) / a3f; |
698 | 0 | if (z > 0 && z < 1) { |
699 | 0 | if (nzeros && a3f < 0) /* order is reversed */ |
700 | 0 | pst[1] = *pst, *pst = z; |
701 | 0 | else |
702 | 0 | pst[nzeros] = z; |
703 | 0 | nzeros++; |
704 | 0 | } |
705 | 0 | } |
706 | 0 | return nzeros; |
707 | 0 | } |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | | /* ---------------- Path optimization for the filling algorithm. ---------------- */ |
712 | | |
713 | | static bool |
714 | | find_contacting_segments(const subpath *sp0, segment *sp0last, |
715 | | const subpath *sp1, segment *sp1last, |
716 | | segment **sc0, segment **sc1) |
717 | 0 | { |
718 | 0 | segment *s0, *s1; |
719 | 0 | const segment *s0s, *s1s; |
720 | 0 | int count0, count1, search_limit = 50; |
721 | 0 | int min_length = fixed_1 * 1; |
722 | | |
723 | | /* This is a simplified algorithm, which only checks for quazi-colinear vertical lines. |
724 | | "Quazi-vertical" means dx <= 1 && dy >= min_length . */ |
725 | | /* To avoid a big unuseful expence of the processor time, |
726 | | we search the first subpath from the end |
727 | | (assuming that it was recently merged near the end), |
728 | | and restrict the search with search_limit segments |
729 | | against a quadratic scanning of two long subpaths. |
730 | | Thus algorithm is not necessary finds anything contacting. |
731 | | Instead it either quickly finds something, or maybe not. */ |
732 | 0 | for (s0 = sp0last, count0 = 0; count0 < search_limit && s0 != (segment *)sp0; s0 = s0->prev, count0++) { |
733 | 0 | s0s = s0->prev; |
734 | 0 | if ((s0->type == s_line || s0->type == s_gap) && |
735 | 0 | (s0s->pt.x == s0->pt.x || |
736 | 0 | (any_abs(s0s->pt.x - s0->pt.x) == 1 && |
737 | 0 | any_abs(s0s->pt.y - s0->pt.y) > min_length))) { |
738 | 0 | for (s1 = sp1last, count1 = 0; count1 < search_limit && s1 != (segment *)sp1; s1 = s1->prev, count1++) { |
739 | 0 | s1s = s1->prev; |
740 | 0 | if ((s1->type == s_line || s1->type == s_gap) && |
741 | 0 | (s1s->pt.x == s1->pt.x || |
742 | 0 | (any_abs(s1s->pt.x - s1->pt.x) == 1 && any_abs(s1s->pt.y - s1->pt.y) > min_length))) { |
743 | 0 | if (s0s->pt.x == s1s->pt.x || s0->pt.x == s1->pt.x || s0->pt.x == s1s->pt.x || s0s->pt.x == s1->pt.x) { |
744 | 0 | if (s0s->pt.y < s0->pt.y && s1s->pt.y > s1->pt.y) { |
745 | 0 | fixed y0 = max(s0s->pt.y, s1->pt.y); |
746 | 0 | fixed y1 = min(s0->pt.y, s1s->pt.y); |
747 | |
|
748 | 0 | if (y0 <= y1) { |
749 | 0 | *sc0 = s0; |
750 | 0 | *sc1 = s1; |
751 | 0 | return true; |
752 | 0 | } |
753 | 0 | } |
754 | 0 | if (s0s->pt.y > s0->pt.y && s1s->pt.y < s1->pt.y) { |
755 | 0 | fixed y0 = max(s0->pt.y, s1s->pt.y); |
756 | 0 | fixed y1 = min(s0s->pt.y, s1->pt.y); |
757 | |
|
758 | 0 | if (y0 <= y1) { |
759 | 0 | *sc0 = s0; |
760 | 0 | *sc1 = s1; |
761 | 0 | return true; |
762 | 0 | } |
763 | 0 | } |
764 | 0 | } |
765 | 0 | } |
766 | 0 | } |
767 | 0 | } |
768 | 0 | } |
769 | 0 | return false; |
770 | 0 | } |
771 | | |
772 | | int |
773 | | gx_path_merge_contacting_contours(gx_path *ppath) |
774 | 0 | { |
775 | | /* Now this is a simplified algorithm, |
776 | | which merge only contours by a common quazi-vertical line. */ |
777 | | /* Note the merged contour is not equivalent to sum of original contours, |
778 | | because we ignore small coordinate differences within fixed_epsilon. */ |
779 | 0 | int window = 5/* max spot holes */ * 6/* segments per subpath */; |
780 | 0 | subpath *sp0 = ppath->segments->contents.subpath_first; |
781 | |
|
782 | 0 | for (; sp0 != NULL; sp0 = (subpath *)sp0->last->next) { |
783 | 0 | segment *sp0last = sp0->last; |
784 | 0 | subpath *sp1 = (subpath *)sp0last->next, *spnext; |
785 | 0 | subpath *sp1p = sp0; |
786 | 0 | int count; |
787 | |
|
788 | 0 | for (count = 0; sp1 != NULL && count < window; sp1 = spnext, count++) { |
789 | 0 | segment *sp1last = sp1->last; |
790 | 0 | segment *sc0, *sc1, *old_first; |
791 | |
|
792 | 0 | spnext = (subpath *)sp1last->next; |
793 | 0 | if (find_contacting_segments(sp0, sp0last, sp1, sp1last, &sc0, &sc1)) { |
794 | | /* Detach the subpath 1 from the path: */ |
795 | 0 | sp1->prev->next = sp1last->next; |
796 | 0 | if (sp1last->next != NULL) |
797 | 0 | sp1last->next->prev = sp1->prev; |
798 | 0 | sp1->prev = 0; |
799 | 0 | sp1last->next = 0; |
800 | 0 | old_first = sp1->next; |
801 | | /* sp1 is not longer in use. Move subpath_current from it for safe removing : */ |
802 | 0 | if (ppath->segments->contents.subpath_current == sp1) { |
803 | 0 | ppath->segments->contents.subpath_current = sp1p; |
804 | 0 | } |
805 | 0 | if (sp1last->type == s_line_close) { |
806 | | /* Change 'closepath' of the subpath 1 to a line (maybe degenerate) : */ |
807 | 0 | sp1last->type = s_line; |
808 | | /* sp1 is not longer in use. Free it : */ |
809 | 0 | gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours"); |
810 | 0 | } else if (sp1last->pt.x == sp1->pt.x && sp1last->pt.y == sp1->pt.y) { |
811 | | /* Implicit closepath with zero length. Don't need a new segment. */ |
812 | | /* sp1 is not longer in use. Free it : */ |
813 | 0 | gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours"); |
814 | 0 | } else { |
815 | | /* Insert the closing line segment. */ |
816 | | /* sp1 is not longer in use. Convert it to the line segment : */ |
817 | 0 | sp1->type = s_line; |
818 | 0 | sp1last->next = (segment *)sp1; |
819 | 0 | sp1->next = NULL; |
820 | 0 | sp1->prev = sp1last; |
821 | 0 | sp1->last = NULL; /* Safety for garbager. */ |
822 | 0 | sp1last = (segment *)sp1; |
823 | 0 | } |
824 | 0 | sp1 = 0; /* Safety. */ |
825 | | /* Rotate the subpath 1 to sc1 : */ |
826 | 0 | { /* Detach s_start and make a loop : */ |
827 | 0 | sp1last->next = old_first; |
828 | 0 | old_first->prev = sp1last; |
829 | | /* Unlink before sc1 : */ |
830 | 0 | sp1last = sc1->prev; |
831 | 0 | sc1->prev->next = 0; |
832 | 0 | sc1->prev = 0; /* Safety. */ |
833 | | /* sp1 is not longer in use. Free it : */ |
834 | 0 | if (ppath->segments->contents.subpath_current == sp1) { |
835 | 0 | ppath->segments->contents.subpath_current = sp1p; |
836 | 0 | } |
837 | 0 | gs_free_object(gs_memory_stable(ppath->memory), sp1, "gx_path_merge_contacting_contours"); |
838 | 0 | sp1 = 0; /* Safety. */ |
839 | 0 | } |
840 | | /* Insert the subpath 1 into the subpath 0 before sc0 :*/ |
841 | 0 | sc0->prev->next = sc1; |
842 | 0 | sc1->prev = sc0->prev; |
843 | 0 | sp1last->next = sc0; |
844 | 0 | sc0->prev = sp1last; |
845 | | /* Remove degenearte "bridge" segments : (fixme: Not done due to low importance). */ |
846 | | /* Edit the subpath count : */ |
847 | 0 | ppath->subpath_count--; |
848 | 0 | } else |
849 | 0 | sp1p = sp1; |
850 | 0 | } |
851 | 0 | } |
852 | 0 | return 0; |
853 | 0 | } |
854 | | |
855 | | static int |
856 | | is_colinear(gs_fixed_rect *rect, fixed x, fixed y) |
857 | 0 | { |
858 | 0 | fixed x0 = rect->p.x; |
859 | 0 | fixed y0 = rect->p.y; |
860 | 0 | fixed x1 = rect->q.x; |
861 | 0 | fixed y1 = rect->q.y; |
862 | |
|
863 | 0 | if (x0 == x1) { |
864 | 0 | if (y0 == y1) { |
865 | | /* Initial case */ |
866 | | /* Still counts as colinear */ |
867 | 0 | } else if (x == x0) { |
868 | | /* OK! */ |
869 | 0 | } else { |
870 | 0 | return 0; /* Not colinear */ |
871 | 0 | } |
872 | 0 | } else if (rect->p.y == rect->q.y) { |
873 | 0 | if (y == rect->p.y) { |
874 | | /* OK */ |
875 | 0 | } else { |
876 | 0 | return 0; /* Not colinear */ |
877 | 0 | } |
878 | 0 | } else { |
879 | | /* Need to do hairy maths */ |
880 | | /* The distance of a point (x,y) from the line passing through |
881 | | * (x0,y0) and (x1,y1) is: |
882 | | * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2) |
883 | | * |
884 | | * We want d <= epsilon to count as colinear. |
885 | | * |
886 | | * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2) <= epsilon |
887 | | * |
888 | | * |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| <= epsilon * SQR((y1-y0)^2 + (x1-x0)^2) |
889 | | * |
890 | | * ((y1-y0)x - (x1-x0)y + x1y0 - y1x0)^2 <= epsilon^2 * ((y1-y0)^2 + (x1-x0)^2) |
891 | | */ |
892 | 0 | int64_t ix1 = ((int64_t)x1); |
893 | 0 | int64_t iy1 = ((int64_t)y1); |
894 | 0 | int64_t dx = ix1 - x0; |
895 | 0 | int64_t dy = iy1 - y0; |
896 | 0 | int64_t num = dy*x - dx*y + ix1*y0 - iy1*x0; |
897 | 0 | int64_t den = dx*dx + dy*dy; |
898 | 0 | int epsilon_squared = 2; |
899 | |
|
900 | 0 | if (num < 0) |
901 | 0 | num = -num; |
902 | 0 | while (num > (1<<30)) { |
903 | 0 | num >>= 2; |
904 | 0 | den >>= 1; |
905 | 0 | if (den == 0) |
906 | 0 | return 0; /* Not colinear */ |
907 | 0 | } |
908 | 0 | num *= num; |
909 | 0 | if (num > epsilon_squared * den) |
910 | 0 | return 0; |
911 | 0 | } |
912 | | /* rect is not really a rect. It's just a pair of points. We guarantee that x0 <= x1. */ |
913 | 0 | if (x == x0) { |
914 | 0 | if (y < y0) |
915 | 0 | rect->p.y = y; |
916 | 0 | else if (y > y1) |
917 | 0 | rect->q.y = y; |
918 | 0 | } else if (x < x0) { |
919 | 0 | rect->p.x = x; |
920 | 0 | rect->p.y = y; |
921 | 0 | } else { |
922 | 0 | rect->q.x = x; |
923 | 0 | rect->q.y = y; |
924 | 0 | } |
925 | |
|
926 | 0 | return 1; |
927 | 0 | } |
928 | | |
929 | | static int |
930 | | gx_path_copy_eliding_1d(const gx_path *ppath_old, gx_path *ppath) |
931 | 0 | { |
932 | 0 | const segment *pseg; |
933 | | /* |
934 | | * Since we're going to be adding to the path, unshare it |
935 | | * before we start. |
936 | | */ |
937 | 0 | int code = gx_path_unshare(ppath); |
938 | |
|
939 | 0 | if (code < 0) |
940 | 0 | return code; |
941 | | #ifdef DEBUG |
942 | | if (gs_debug_c('P')) |
943 | | gx_dump_path(ppath_old, "before eliding_1d"); |
944 | | #endif |
945 | | |
946 | 0 | pseg = (const segment *)(ppath_old->first_subpath); |
947 | 0 | while (pseg != NULL) { |
948 | 0 | const segment *look = pseg; |
949 | 0 | gs_fixed_rect rect; |
950 | |
|
951 | 0 | rect.p.x = rect.q.x = look->pt.x; |
952 | 0 | rect.p.y = rect.q.y = look->pt.y; |
953 | |
|
954 | 0 | if (look->type != s_start) { |
955 | 0 | dlprintf("Unlikely?"); |
956 | 0 | } |
957 | |
|
958 | 0 | look = look->next; |
959 | 0 | while (look != NULL && look->type != s_start) { |
960 | 0 | if (look->type == s_curve) { |
961 | 0 | const curve_segment *pc = (const curve_segment *)look; |
962 | 0 | if (!is_colinear(&rect, pc->p1.x, pc->p1.y) || |
963 | 0 | !is_colinear(&rect, pc->p2.x, pc->p2.y) || |
964 | 0 | !is_colinear(&rect, pc->pt.x, pc->pt.y)) |
965 | 0 | goto not_colinear; |
966 | 0 | } else if (!is_colinear(&rect, look->pt.x, look->pt.y)) { |
967 | 0 | goto not_colinear; |
968 | 0 | } |
969 | 0 | look = look->next; |
970 | 0 | } |
971 | 0 | pseg = look; |
972 | 0 | if (0) |
973 | 0 | { |
974 | 0 | not_colinear: |
975 | | /* Not colinear. We want to keep this section. */ |
976 | 0 | while (look != NULL && look->type != s_start) |
977 | 0 | look = look->next; |
978 | 0 | while (pseg != look && code >= 0) { |
979 | | /* Copy */ |
980 | 0 | switch (pseg->type) { |
981 | 0 | case s_start: |
982 | 0 | code = gx_path_add_point(ppath, |
983 | 0 | pseg->pt.x, pseg->pt.y); |
984 | 0 | break; |
985 | 0 | case s_curve: |
986 | 0 | { |
987 | 0 | const curve_segment *pc = (const curve_segment *)pseg; |
988 | |
|
989 | 0 | code = gx_path_add_curve_notes(ppath, |
990 | 0 | pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y, |
991 | 0 | pc->pt.x, pc->pt.y, pseg->notes); |
992 | 0 | break; |
993 | 0 | } |
994 | 0 | case s_line: |
995 | 0 | code = gx_path_add_line_notes(ppath, |
996 | 0 | pseg->pt.x, pseg->pt.y, pseg->notes); |
997 | 0 | break; |
998 | 0 | case s_gap: |
999 | 0 | code = gx_path_add_gap_notes(ppath, |
1000 | 0 | pseg->pt.x, pseg->pt.y, pseg->notes); |
1001 | 0 | break; |
1002 | 0 | case s_dash: |
1003 | 0 | { |
1004 | 0 | const dash_segment *pd = (const dash_segment *)pseg; |
1005 | |
|
1006 | 0 | code = gx_path_add_dash_notes(ppath, |
1007 | 0 | pd->pt.x, pd->pt.y, pd->tangent.x, pd->tangent.y, pseg->notes); |
1008 | 0 | break; |
1009 | 0 | } |
1010 | 0 | case s_line_close: |
1011 | 0 | code = gx_path_close_subpath(ppath); |
1012 | 0 | break; |
1013 | 0 | default: /* can't happen */ |
1014 | 0 | code = gs_note_error(gs_error_unregistered); |
1015 | 0 | } |
1016 | 0 | pseg = pseg->next; |
1017 | 0 | } |
1018 | 0 | if (code < 0) { |
1019 | 0 | gx_path_new(ppath); |
1020 | 0 | return code; |
1021 | 0 | } |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | ppath->bbox_set = false; |
1025 | | #ifdef DEBUG |
1026 | | if (gs_debug_c('P')) |
1027 | | gx_dump_path(ppath, "after eliding_1d"); |
1028 | | #endif |
1029 | 0 | return 0; |
1030 | 0 | } |
1031 | | |
1032 | | int |
1033 | | gx_path_elide_1d(gx_path *ppath) |
1034 | 0 | { |
1035 | 0 | int code; |
1036 | 0 | gx_path path; |
1037 | |
|
1038 | 0 | gx_path_init_local(&path, ppath->memory); |
1039 | 0 | code = gx_path_copy_eliding_1d(ppath, &path); |
1040 | 0 | if (code < 0) |
1041 | 0 | return code; |
1042 | 0 | gx_path_assign_free(ppath, &path); |
1043 | 0 | gx_path_free(&path, "gx_path_elide_1d"); |
1044 | |
|
1045 | 0 | return 0; |
1046 | 0 | } |