/work/workdir/UnpackedTarball/cairo/src/cairo-hull.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* cairo - a vector graphics library with display and print output |
2 | | * |
3 | | * Copyright © 2003 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-error-private.h" |
40 | | #include "cairo-slope-private.h" |
41 | | |
42 | | typedef struct cairo_hull { |
43 | | cairo_point_t point; |
44 | | cairo_slope_t slope; |
45 | | int discard; |
46 | | int id; |
47 | | } cairo_hull_t; |
48 | | |
49 | | static void |
50 | | _cairo_hull_init (cairo_hull_t *hull, |
51 | | cairo_pen_vertex_t *vertices, |
52 | | int num_vertices) |
53 | 0 | { |
54 | 0 | cairo_point_t *p, *extremum, tmp; |
55 | 0 | int i; |
56 | |
|
57 | 0 | extremum = &vertices[0].point; |
58 | 0 | for (i = 1; i < num_vertices; i++) { |
59 | 0 | p = &vertices[i].point; |
60 | 0 | if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x)) |
61 | 0 | extremum = p; |
62 | 0 | } |
63 | | /* Put the extremal point at the beginning of the array */ |
64 | 0 | tmp = *extremum; |
65 | 0 | *extremum = vertices[0].point; |
66 | 0 | vertices[0].point = tmp; |
67 | |
|
68 | 0 | for (i = 0; i < num_vertices; i++) { |
69 | 0 | hull[i].point = vertices[i].point; |
70 | 0 | _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point); |
71 | | |
72 | | /* give each point a unique id for later comparison */ |
73 | 0 | hull[i].id = i; |
74 | | |
75 | | /* Don't discard by default */ |
76 | 0 | hull[i].discard = 0; |
77 | | |
78 | | /* Discard all points coincident with the extremal point */ |
79 | 0 | if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0) |
80 | 0 | hull[i].discard = 1; |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | static inline cairo_int64_t |
85 | | _slope_length (cairo_slope_t *slope) |
86 | 0 | { |
87 | 0 | return _cairo_int64_add (_cairo_int32x32_64_mul (slope->dx, slope->dx), |
88 | 0 | _cairo_int32x32_64_mul (slope->dy, slope->dy)); |
89 | 0 | } |
90 | | |
91 | | static int |
92 | | _cairo_hull_vertex_compare (const void *av, const void *bv) |
93 | 0 | { |
94 | 0 | cairo_hull_t *a = (cairo_hull_t *) av; |
95 | 0 | cairo_hull_t *b = (cairo_hull_t *) bv; |
96 | 0 | int ret; |
97 | | |
98 | | /* Some libraries are reported to actually compare identical |
99 | | * pointers and require the result to be 0. This is the crazy world we |
100 | | * have to live in. |
101 | | */ |
102 | 0 | if (a == b) |
103 | 0 | return 0; |
104 | | |
105 | 0 | ret = _cairo_slope_compare (&a->slope, &b->slope); |
106 | | |
107 | | /* |
108 | | * In the case of two vertices with identical slope from the |
109 | | * extremal point discard the nearer point. |
110 | | */ |
111 | 0 | if (ret == 0) { |
112 | 0 | int cmp; |
113 | |
|
114 | 0 | cmp = _cairo_int64_cmp (_slope_length (&a->slope), |
115 | 0 | _slope_length (&b->slope)); |
116 | | |
117 | | /* |
118 | | * Use the points' ids to ensure a well-defined ordering, |
119 | | * and avoid setting discard on both points. |
120 | | */ |
121 | 0 | if (cmp < 0 || (cmp == 0 && a->id < b->id)) { |
122 | 0 | a->discard = 1; |
123 | 0 | ret = -1; |
124 | 0 | } else { |
125 | 0 | b->discard = 1; |
126 | 0 | ret = 1; |
127 | 0 | } |
128 | 0 | } |
129 | |
|
130 | 0 | return ret; |
131 | 0 | } |
132 | | |
133 | | static int |
134 | | _cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index) |
135 | 0 | { |
136 | | /* hull[0] is always valid, and we never need to wraparound, (if |
137 | | * we are passed an index of 0 here, then the calling loop is just |
138 | | * about to terminate). */ |
139 | 0 | if (index == 0) |
140 | 0 | return 0; |
141 | | |
142 | 0 | do { |
143 | 0 | index--; |
144 | 0 | } while (hull[index].discard); |
145 | |
|
146 | 0 | return index; |
147 | 0 | } |
148 | | |
149 | | static int |
150 | | _cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index) |
151 | 0 | { |
152 | 0 | do { |
153 | 0 | index = (index + 1) % num_hull; |
154 | 0 | } while (hull[index].discard); |
155 | |
|
156 | 0 | return index; |
157 | 0 | } |
158 | | |
159 | | static void |
160 | | _cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull) |
161 | 0 | { |
162 | 0 | int i, j, k; |
163 | 0 | cairo_slope_t slope_ij, slope_jk; |
164 | |
|
165 | 0 | i = 0; |
166 | 0 | j = _cairo_hull_next_valid (hull, num_hull, i); |
167 | 0 | k = _cairo_hull_next_valid (hull, num_hull, j); |
168 | |
|
169 | 0 | do { |
170 | 0 | _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point); |
171 | 0 | _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point); |
172 | | |
173 | | /* Is the angle formed by ij and jk concave? */ |
174 | 0 | if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) { |
175 | 0 | if (i == k) |
176 | 0 | return; |
177 | 0 | hull[j].discard = 1; |
178 | 0 | j = i; |
179 | 0 | i = _cairo_hull_prev_valid (hull, num_hull, j); |
180 | 0 | } else { |
181 | 0 | i = j; |
182 | 0 | j = k; |
183 | 0 | k = _cairo_hull_next_valid (hull, num_hull, j); |
184 | 0 | } |
185 | 0 | } while (j != 0); |
186 | 0 | } |
187 | | |
188 | | static void |
189 | | _cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices) |
190 | 0 | { |
191 | 0 | int i, j = 0; |
192 | |
|
193 | 0 | for (i = 0; i < *num_vertices; i++) { |
194 | 0 | if (hull[i].discard) |
195 | 0 | continue; |
196 | 0 | vertices[j++].point = hull[i].point; |
197 | 0 | } |
198 | |
|
199 | 0 | *num_vertices = j; |
200 | 0 | } |
201 | | |
202 | | /* Given a set of vertices, compute the convex hull using the Graham |
203 | | scan algorithm. */ |
204 | | cairo_status_t |
205 | | _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) |
206 | 0 | { |
207 | 0 | cairo_hull_t hull_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_hull_t)]; |
208 | 0 | cairo_hull_t *hull; |
209 | 0 | int num_hull = *num_vertices; |
210 | |
|
211 | 0 | if (CAIRO_INJECT_FAULT ()) |
212 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
213 | | |
214 | 0 | if (num_hull > ARRAY_LENGTH (hull_stack)) { |
215 | 0 | hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); |
216 | 0 | if (unlikely (hull == NULL)) |
217 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
218 | 0 | } else { |
219 | 0 | hull = hull_stack; |
220 | 0 | } |
221 | | |
222 | 0 | _cairo_hull_init (hull, vertices, num_hull); |
223 | |
|
224 | 0 | qsort (hull + 1, num_hull - 1, |
225 | 0 | sizeof (cairo_hull_t), _cairo_hull_vertex_compare); |
226 | |
|
227 | 0 | _cairo_hull_eliminate_concave (hull, num_hull); |
228 | |
|
229 | 0 | _cairo_hull_to_pen (hull, vertices, num_vertices); |
230 | |
|
231 | 0 | if (hull != hull_stack) |
232 | 0 | free (hull); |
233 | |
|
234 | 0 | return CAIRO_STATUS_SUCCESS; |
235 | 0 | } |