/work/workdir/UnpackedTarball/cairo/src/cairo-spline.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* cairo - a vector graphics library with display and print output |
2 | | * |
3 | | * Copyright © 2002 University of Southern California |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it either under the terms of the GNU Lesser General Public |
7 | | * License version 2.1 as published by the Free Software Foundation |
8 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
9 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
10 | | * notice, a recipient may use your version of this file under either |
11 | | * the MPL or the LGPL. |
12 | | * |
13 | | * You should have received a copy of the LGPL along with this library |
14 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
15 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
16 | | * You should have received a copy of the MPL along with this library |
17 | | * in the file COPYING-MPL-1.1 |
18 | | * |
19 | | * The contents of this file are subject to the Mozilla Public License |
20 | | * Version 1.1 (the "License"); you may not use this file except in |
21 | | * compliance with the License. You may obtain a copy of the License at |
22 | | * http://www.mozilla.org/MPL/ |
23 | | * |
24 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
25 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
26 | | * the specific language governing rights and limitations. |
27 | | * |
28 | | * The Original Code is the cairo graphics library. |
29 | | * |
30 | | * The Initial Developer of the Original Code is University of Southern |
31 | | * California. |
32 | | * |
33 | | * Contributor(s): |
34 | | * Carl D. Worth <cworth@cworth.org> |
35 | | */ |
36 | | |
37 | | #include "cairoint.h" |
38 | | |
39 | | #include "cairo-box-inline.h" |
40 | | #include "cairo-slope-private.h" |
41 | | |
42 | | cairo_bool_t |
43 | | _cairo_spline_intersects (const cairo_point_t *a, |
44 | | const cairo_point_t *b, |
45 | | const cairo_point_t *c, |
46 | | const cairo_point_t *d, |
47 | | const cairo_box_t *box) |
48 | 53.0k | { |
49 | 53.0k | cairo_box_t bounds; |
50 | | |
51 | 53.0k | if (_cairo_box_contains_point (box, a) || |
52 | 53.0k | _cairo_box_contains_point (box, b) || |
53 | 53.0k | _cairo_box_contains_point (box, c) || |
54 | 53.0k | _cairo_box_contains_point (box, d)) |
55 | 19.7k | { |
56 | 19.7k | return TRUE; |
57 | 19.7k | } |
58 | | |
59 | 33.3k | bounds.p2 = bounds.p1 = *a; |
60 | 33.3k | _cairo_box_add_point (&bounds, b); |
61 | 33.3k | _cairo_box_add_point (&bounds, c); |
62 | 33.3k | _cairo_box_add_point (&bounds, d); |
63 | | |
64 | 33.3k | if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || |
65 | 33.3k | bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) |
66 | 30.5k | { |
67 | 30.5k | return FALSE; |
68 | 30.5k | } |
69 | | |
70 | | #if 0 /* worth refining? */ |
71 | | bounds.p2 = bounds.p1 = *a; |
72 | | _cairo_box_add_curve_to (&bounds, b, c, d); |
73 | | if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || |
74 | | bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) |
75 | | { |
76 | | return FALSE; |
77 | | } |
78 | | #endif |
79 | | |
80 | 2.83k | return TRUE; |
81 | 33.3k | } |
82 | | |
83 | | cairo_bool_t |
84 | | _cairo_spline_init (cairo_spline_t *spline, |
85 | | cairo_spline_add_point_func_t add_point_func, |
86 | | void *closure, |
87 | | const cairo_point_t *a, const cairo_point_t *b, |
88 | | const cairo_point_t *c, const cairo_point_t *d) |
89 | 23.1k | { |
90 | | /* If both tangents are zero, this is just a straight line */ |
91 | 23.1k | if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y) |
92 | 59 | return FALSE; |
93 | | |
94 | 23.0k | spline->add_point_func = add_point_func; |
95 | 23.0k | spline->closure = closure; |
96 | | |
97 | 23.0k | spline->knots.a = *a; |
98 | 23.0k | spline->knots.b = *b; |
99 | 23.0k | spline->knots.c = *c; |
100 | 23.0k | spline->knots.d = *d; |
101 | | |
102 | 23.0k | if (a->x != b->x || a->y != b->y) |
103 | 20.2k | _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); |
104 | 2.83k | else if (a->x != c->x || a->y != c->y) |
105 | 1.75k | _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); |
106 | 1.08k | else if (a->x != d->x || a->y != d->y) |
107 | 1.08k | _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); |
108 | 0 | else |
109 | 0 | return FALSE; |
110 | | |
111 | 23.0k | if (c->x != d->x || c->y != d->y) |
112 | 21.0k | _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); |
113 | 1.98k | else if (b->x != d->x || b->y != d->y) |
114 | 933 | _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); |
115 | 1.05k | else |
116 | 1.05k | return FALSE; /* just treat this as a straight-line from a -> d */ |
117 | | |
118 | | /* XXX if the initial, final and vector are all equal, this is just a line */ |
119 | | |
120 | 22.0k | return TRUE; |
121 | 23.0k | } |
122 | | |
123 | | static cairo_status_t |
124 | | _cairo_spline_add_point (cairo_spline_t *spline, |
125 | | const cairo_point_t *point, |
126 | | const cairo_point_t *knot) |
127 | 33.0M | { |
128 | 33.0M | cairo_point_t *prev; |
129 | 33.0M | cairo_slope_t slope; |
130 | | |
131 | 33.0M | prev = &spline->last_point; |
132 | 33.0M | if (prev->x == point->x && prev->y == point->y) |
133 | 22.0k | return CAIRO_STATUS_SUCCESS; |
134 | | |
135 | 32.9M | _cairo_slope_init (&slope, point, knot); |
136 | | |
137 | 32.9M | spline->last_point = *point; |
138 | 32.9M | return spline->add_point_func (spline->closure, point, &slope); |
139 | 33.0M | } |
140 | | |
141 | | static void |
142 | | _lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result) |
143 | 197M | { |
144 | 197M | result->x = a->x + ((b->x - a->x) >> 1); |
145 | 197M | result->y = a->y + ((b->y - a->y) >> 1); |
146 | 197M | } |
147 | | |
148 | | static void |
149 | | _de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2) |
150 | 32.9M | { |
151 | 32.9M | cairo_point_t ab, bc, cd; |
152 | 32.9M | cairo_point_t abbc, bccd; |
153 | 32.9M | cairo_point_t final; |
154 | | |
155 | 32.9M | _lerp_half (&s1->a, &s1->b, &ab); |
156 | 32.9M | _lerp_half (&s1->b, &s1->c, &bc); |
157 | 32.9M | _lerp_half (&s1->c, &s1->d, &cd); |
158 | 32.9M | _lerp_half (&ab, &bc, &abbc); |
159 | 32.9M | _lerp_half (&bc, &cd, &bccd); |
160 | 32.9M | _lerp_half (&abbc, &bccd, &final); |
161 | | |
162 | 32.9M | s2->a = final; |
163 | 32.9M | s2->b = bccd; |
164 | 32.9M | s2->c = cd; |
165 | 32.9M | s2->d = s1->d; |
166 | | |
167 | 32.9M | s1->b = ab; |
168 | 32.9M | s1->c = abbc; |
169 | 32.9M | s1->d = final; |
170 | 32.9M | } |
171 | | |
172 | | /* Return an upper bound on the error (squared) that could result from |
173 | | * approximating a spline as a line segment connecting the two endpoints. */ |
174 | | static double |
175 | | _cairo_spline_error_squared (const cairo_spline_knots_t *knots) |
176 | 65.9M | { |
177 | 65.9M | double bdx, bdy, berr; |
178 | 65.9M | double cdx, cdy, cerr; |
179 | | |
180 | | /* We are going to compute the distance (squared) between each of the the b |
181 | | * and c control points and the segment a-b. The maximum of these two |
182 | | * distances will be our approximation error. */ |
183 | | |
184 | 65.9M | bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); |
185 | 65.9M | bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); |
186 | | |
187 | 65.9M | cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x); |
188 | 65.9M | cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); |
189 | | |
190 | 65.9M | if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { |
191 | | /* Intersection point (px): |
192 | | * px = p1 + u(p2 - p1) |
193 | | * (p - px) ∙ (p2 - p1) = 0 |
194 | | * Thus: |
195 | | * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; |
196 | | */ |
197 | | |
198 | 65.9M | double dx, dy, u, v; |
199 | | |
200 | 65.9M | dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); |
201 | 65.9M | dy = _cairo_fixed_to_double (knots->d.y - knots->a.y); |
202 | 65.9M | v = dx * dx + dy * dy; |
203 | | |
204 | 65.9M | u = bdx * dx + bdy * dy; |
205 | 65.9M | if (u <= 0) { |
206 | | /* bdx -= 0; |
207 | | * bdy -= 0; |
208 | | */ |
209 | 65.9M | } else if (u >= v) { |
210 | 42.6k | bdx -= dx; |
211 | 42.6k | bdy -= dy; |
212 | 65.8M | } else { |
213 | 65.8M | bdx -= u/v * dx; |
214 | 65.8M | bdy -= u/v * dy; |
215 | 65.8M | } |
216 | | |
217 | 65.9M | u = cdx * dx + cdy * dy; |
218 | 65.9M | if (u <= 0) { |
219 | | /* cdx -= 0; |
220 | | * cdy -= 0; |
221 | | */ |
222 | 65.9M | } else if (u >= v) { |
223 | 70.4k | cdx -= dx; |
224 | 70.4k | cdy -= dy; |
225 | 65.8M | } else { |
226 | 65.8M | cdx -= u/v * dx; |
227 | 65.8M | cdy -= u/v * dy; |
228 | 65.8M | } |
229 | 65.9M | } |
230 | | |
231 | 65.9M | berr = bdx * bdx + bdy * bdy; |
232 | 65.9M | cerr = cdx * cdx + cdy * cdy; |
233 | 65.9M | if (berr > cerr) |
234 | 32.0M | return berr; |
235 | 33.9M | else |
236 | 33.9M | return cerr; |
237 | 65.9M | } |
238 | | |
239 | | static cairo_status_t |
240 | | _cairo_spline_decompose_into (cairo_spline_knots_t *s1, |
241 | | double tolerance_squared, |
242 | | cairo_spline_t *result) |
243 | 65.9M | { |
244 | 65.9M | cairo_spline_knots_t s2; |
245 | 65.9M | cairo_status_t status; |
246 | | |
247 | 65.9M | if (_cairo_spline_error_squared (s1) < tolerance_squared) |
248 | 33.0M | return _cairo_spline_add_point (result, &s1->a, &s1->b); |
249 | | |
250 | 32.9M | _de_casteljau (s1, &s2); |
251 | | |
252 | 32.9M | status = _cairo_spline_decompose_into (s1, tolerance_squared, result); |
253 | 32.9M | if (unlikely (status)) |
254 | 0 | return status; |
255 | | |
256 | 32.9M | return _cairo_spline_decompose_into (&s2, tolerance_squared, result); |
257 | 32.9M | } |
258 | | |
259 | | cairo_status_t |
260 | | _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) |
261 | 22.0k | { |
262 | 22.0k | cairo_spline_knots_t s1; |
263 | 22.0k | cairo_status_t status; |
264 | | |
265 | 22.0k | s1 = spline->knots; |
266 | 22.0k | spline->last_point = s1.a; |
267 | 22.0k | status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); |
268 | 22.0k | if (unlikely (status)) |
269 | 0 | return status; |
270 | | |
271 | 22.0k | return spline->add_point_func (spline->closure, |
272 | 22.0k | &spline->knots.d, &spline->final_slope); |
273 | 22.0k | } |
274 | | |
275 | | /* Note: this function is only good for computing bounds in device space. */ |
276 | | cairo_status_t |
277 | | _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, |
278 | | void *closure, |
279 | | const cairo_point_t *p0, const cairo_point_t *p1, |
280 | | const cairo_point_t *p2, const cairo_point_t *p3) |
281 | 4.75k | { |
282 | 4.75k | double x0, x1, x2, x3; |
283 | 4.75k | double y0, y1, y2, y3; |
284 | 4.75k | double a, b, c; |
285 | 4.75k | double t[4]; |
286 | 4.75k | int t_num = 0, i; |
287 | 4.75k | cairo_status_t status; |
288 | | |
289 | 4.75k | x0 = _cairo_fixed_to_double (p0->x); |
290 | 4.75k | y0 = _cairo_fixed_to_double (p0->y); |
291 | 4.75k | x1 = _cairo_fixed_to_double (p1->x); |
292 | 4.75k | y1 = _cairo_fixed_to_double (p1->y); |
293 | 4.75k | x2 = _cairo_fixed_to_double (p2->x); |
294 | 4.75k | y2 = _cairo_fixed_to_double (p2->y); |
295 | 4.75k | x3 = _cairo_fixed_to_double (p3->x); |
296 | 4.75k | y3 = _cairo_fixed_to_double (p3->y); |
297 | | |
298 | | /* The spline can be written as a polynomial of the four points: |
299 | | * |
300 | | * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 |
301 | | * |
302 | | * for 0≤t≤1. Now, the X and Y components of the spline follow the |
303 | | * same polynomial but with x and y replaced for p. To find the |
304 | | * bounds of the spline, we just need to find the X and Y bounds. |
305 | | * To find the bound, we take the derivative and equal it to zero, |
306 | | * and solve to find the t's that give the extreme points. |
307 | | * |
308 | | * Here is the derivative of the curve, sorted on t: |
309 | | * |
310 | | * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 |
311 | | * |
312 | | * Let: |
313 | | * |
314 | | * a = -p0+3p1-3p2+p3 |
315 | | * b = p0-2p1+p2 |
316 | | * c = -p0+p1 |
317 | | * |
318 | | * Gives: |
319 | | * |
320 | | * a.t² + 2b.t + c = 0 |
321 | | * |
322 | | * With: |
323 | | * |
324 | | * delta = b*b - a*c |
325 | | * |
326 | | * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if |
327 | | * delta is positive, and at -b/a if delta is zero. |
328 | | */ |
329 | | |
330 | 4.75k | #define ADD(t0) \ |
331 | 12.4k | { \ |
332 | 12.4k | double _t0 = (t0); \ |
333 | 12.4k | if (0 < _t0 && _t0 < 1) \ |
334 | 12.4k | t[t_num++] = _t0; \ |
335 | 12.4k | } |
336 | | |
337 | 4.75k | #define FIND_EXTREMES(a,b,c) \ |
338 | 9.50k | { \ |
339 | 9.50k | if (a == 0) { \ |
340 | 942 | if (b != 0) \ |
341 | 942 | ADD (-c / (2*b)); \ |
342 | 8.56k | } else { \ |
343 | 8.56k | double b2 = b * b; \ |
344 | 8.56k | double delta = b2 - a * c; \ |
345 | 8.56k | if (delta > 0) { \ |
346 | 7.90k | cairo_bool_t feasible; \ |
347 | 7.90k | double _2ab = 2 * a * b; \ |
348 | | /* We are only interested in solutions t that satisfy 0<t<1 \ |
349 | | * here. We do some checks to avoid sqrt if the solutions \ |
350 | | * are not in that range. The checks can be derived from: \ |
351 | | * \ |
352 | | * 0 < (-b±√delta)/a < 1 \ |
353 | | */ \ |
354 | 7.90k | if (_2ab >= 0) \ |
355 | 7.90k | feasible = delta > b2 && delta < a*a + b2 + _2ab; \ |
356 | 7.90k | else if (-b / a >= 1) \ |
357 | 5.45k | feasible = delta < b2 && delta > a*a + b2 + _2ab; \ |
358 | 5.45k | else \ |
359 | 5.45k | feasible = delta < b2 || delta < a*a + b2 + _2ab; \ |
360 | 7.90k | \ |
361 | 7.90k | if (unlikely (feasible)) { \ |
362 | 5.74k | double sqrt_delta = sqrt (delta); \ |
363 | 5.74k | ADD ((-b - sqrt_delta) / a); \ |
364 | 5.74k | ADD ((-b + sqrt_delta) / a); \ |
365 | 5.74k | } \ |
366 | 7.90k | } else if (delta == 0) { \ |
367 | 328 | ADD (-b / a); \ |
368 | 328 | } \ |
369 | 8.56k | } \ |
370 | 9.50k | } |
371 | | |
372 | | /* Find X extremes */ |
373 | 4.75k | a = -x0 + 3*x1 - 3*x2 + x3; |
374 | 4.75k | b = x0 - 2*x1 + x2; |
375 | 4.75k | c = -x0 + x1; |
376 | 4.75k | FIND_EXTREMES (a, b, c); |
377 | | |
378 | | /* Find Y extremes */ |
379 | 4.75k | a = -y0 + 3*y1 - 3*y2 + y3; |
380 | 4.75k | b = y0 - 2*y1 + y2; |
381 | 4.75k | c = -y0 + y1; |
382 | 4.75k | FIND_EXTREMES (a, b, c); |
383 | | |
384 | 4.75k | status = add_point_func (closure, p0, NULL); |
385 | 4.75k | if (unlikely (status)) |
386 | 0 | return status; |
387 | | |
388 | 12.0k | for (i = 0; i < t_num; i++) { |
389 | 7.31k | cairo_point_t p; |
390 | 7.31k | double x, y; |
391 | 7.31k | double t_1_0, t_0_1; |
392 | 7.31k | double t_2_0, t_0_2; |
393 | 7.31k | double t_3_0, t_2_1_3, t_1_2_3, t_0_3; |
394 | | |
395 | 7.31k | t_1_0 = t[i]; /* t */ |
396 | 7.31k | t_0_1 = 1 - t_1_0; /* (1 - t) */ |
397 | | |
398 | 7.31k | t_2_0 = t_1_0 * t_1_0; /* t * t */ |
399 | 7.31k | t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ |
400 | | |
401 | 7.31k | t_3_0 = t_2_0 * t_1_0; /* t * t * t */ |
402 | 7.31k | t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ |
403 | 7.31k | t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ |
404 | 7.31k | t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ |
405 | | |
406 | | /* Bezier polynomial */ |
407 | 7.31k | x = x0 * t_0_3 |
408 | 7.31k | + x1 * t_1_2_3 |
409 | 7.31k | + x2 * t_2_1_3 |
410 | 7.31k | + x3 * t_3_0; |
411 | 7.31k | y = y0 * t_0_3 |
412 | 7.31k | + y1 * t_1_2_3 |
413 | 7.31k | + y2 * t_2_1_3 |
414 | 7.31k | + y3 * t_3_0; |
415 | | |
416 | 7.31k | p.x = _cairo_fixed_from_double (x); |
417 | 7.31k | p.y = _cairo_fixed_from_double (y); |
418 | 7.31k | status = add_point_func (closure, &p, NULL); |
419 | 7.31k | if (unlikely (status)) |
420 | 0 | return status; |
421 | 7.31k | } |
422 | | |
423 | 4.75k | return add_point_func (closure, p3, NULL); |
424 | 4.75k | } |