LCOV - code coverage report
Current view: top level - utils - path2d.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 665 704 94.5 %
Date: 2021-04-29 23:48:07 Functions: 38 38 100.0 %

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

Generated by: LCOV version 1.13