1 /*
2 * Copyright 2012 Google, Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "glyphy-common.hh"
24 #include "glyphy-geometry.hh"
25 #include "glyphy-arcs-bezier.hh"
26
27 using namespace GLyphy::Geometry;
28 using namespace GLyphy::ArcsBezier;
29
30
31
32 /*
33 * Approximate outlines with multiple arcs
34 */
35
36
37 struct glyphy_arc_accumulator_t {
38 unsigned int refcount;
39
40 double tolerance;
41 double max_d;
42 unsigned int d_bits;
43 glyphy_arc_endpoint_accumulator_callback_t callback;
44 void *user_data;
45
46 glyphy_point_t start_point;
47 glyphy_point_t current_point;
48 bool need_moveto;
49 unsigned int num_endpoints;
50 double max_error;
51 glyphy_bool_t success;
52 };
53
54
55 glyphy_arc_accumulator_t *
glyphy_arc_accumulator_create(void)56 glyphy_arc_accumulator_create (void)
57 {
58 glyphy_arc_accumulator_t *acc = (glyphy_arc_accumulator_t *) calloc (1, sizeof (glyphy_arc_accumulator_t));
59 acc->refcount = 1;
60
61 acc->tolerance = 5e-4;
62 acc->max_d = GLYPHY_MAX_D;
63 acc->d_bits = 8;
64 acc->callback = NULL;
65 acc->user_data = NULL;
66
67 glyphy_arc_accumulator_reset (acc);
68
69 return acc;
70 }
71
72 void
glyphy_arc_accumulator_reset(glyphy_arc_accumulator_t * acc)73 glyphy_arc_accumulator_reset (glyphy_arc_accumulator_t *acc)
74 {
75 acc->start_point = acc->current_point = Point (0, 0);
76 acc->need_moveto = true;
77 acc->num_endpoints = 0;
78 acc->max_error = 0;
79 acc->success = true;
80 }
81
82 void
glyphy_arc_accumulator_destroy(glyphy_arc_accumulator_t * acc)83 glyphy_arc_accumulator_destroy (glyphy_arc_accumulator_t *acc)
84 {
85 if (!acc || --acc->refcount)
86 return;
87
88 free (acc);
89 }
90
91 glyphy_arc_accumulator_t *
glyphy_arc_accumulator_reference(glyphy_arc_accumulator_t * acc)92 glyphy_arc_accumulator_reference (glyphy_arc_accumulator_t *acc)
93 {
94 if (acc)
95 acc->refcount++;
96 return acc;
97 }
98
99
100 /* Configure acc */
101
102 void
glyphy_arc_accumulator_set_tolerance(glyphy_arc_accumulator_t * acc,double tolerance)103 glyphy_arc_accumulator_set_tolerance (glyphy_arc_accumulator_t *acc,
104 double tolerance)
105 {
106 acc->tolerance = tolerance;
107 }
108
109 double
glyphy_arc_accumulator_get_tolerance(glyphy_arc_accumulator_t * acc)110 glyphy_arc_accumulator_get_tolerance (glyphy_arc_accumulator_t *acc)
111 {
112 return acc->tolerance;
113 }
114
115 void
glyphy_arc_accumulator_set_callback(glyphy_arc_accumulator_t * acc,glyphy_arc_endpoint_accumulator_callback_t callback,void * user_data)116 glyphy_arc_accumulator_set_callback (glyphy_arc_accumulator_t *acc,
117 glyphy_arc_endpoint_accumulator_callback_t callback,
118 void *user_data)
119 {
120 acc->callback = callback;
121 acc->user_data = user_data;
122 }
123
124 void
glyphy_arc_accumulator_get_callback(glyphy_arc_accumulator_t * acc,glyphy_arc_endpoint_accumulator_callback_t * callback,void ** user_data)125 glyphy_arc_accumulator_get_callback (glyphy_arc_accumulator_t *acc,
126 glyphy_arc_endpoint_accumulator_callback_t *callback,
127 void **user_data)
128 {
129 *callback = acc->callback;
130 *user_data = acc->user_data;
131 }
132
133 void
glyphy_arc_accumulator_set_d_metrics(glyphy_arc_accumulator_t * acc,double max_d,double d_bits)134 glyphy_arc_accumulator_set_d_metrics (glyphy_arc_accumulator_t *acc,
135 double max_d,
136 double d_bits)
137 {
138 acc->max_d = max_d;
139 acc->d_bits = d_bits;
140 }
141
142 void
glyphy_arc_accumulator_get_d_metrics(glyphy_arc_accumulator_t * acc,double * max_d,double * d_bits)143 glyphy_arc_accumulator_get_d_metrics (glyphy_arc_accumulator_t *acc,
144 double *max_d,
145 double *d_bits)
146 {
147 *max_d = acc->max_d;
148 *d_bits = acc->d_bits;
149 }
150
151
152 /* Accumulation results */
153
154 unsigned int
glyphy_arc_accumulator_get_num_endpoints(glyphy_arc_accumulator_t * acc)155 glyphy_arc_accumulator_get_num_endpoints (glyphy_arc_accumulator_t *acc)
156 {
157 return acc->num_endpoints;
158 }
159
160 double
glyphy_arc_accumulator_get_error(glyphy_arc_accumulator_t * acc)161 glyphy_arc_accumulator_get_error (glyphy_arc_accumulator_t *acc)
162 {
163 return acc->max_error;
164 }
165
166 glyphy_bool_t
glyphy_arc_accumulator_successful(glyphy_arc_accumulator_t * acc)167 glyphy_arc_accumulator_successful (glyphy_arc_accumulator_t *acc)
168 {
169 return acc->success;
170 }
171
172
173 /* Accumulate */
174
175 static void
emit(glyphy_arc_accumulator_t * acc,const Point & p,double d)176 emit (glyphy_arc_accumulator_t *acc, const Point &p, double d)
177 {
178 glyphy_arc_endpoint_t endpoint = {p, d};
179 acc->success = acc->success && acc->callback (&endpoint, acc->user_data);
180 if (acc->success) {
181 acc->num_endpoints++;
182 acc->current_point = p;
183 }
184 }
185
186 static void
accumulate(glyphy_arc_accumulator_t * acc,const Point & p,double d)187 accumulate (glyphy_arc_accumulator_t *acc, const Point &p, double d)
188 {
189 if (Point (acc->current_point) == p)
190 return;
191 if (d == GLYPHY_INFINITY) {
192 /* Emit moveto lazily, for cleaner outlines */
193 acc->need_moveto = true;
194 acc->current_point = p;
195 return;
196 }
197 if (acc->need_moveto) {
198 emit (acc, acc->current_point, GLYPHY_INFINITY);
199 if (acc->success) {
200 acc->start_point = acc->current_point;
201 acc->need_moveto = false;
202 }
203 }
204 emit (acc, p, d);
205 }
206
207 static void
move_to(glyphy_arc_accumulator_t * acc,const Point & p)208 move_to (glyphy_arc_accumulator_t *acc, const Point &p)
209 {
210 if (!acc->num_endpoints || p != acc->current_point)
211 accumulate (acc, p, GLYPHY_INFINITY);
212 }
213
214 static void
arc_to(glyphy_arc_accumulator_t * acc,const Point & p1,double d)215 arc_to (glyphy_arc_accumulator_t *acc, const Point &p1, double d)
216 {
217 accumulate (acc, p1, d);
218 }
219
220 static void
bezier(glyphy_arc_accumulator_t * acc,const Bezier & b)221 bezier (glyphy_arc_accumulator_t *acc, const Bezier &b)
222 {
223 double e;
224
225 std::vector<Arc> arcs;
226 typedef ArcBezierApproximatorQuantizedDefault _ArcBezierApproximator;
227 _ArcBezierApproximator appx (acc->max_d, acc->d_bits);
228 ArcsBezierApproximatorSpringSystem<_ArcBezierApproximator>
229 ::approximate_bezier_with_arcs (b, acc->tolerance, appx, arcs, &e);
230
231 acc->max_error = std::max (acc->max_error, e);
232
233 move_to (acc, b.p0);
234 for (unsigned int i = 0; i < arcs.size (); i++)
235 arc_to (acc, arcs[i].p1, arcs[i].d);
236 }
237
238 static void
close_path(glyphy_arc_accumulator_t * acc)239 close_path (glyphy_arc_accumulator_t *acc)
240 {
241 if (!acc->need_moveto && Point (acc->current_point) != Point (acc->start_point))
242 arc_to (acc, acc->start_point, 0);
243 }
244
245 void
glyphy_arc_accumulator_move_to(glyphy_arc_accumulator_t * acc,const glyphy_point_t * p0)246 glyphy_arc_accumulator_move_to (glyphy_arc_accumulator_t *acc,
247 const glyphy_point_t *p0)
248 {
249 move_to (acc, *p0);
250 }
251
252 void
glyphy_arc_accumulator_line_to(glyphy_arc_accumulator_t * acc,const glyphy_point_t * p1)253 glyphy_arc_accumulator_line_to (glyphy_arc_accumulator_t *acc,
254 const glyphy_point_t *p1)
255 {
256 arc_to (acc, *p1, 0);
257 }
258
259 void
glyphy_arc_accumulator_conic_to(glyphy_arc_accumulator_t * acc,const glyphy_point_t * p1,const glyphy_point_t * p2)260 glyphy_arc_accumulator_conic_to (glyphy_arc_accumulator_t *acc,
261 const glyphy_point_t *p1,
262 const glyphy_point_t *p2)
263 {
264 bezier (acc, Bezier (acc->current_point,
265 Point (acc->current_point).lerp (2/3., *p1),
266 Point (*p2).lerp (2/3., *p1),
267 *p2));
268 }
269
270 void
glyphy_arc_accumulator_cubic_to(glyphy_arc_accumulator_t * acc,const glyphy_point_t * p1,const glyphy_point_t * p2,const glyphy_point_t * p3)271 glyphy_arc_accumulator_cubic_to (glyphy_arc_accumulator_t *acc,
272 const glyphy_point_t *p1,
273 const glyphy_point_t *p2,
274 const glyphy_point_t *p3)
275 {
276 bezier (acc, Bezier (acc->current_point, *p1, *p2, *p3));
277 }
278
279 void
glyphy_arc_accumulator_arc_to(glyphy_arc_accumulator_t * acc,const glyphy_point_t * p1,double d)280 glyphy_arc_accumulator_arc_to (glyphy_arc_accumulator_t *acc,
281 const glyphy_point_t *p1,
282 double d)
283 {
284 arc_to (acc, *p1, d);
285 }
286
287 void
glyphy_arc_accumulator_close_path(glyphy_arc_accumulator_t * acc)288 glyphy_arc_accumulator_close_path (glyphy_arc_accumulator_t *acc)
289 {
290 close_path (acc);
291 }
292
293
294
295 /*
296 * Outline extents from arc list
297 */
298
299
300 void
glyphy_arc_list_extents(const glyphy_arc_endpoint_t * endpoints,unsigned int num_endpoints,glyphy_extents_t * extents)301 glyphy_arc_list_extents (const glyphy_arc_endpoint_t *endpoints,
302 unsigned int num_endpoints,
303 glyphy_extents_t *extents)
304 {
305 Point p0 (0, 0);
306 glyphy_extents_clear (extents);
307 for (unsigned int i = 0; i < num_endpoints; i++) {
308 const glyphy_arc_endpoint_t &endpoint = endpoints[i];
309 if (endpoint.d == GLYPHY_INFINITY) {
310 p0 = endpoint.p;
311 continue;
312 }
313 Arc arc (p0, endpoint.p, endpoint.d);
314 p0 = endpoint.p;
315
316 glyphy_extents_t arc_extents;
317 arc.extents (arc_extents);
318 glyphy_extents_extend (extents, &arc_extents);
319 }
320 }
321