/work/workdir/UnpackedTarball/cairo/src/cairo-pen.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 | | * Copyright © 2008 Chris Wilson |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it either under the terms of the GNU Lesser General Public |
8 | | * License version 2.1 as published by the Free Software Foundation |
9 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
10 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
11 | | * notice, a recipient may use your version of this file under either |
12 | | * the MPL or the LGPL. |
13 | | * |
14 | | * You should have received a copy of the LGPL along with this library |
15 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
17 | | * You should have received a copy of the MPL along with this library |
18 | | * in the file COPYING-MPL-1.1 |
19 | | * |
20 | | * The contents of this file are subject to the Mozilla Public License |
21 | | * Version 1.1 (the "License"); you may not use this file except in |
22 | | * compliance with the License. You may obtain a copy of the License at |
23 | | * http://www.mozilla.org/MPL/ |
24 | | * |
25 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
26 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
27 | | * the specific language governing rights and limitations. |
28 | | * |
29 | | * The Original Code is the cairo graphics library. |
30 | | * |
31 | | * The Initial Developer of the Original Code is University of Southern |
32 | | * California. |
33 | | * |
34 | | * Contributor(s): |
35 | | * Carl D. Worth <cworth@cworth.org> |
36 | | * Chris Wilson <chris@chris-wilson.co.uk> |
37 | | */ |
38 | | |
39 | | #include "cairoint.h" |
40 | | |
41 | | #include "cairo-error-private.h" |
42 | | #include "cairo-slope-private.h" |
43 | | |
44 | | static void |
45 | | _cairo_pen_compute_slopes (cairo_pen_t *pen); |
46 | | |
47 | | cairo_status_t |
48 | | _cairo_pen_init (cairo_pen_t *pen, |
49 | | double radius, |
50 | | double tolerance, |
51 | | const cairo_matrix_t *ctm) |
52 | 49 | { |
53 | 49 | int i; |
54 | 49 | int reflect; |
55 | | |
56 | 49 | if (CAIRO_INJECT_FAULT ()) |
57 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
58 | | |
59 | 49 | VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); |
60 | | |
61 | 49 | pen->radius = radius; |
62 | 49 | pen->tolerance = tolerance; |
63 | | |
64 | 49 | reflect = _cairo_matrix_compute_determinant (ctm) < 0.; |
65 | | |
66 | 49 | pen->num_vertices = _cairo_pen_vertices_needed (tolerance, |
67 | 49 | radius, |
68 | 49 | ctm); |
69 | | |
70 | 49 | if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { |
71 | 0 | pen->vertices = _cairo_malloc_ab (pen->num_vertices, |
72 | 0 | sizeof (cairo_pen_vertex_t)); |
73 | 0 | if (unlikely (pen->vertices == NULL)) |
74 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
75 | 49 | } else { |
76 | 49 | pen->vertices = pen->vertices_embedded; |
77 | 49 | } |
78 | | |
79 | | /* |
80 | | * Compute pen coordinates. To generate the right ellipse, compute points around |
81 | | * a circle in user space and transform them to device space. To get a consistent |
82 | | * orientation in device space, flip the pen if the transformation matrix |
83 | | * is reflecting |
84 | | */ |
85 | 553 | for (i=0; i < pen->num_vertices; i++) { |
86 | 504 | cairo_pen_vertex_t *v = &pen->vertices[i]; |
87 | 504 | double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy; |
88 | 504 | if (reflect) |
89 | 0 | theta = -theta; |
90 | 504 | dx = radius * cos (theta); |
91 | 504 | dy = radius * sin (theta); |
92 | 504 | cairo_matrix_transform_distance (ctm, &dx, &dy); |
93 | 504 | v->point.x = _cairo_fixed_from_double (dx); |
94 | 504 | v->point.y = _cairo_fixed_from_double (dy); |
95 | 504 | } |
96 | | |
97 | 49 | _cairo_pen_compute_slopes (pen); |
98 | | |
99 | 49 | return CAIRO_STATUS_SUCCESS; |
100 | 49 | } |
101 | | |
102 | | void |
103 | | _cairo_pen_fini (cairo_pen_t *pen) |
104 | 49 | { |
105 | 49 | if (pen->vertices != pen->vertices_embedded) |
106 | 0 | free (pen->vertices); |
107 | | |
108 | | |
109 | 49 | VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); |
110 | 49 | } |
111 | | |
112 | | cairo_status_t |
113 | | _cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) |
114 | 0 | { |
115 | 0 | VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); |
116 | |
|
117 | 0 | *pen = *other; |
118 | |
|
119 | 0 | if (CAIRO_INJECT_FAULT ()) |
120 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
121 | | |
122 | 0 | pen->vertices = pen->vertices_embedded; |
123 | 0 | if (pen->num_vertices) { |
124 | 0 | if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { |
125 | 0 | pen->vertices = _cairo_malloc_ab (pen->num_vertices, |
126 | 0 | sizeof (cairo_pen_vertex_t)); |
127 | 0 | if (unlikely (pen->vertices == NULL)) |
128 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
129 | 0 | } |
130 | | |
131 | 0 | memcpy (pen->vertices, other->vertices, |
132 | 0 | pen->num_vertices * sizeof (cairo_pen_vertex_t)); |
133 | 0 | } |
134 | | |
135 | 0 | return CAIRO_STATUS_SUCCESS; |
136 | 0 | } |
137 | | |
138 | | cairo_status_t |
139 | | _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) |
140 | 0 | { |
141 | 0 | cairo_status_t status; |
142 | 0 | int num_vertices; |
143 | 0 | int i; |
144 | |
|
145 | 0 | if (CAIRO_INJECT_FAULT ()) |
146 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
147 | | |
148 | 0 | num_vertices = pen->num_vertices + num_points; |
149 | 0 | if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || |
150 | 0 | pen->vertices != pen->vertices_embedded) |
151 | 0 | { |
152 | 0 | cairo_pen_vertex_t *vertices; |
153 | |
|
154 | 0 | if (pen->vertices == pen->vertices_embedded) { |
155 | 0 | vertices = _cairo_malloc_ab (num_vertices, |
156 | 0 | sizeof (cairo_pen_vertex_t)); |
157 | 0 | if (unlikely (vertices == NULL)) |
158 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
159 | | |
160 | 0 | memcpy (vertices, pen->vertices, |
161 | 0 | pen->num_vertices * sizeof (cairo_pen_vertex_t)); |
162 | 0 | } else { |
163 | 0 | vertices = _cairo_realloc_ab (pen->vertices, |
164 | 0 | num_vertices, |
165 | 0 | sizeof (cairo_pen_vertex_t)); |
166 | 0 | if (unlikely (vertices == NULL)) |
167 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
168 | 0 | } |
169 | | |
170 | 0 | pen->vertices = vertices; |
171 | 0 | } |
172 | | |
173 | 0 | pen->num_vertices = num_vertices; |
174 | | |
175 | | /* initialize new vertices */ |
176 | 0 | for (i=0; i < num_points; i++) |
177 | 0 | pen->vertices[pen->num_vertices-num_points+i].point = point[i]; |
178 | |
|
179 | 0 | status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); |
180 | 0 | if (unlikely (status)) |
181 | 0 | return status; |
182 | | |
183 | 0 | _cairo_pen_compute_slopes (pen); |
184 | |
|
185 | 0 | return CAIRO_STATUS_SUCCESS; |
186 | 0 | } |
187 | | |
188 | | /* |
189 | | The circular pen in user space is transformed into an ellipse in |
190 | | device space. |
191 | | |
192 | | We construct the pen by computing points along the circumference |
193 | | using equally spaced angles. |
194 | | |
195 | | We show that this approximation to the ellipse has maximum error at the |
196 | | major axis of the ellipse. |
197 | | |
198 | | Set |
199 | | |
200 | | M = major axis length |
201 | | m = minor axis length |
202 | | |
203 | | Align 'M' along the X axis and 'm' along the Y axis and draw |
204 | | an ellipse parameterized by angle 't': |
205 | | |
206 | | x = M cos t y = m sin t |
207 | | |
208 | | Perturb t by ± d and compute two new points (x+,y+), (x-,y-). |
209 | | The distance from the average of these two points to (x,y) represents |
210 | | the maximum error in approximating the ellipse with a polygon formed |
211 | | from vertices 2∆ radians apart. |
212 | | |
213 | | x+ = M cos (t+∆) y+ = m sin (t+∆) |
214 | | x- = M cos (t-∆) y- = m sin (t-∆) |
215 | | |
216 | | Now compute the approximation error, E: |
217 | | |
218 | | Ex = (x - (x+ + x-) / 2) |
219 | | Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) |
220 | | = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + |
221 | | cos(t)cos(∆) - sin(t)sin(∆))/2) |
222 | | = M(cos(t) - cos(t)cos(∆)) |
223 | | = M cos(t) (1 - cos(∆)) |
224 | | |
225 | | Ey = y - (y+ - y-) / 2 |
226 | | = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 |
227 | | = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + |
228 | | sin(t)cos(∆) - cos(t)sin(∆))/2) |
229 | | = m (sin(t) - sin(t)cos(∆)) |
230 | | = m sin(t) (1 - cos(∆)) |
231 | | |
232 | | E² = Ex² + Ey² |
233 | | = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² |
234 | | = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) |
235 | | = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) |
236 | | = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² |
237 | | |
238 | | Find the extremum by differentiation wrt t and setting that to zero |
239 | | |
240 | | ∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) |
241 | | |
242 | | 0 = 2 cos (t) sin (t) |
243 | | 0 = sin (2t) |
244 | | t = nπ |
245 | | |
246 | | Which is to say that the maximum and minimum errors occur on the |
247 | | axes of the ellipse at 0 and π radians: |
248 | | |
249 | | E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² |
250 | | = (1-cos(∆))² M² |
251 | | E²(π) = (1-cos(∆))² m² |
252 | | |
253 | | maximum error = M (1-cos(∆)) |
254 | | minimum error = m (1-cos(∆)) |
255 | | |
256 | | We must make maximum error ≤ tolerance, so compute the ∆ needed: |
257 | | |
258 | | tolerance = M (1-cos(∆)) |
259 | | tolerance / M = 1 - cos (∆) |
260 | | cos(∆) = 1 - tolerance/M |
261 | | ∆ = acos (1 - tolerance / M); |
262 | | |
263 | | Remembering that ∆ is half of our angle between vertices, |
264 | | the number of vertices is then |
265 | | |
266 | | vertices = ceil(2π/2∆). |
267 | | = ceil(π/∆). |
268 | | |
269 | | Note that this also equation works for M == m (a circle) as it |
270 | | doesn't matter where on the circle the error is computed. |
271 | | */ |
272 | | |
273 | | int |
274 | | _cairo_pen_vertices_needed (double tolerance, |
275 | | double radius, |
276 | | const cairo_matrix_t *matrix) |
277 | 834k | { |
278 | | /* |
279 | | * the pen is a circle that gets transformed to an ellipse by matrix. |
280 | | * compute major axis length for a pen with the specified radius. |
281 | | * we don't need the minor axis length. |
282 | | */ |
283 | 834k | double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, |
284 | 834k | radius); |
285 | 834k | int num_vertices; |
286 | | |
287 | 834k | if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */ |
288 | 203 | num_vertices = 1; |
289 | 834k | } else if (tolerance >= major_axis) { |
290 | 0 | num_vertices = 4; |
291 | 834k | } else { |
292 | 834k | double divisor = acos (1 - tolerance / major_axis); |
293 | | |
294 | 834k | if (divisor == 0.0) |
295 | 718 | return 4; |
296 | | |
297 | 834k | num_vertices = ceil (2*M_PI / divisor); |
298 | | |
299 | | /* number of vertices must be even */ |
300 | 834k | if (num_vertices % 2) |
301 | 1.12k | num_vertices++; |
302 | | |
303 | | /* And we must always have at least 4 vertices. */ |
304 | 834k | if (num_vertices < 4) |
305 | 0 | num_vertices = 4; |
306 | 834k | } |
307 | | |
308 | 834k | return num_vertices; |
309 | 834k | } |
310 | | |
311 | | static void |
312 | | _cairo_pen_compute_slopes (cairo_pen_t *pen) |
313 | 49 | { |
314 | 49 | int i, i_prev; |
315 | 49 | cairo_pen_vertex_t *prev, *v, *next; |
316 | | |
317 | 49 | for (i=0, i_prev = pen->num_vertices - 1; |
318 | 553 | i < pen->num_vertices; |
319 | 504 | i_prev = i++) { |
320 | 504 | prev = &pen->vertices[i_prev]; |
321 | 504 | v = &pen->vertices[i]; |
322 | 504 | next = &pen->vertices[(i + 1) % pen->num_vertices]; |
323 | | |
324 | 504 | _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); |
325 | 504 | _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); |
326 | 504 | } |
327 | 49 | } |
328 | | /* |
329 | | * Find active pen vertex for clockwise edge of stroke at the given slope. |
330 | | * |
331 | | * The strictness of the inequalities here is delicate. The issue is |
332 | | * that the slope_ccw member of one pen vertex will be equivalent to |
333 | | * the slope_cw member of the next pen vertex in a counterclockwise |
334 | | * order. However, for this function, we care strongly about which |
335 | | * vertex is returned. |
336 | | * |
337 | | * [I think the "care strongly" above has to do with ensuring that the |
338 | | * pen's "extra points" from the spline's initial and final slopes are |
339 | | * properly found when beginning the spline stroking.] |
340 | | */ |
341 | | int |
342 | | _cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, |
343 | | const cairo_slope_t *slope) |
344 | 0 | { |
345 | 0 | int i; |
346 | |
|
347 | 0 | for (i=0; i < pen->num_vertices; i++) { |
348 | 0 | if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && |
349 | 0 | (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) |
350 | 0 | break; |
351 | 0 | } |
352 | | |
353 | | /* If the desired slope cannot be found between any of the pen |
354 | | * vertices, then we must have a degenerate pen, (such as a pen |
355 | | * that's been transformed to a line). In that case, we consider |
356 | | * the first pen vertex as the appropriate clockwise vertex. |
357 | | */ |
358 | 0 | if (i == pen->num_vertices) |
359 | 0 | i = 0; |
360 | |
|
361 | 0 | return i; |
362 | 0 | } |
363 | | |
364 | | /* Find active pen vertex for counterclockwise edge of stroke at the given slope. |
365 | | * |
366 | | * Note: See the comments for _cairo_pen_find_active_cw_vertex_index |
367 | | * for some details about the strictness of the inequalities here. |
368 | | */ |
369 | | int |
370 | | _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, |
371 | | const cairo_slope_t *slope) |
372 | 0 | { |
373 | 0 | cairo_slope_t slope_reverse; |
374 | 0 | int i; |
375 | |
|
376 | 0 | slope_reverse = *slope; |
377 | 0 | slope_reverse.dx = -slope_reverse.dx; |
378 | 0 | slope_reverse.dy = -slope_reverse.dy; |
379 | |
|
380 | 0 | for (i=pen->num_vertices-1; i >= 0; i--) { |
381 | 0 | if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && |
382 | 0 | (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) |
383 | 0 | break; |
384 | 0 | } |
385 | | |
386 | | /* If the desired slope cannot be found between any of the pen |
387 | | * vertices, then we must have a degenerate pen, (such as a pen |
388 | | * that's been transformed to a line). In that case, we consider |
389 | | * the last pen vertex as the appropriate counterclockwise vertex. |
390 | | */ |
391 | 0 | if (i < 0) |
392 | 0 | i = pen->num_vertices - 1; |
393 | |
|
394 | 0 | return i; |
395 | 0 | } |
396 | | |
397 | | void |
398 | | _cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, |
399 | | const cairo_slope_t *in, |
400 | | const cairo_slope_t *out, |
401 | | int *start, int *stop) |
402 | 1.12k | { |
403 | | |
404 | 1.12k | int lo = 0, hi = pen->num_vertices; |
405 | 1.12k | int i; |
406 | | |
407 | 1.12k | i = (lo + hi) >> 1; |
408 | 3.67k | do { |
409 | 3.67k | if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) |
410 | 1.72k | lo = i; |
411 | 1.94k | else |
412 | 1.94k | hi = i; |
413 | 3.67k | i = (lo + hi) >> 1; |
414 | 3.67k | } while (hi - lo > 1); |
415 | 1.12k | if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) |
416 | 1.12k | if (++i == pen->num_vertices) |
417 | 26 | i = 0; |
418 | 1.12k | *start = i; |
419 | | |
420 | 1.12k | if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) { |
421 | 1.12k | lo = i; |
422 | 1.12k | hi = i + pen->num_vertices; |
423 | 1.12k | i = (lo + hi) >> 1; |
424 | 4.04k | do { |
425 | 4.04k | int j = i; |
426 | 4.04k | if (j >= pen->num_vertices) |
427 | 2.02k | j -= pen->num_vertices; |
428 | 4.04k | if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0) |
429 | 1.58k | hi = i; |
430 | 2.46k | else |
431 | 2.46k | lo = i; |
432 | 4.04k | i = (lo + hi) >> 1; |
433 | 4.04k | } while (hi - lo > 1); |
434 | 1.12k | if (i >= pen->num_vertices) |
435 | 591 | i -= pen->num_vertices; |
436 | 1.12k | } |
437 | 1.12k | *stop = i; |
438 | 1.12k | } |
439 | | |
440 | | void |
441 | | _cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen, |
442 | | const cairo_slope_t *in, |
443 | | const cairo_slope_t *out, |
444 | | int *start, int *stop) |
445 | 927 | { |
446 | 927 | int lo = 0, hi = pen->num_vertices; |
447 | 927 | int i; |
448 | | |
449 | 927 | i = (lo + hi) >> 1; |
450 | 3.10k | do { |
451 | 3.10k | if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) |
452 | 2.38k | lo = i; |
453 | 718 | else |
454 | 718 | hi = i; |
455 | 3.10k | i = (lo + hi) >> 1; |
456 | 3.10k | } while (hi - lo > 1); |
457 | 927 | if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) |
458 | 927 | if (++i == pen->num_vertices) |
459 | 250 | i = 0; |
460 | 927 | *start = i; |
461 | | |
462 | 927 | if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) { |
463 | 362 | lo = i; |
464 | 362 | hi = i + pen->num_vertices; |
465 | 362 | i = (lo + hi) >> 1; |
466 | 1.26k | do { |
467 | 1.26k | int j = i; |
468 | 1.26k | if (j >= pen->num_vertices) |
469 | 982 | j -= pen->num_vertices; |
470 | 1.26k | if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0) |
471 | 489 | hi = i; |
472 | 780 | else |
473 | 780 | lo = i; |
474 | 1.26k | i = (lo + hi) >> 1; |
475 | 1.26k | } while (hi - lo > 1); |
476 | 362 | if (i >= pen->num_vertices) |
477 | 286 | i -= pen->num_vertices; |
478 | 362 | } |
479 | 927 | *stop = i; |
480 | 927 | } |