Coverage Report

Created: 2025-11-24 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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)