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