/work/workdir/UnpackedTarball/cairo/src/cairo-contour.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright © 2004 Carl Worth |
3 | | * Copyright © 2006 Red Hat, Inc. |
4 | | * Copyright © 2008 Chris Wilson |
5 | | * Copyright © 2011 Intel Corporation |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it either under the terms of the GNU Lesser General Public |
9 | | * License version 2.1 as published by the Free Software Foundation |
10 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
11 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
12 | | * notice, a recipient may use your version of this file under either |
13 | | * the MPL or the LGPL. |
14 | | * |
15 | | * You should have received a copy of the LGPL along with this library |
16 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
17 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
18 | | * You should have received a copy of the MPL along with this library |
19 | | * in the file COPYING-MPL-1.1 |
20 | | * |
21 | | * The contents of this file are subject to the Mozilla Public License |
22 | | * Version 1.1 (the "License"); you may not use this file except in |
23 | | * compliance with the License. You may obtain a copy of the License at |
24 | | * http://www.mozilla.org/MPL/ |
25 | | * |
26 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
27 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
28 | | * the specific language governing rights and limitations. |
29 | | * |
30 | | * The Original Code is the cairo graphics library. |
31 | | * |
32 | | * The Initial Developer of the Original Code is Carl Worth |
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-freelist-private.h" |
43 | | #include "cairo-combsort-inline.h" |
44 | | #include "cairo-contour-inline.h" |
45 | | #include "cairo-contour-private.h" |
46 | | |
47 | | void |
48 | | _cairo_contour_init (cairo_contour_t *contour, |
49 | | int direction) |
50 | 59.0k | { |
51 | 59.0k | contour->direction = direction; |
52 | 59.0k | contour->chain.points = contour->embedded_points; |
53 | 59.0k | contour->chain.next = NULL; |
54 | 59.0k | contour->chain.num_points = 0; |
55 | 59.0k | contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points); |
56 | 59.0k | contour->tail = &contour->chain; |
57 | 59.0k | } |
58 | | |
59 | | cairo_int_status_t |
60 | | __cairo_contour_add_point (cairo_contour_t *contour, |
61 | | const cairo_point_t *point) |
62 | 10.1k | { |
63 | 10.1k | cairo_contour_chain_t *tail = contour->tail; |
64 | 10.1k | cairo_contour_chain_t *next; |
65 | | |
66 | 10.1k | assert (tail->next == NULL); |
67 | | |
68 | 10.1k | next = _cairo_malloc_ab_plus_c (tail->size_points*2, |
69 | 10.1k | sizeof (cairo_point_t), |
70 | 10.1k | sizeof (cairo_contour_chain_t)); |
71 | 10.1k | if (unlikely (next == NULL)) |
72 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
73 | | |
74 | 10.1k | next->size_points = tail->size_points*2; |
75 | 10.1k | next->num_points = 1; |
76 | 10.1k | next->points = (cairo_point_t *)(next+1); |
77 | 10.1k | next->next = NULL; |
78 | 10.1k | tail->next = next; |
79 | 10.1k | contour->tail = next; |
80 | | |
81 | 10.1k | next->points[0] = *point; |
82 | 10.1k | return CAIRO_INT_STATUS_SUCCESS; |
83 | 10.1k | } |
84 | | |
85 | | static void |
86 | | first_inc (cairo_contour_t *contour, |
87 | | cairo_point_t **p, |
88 | | cairo_contour_chain_t **chain) |
89 | 0 | { |
90 | 0 | if (*p == (*chain)->points + (*chain)->num_points) { |
91 | 0 | assert ((*chain)->next); |
92 | 0 | *chain = (*chain)->next; |
93 | 0 | *p = &(*chain)->points[0]; |
94 | 0 | } else |
95 | 0 | ++*p; |
96 | 0 | } |
97 | | |
98 | | static void |
99 | | last_dec (cairo_contour_t *contour, |
100 | | cairo_point_t **p, |
101 | | cairo_contour_chain_t **chain) |
102 | 0 | { |
103 | 0 | if (*p == (*chain)->points) { |
104 | 0 | cairo_contour_chain_t *prev; |
105 | 0 | assert (*chain != &contour->chain); |
106 | 0 | for (prev = &contour->chain; prev->next != *chain; prev = prev->next) |
107 | 0 | ; |
108 | 0 | *chain = prev; |
109 | 0 | *p = &(*chain)->points[(*chain)->num_points-1]; |
110 | 0 | } else |
111 | 0 | --*p; |
112 | 0 | } |
113 | | |
114 | | void |
115 | | _cairo_contour_reverse (cairo_contour_t *contour) |
116 | 0 | { |
117 | 0 | cairo_contour_chain_t *first_chain, *last_chain; |
118 | 0 | cairo_point_t *first, *last; |
119 | |
|
120 | 0 | contour->direction = -contour->direction; |
121 | |
|
122 | 0 | if (contour->chain.num_points <= 1) |
123 | 0 | return; |
124 | | |
125 | 0 | first_chain = &contour->chain; |
126 | 0 | last_chain = contour->tail; |
127 | |
|
128 | 0 | first = &first_chain->points[0]; |
129 | 0 | last = &last_chain->points[last_chain->num_points-1]; |
130 | |
|
131 | 0 | while (first != last) { |
132 | 0 | cairo_point_t p; |
133 | |
|
134 | 0 | p = *first; |
135 | 0 | *first = *last; |
136 | 0 | *last = p; |
137 | |
|
138 | 0 | first_inc (contour, &first, &first_chain); |
139 | 0 | last_dec (contour, &last, &last_chain); |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | cairo_int_status_t |
144 | | _cairo_contour_add (cairo_contour_t *dst, |
145 | | const cairo_contour_t *src) |
146 | 0 | { |
147 | 0 | const cairo_contour_chain_t *chain; |
148 | 0 | cairo_int_status_t status; |
149 | 0 | int i; |
150 | |
|
151 | 0 | for (chain = &src->chain; chain; chain = chain->next) { |
152 | 0 | for (i = 0; i < chain->num_points; i++) { |
153 | 0 | status = _cairo_contour_add_point (dst, &chain->points[i]); |
154 | 0 | if (unlikely (status)) |
155 | 0 | return status; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | 0 | return CAIRO_INT_STATUS_SUCCESS; |
160 | 0 | } |
161 | | |
162 | | static inline cairo_bool_t |
163 | | iter_next (cairo_contour_iter_t *iter) |
164 | 0 | { |
165 | 0 | if (iter->point == &iter->chain->points[iter->chain->size_points-1]) { |
166 | 0 | iter->chain = iter->chain->next; |
167 | 0 | if (iter->chain == NULL) |
168 | 0 | return FALSE; |
169 | | |
170 | 0 | iter->point = &iter->chain->points[0]; |
171 | 0 | return TRUE; |
172 | 0 | } else { |
173 | 0 | iter->point++; |
174 | 0 | return TRUE; |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | static cairo_bool_t |
179 | | iter_equal (const cairo_contour_iter_t *i1, |
180 | | const cairo_contour_iter_t *i2) |
181 | 0 | { |
182 | 0 | return i1->chain == i2->chain && i1->point == i2->point; |
183 | 0 | } |
184 | | |
185 | | static void |
186 | | iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour) |
187 | 0 | { |
188 | 0 | iter->chain = &contour->chain; |
189 | 0 | iter->point = &contour->chain.points[0]; |
190 | 0 | } |
191 | | |
192 | | static void |
193 | | iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour) |
194 | 0 | { |
195 | 0 | iter->chain = contour->tail; |
196 | 0 | iter->point = &contour->tail->points[contour->tail->num_points-1]; |
197 | 0 | } |
198 | | |
199 | | static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour, |
200 | | const cairo_contour_chain_t *chain) |
201 | 0 | { |
202 | 0 | const cairo_contour_chain_t *prev; |
203 | |
|
204 | 0 | if (chain == &contour->chain) |
205 | 0 | return NULL; |
206 | | |
207 | 0 | for (prev = &contour->chain; prev->next != chain; prev = prev->next) |
208 | 0 | ; |
209 | |
|
210 | 0 | return prev; |
211 | 0 | } |
212 | | |
213 | | cairo_int_status_t |
214 | | _cairo_contour_add_reversed (cairo_contour_t *dst, |
215 | | const cairo_contour_t *src) |
216 | 0 | { |
217 | 0 | const cairo_contour_chain_t *last; |
218 | 0 | cairo_int_status_t status; |
219 | 0 | int i; |
220 | |
|
221 | 0 | if (src->chain.num_points == 0) |
222 | 0 | return CAIRO_INT_STATUS_SUCCESS; |
223 | | |
224 | 0 | for (last = src->tail; last; last = prev_const_chain (src, last)) { |
225 | 0 | for (i = last->num_points-1; i >= 0; i--) { |
226 | 0 | status = _cairo_contour_add_point (dst, &last->points[i]); |
227 | 0 | if (unlikely (status)) |
228 | 0 | return status; |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | 0 | return CAIRO_INT_STATUS_SUCCESS; |
233 | 0 | } |
234 | | |
235 | | static cairo_uint64_t |
236 | | point_distance_sq (const cairo_point_t *p1, |
237 | | const cairo_point_t *p2) |
238 | 0 | { |
239 | 0 | int32_t dx = p1->x - p2->x; |
240 | 0 | int32_t dy = p1->y - p2->y; |
241 | 0 | return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); |
242 | 0 | } |
243 | | |
244 | 0 | #define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX) |
245 | 0 | #define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX) |
246 | | |
247 | | static cairo_bool_t |
248 | | _cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance, |
249 | | const cairo_contour_iter_t *first, |
250 | | const cairo_contour_iter_t *last) |
251 | 0 | { |
252 | 0 | cairo_contour_iter_t iter, furthest; |
253 | 0 | uint64_t max_error; |
254 | 0 | int x0, y0; |
255 | 0 | int nx, ny; |
256 | 0 | int count; |
257 | |
|
258 | 0 | iter = *first; |
259 | 0 | iter_next (&iter); |
260 | 0 | if (iter_equal (&iter, last)) |
261 | 0 | return FALSE; |
262 | | |
263 | 0 | x0 = first->point->x; |
264 | 0 | y0 = first->point->y; |
265 | 0 | nx = last->point->y - y0; |
266 | 0 | ny = x0 - last->point->x; |
267 | |
|
268 | 0 | count = 0; |
269 | 0 | max_error = 0; |
270 | 0 | do { |
271 | 0 | cairo_point_t *p = iter.point; |
272 | 0 | if (! DELETED(p)) { |
273 | 0 | uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y); |
274 | 0 | if (d * d > max_error) { |
275 | 0 | max_error = d * d; |
276 | 0 | furthest = iter; |
277 | 0 | } |
278 | 0 | count++; |
279 | 0 | } |
280 | 0 | iter_next (&iter); |
281 | 0 | } while (! iter_equal (&iter, last)); |
282 | 0 | if (count == 0) |
283 | 0 | return FALSE; |
284 | | |
285 | 0 | if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) { |
286 | 0 | cairo_bool_t simplified; |
287 | |
|
288 | 0 | simplified = FALSE; |
289 | 0 | simplified |= _cairo_contour_simplify_chain (contour, tolerance, |
290 | 0 | first, &furthest); |
291 | 0 | simplified |= _cairo_contour_simplify_chain (contour, tolerance, |
292 | 0 | &furthest, last); |
293 | 0 | return simplified; |
294 | 0 | } else { |
295 | 0 | iter = *first; |
296 | 0 | iter_next (&iter); |
297 | 0 | do { |
298 | 0 | MARK_DELETED (iter.point); |
299 | 0 | iter_next (&iter); |
300 | 0 | } while (! iter_equal (&iter, last)); |
301 | |
|
302 | 0 | return TRUE; |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | void |
307 | | _cairo_contour_simplify (cairo_contour_t *contour, double tolerance) |
308 | 0 | { |
309 | 0 | cairo_contour_chain_t *chain; |
310 | 0 | cairo_point_t *last = NULL; |
311 | 0 | cairo_contour_iter_t iter, furthest; |
312 | 0 | cairo_bool_t simplified; |
313 | 0 | uint64_t max = 0; |
314 | 0 | int i; |
315 | |
|
316 | 0 | if (contour->chain.num_points <= 2) |
317 | 0 | return; |
318 | | |
319 | 0 | tolerance = tolerance * CAIRO_FIXED_ONE; |
320 | 0 | tolerance *= tolerance; |
321 | | |
322 | | /* stage 1: vertex reduction */ |
323 | 0 | for (chain = &contour->chain; chain; chain = chain->next) { |
324 | 0 | for (i = 0; i < chain->num_points; i++) { |
325 | 0 | if (last == NULL || |
326 | 0 | point_distance_sq (last, &chain->points[i]) > tolerance) { |
327 | 0 | last = &chain->points[i]; |
328 | 0 | } else { |
329 | 0 | MARK_DELETED (&chain->points[i]); |
330 | 0 | } |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | /* stage2: polygon simplification using Douglas-Peucker */ |
335 | 0 | do { |
336 | 0 | last = &contour->chain.points[0]; |
337 | 0 | iter_init (&furthest, contour); |
338 | 0 | max = 0; |
339 | 0 | for (chain = &contour->chain; chain; chain = chain->next) { |
340 | 0 | for (i = 0; i < chain->num_points; i++) { |
341 | 0 | uint64_t d; |
342 | |
|
343 | 0 | if (DELETED (&chain->points[i])) |
344 | 0 | continue; |
345 | | |
346 | 0 | d = point_distance_sq (last, &chain->points[i]); |
347 | 0 | if (d > max) { |
348 | 0 | furthest.chain = chain; |
349 | 0 | furthest.point = &chain->points[i]; |
350 | 0 | max = d; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | } |
354 | 0 | assert (max); |
355 | | |
356 | 0 | simplified = FALSE; |
357 | 0 | iter_init (&iter, contour); |
358 | 0 | simplified |= _cairo_contour_simplify_chain (contour, tolerance, |
359 | 0 | &iter, &furthest); |
360 | |
|
361 | 0 | iter_init_last (&iter, contour); |
362 | 0 | if (! iter_equal (&furthest, &iter)) |
363 | 0 | simplified |= _cairo_contour_simplify_chain (contour, tolerance, |
364 | 0 | &furthest, &iter); |
365 | 0 | } while (simplified); |
366 | | |
367 | 0 | iter_init (&iter, contour); |
368 | 0 | for (chain = &contour->chain; chain; chain = chain->next) { |
369 | 0 | int num_points = chain->num_points; |
370 | 0 | chain->num_points = 0; |
371 | 0 | for (i = 0; i < num_points; i++) { |
372 | 0 | if (! DELETED(&chain->points[i])) { |
373 | 0 | if (iter.point != &chain->points[i]) |
374 | 0 | *iter.point = chain->points[i]; |
375 | 0 | iter.chain->num_points++; |
376 | 0 | iter_next (&iter); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | |
|
381 | 0 | if (iter.chain) { |
382 | 0 | cairo_contour_chain_t *next; |
383 | |
|
384 | 0 | for (chain = iter.chain->next; chain; chain = next) { |
385 | 0 | next = chain->next; |
386 | 0 | free (chain); |
387 | 0 | } |
388 | |
|
389 | 0 | iter.chain->next = NULL; |
390 | 0 | contour->tail = iter.chain; |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | void |
395 | | _cairo_contour_reset (cairo_contour_t *contour) |
396 | 50.5k | { |
397 | 50.5k | _cairo_contour_fini (contour); |
398 | 50.5k | _cairo_contour_init (contour, contour->direction); |
399 | 50.5k | } |
400 | | |
401 | | void |
402 | | _cairo_contour_fini (cairo_contour_t *contour) |
403 | 59.0k | { |
404 | 59.0k | cairo_contour_chain_t *chain, *next; |
405 | | |
406 | 69.1k | for (chain = contour->chain.next; chain; chain = next) { |
407 | 10.1k | next = chain->next; |
408 | 10.1k | free (chain); |
409 | 10.1k | } |
410 | 59.0k | } |
411 | | |
412 | | void |
413 | | _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour) |
414 | 0 | { |
415 | 0 | cairo_contour_chain_t *chain; |
416 | 0 | int num_points, size_points; |
417 | 0 | int i; |
418 | |
|
419 | 0 | num_points = 0; |
420 | 0 | size_points = 0; |
421 | 0 | for (chain = &contour->chain; chain; chain = chain->next) { |
422 | 0 | num_points += chain->num_points; |
423 | 0 | size_points += chain->size_points; |
424 | 0 | } |
425 | |
|
426 | 0 | fprintf (file, "contour: direction=%d, num_points=%d / %d\n", |
427 | 0 | contour->direction, num_points, size_points); |
428 | |
|
429 | 0 | num_points = 0; |
430 | 0 | for (chain = &contour->chain; chain; chain = chain->next) { |
431 | 0 | for (i = 0; i < chain->num_points; i++) { |
432 | 0 | fprintf (file, " [%d] = (%f, %f)\n", |
433 | 0 | num_points++, |
434 | 0 | _cairo_fixed_to_double (chain->points[i].x), |
435 | 0 | _cairo_fixed_to_double (chain->points[i].y)); |
436 | 0 | } |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | void |
441 | | __cairo_contour_remove_last_chain (cairo_contour_t *contour) |
442 | 0 | { |
443 | 0 | cairo_contour_chain_t *chain; |
444 | |
|
445 | 0 | if (contour->tail == &contour->chain) |
446 | 0 | return; |
447 | | |
448 | 0 | for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next) |
449 | 0 | ; |
450 | 0 | free (contour->tail); |
451 | 0 | contour->tail = chain; |
452 | 0 | chain->next = NULL; |
453 | 0 | } |