/src/gpac/src/utils/path2d.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / common tools sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | |
27 | | #include <gpac/path2d.h> |
28 | | |
29 | | #if !defined(GPAC_DISABLE_EVG) || defined(GPAC_HAS_FREETYPE) |
30 | | |
31 | | GF_EXPORT |
32 | | GF_Path *gf_path_new() |
33 | 0 | { |
34 | 0 | GF_Path *gp; |
35 | 0 | GF_SAFEALLOC(gp, GF_Path); |
36 | 0 | if (!gp) return NULL; |
37 | 0 | gp->fineness = FIX_ONE; |
38 | 0 | return gp; |
39 | 0 | } |
40 | | |
41 | | GF_EXPORT |
42 | | void gf_path_reset(GF_Path *gp) |
43 | 0 | { |
44 | 0 | Fixed fineness; |
45 | 0 | u32 flags; |
46 | 0 | if (!gp) return; |
47 | 0 | if (gp->contours) gf_free(gp->contours); |
48 | 0 | if (gp->tags) gf_free(gp->tags); |
49 | 0 | if (gp->points) gf_free(gp->points); |
50 | 0 | fineness = gp->fineness ? gp->fineness : FIX_ONE; |
51 | 0 | flags = gp->flags; |
52 | 0 | memset(gp, 0, sizeof(GF_Path)); |
53 | 0 | gp->flags = flags | GF_PATH_FLATTENED | GF_PATH_BBOX_DIRTY; |
54 | 0 | gp->fineness = fineness; |
55 | 0 | } |
56 | | |
57 | | GF_EXPORT |
58 | | GF_Path *gf_path_clone(GF_Path *gp) |
59 | 0 | { |
60 | 0 | GF_Path *dst; |
61 | 0 | GF_SAFEALLOC(dst, GF_Path); |
62 | 0 | if (!dst) return NULL; |
63 | 0 | dst->contours = (u32 *)gf_malloc(sizeof(u32)*gp->n_contours); |
64 | 0 | if (!dst->contours) { |
65 | 0 | gf_free(dst); |
66 | 0 | return NULL; |
67 | 0 | } |
68 | 0 | dst->points = (GF_Point2D *) gf_malloc(sizeof(GF_Point2D)*gp->n_points); |
69 | 0 | if (!dst->points) { |
70 | 0 | gf_free(dst->contours); |
71 | 0 | gf_free(dst); |
72 | 0 | return NULL; |
73 | 0 | } |
74 | 0 | dst->tags = (u8 *) gf_malloc(sizeof(u8)*gp->n_points); |
75 | 0 | if (!dst->tags) { |
76 | 0 | gf_free(dst->points); |
77 | 0 | gf_free(dst->contours); |
78 | 0 | gf_free(dst); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | 0 | memcpy(dst->contours, gp->contours, sizeof(u32)*gp->n_contours); |
82 | 0 | dst->n_contours = gp->n_contours; |
83 | 0 | memcpy(dst->points, gp->points, sizeof(GF_Point2D)*gp->n_points); |
84 | 0 | memcpy(dst->tags, gp->tags, sizeof(u8)*gp->n_points); |
85 | 0 | dst->n_alloc_points = dst->n_points = gp->n_points; |
86 | 0 | dst->flags = gp->flags; |
87 | 0 | dst->bbox = gp->bbox; |
88 | 0 | dst->fineness = gp->fineness; |
89 | 0 | return dst; |
90 | 0 | } |
91 | | |
92 | | GF_EXPORT |
93 | | void gf_path_del(GF_Path *gp) |
94 | 0 | { |
95 | 0 | if (!gp) return; |
96 | 0 | if (gp->contours) gf_free(gp->contours); |
97 | 0 | if (gp->tags) gf_free(gp->tags); |
98 | 0 | if (gp->points) gf_free(gp->points); |
99 | 0 | gf_free(gp); |
100 | 0 | } |
101 | | |
102 | | #define GF_2D_REALLOC(_gp) \ |
103 | 0 | if (_gp->n_alloc_points < _gp->n_points+3) { \ |
104 | 0 | _gp->n_alloc_points = (_gp->n_alloc_points<5) ? 10 : (_gp->n_alloc_points*2); \ |
105 | 0 | _gp->points = (GF_Point2D *)gf_realloc(_gp->points, sizeof(GF_Point2D)*(_gp->n_alloc_points)); \ |
106 | 0 | _gp->tags = (u8 *) gf_realloc(_gp->tags, sizeof(u8)*(_gp->n_alloc_points)); \ |
107 | 0 | } \ |
108 | | |
109 | | |
110 | | GF_EXPORT |
111 | | GF_Err gf_path_add_move_to(GF_Path *gp, Fixed x, Fixed y) |
112 | 0 | { |
113 | 0 | if (!gp) return GF_BAD_PARAM; |
114 | | |
115 | | #if 0 |
116 | | /*skip empty paths*/ |
117 | | if ((gp->n_contours>=2) && (gp->contours[gp->n_contours-2]+1==gp->contours[gp->n_contours-1])) { |
118 | | gp->points[gp->n_points].x = x; |
119 | | gp->points[gp->n_points].y = y; |
120 | | return GF_OK; |
121 | | } |
122 | | #endif |
123 | | |
124 | 0 | gp->contours = (u32 *) gf_realloc(gp->contours, sizeof(u32)*(gp->n_contours+1)); |
125 | 0 | GF_2D_REALLOC(gp) |
126 | |
|
127 | 0 | gp->points[gp->n_points].x = x; |
128 | 0 | gp->points[gp->n_points].y = y; |
129 | 0 | gp->tags[gp->n_points] = 1; |
130 | | /*set end point*/ |
131 | 0 | gp->contours[gp->n_contours] = gp->n_points; |
132 | | /*new contour*/ |
133 | 0 | gp->n_contours++; |
134 | 0 | gp->n_points++; |
135 | 0 | gp->flags |= GF_PATH_BBOX_DIRTY; |
136 | 0 | return GF_OK; |
137 | 0 | } |
138 | | |
139 | | GF_EXPORT |
140 | 0 | GF_Err gf_path_add_move_to_vec(GF_Path *gp, GF_Point2D *pt) { |
141 | 0 | return gf_path_add_move_to(gp, pt->x, pt->y); |
142 | 0 | } |
143 | | |
144 | | GF_EXPORT |
145 | | GF_Err gf_path_add_line_to(GF_Path *gp, Fixed x, Fixed y) |
146 | 0 | { |
147 | 0 | if (!gp || !gp->n_contours) return GF_BAD_PARAM; |
148 | | /*we allow line to same point as move (seen in SVG sequences) - striking will make a point*/ |
149 | | |
150 | 0 | GF_2D_REALLOC(gp) |
151 | 0 | gp->points[gp->n_points].x = x; |
152 | 0 | gp->points[gp->n_points].y = y; |
153 | 0 | gp->tags[gp->n_points] = 1; |
154 | | /*set end point*/ |
155 | 0 | gp->contours[gp->n_contours-1] = gp->n_points; |
156 | 0 | gp->n_points++; |
157 | 0 | gp->flags |= GF_PATH_BBOX_DIRTY; |
158 | 0 | return GF_OK; |
159 | 0 | } |
160 | | |
161 | | GF_EXPORT |
162 | 0 | GF_Err gf_path_add_line_to_vec(GF_Path *gp, GF_Point2D *pt) { |
163 | 0 | return gf_path_add_line_to(gp, pt->x, pt->y); |
164 | 0 | } |
165 | | |
166 | | GF_EXPORT |
167 | | GF_Err gf_path_close(GF_Path *gp) |
168 | 0 | { |
169 | 0 | GF_Point2D start, end; |
170 | 0 | if (!gp || !gp->n_contours) return GF_BAD_PARAM; |
171 | | |
172 | 0 | if (gp->n_contours<=1) start = gp->points[0]; |
173 | 0 | else start = gp->points[gp->contours[gp->n_contours-2] + 1]; |
174 | 0 | end = gp->points[gp->n_points-1]; |
175 | 0 | end.x -= start.x; |
176 | 0 | end.y -= start.y; |
177 | 0 | if (FIX_ONE/100 < ABS(end.x) || FIX_ONE/100 < ABS(end.y)) { |
178 | 0 | GF_Err e = gf_path_add_line_to(gp, start.x, start.y); |
179 | 0 | if (e) return e; |
180 | 0 | } |
181 | 0 | gp->tags[gp->n_points-1] = GF_PATH_CLOSE; |
182 | 0 | return GF_OK; |
183 | 0 | } |
184 | | |
185 | | GF_EXPORT |
186 | | GF_Err gf_path_add_cubic_to(GF_Path *gp, Fixed c1_x, Fixed c1_y, Fixed c2_x, Fixed c2_y, Fixed x, Fixed y) |
187 | 0 | { |
188 | 0 | if (!gp || !gp->n_contours) return GF_BAD_PARAM; |
189 | 0 | GF_2D_REALLOC(gp) |
190 | 0 | gp->points[gp->n_points].x = c1_x; |
191 | 0 | gp->points[gp->n_points].y = c1_y; |
192 | 0 | gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC; |
193 | 0 | gp->n_points++; |
194 | 0 | gp->points[gp->n_points].x = c2_x; |
195 | 0 | gp->points[gp->n_points].y = c2_y; |
196 | 0 | gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC; |
197 | 0 | gp->n_points++; |
198 | 0 | gp->points[gp->n_points].x = x; |
199 | 0 | gp->points[gp->n_points].y = y; |
200 | 0 | gp->tags[gp->n_points] = GF_PATH_CURVE_ON; |
201 | | /*set end point*/ |
202 | 0 | gp->contours[gp->n_contours-1] = gp->n_points; |
203 | 0 | gp->n_points++; |
204 | 0 | gp->flags |= GF_PATH_BBOX_DIRTY; |
205 | 0 | gp->flags &= ~GF_PATH_FLATTENED; |
206 | 0 | return GF_OK; |
207 | 0 | } |
208 | | |
209 | | GF_EXPORT |
210 | | GF_Err gf_path_add_cubic_to_vec(GF_Path *gp, GF_Point2D *c1, GF_Point2D *c2, GF_Point2D *pt) |
211 | 0 | { |
212 | 0 | return gf_path_add_cubic_to(gp, c1->x, c1->y, c2->x, c2->y, pt->x, pt->y); |
213 | 0 | } |
214 | | |
215 | | |
216 | | GF_EXPORT |
217 | | GF_Err gf_path_add_quadratic_to(GF_Path *gp, Fixed c_x, Fixed c_y, Fixed x, Fixed y) |
218 | 0 | { |
219 | 0 | if (!gp || !gp->n_contours) return GF_BAD_PARAM; |
220 | 0 | GF_2D_REALLOC(gp) |
221 | 0 | gp->points[gp->n_points].x = c_x; |
222 | 0 | gp->points[gp->n_points].y = c_y; |
223 | 0 | gp->tags[gp->n_points] = GF_PATH_CURVE_CONIC; |
224 | 0 | gp->n_points++; |
225 | 0 | gp->points[gp->n_points].x = x; |
226 | 0 | gp->points[gp->n_points].y = y; |
227 | 0 | gp->tags[gp->n_points] = GF_PATH_CURVE_ON; |
228 | | /*set end point*/ |
229 | 0 | gp->contours[gp->n_contours-1] = gp->n_points; |
230 | 0 | gp->n_points++; |
231 | 0 | gp->flags |= GF_PATH_BBOX_DIRTY; |
232 | 0 | gp->flags &= ~GF_PATH_FLATTENED; |
233 | 0 | return GF_OK; |
234 | 0 | } |
235 | | GF_EXPORT |
236 | | GF_Err gf_path_add_quadratic_to_vec(GF_Path *gp, GF_Point2D *c, GF_Point2D *pt) |
237 | 0 | { |
238 | 0 | return gf_path_add_quadratic_to(gp, c->x, c->y, pt->x, pt->y); |
239 | 0 | } |
240 | | |
241 | | /*adds rectangle centered on cx, cy*/ |
242 | | GF_EXPORT |
243 | | GF_Err gf_path_add_rect_center(GF_Path *gp, Fixed cx, Fixed cy, Fixed w, Fixed h) |
244 | 0 | { |
245 | 0 | GF_Err e = gf_path_add_move_to(gp, cx - w/2, cy - h/2); |
246 | 0 | if (e) return e; |
247 | 0 | e = gf_path_add_line_to(gp, cx + w/2, cy - h/2); |
248 | 0 | if (e) return e; |
249 | 0 | e = gf_path_add_line_to(gp, cx + w/2, cy + h/2); |
250 | 0 | if (e) return e; |
251 | 0 | e = gf_path_add_line_to(gp, cx - w/2, cy + h/2); |
252 | 0 | if (e) return e; |
253 | 0 | return gf_path_close(gp); |
254 | 0 | } |
255 | | |
256 | | GF_EXPORT |
257 | | GF_Err gf_path_add_rect(GF_Path *gp, Fixed ox, Fixed oy, Fixed w, Fixed h) |
258 | 0 | { |
259 | 0 | GF_Err e = gf_path_add_move_to(gp, ox, oy); |
260 | 0 | if (e) return e; |
261 | 0 | e = gf_path_add_line_to(gp, ox + w, oy); |
262 | 0 | if (e) return e; |
263 | 0 | e = gf_path_add_line_to(gp, ox+w, oy-h); |
264 | 0 | if (e) return e; |
265 | 0 | e = gf_path_add_line_to(gp, ox, oy-h); |
266 | 0 | if (e) return e; |
267 | 0 | return gf_path_close(gp); |
268 | 0 | } |
269 | | |
270 | 0 | #define GF_2D_DEFAULT_RES 64 |
271 | | |
272 | | GF_EXPORT |
273 | | GF_Err gf_path_add_ellipse(GF_Path *gp, Fixed cx, Fixed cy, Fixed a_axis, Fixed b_axis) |
274 | 0 | { |
275 | 0 | GF_Err e; |
276 | 0 | Fixed _vx, _vy, cur; |
277 | 0 | u32 i; |
278 | 0 | a_axis /= 2; |
279 | 0 | b_axis /= 2; |
280 | 0 | e = gf_path_add_move_to(gp, cx+a_axis, cy); |
281 | 0 | if (e) return e; |
282 | 0 | for (i=1; i<GF_2D_DEFAULT_RES; i++) { |
283 | 0 | cur = GF_2PI*i/GF_2D_DEFAULT_RES; |
284 | 0 | _vx = gf_mulfix(a_axis, gf_cos(cur) ); |
285 | 0 | _vy = gf_mulfix(b_axis, gf_sin(cur) ); |
286 | 0 | e = gf_path_add_line_to(gp, _vx + cx, _vy + cy); |
287 | 0 | if (e) return e; |
288 | 0 | } |
289 | 0 | return gf_path_close(gp); |
290 | 0 | } |
291 | | |
292 | | GF_Err gf_path_add_subpath(GF_Path *gp, GF_Path *src, GF_Matrix2D *mx) |
293 | 0 | { |
294 | 0 | u32 i; |
295 | 0 | if (!src) return GF_OK; |
296 | 0 | gp->contours = (u32*)gf_realloc(gp->contours, sizeof(u32) * (gp->n_contours + src->n_contours)); |
297 | 0 | if (!gp->contours) return GF_OUT_OF_MEM; |
298 | 0 | for (i=0; i<src->n_contours; i++) { |
299 | 0 | gp->contours[i+gp->n_contours] = src->contours[i] + gp->n_points; |
300 | 0 | } |
301 | 0 | gp->n_contours += src->n_contours; |
302 | 0 | gp->n_alloc_points += src->n_alloc_points; |
303 | 0 | gp->points = (GF_Point2D*)gf_realloc(gp->points, sizeof(GF_Point2D)*gp->n_alloc_points); |
304 | 0 | if (!gp->points) return GF_OUT_OF_MEM; |
305 | 0 | gp->tags = (u8*)gf_realloc(gp->tags, sizeof(u8)*gp->n_alloc_points); |
306 | 0 | if (!gp->tags) return GF_OUT_OF_MEM; |
307 | 0 | if (src->n_points) { |
308 | 0 | memcpy(gp->points + gp->n_points, src->points, sizeof(GF_Point2D)*src->n_points); |
309 | 0 | if (mx) { |
310 | 0 | for (i=0; i<src->n_points; i++) { |
311 | 0 | gf_mx2d_apply_coords(mx, &gp->points[i+gp->n_points].x, &gp->points[i+gp->n_points].y); |
312 | 0 | } |
313 | 0 | } |
314 | 0 | memcpy(gp->tags + gp->n_points, src->tags, sizeof(u8)*src->n_points); |
315 | 0 | gp->n_points += src->n_points; |
316 | 0 | } |
317 | 0 | gf_rect_union(&gp->bbox, &src->bbox); |
318 | 0 | if (!(src->flags & GF_PATH_FLATTENED)) gp->flags &= ~GF_PATH_FLATTENED; |
319 | 0 | if (src->flags & GF_PATH_BBOX_DIRTY) gp->flags |= GF_PATH_BBOX_DIRTY; |
320 | 0 | return GF_OK; |
321 | 0 | } |
322 | | |
323 | | /*generic N-bezier*/ |
324 | | static void NBezier(GF_Point2D *pts, s32 n, Double mu, GF_Point2D *pt_out) |
325 | 0 | { |
326 | 0 | s32 k,kn,nn,nkn; |
327 | 0 | Double blend, muk, munk; |
328 | 0 | pt_out->x = pt_out->y = 0; |
329 | |
|
330 | 0 | muk = 1; |
331 | 0 | munk = pow(1-mu,(Double)n); |
332 | 0 | for (k=0; k<=n; k++) { |
333 | 0 | nn = n; |
334 | 0 | kn = k; |
335 | 0 | nkn = n - k; |
336 | 0 | blend = muk * munk; |
337 | 0 | muk *= mu; |
338 | 0 | munk /= (1-mu); |
339 | 0 | while (nn >= 1) { |
340 | 0 | blend *= nn; |
341 | 0 | nn--; |
342 | 0 | if (kn > 1) { |
343 | 0 | blend /= (double)kn; |
344 | 0 | kn--; |
345 | 0 | } |
346 | 0 | if (nkn > 1) { |
347 | 0 | blend /= (double)nkn; |
348 | 0 | nkn--; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | pt_out->x += gf_mulfix(pts[k].x, FLT2FIX(blend)); |
352 | 0 | pt_out->y += gf_mulfix(pts[k].y, FLT2FIX(blend)); |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | | static void gf_add_n_bezier(GF_Path *gp, GF_Point2D *newpts, u32 nbPoints) |
357 | 0 | { |
358 | 0 | Double mu; |
359 | 0 | u32 numPoints, i; |
360 | 0 | GF_Point2D end; |
361 | 0 | numPoints = (u32) FIX2INT(GF_2D_DEFAULT_RES * gp->fineness); |
362 | 0 | mu = 0.0; |
363 | 0 | if (numPoints) mu = 1/(Double)numPoints; |
364 | 0 | for (i=1; i<numPoints; i++) { |
365 | 0 | NBezier(newpts, nbPoints - 1, i*mu, &end); |
366 | 0 | gf_path_add_line_to(gp, end.x, end.y); |
367 | 0 | } |
368 | 0 | gf_path_add_line_to(gp, newpts[nbPoints-1].x, newpts[nbPoints-1].y); |
369 | 0 | } |
370 | | |
371 | | GF_EXPORT |
372 | | GF_Err gf_path_add_bezier(GF_Path *gp, GF_Point2D *pts, u32 nbPoints) |
373 | 0 | { |
374 | 0 | GF_Point2D *newpts; |
375 | 0 | if (!gp->n_points) return GF_BAD_PARAM; |
376 | | |
377 | 0 | newpts = (GF_Point2D *) gf_malloc(sizeof(GF_Point2D) * (nbPoints+1)); |
378 | 0 | newpts[0] = gp->points[gp->n_points-1]; |
379 | 0 | memcpy(&newpts[1], pts, sizeof(GF_Point2D) * nbPoints); |
380 | |
|
381 | 0 | gf_add_n_bezier(gp, newpts, nbPoints + 1); |
382 | |
|
383 | 0 | gf_free(newpts); |
384 | 0 | return GF_OK; |
385 | 0 | } |
386 | | |
387 | | GF_EXPORT |
388 | | GF_Err gf_path_add_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed fa_x, Fixed fa_y, Fixed fb_x, Fixed fb_y, Bool cw) |
389 | 0 | { |
390 | 0 | GF_Matrix2D mat, inv; |
391 | 0 | Fixed angle, start_angle, end_angle, sweep, axis_w, axis_h, tmp, cx, cy, _vx, _vy, start_x, start_y; |
392 | 0 | s32 i, num_steps; |
393 | |
|
394 | 0 | if (!gp->n_points) return GF_BAD_PARAM; |
395 | | |
396 | 0 | start_x = gp->points[gp->n_points-1].x; |
397 | 0 | start_y = gp->points[gp->n_points-1].y; |
398 | |
|
399 | 0 | cx = (fb_x + fa_x)/2; |
400 | 0 | cy = (fb_y + fa_y)/2; |
401 | |
|
402 | 0 | angle = gf_atan2(fb_y-fa_y, fb_x-fa_x); |
403 | 0 | gf_mx2d_init(mat); |
404 | 0 | gf_mx2d_add_rotation(&mat, 0, 0, angle); |
405 | 0 | gf_mx2d_add_translation(&mat, cx, cy); |
406 | |
|
407 | 0 | gf_mx2d_copy(inv, mat); |
408 | 0 | gf_mx2d_inverse(&inv); |
409 | 0 | gf_mx2d_apply_coords(&inv, &start_x, &start_y); |
410 | 0 | gf_mx2d_apply_coords(&inv, &end_x, &end_y); |
411 | 0 | gf_mx2d_apply_coords(&inv, &fa_x, &fa_y); |
412 | 0 | gf_mx2d_apply_coords(&inv, &fb_x, &fb_y); |
413 | | |
414 | | //start angle and end angle |
415 | 0 | start_angle = gf_atan2(start_y, start_x); |
416 | 0 | end_angle = gf_atan2(end_y, end_x); |
417 | 0 | tmp = gf_mulfix((start_x - fa_x), (start_x - fa_x)) + gf_mulfix((start_y - fa_y), (start_y - fa_y)); |
418 | 0 | axis_w = gf_sqrt(tmp); |
419 | 0 | tmp = gf_mulfix((start_x - fb_x) , (start_x - fb_x)) + gf_mulfix((start_y - fb_y), (start_y - fb_y)); |
420 | 0 | axis_w += gf_sqrt(tmp); |
421 | 0 | axis_w /= 2; |
422 | 0 | axis_h = gf_sqrt(gf_mulfix(axis_w, axis_w) - gf_mulfix(fa_x,fa_x)); |
423 | 0 | sweep = end_angle - start_angle; |
424 | |
|
425 | 0 | if (cw) { |
426 | 0 | if (sweep>0) sweep -= 2*GF_PI; |
427 | 0 | } else { |
428 | 0 | if (sweep<0) sweep += 2*GF_PI; |
429 | 0 | } |
430 | 0 | num_steps = GF_2D_DEFAULT_RES/2; |
431 | 0 | for (i=1; i<=num_steps; i++) { |
432 | 0 | angle = start_angle + sweep*i/num_steps; |
433 | 0 | _vx = gf_mulfix(axis_w, gf_cos(angle)); |
434 | 0 | _vy = gf_mulfix(axis_h, gf_sin(angle)); |
435 | | /*re-invert*/ |
436 | 0 | gf_mx2d_apply_coords(&mat, &_vx, &_vy); |
437 | 0 | gf_path_add_line_to(gp, _vx, _vy); |
438 | 0 | } |
439 | 0 | return GF_OK; |
440 | 0 | } |
441 | | |
442 | | |
443 | | |
444 | | GF_EXPORT |
445 | | GF_Err gf_path_add_svg_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed r_x, Fixed r_y, Fixed x_axis_rotation, Bool large_arc_flag, Bool sweep_flag) |
446 | 0 | { |
447 | 0 | Fixed start_x, start_y; |
448 | 0 | Fixed xmid,ymid; |
449 | 0 | Fixed xmidp,ymidp; |
450 | 0 | Fixed xmidpsq,ymidpsq; |
451 | 0 | Fixed phi, cos_phi, sin_phi; |
452 | 0 | Fixed c_x, c_y; |
453 | 0 | Fixed cxp, cyp; |
454 | 0 | Fixed scale; |
455 | 0 | Fixed rxsq, rysq; |
456 | 0 | Fixed start_angle, sweep_angle; |
457 | 0 | Fixed radius_scale; |
458 | 0 | Fixed vx, vy, normv; |
459 | 0 | Fixed ux, uy, normu; |
460 | 0 | Fixed sign; |
461 | 0 | u32 i, num_steps; |
462 | |
|
463 | 0 | if (!gp->n_points) return GF_BAD_PARAM; |
464 | | |
465 | 0 | if (!r_x || !r_y) { |
466 | 0 | gf_path_add_line_to(gp, end_x, end_y); |
467 | 0 | return GF_OK; |
468 | 0 | } |
469 | | |
470 | 0 | if (r_x < 0) r_x = -r_x; |
471 | 0 | if (r_y < 0) r_y = -r_y; |
472 | |
|
473 | 0 | start_x = gp->points[gp->n_points-1].x; |
474 | 0 | start_y = gp->points[gp->n_points-1].y; |
475 | |
|
476 | 0 | phi = gf_mulfix(x_axis_rotation/180, GF_PI); |
477 | 0 | cos_phi = gf_cos(phi); |
478 | 0 | sin_phi = gf_sin(phi); |
479 | 0 | xmid = (start_x - end_x)/2; |
480 | 0 | ymid = (start_y - end_y)/2; |
481 | 0 | if (!xmid && !ymid) { |
482 | 0 | gf_path_add_line_to(gp, end_x, end_y); |
483 | 0 | return GF_OK; |
484 | 0 | } |
485 | | |
486 | 0 | xmidp = gf_mulfix(cos_phi, xmid) + gf_mulfix(sin_phi, ymid); |
487 | 0 | ymidp = gf_mulfix(-sin_phi, xmid) + gf_mulfix(cos_phi, ymid); |
488 | 0 | xmidpsq = gf_mulfix(xmidp, xmidp); |
489 | 0 | ymidpsq = gf_mulfix(ymidp, ymidp); |
490 | |
|
491 | 0 | rxsq = gf_mulfix(r_x, r_x); |
492 | 0 | rysq = gf_mulfix(r_y, r_y); |
493 | 0 | gf_assert(rxsq && rysq); |
494 | |
|
495 | 0 | radius_scale = gf_divfix(xmidpsq, rxsq) + gf_divfix(ymidpsq, rysq); |
496 | 0 | if (radius_scale > FIX_ONE) { |
497 | 0 | r_x = gf_mulfix(gf_sqrt(radius_scale), r_x); |
498 | 0 | r_y = gf_mulfix(gf_sqrt(radius_scale), r_y); |
499 | 0 | rxsq = gf_mulfix(r_x, r_x); |
500 | 0 | rysq = gf_mulfix(r_y, r_y); |
501 | 0 | } |
502 | |
|
503 | | #if 0 |
504 | | /* Old code with overflow problems in fixed point, |
505 | | sign was sometimes negative (cf tango SVG icons appointment-new.svg)*/ |
506 | | |
507 | | sign = gf_mulfix(rxsq,ymidpsq) + gf_mulfix(rysq, xmidpsq); |
508 | | scale = FIX_ONE; |
509 | | /*FIXME - what if scale is 0 ??*/ |
510 | | if (sign) scale = gf_divfix( |
511 | | (gf_mulfix(rxsq,rysq) - gf_mulfix(rxsq, ymidpsq) - gf_mulfix(rysq,xmidpsq)), |
512 | | sign |
513 | | ); |
514 | | #else |
515 | | /* New code: the sign variable computation is split into simpler cases and |
516 | | the expression is divided by rxsq to reduce the range */ |
517 | 0 | if ((rxsq == 0 || ymidpsq ==0) && (rysq == 0 || xmidpsq == 0)) { |
518 | 0 | scale = FIX_ONE; |
519 | 0 | } else if (rxsq == 0 || ymidpsq ==0) { |
520 | 0 | scale = gf_divfix(rxsq,xmidpsq) - FIX_ONE; |
521 | 0 | } else if (rysq == 0 || xmidpsq == 0) { |
522 | 0 | scale = gf_divfix(rysq,ymidpsq) - FIX_ONE; |
523 | 0 | } else { |
524 | 0 | Fixed tmp; |
525 | 0 | tmp = gf_mulfix(gf_divfix(rysq, rxsq), xmidpsq); |
526 | 0 | sign = ymidpsq + tmp; |
527 | 0 | scale = gf_divfix((rysq - ymidpsq - tmp),sign); |
528 | 0 | } |
529 | 0 | #endif |
530 | | /* precision problem may lead to negative value around zero, we need to take care of it before sqrt */ |
531 | 0 | scale = gf_sqrt(ABS(scale)); |
532 | |
|
533 | 0 | cxp = gf_mulfix(scale, gf_divfix(gf_mulfix(r_x, ymidp),r_y)); |
534 | 0 | cyp = gf_mulfix(scale, -gf_divfix(gf_mulfix(r_y, xmidp),r_x)); |
535 | 0 | cxp = (large_arc_flag == sweep_flag ? - cxp : cxp); |
536 | 0 | cyp = (large_arc_flag == sweep_flag ? - cyp : cyp); |
537 | |
|
538 | 0 | c_x = gf_mulfix(cos_phi, cxp) - gf_mulfix(sin_phi, cyp) + (start_x + end_x)/2; |
539 | 0 | c_y = gf_mulfix(sin_phi, cxp) + gf_mulfix(cos_phi, cyp) + (start_y + end_y)/2; |
540 | |
|
541 | 0 | vx = gf_divfix(xmidp-cxp,r_x); |
542 | 0 | vy = gf_divfix(ymidp-cyp,r_y); |
543 | 0 | normv = gf_sqrt(gf_mulfix(vx, vx) + gf_mulfix(vy,vy)); |
544 | |
|
545 | 0 | sign = vy; |
546 | 0 | start_angle = gf_acos(gf_divfix(vx,normv)); |
547 | 0 | start_angle = (sign > 0 ? start_angle: -start_angle); |
548 | |
|
549 | 0 | ux = vx; |
550 | 0 | uy = vy; |
551 | |
|
552 | 0 | vx = gf_divfix(-xmidp-cxp,r_x); |
553 | 0 | vy = gf_divfix(-ymidp-cyp,r_y); |
554 | 0 | normu = gf_sqrt(gf_mulfix(ux, ux) + gf_mulfix(uy,uy)); |
555 | |
|
556 | 0 | sign = gf_mulfix(ux, vy) - gf_mulfix(uy, vx); |
557 | 0 | sweep_angle = gf_divfix( gf_mulfix(ux,vx) + gf_mulfix(uy, vy), gf_mulfix(normu, normv) ); |
558 | | /*numerical stability safety*/ |
559 | 0 | sweep_angle = MAX(-FIX_ONE, MIN(sweep_angle, FIX_ONE)); |
560 | 0 | sweep_angle = gf_acos(sweep_angle); |
561 | 0 | sweep_angle = (sign > 0 ? sweep_angle: -sweep_angle); |
562 | 0 | if (sweep_flag == 0) { |
563 | 0 | if (sweep_angle > 0) sweep_angle -= GF_2PI; |
564 | 0 | } else { |
565 | 0 | if (sweep_angle < 0) sweep_angle += GF_2PI; |
566 | 0 | } |
567 | |
|
568 | 0 | num_steps = GF_2D_DEFAULT_RES/2; |
569 | 0 | for (i=1; i<=num_steps; i++) { |
570 | 0 | Fixed _vx, _vy; |
571 | 0 | Fixed _vxp, _vyp; |
572 | 0 | Fixed angle = start_angle + sweep_angle*(s32)i/(s32)num_steps; |
573 | 0 | _vx = gf_mulfix(r_x, gf_cos(angle)); |
574 | 0 | _vy = gf_mulfix(r_y, gf_sin(angle)); |
575 | 0 | _vxp = gf_mulfix(cos_phi, _vx) - gf_mulfix(sin_phi, _vy) + c_x; |
576 | 0 | _vyp = gf_mulfix(sin_phi, _vx) + gf_mulfix(cos_phi, _vy) + c_y; |
577 | 0 | gf_path_add_line_to(gp, _vxp, _vyp); |
578 | 0 | } |
579 | 0 | return GF_OK; |
580 | 0 | } |
581 | | |
582 | | GF_EXPORT |
583 | | GF_Err gf_path_add_arc(GF_Path *gp, Fixed radius, Fixed start_angle, Fixed end_angle, GF_Path2DArcCloseType close_type) |
584 | 0 | { |
585 | 0 | GF_Err e; |
586 | 0 | Fixed _vx, _vy, step, cur; |
587 | 0 | s32 i, do_run; |
588 | |
|
589 | 0 | step = (end_angle - start_angle) / (GF_2D_DEFAULT_RES); |
590 | 0 | radius *= 2; |
591 | | |
592 | | /*pie*/ |
593 | 0 | i=0; |
594 | 0 | if (close_type==GF_PATH2D_ARC_PIE) { |
595 | 0 | gf_path_add_move_to(gp, 0, 0); |
596 | 0 | i=1; |
597 | 0 | } |
598 | 0 | do_run = 1; |
599 | 0 | cur=start_angle; |
600 | 0 | while (do_run) { |
601 | 0 | if (cur>=end_angle) { |
602 | 0 | do_run = 0; |
603 | 0 | cur = end_angle; |
604 | 0 | } |
605 | 0 | _vx = gf_mulfix(radius, gf_cos(cur)); |
606 | 0 | _vy = gf_mulfix(radius, gf_sin(cur)); |
607 | 0 | if (!i) { |
608 | 0 | e = gf_path_add_move_to(gp, _vx, _vy); |
609 | 0 | i = 1; |
610 | 0 | } else { |
611 | 0 | e = gf_path_add_line_to(gp, _vx, _vy); |
612 | 0 | } |
613 | 0 | if (e) return e; |
614 | 0 | cur+=step; |
615 | 0 | } |
616 | 0 | if (close_type!=GF_PATH2D_ARC_OPEN) e = gf_path_close(gp); |
617 | 0 | return e; |
618 | 0 | } |
619 | | |
620 | | |
621 | | GF_EXPORT |
622 | | GF_Err gf_path_get_control_bounds(GF_Path *gp, GF_Rect *rc) |
623 | 0 | { |
624 | 0 | GF_Point2D *pt, *end; |
625 | 0 | Fixed xMin, xMax, yMin, yMax; |
626 | 0 | if (!gp || !rc) return GF_BAD_PARAM; |
627 | | |
628 | 0 | if (!gp->n_points) { |
629 | 0 | rc->x = rc->y = rc->width = rc->height = 0; |
630 | 0 | return GF_OK; |
631 | 0 | } |
632 | 0 | pt = gp->points; |
633 | 0 | end = pt + gp->n_points; |
634 | 0 | xMin = xMax = pt->x; |
635 | 0 | yMin = yMax = pt->y; |
636 | 0 | pt++; |
637 | 0 | for ( ; pt < end; pt++ ) { |
638 | 0 | Fixed v; |
639 | 0 | v = pt->x; |
640 | 0 | if (v < xMin) xMin = v; |
641 | 0 | if (v > xMax) xMax = v; |
642 | 0 | v = pt->y; |
643 | 0 | if (v < yMin) yMin = v; |
644 | 0 | if (v > yMax) yMax = v; |
645 | 0 | } |
646 | 0 | rc->x = xMin; |
647 | 0 | rc->y = yMax; |
648 | 0 | rc->width = xMax - xMin; |
649 | 0 | rc->height = yMax - yMin; |
650 | |
|
651 | | #if 0 |
652 | | /*take care of straight line path by adding a default width if height and vice-versa*/ |
653 | | if (rc->height && !rc->width) { |
654 | | rc->width = 2*FIX_ONE; |
655 | | rc->x -= FIX_ONE; |
656 | | } |
657 | | else if (!rc->height && rc->width) { |
658 | | rc->height = 2*FIX_ONE; |
659 | | rc->y += FIX_ONE; |
660 | | } |
661 | | #endif |
662 | 0 | return GF_OK; |
663 | 0 | } |
664 | | |
665 | | /* |
666 | | * conic bbox computing taken from freetype |
667 | | * Copyright 1996-2001, 2002, 2004 by |
668 | | * David Turner, Robert Wilhelm, and Werner Lemberg. |
669 | | * License: FTL or GPL |
670 | | */ |
671 | | static void gf_conic_check(Fixed y1, Fixed y2, Fixed y3, Fixed *min, Fixed *max) |
672 | 0 | { |
673 | | /* flat arc */ |
674 | 0 | if ((y1 <= y3) && (y2 == y1)) goto Suite; |
675 | | |
676 | 0 | if ( y1 < y3 ) { |
677 | | /* ascending arc */ |
678 | 0 | if ((y2 >= y1) && (y2 <= y3)) goto Suite; |
679 | 0 | } else { |
680 | | /* descending arc */ |
681 | 0 | if ((y2 >= y3) && (y2 <= y1)) { |
682 | 0 | y2 = y1; |
683 | 0 | y1 = y3; |
684 | 0 | y3 = y2; |
685 | 0 | goto Suite; |
686 | 0 | } |
687 | 0 | } |
688 | 0 | y1 = y3 = y1 - gf_muldiv(y2 - y1, y2 - y1, y1 - 2*y2 + y3); |
689 | |
|
690 | 0 | Suite: |
691 | 0 | if ( y1 < *min ) *min = y1; |
692 | 0 | if ( y3 > *max ) *max = y3; |
693 | 0 | } |
694 | | |
695 | | |
696 | | /* |
697 | | * cubic bbox computing taken from freetype |
698 | | * Copyright 1996-2001, 2002, 2004 by |
699 | | * David Turner, Robert Wilhelm, and Werner Lemberg. |
700 | | * License: FTL or GPL |
701 | | |
702 | | * Note: we're using the decomposition method, not the equation one which is not usable with Floats (only 16.16 fixed) |
703 | | */ |
704 | | static void gf_cubic_check(Fixed p1, Fixed p2, Fixed p3, Fixed p4, Fixed *min, Fixed *max) |
705 | 0 | { |
706 | 0 | Fixed stack[32*3 + 1], *arc; |
707 | 0 | arc = stack; |
708 | 0 | arc[0] = p1; |
709 | 0 | arc[1] = p2; |
710 | 0 | arc[2] = p3; |
711 | 0 | arc[3] = p4; |
712 | |
|
713 | 0 | do { |
714 | 0 | Fixed y1 = arc[0]; |
715 | 0 | Fixed y2 = arc[1]; |
716 | 0 | Fixed y3 = arc[2]; |
717 | 0 | Fixed y4 = arc[3]; |
718 | |
|
719 | 0 | if (ABS(y1)<FIX_EPSILON) arc[0] = y1 = 0; |
720 | 0 | if (ABS(y2)<FIX_EPSILON) arc[1] = y2 = 0; |
721 | 0 | if (ABS(y3)<FIX_EPSILON) arc[2] = y3 = 0; |
722 | 0 | if (ABS(y4)<FIX_EPSILON) arc[3] = y4 = 0; |
723 | |
|
724 | 0 | if ( y1 == y4 ) { |
725 | | /* flat */ |
726 | 0 | if ((y1 == y2) && (y1 == y3)) goto Test; |
727 | 0 | } |
728 | 0 | else if ( y1 < y4 ) { |
729 | | /* ascending */ |
730 | 0 | if ((y2 >= y1) && (y2 <= y4) && (y3 >= y1) && (y3 <= y4)) goto Test; |
731 | 0 | } else { |
732 | | /* descending */ |
733 | 0 | if ((y2 >= y4) && (y2 <= y1) && (y3 >= y4) && (y3 <= y1)) { |
734 | 0 | y2 = y1; |
735 | 0 | y1 = y4; |
736 | 0 | y4 = y2; |
737 | 0 | goto Test; |
738 | 0 | } |
739 | 0 | } |
740 | | /* unknown direction -- split the arc in two */ |
741 | 0 | arc[6] = y4; |
742 | 0 | arc[1] = y1 = ( y1 + y2 ) / 2; |
743 | 0 | arc[5] = y4 = ( y4 + y3 ) / 2; |
744 | 0 | y2 = ( y2 + y3 ) / 2; |
745 | 0 | arc[2] = y1 = ( y1 + y2 ) / 2; |
746 | 0 | arc[4] = y4 = ( y4 + y2 ) / 2; |
747 | 0 | arc[3] = ( y1 + y4 ) / 2; |
748 | |
|
749 | 0 | arc += 3; |
750 | 0 | goto Suite; |
751 | | |
752 | 0 | Test: |
753 | 0 | if ( y1 < *min ) *min = y1; |
754 | 0 | if ( y4 > *max ) *max = y4; |
755 | 0 | arc -= 3; |
756 | 0 | Suite: |
757 | 0 | ; |
758 | 0 | } |
759 | 0 | while ( arc >= stack ); |
760 | 0 | } |
761 | | |
762 | | |
763 | | GF_EXPORT |
764 | | GF_Err gf_path_get_bounds(GF_Path *gp, GF_Rect *rc) |
765 | 0 | { |
766 | 0 | u32 i; |
767 | 0 | GF_Point2D *pt, *end; |
768 | 0 | Fixed xMin, xMax, yMin, yMax, cxMin, cxMax, cyMin, cyMax; |
769 | 0 | if (!gp || !rc) return GF_BAD_PARAM; |
770 | | |
771 | 0 | if (!(gp->flags & GF_PATH_BBOX_DIRTY)) { |
772 | 0 | *rc = gp->bbox; |
773 | 0 | return GF_OK; |
774 | 0 | } |
775 | | /*no curves in path*/ |
776 | 0 | if (gp->flags & GF_PATH_FLATTENED) { |
777 | 0 | GF_Err e; |
778 | 0 | gp->flags &= ~GF_PATH_BBOX_DIRTY; |
779 | 0 | e = gf_path_get_control_bounds(gp, &gp->bbox); |
780 | 0 | *rc = gp->bbox; |
781 | 0 | return e; |
782 | 0 | } |
783 | | |
784 | 0 | gp->flags &= ~GF_PATH_BBOX_DIRTY; |
785 | |
|
786 | 0 | if (!gp->n_points) { |
787 | 0 | gp->bbox.x = gp->bbox.y = gp->bbox.width = gp->bbox.height = 0; |
788 | 0 | *rc = gp->bbox; |
789 | 0 | return GF_OK; |
790 | 0 | } |
791 | 0 | pt = gp->points; |
792 | 0 | xMin = xMax = cxMin = cxMax = pt->x; |
793 | 0 | yMin = yMax = cyMin = cyMax = pt->y; |
794 | 0 | pt++; |
795 | 0 | for (i=1 ; i < gp->n_points; i++ ) { |
796 | 0 | Fixed x, y; |
797 | 0 | x = pt->x; |
798 | 0 | y = pt->y; |
799 | 0 | if (x < cxMin) cxMin = x; |
800 | 0 | if (x > cxMax) cxMax = x; |
801 | 0 | if (y < cyMin) cyMin = y; |
802 | 0 | if (y > cyMax) cyMax = y; |
803 | | /*point on curve, update*/ |
804 | 0 | if (gp->tags[i] & GF_PATH_CURVE_ON) { |
805 | 0 | if (x < xMin) xMin = x; |
806 | 0 | if (x > xMax) xMax = x; |
807 | 0 | if (y < yMin) yMin = y; |
808 | 0 | if (y > yMax) yMax = y; |
809 | 0 | } |
810 | 0 | pt++; |
811 | 0 | } |
812 | | |
813 | | /*control box is bigger than box , decompose curves*/ |
814 | 0 | if ((cxMin < xMin) || (cxMax > xMax) || (cyMin < yMin) || (cyMax > yMax)) { |
815 | 0 | GF_Point2D *ctrl1, *ctrl2; |
816 | | /*decompose all control points*/ |
817 | 0 | pt = gp->points; |
818 | 0 | for (i=1 ; i < gp->n_points; ) { |
819 | 0 | switch (gp->tags[i]) { |
820 | 0 | case GF_PATH_CURVE_ON: |
821 | 0 | case GF_PATH_CLOSE: |
822 | 0 | pt = &gp->points[i]; |
823 | 0 | i++; |
824 | 0 | break; |
825 | 0 | case GF_PATH_CURVE_CONIC: |
826 | | /*decompose*/ |
827 | 0 | ctrl1 = &gp->points[i]; |
828 | 0 | end = &gp->points[i+1]; |
829 | 0 | if ((ctrl1->x < xMin) || (ctrl1->x > xMax)) |
830 | 0 | gf_conic_check(pt->x, ctrl1->x, end->x, &xMin, &xMax); |
831 | |
|
832 | 0 | if ((ctrl1->y < yMin) || (ctrl1->y > yMax)) |
833 | 0 | gf_conic_check(pt->y, ctrl1->y, end->y, &yMin, &yMax); |
834 | | |
835 | | /*and move*/ |
836 | 0 | pt = end; |
837 | 0 | i+=2; |
838 | 0 | break; |
839 | 0 | case GF_PATH_CURVE_CUBIC: |
840 | | /*decompose*/ |
841 | 0 | ctrl1 = &gp->points[i]; |
842 | 0 | ctrl2 = &gp->points[i+1]; |
843 | 0 | end = &gp->points[i+2]; |
844 | 0 | if ((ctrl1->x < xMin) || (ctrl1->x > xMax) || (ctrl2->x < xMin) || (ctrl2->x > xMax)) |
845 | 0 | gf_cubic_check(pt->x, ctrl1->x, ctrl2->x, end->x, &xMin, &xMax); |
846 | |
|
847 | 0 | if ((ctrl1->y < yMin) || (ctrl1->y > yMax) || (ctrl2->y < yMin) || (ctrl2->y > yMax)) |
848 | 0 | gf_cubic_check(pt->y, ctrl1->y, ctrl2->y, end->y, &yMin, &yMax); |
849 | | |
850 | | /*and move*/ |
851 | 0 | pt = end; |
852 | 0 | i+=3; |
853 | 0 | break; |
854 | 0 | } |
855 | 0 | } |
856 | 0 | } |
857 | 0 | gp->bbox.x = xMin; |
858 | 0 | gp->bbox.y = yMax; |
859 | 0 | gp->bbox.width = xMax - xMin; |
860 | 0 | gp->bbox.height = yMax - yMin; |
861 | 0 | *rc = gp->bbox; |
862 | 0 | return GF_OK; |
863 | 0 | } |
864 | | |
865 | | /*flattening algo taken from libart but passed to sqrt tests for line distance to avoid 16.16 fixed overflow*/ |
866 | | static GF_Err gf_subdivide_cubic(GF_Path *gp, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, Fixed fineness) |
867 | 0 | { |
868 | 0 | GF_Point2D pt; |
869 | 0 | Fixed x3_0, y3_0, z3_0, z1_0, z1_dot, z2_dot, z1_perp, z2_perp; |
870 | 0 | Fixed max_perp; |
871 | 0 | Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2; |
872 | 0 | GF_Err e; |
873 | |
|
874 | 0 | pt.x = x3_0 = x3 - x0; |
875 | 0 | pt.y = y3_0 = y3 - y0; |
876 | | |
877 | | /*z3_0 is dist z0-z3*/ |
878 | 0 | z3_0 = gf_v2d_len(&pt); |
879 | |
|
880 | 0 | pt.x = x1 - x0; |
881 | 0 | pt.y = y1 - y0; |
882 | 0 | z1_0 = gf_v2d_len(&pt); |
883 | |
|
884 | 0 | if ((z3_0*100 < FIX_ONE) && (z1_0*100 < FIX_ONE)) |
885 | 0 | goto nosubdivide; |
886 | | |
887 | | /* perp is distance from line, multiplied by dist z0-z3 */ |
888 | 0 | max_perp = gf_mulfix(fineness, z3_0); |
889 | |
|
890 | 0 | z1_perp = gf_mulfix((y1 - y0), x3_0) - gf_mulfix((x1 - x0), y3_0); |
891 | 0 | if (ABS(z1_perp) > max_perp) |
892 | 0 | goto subdivide; |
893 | | |
894 | 0 | z2_perp = gf_mulfix((y3 - y2), x3_0) - gf_mulfix((x3 - x2), y3_0); |
895 | 0 | if (ABS(z2_perp) > max_perp) |
896 | 0 | goto subdivide; |
897 | | |
898 | 0 | z1_dot = gf_mulfix((x1 - x0), x3_0) + gf_mulfix((y1 - y0), y3_0); |
899 | 0 | if ((z1_dot < 0) && (ABS(z1_dot) > max_perp)) |
900 | 0 | goto subdivide; |
901 | | |
902 | 0 | z2_dot = gf_mulfix((x3 - x2), x3_0) + gf_mulfix((y3 - y2), y3_0); |
903 | 0 | if ((z2_dot < 0) && (ABS(z2_dot) > max_perp)) |
904 | 0 | goto subdivide; |
905 | | |
906 | 0 | if (gf_divfix(z1_dot + z1_dot, z3_0) > z3_0) |
907 | 0 | goto subdivide; |
908 | | |
909 | 0 | if (gf_divfix(z2_dot + z2_dot, z3_0) > z3_0) |
910 | 0 | goto subdivide; |
911 | | |
912 | 0 | nosubdivide: |
913 | | /* don't subdivide */ |
914 | 0 | return gf_path_add_line_to(gp, x3, y3); |
915 | | |
916 | 0 | subdivide: |
917 | 0 | xa1 = (x0 + x1) / 2; |
918 | 0 | ya1 = (y0 + y1) / 2; |
919 | 0 | xa2 = (x0 + 2 * x1 + x2) / 4; |
920 | 0 | ya2 = (y0 + 2 * y1 + y2) / 4; |
921 | 0 | xb1 = (x1 + 2 * x2 + x3) / 4; |
922 | 0 | yb1 = (y1 + 2 * y2 + y3) / 4; |
923 | 0 | xb2 = (x2 + x3) / 2; |
924 | 0 | yb2 = (y2 + y3) / 2; |
925 | 0 | x_m = (xa2 + xb1) / 2; |
926 | 0 | y_m = (ya2 + yb1) / 2; |
927 | | /*safeguard for numerical stability*/ |
928 | 0 | if ( (ABS(x_m-x0) < FIX_EPSILON) && (ABS(y_m-y0) < FIX_EPSILON)) |
929 | 0 | return gf_path_add_line_to(gp, x3, y3); |
930 | 0 | if ( (ABS(x3-x_m) < FIX_EPSILON) && (ABS(y3-y_m) < FIX_EPSILON)) |
931 | 0 | return gf_path_add_line_to(gp, x3, y3); |
932 | | |
933 | 0 | e = gf_subdivide_cubic(gp, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, fineness); |
934 | 0 | if (e) return e; |
935 | 0 | return gf_subdivide_cubic(gp, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, fineness); |
936 | 0 | } |
937 | | |
938 | | GF_EXPORT |
939 | | GF_Path *gf_path_get_flatten(GF_Path *gp) |
940 | 0 | { |
941 | 0 | GF_Path *ngp; |
942 | 0 | Fixed fineness; |
943 | 0 | u32 i, *countour; |
944 | 0 | GF_Point2D *pt; |
945 | 0 | if (!gp || !gp->n_points) return NULL; |
946 | | |
947 | 0 | if (gp->flags & GF_PATH_FLATTENED) return gf_path_clone(gp); |
948 | | |
949 | | /*avoid too high precision */ |
950 | 0 | fineness = MAX(FIX_ONE - gp->fineness, FIX_ONE / 100); |
951 | |
|
952 | 0 | ngp = gf_path_new(); |
953 | 0 | pt = &gp->points[0]; |
954 | 0 | gf_path_add_move_to_vec(ngp, pt); |
955 | 0 | countour = gp->contours; |
956 | 0 | for (i=1; i<gp->n_points; ) { |
957 | 0 | switch (gp->tags[i]) { |
958 | 0 | case GF_PATH_CURVE_ON: |
959 | 0 | case GF_PATH_CLOSE: |
960 | 0 | pt = &gp->points[i]; |
961 | 0 | if (*countour == i-1) { |
962 | 0 | gf_path_add_move_to_vec(ngp, pt); |
963 | 0 | countour++; |
964 | 0 | } else { |
965 | 0 | gf_path_add_line_to_vec(ngp, pt); |
966 | 0 | } |
967 | 0 | if (gp->tags[i]==GF_PATH_CLOSE) gf_path_close(ngp); |
968 | 0 | i++; |
969 | 0 | break; |
970 | 0 | case GF_PATH_CURVE_CONIC: |
971 | 0 | { |
972 | 0 | GF_Point2D *ctl, *end, c1, c2; |
973 | 0 | ctl = &gp->points[i]; |
974 | 0 | end = &gp->points[i+1]; |
975 | 0 | c1.x = pt->x + 2*(ctl->x - pt->x)/3; |
976 | 0 | c1.y = pt->y + 2*(ctl->y - pt->y)/3; |
977 | 0 | c2.x = c1.x + (end->x - pt->x) / 3; |
978 | 0 | c2.y = c1.y + (end->y - pt->y) / 3; |
979 | |
|
980 | 0 | gf_subdivide_cubic(ngp, pt->x, pt->y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, fineness); |
981 | 0 | pt = end; |
982 | 0 | if (gp->tags[i+1]==GF_PATH_CLOSE) gf_path_close(ngp); |
983 | 0 | i+=2; |
984 | 0 | } |
985 | 0 | break; |
986 | 0 | case GF_PATH_CURVE_CUBIC: |
987 | 0 | gf_subdivide_cubic(ngp, pt->x, pt->y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, fineness); |
988 | 0 | pt = &gp->points[i+2]; |
989 | 0 | if (gp->tags[i+2]==GF_PATH_CLOSE) gf_path_close(ngp); |
990 | 0 | i+=3; |
991 | 0 | break; |
992 | 0 | } |
993 | 0 | } |
994 | 0 | if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) ngp->flags |= GF_PATH_FILL_ZERO_NONZERO; |
995 | 0 | ngp->flags |= (GF_PATH_BBOX_DIRTY | GF_PATH_FLATTENED); |
996 | 0 | return ngp; |
997 | 0 | } |
998 | | |
999 | | GF_EXPORT |
1000 | | void gf_path_flatten(GF_Path *gp) |
1001 | 0 | { |
1002 | 0 | GF_Path *res; |
1003 | 0 | u32 flags = gp->flags; |
1004 | 0 | if (gp->flags & GF_PATH_FLATTENED) return; |
1005 | 0 | if (!gp->n_points) return; |
1006 | 0 | res = gf_path_get_flatten(gp); |
1007 | 0 | if (gp->contours) gf_free(gp->contours); |
1008 | 0 | if (gp->points) gf_free(gp->points); |
1009 | 0 | if (gp->tags) gf_free(gp->tags); |
1010 | 0 | memcpy(gp, res, sizeof(GF_Path)); |
1011 | 0 | gp->flags |= (flags & (GF_PATH_FILL_ZERO_NONZERO|GF_PATH_FILL_EVEN)); |
1012 | 0 | gf_free(res); |
1013 | 0 | } |
1014 | | |
1015 | | |
1016 | | |
1017 | | #define isLeft(P0, P1, P2) \ |
1018 | 0 | ( gf_mulfix((P1.x - P0.x), (P2.y - P0.y)) - gf_mulfix((P2.x - P0.x), (P1.y - P0.y)) ) |
1019 | | |
1020 | | |
1021 | | static void gf_subdivide_cubic_hit_test(Fixed h_x, Fixed h_y, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, s32 *wn) |
1022 | 0 | { |
1023 | 0 | GF_Point2D s, e, pt; |
1024 | 0 | Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2, y_min, y_max; |
1025 | | |
1026 | | /*if hit line doesn't intersects control box abort*/ |
1027 | 0 | y_min = MIN(y0, MIN(y1, MIN(y2, y3))); |
1028 | 0 | y_max = MAX(y0, MAX(y1, MAX(y2, y3))); |
1029 | 0 | if ((h_y<y_min) || (h_y>y_max) ) return; |
1030 | | |
1031 | | /*if vert diff between end points larger than 1 pixels, subdivide (we need pixel accuracy for is_over)*/ |
1032 | 0 | if (y_max - y_min > FIX_ONE) { |
1033 | 0 | xa1 = (x0 + x1) / 2; |
1034 | 0 | ya1 = (y0 + y1) / 2; |
1035 | 0 | xa2 = (x0 + 2 * x1 + x2) / 4; |
1036 | 0 | ya2 = (y0 + 2 * y1 + y2) / 4; |
1037 | 0 | xb1 = (x1 + 2 * x2 + x3) / 4; |
1038 | 0 | yb1 = (y1 + 2 * y2 + y3) / 4; |
1039 | 0 | xb2 = (x2 + x3) / 2; |
1040 | 0 | yb2 = (y2 + y3) / 2; |
1041 | 0 | x_m = (xa2 + xb1) / 2; |
1042 | 0 | y_m = (ya2 + yb1) / 2; |
1043 | |
|
1044 | 0 | gf_subdivide_cubic_hit_test(h_x, h_y, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, wn); |
1045 | 0 | gf_subdivide_cubic_hit_test(h_x, h_y, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, wn); |
1046 | 0 | return; |
1047 | 0 | } |
1048 | | |
1049 | 0 | s.x = x0; |
1050 | 0 | s.y = y0; |
1051 | 0 | e.x = x3; |
1052 | 0 | e.y = y3; |
1053 | 0 | pt.x = h_x; |
1054 | 0 | pt.y= h_y; |
1055 | |
|
1056 | 0 | if (s.y<=pt.y) { |
1057 | 0 | if (e.y>pt.y) { |
1058 | 0 | if (isLeft(s, e, pt) > 0) |
1059 | 0 | (*wn)++; |
1060 | 0 | } |
1061 | 0 | } |
1062 | 0 | else if (e.y<=pt.y) { |
1063 | 0 | if (isLeft(s, e, pt) < 0) |
1064 | 0 | (*wn)--; |
1065 | 0 | } |
1066 | 0 | } |
1067 | | |
1068 | | GF_EXPORT |
1069 | | Bool gf_path_point_over(GF_Path *gp, Fixed x, Fixed y) |
1070 | 0 | { |
1071 | 0 | u32 i, *contour, start_idx; |
1072 | 0 | s32 wn; |
1073 | 0 | GF_Point2D start, s, e, pt; |
1074 | 0 | GF_Rect rc; |
1075 | 0 | GF_Err err; |
1076 | | |
1077 | | /*check if not in bounds*/ |
1078 | 0 | err = gf_path_get_bounds(gp, &rc); |
1079 | 0 | if (err) return GF_FALSE; |
1080 | 0 | if ((x<rc.x) || (y>rc.y) || (x>rc.x+rc.width) || (y<rc.y-rc.height)) return GF_FALSE; |
1081 | | |
1082 | 0 | if (!gp || (gp->n_points<2)) return GF_FALSE; |
1083 | | |
1084 | 0 | pt.x = x; |
1085 | 0 | pt.y = y; |
1086 | 0 | wn = 0; |
1087 | 0 | s = start = gp->points[0]; |
1088 | 0 | start_idx = 0; |
1089 | 0 | contour = gp->contours; |
1090 | 0 | for (i=1; i<gp->n_points; ) { |
1091 | 0 | switch (gp->tags[i]) { |
1092 | 0 | case GF_PATH_CURVE_ON: |
1093 | 0 | case GF_PATH_CLOSE: |
1094 | 0 | e = gp->points[i]; |
1095 | 0 | if (s.y<=pt.y) { |
1096 | 0 | if (e.y>pt.y) { |
1097 | 0 | if (isLeft(s, e, pt) > 0) wn++; |
1098 | 0 | } |
1099 | 0 | } |
1100 | 0 | else if (e.y<=pt.y) { |
1101 | 0 | if (isLeft(s, e, pt) < 0) wn--; |
1102 | 0 | } |
1103 | 0 | s = e; |
1104 | 0 | i++; |
1105 | 0 | break; |
1106 | 0 | case GF_PATH_CURVE_CONIC: |
1107 | 0 | { |
1108 | 0 | GF_Point2D *ctl, *end, c1, c2; |
1109 | 0 | ctl = &gp->points[i]; |
1110 | 0 | end = &gp->points[i+1]; |
1111 | 0 | c1.x = s.x + 2*(ctl->x - s.x) / 3; |
1112 | 0 | c1.y = s.y + 2*(ctl->y - s.y) / 3; |
1113 | 0 | c2.x = c1.x + (end->x - s.x) / 3; |
1114 | 0 | c2.y = c1.y + (end->y - s.y) / 3; |
1115 | 0 | gf_subdivide_cubic_hit_test(x, y, s.x, s.y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, &wn); |
1116 | 0 | s = *end; |
1117 | 0 | } |
1118 | 0 | i+=2; |
1119 | 0 | break; |
1120 | 0 | case GF_PATH_CURVE_CUBIC: |
1121 | 0 | gf_subdivide_cubic_hit_test(x, y, s.x, s.y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, &wn); |
1122 | 0 | s = gp->points[i+2]; |
1123 | 0 | i+=3; |
1124 | 0 | break; |
1125 | 0 | } |
1126 | | /*end of subpath*/ |
1127 | 0 | if (*contour==i-1) { |
1128 | | /*close path if needed, but don't test for lines...*/ |
1129 | 0 | if ((i-start_idx > 2) && (pt.y<s.y)) { |
1130 | 0 | if ((start.x != s.x) || (start.y != s.y)) { |
1131 | 0 | e = start; |
1132 | 0 | if (s.x<=pt.x) { |
1133 | 0 | if (e.y>pt.y) { |
1134 | 0 | if (isLeft(s, e, pt) > 0) wn++; |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 | else if (e.y<=pt.y) { |
1138 | 0 | if (isLeft(s, e, pt) < 0) wn--; |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | } |
1142 | 0 | if ( i < gp->n_points ) |
1143 | 0 | s = start = gp->points[i]; |
1144 | 0 | i++; |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) return wn ? GF_TRUE : GF_FALSE; |
1148 | 0 | return (wn%2) ? GF_TRUE : GF_FALSE; |
1149 | 0 | } |
1150 | | |
1151 | | GF_EXPORT |
1152 | | Bool gf_path_is_empty(GF_Path *gp) |
1153 | 0 | { |
1154 | 0 | if (gp && gp->contours) return GF_FALSE; |
1155 | 0 | return GF_TRUE; |
1156 | 0 | } |
1157 | | |
1158 | | /*iteration info*/ |
1159 | | typedef struct |
1160 | | { |
1161 | | Fixed len; |
1162 | | Fixed dx, dy; |
1163 | | Fixed start_x, start_y; |
1164 | | } IterInfo; |
1165 | | |
1166 | | struct _path_iterator |
1167 | | { |
1168 | | u32 num_seg; |
1169 | | IterInfo *seg; |
1170 | | Fixed length; |
1171 | | }; |
1172 | | |
1173 | | GF_EXPORT |
1174 | | GF_PathIterator *gf_path_iterator_new(GF_Path *gp) |
1175 | 0 | { |
1176 | 0 | GF_Path *flat; |
1177 | 0 | GF_PathIterator *it; |
1178 | 0 | u32 i, j, cur; |
1179 | 0 | GF_Point2D start, end; |
1180 | |
|
1181 | 0 | GF_SAFEALLOC(it, GF_PathIterator); |
1182 | 0 | if (!it) return NULL; |
1183 | 0 | flat = gf_path_get_flatten(gp); |
1184 | 0 | if (!flat) { |
1185 | 0 | gf_free(it); |
1186 | 0 | return NULL; |
1187 | 0 | } |
1188 | 0 | it->seg = (IterInfo *) gf_malloc(sizeof(IterInfo) * flat->n_points); |
1189 | 0 | it->num_seg = 0; |
1190 | 0 | it->length = 0; |
1191 | 0 | cur = 0; |
1192 | 0 | for (i=0; i<flat->n_contours; i++) { |
1193 | 0 | Fixed dx, dy; |
1194 | 0 | u32 nb_pts = 1+flat->contours[i]-cur; |
1195 | 0 | start = flat->points[cur]; |
1196 | 0 | for (j=1; j<nb_pts; j++) { |
1197 | 0 | end = flat->points[cur+j]; |
1198 | 0 | it->seg[it->num_seg].start_x = start.x; |
1199 | 0 | it->seg[it->num_seg].start_y = start.y; |
1200 | 0 | dx = it->seg[it->num_seg].dx = end.x - start.x; |
1201 | 0 | dy = it->seg[it->num_seg].dy = end.y - start.y; |
1202 | 0 | it->seg[it->num_seg].len = gf_sqrt(gf_mulfix(dx, dx) + gf_mulfix(dy, dy)); |
1203 | 0 | it->length += it->seg[it->num_seg].len; |
1204 | 0 | start = end; |
1205 | 0 | it->num_seg++; |
1206 | 0 | } |
1207 | 0 | cur += nb_pts; |
1208 | 0 | } |
1209 | 0 | gf_path_del(flat); |
1210 | 0 | return it; |
1211 | 0 | } |
1212 | | |
1213 | | GF_EXPORT |
1214 | | Fixed gf_path_iterator_get_length(GF_PathIterator *it) |
1215 | 0 | { |
1216 | 0 | return it ? it->length : 0; |
1217 | 0 | } |
1218 | | |
1219 | | GF_EXPORT |
1220 | | Bool gf_path_iterator_get_transform(GF_PathIterator *path, Fixed offset, Bool follow_tangent, GF_Matrix2D *mat, Bool smooth_edges, Fixed length_after_point) |
1221 | 0 | { |
1222 | 0 | GF_Matrix2D final, rot; |
1223 | 0 | Bool tang = GF_FALSE; |
1224 | 0 | Fixed res, angle, angleNext; |
1225 | 0 | u32 i; |
1226 | 0 | Fixed curLen = 0; |
1227 | 0 | if (!path) return GF_FALSE; |
1228 | | |
1229 | 0 | for (i=0; i<path->num_seg; i++) { |
1230 | 0 | if (curLen + path->seg[i].len >= offset) goto found; |
1231 | 0 | curLen += path->seg[i].len; |
1232 | 0 | } |
1233 | 0 | if (!follow_tangent) return GF_FALSE; |
1234 | 0 | tang = GF_TRUE; |
1235 | 0 | i--; |
1236 | |
|
1237 | 0 | found: |
1238 | 0 | gf_mx2d_init(final); |
1239 | |
|
1240 | 0 | res = gf_divfix(offset - curLen, path->seg[i].len); |
1241 | 0 | if (tang) res += 1; |
1242 | | |
1243 | | /*move to current point*/ |
1244 | 0 | gf_mx2d_add_translation(&final, path->seg[i].start_x + gf_mulfix(path->seg[i].dx, res), path->seg[i].start_y + gf_mulfix(path->seg[i].dy, res)); |
1245 | |
|
1246 | 0 | if (!path->seg[i].dx) { |
1247 | 0 | angle = GF_PI2; |
1248 | 0 | } else { |
1249 | 0 | angle = gf_acos( gf_divfix(path->seg[i].dx , path->seg[i].len) ); |
1250 | 0 | } |
1251 | 0 | if (path->seg[i].dy<0) angle *= -1; |
1252 | |
|
1253 | 0 | if (smooth_edges) { |
1254 | 0 | if (offset + length_after_point > curLen + path->seg[i].len) { |
1255 | 0 | Fixed ratio = gf_divfix(curLen + path->seg[i].len-offset, length_after_point); |
1256 | 0 | if (i < path->num_seg - 1) { |
1257 | 0 | if (!path->seg[i+1].dx) { |
1258 | 0 | angleNext = GF_PI2; |
1259 | 0 | } else { |
1260 | 0 | angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) ); |
1261 | 0 | } |
1262 | 0 | if (path->seg[i+1].dy<0) angleNext *= -1; |
1263 | |
|
1264 | 0 | if (angle<0 && angleNext>0) { |
1265 | 0 | angle = gf_mulfix(FIX_ONE-ratio, angleNext) - gf_mulfix(ratio, angle); |
1266 | 0 | } else { |
1267 | 0 | angle = gf_mulfix(ratio, angle) + gf_mulfix(FIX_ONE-ratio, angleNext); |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | } |
1271 | 0 | } |
1272 | | /*handle res=0 case for rotation (point on line join)*/ |
1273 | 0 | else if (res==1) { |
1274 | 0 | if (i < path->num_seg - 1) { |
1275 | 0 | if (!path->seg[i+1].dx) { |
1276 | 0 | angleNext = GF_PI2; |
1277 | 0 | } else { |
1278 | 0 | angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) ); |
1279 | 0 | } |
1280 | 0 | if (path->seg[i+1].dy<0) angleNext *= -1; |
1281 | 0 | angle = ( angle + angleNext) / 2; |
1282 | 0 | } |
1283 | 0 | } |
1284 | |
|
1285 | 0 | gf_mx2d_init(rot); |
1286 | 0 | gf_mx2d_add_rotation(&rot, 0, 0, angle); |
1287 | 0 | gf_mx2d_add_matrix(mat, &rot); |
1288 | 0 | gf_mx2d_add_matrix(mat, &final); |
1289 | 0 | return GF_TRUE; |
1290 | 0 | } |
1291 | | |
1292 | | GF_EXPORT |
1293 | | void gf_path_iterator_del(GF_PathIterator *it) |
1294 | 0 | { |
1295 | 0 | if (it->seg) gf_free(it->seg); |
1296 | 0 | gf_free(it); |
1297 | 0 | } |
1298 | | |
1299 | | |
1300 | | #define ConvexCompare(delta) \ |
1301 | 0 | ( (delta.x > 0) ? -1 : \ |
1302 | 0 | (delta.x < 0) ? 1 : \ |
1303 | 0 | (delta.y > 0) ? -1 : \ |
1304 | 0 | (delta.y < 0) ? 1 : \ |
1305 | 0 | 0 ) |
1306 | | |
1307 | | #define ConvexGetPointDelta(delta, pprev, pcur ) \ |
1308 | | /* Given a previous point 'pprev', read a new point into 'pcur' */ \ |
1309 | | /* and return delta in 'delta'. */ \ |
1310 | 0 | pcur = pts[iread++]; \ |
1311 | 0 | delta.x = pcur.x - pprev.x; \ |
1312 | 0 | delta.y = pcur.y - pprev.y; \ |
1313 | | |
1314 | 0 | #define ConvexCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x); |
1315 | | |
1316 | | #define ConvexCheckTriple \ |
1317 | 0 | if ( (thisDir = ConvexCompare(dcur)) == -curDir ) { \ |
1318 | 0 | ++dirChanges; \ |
1319 | 0 | /* if ( dirChanges > 2 ) return NotConvex; */ \ |
1320 | 0 | } \ |
1321 | 0 | curDir = thisDir; \ |
1322 | 0 | cross = ConvexCross(dprev, dcur); \ |
1323 | 0 | if ( cross > 0 ) { \ |
1324 | 0 | if ( angleSign == -1 ) return GF_POLYGON_COMPLEX_CW; \ |
1325 | 0 | angleSign = 1; \ |
1326 | 0 | } \ |
1327 | 0 | else if (cross < 0) { \ |
1328 | 0 | if (angleSign == 1) return GF_POLYGON_COMPLEX_CCW; \ |
1329 | 0 | angleSign = -1; \ |
1330 | 0 | } \ |
1331 | 0 | pSecond = pThird; \ |
1332 | 0 | dprev.x = dcur.x; \ |
1333 | 0 | dprev.y = dcur.y; \ |
1334 | | |
1335 | | GF_EXPORT |
1336 | | u32 gf_polygone2d_get_convexity(GF_Point2D *pts, u32 len) |
1337 | 0 | { |
1338 | 0 | s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0; |
1339 | 0 | u32 iread; |
1340 | 0 | Fixed cross; |
1341 | 0 | GF_Point2D pSecond, pThird, pSaveSecond; |
1342 | 0 | GF_Point2D dprev, dcur; |
1343 | | |
1344 | | /* Get different point, return if less than 3 diff points. */ |
1345 | 0 | if (len < 3 ) return GF_POLYGON_CONVEX_LINE; |
1346 | 0 | iread = 1; |
1347 | 0 | ConvexGetPointDelta(dprev, (pts[0]), pSecond); |
1348 | 0 | pSaveSecond = pSecond; |
1349 | | /*initial direction */ |
1350 | 0 | curDir = ConvexCompare(dprev); |
1351 | 0 | while ( iread < len) { |
1352 | | /* Get different point, break if no more points */ |
1353 | 0 | ConvexGetPointDelta(dcur, pSecond, pThird ); |
1354 | 0 | if ( (dcur.x == 0) && (dcur.y == 0) ) continue; |
1355 | | /* Check current three points */ |
1356 | 0 | ConvexCheckTriple; |
1357 | 0 | } |
1358 | | |
1359 | | /* Must check for direction changes from last vertex back to first */ |
1360 | | /* Prepare for 'ConvexCheckTriple' */ |
1361 | 0 | pThird = pts[0]; |
1362 | 0 | dcur.x = pThird.x - pSecond.x; |
1363 | 0 | dcur.y = pThird.y - pSecond.y; |
1364 | 0 | if ( ConvexCompare(dcur) ) { |
1365 | 0 | ConvexCheckTriple; |
1366 | 0 | } |
1367 | | /* and check for direction changes back to second vertex */ |
1368 | 0 | dcur.x = pSaveSecond.x - pSecond.x; |
1369 | 0 | dcur.y = pSaveSecond.y - pSecond.y; |
1370 | | /* Don't care about 'pThird' now */ |
1371 | 0 | ConvexCheckTriple; |
1372 | | |
1373 | | /* Decide on polygon type given accumulated status */ |
1374 | 0 | if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX; |
1375 | 0 | if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW; |
1376 | 0 | if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW; |
1377 | 0 | return GF_POLYGON_CONVEX_LINE; |
1378 | 0 | } |
1379 | | |
1380 | | #endif // !defined(GPAC_DISABLE_EVG) || defined(GPAC_HAS_FREETYPE) |