LCOV - code coverage report
Current view: top level - utils - path2d_stroker.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 556 617 90.1 %
Date: 2021-04-29 23:48:07 Functions: 24 24 100.0 %

          Line data    Source code
       1             : /***************************************************************************/
       2             : /*                                                                         */
       3             : /*  ftstroke.c                                                             */
       4             : /*                                                                         */
       5             : /*    FreeType path stroker (body).                                        */
       6             : /*                                                                         */
       7             : /*  Copyright 2002, 2003, 2004 by                                          */
       8             : /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
       9             : /*                                                                         */
      10             : /*  This file is part of the FreeType project, and may only be used,       */
      11             : /*  modified, and distributed under the terms of the FreeType project      */
      12             : /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
      13             : /*  this file you indicate that you have read the license and              */
      14             : /*  understand and accept it fully.                                        */
      15             : /*                                                                         */
      16             : /***************************************************************************/
      17             : 
      18             : 
      19             : #include <gpac/path2d.h>
      20             : 
      21             : /***************************************************************************/
      22             : /***************************************************************************/
      23             : /*****                                                                 *****/
      24             : /*****                       BEZIER COMPUTATIONS                       *****/
      25             : /*****                                                                 *****/
      26             : /***************************************************************************/
      27             : /***************************************************************************/
      28             : 
      29             : #define FT_SMALL_CONIC_THRESHOLD  ( GF_PI / 6 )
      30             : #define FT_SMALL_CUBIC_THRESHOLD  ( GF_PI / 6 )
      31             : 
      32             : #define FT_IS_SMALL( x )  ( (x) > -FIX_EPSILON && (x) < FIX_EPSILON )
      33             : 
      34             : #if 0 //unused
      35             : 
      36             : static void ft_conic_split(GF_Point2D*  base )
      37             : {
      38             :         Fixed  a, b;
      39             : 
      40             :         base[4].x = base[2].x;
      41             :         b = base[1].x;
      42             :         a = base[3].x = ( base[2].x + b ) / 2;
      43             :         b = base[1].x = ( base[0].x + b ) / 2;
      44             :         base[2].x = ( a + b ) / 2;
      45             : 
      46             :         base[4].y = base[2].y;
      47             :         b = base[1].y;
      48             :         a = base[3].y = ( base[2].y + b ) / 2;
      49             :         b = base[1].y = ( base[0].y + b ) / 2;
      50             :         base[2].y = ( a + b ) / 2;
      51             : }
      52             : 
      53             : 
      54             : 
      55             : static Bool ft_conic_is_small_enough( GF_Point2D*  base, Fixed *angle_in, Fixed *angle_out)
      56             : {
      57             :         GF_Point2D  d1, d2;
      58             :         Fixed theta;
      59             :         s32 close1, close2;
      60             :         d1.x = base[1].x - base[2].x;
      61             :         d1.y = base[1].y - base[2].y;
      62             :         d2.x = base[0].x - base[1].x;
      63             :         d2.y = base[0].y - base[1].y;
      64             :         close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
      65             :         close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
      66             : 
      67             :         if ( close1 ) {
      68             :                 if ( close2 )
      69             :                         *angle_in = *angle_out = 0;
      70             :                 else
      71             :                         *angle_in = *angle_out = gf_atan2(d2.y, d2.x);
      72             :         }
      73             :         else if ( close2 ) {
      74             :                 *angle_in = *angle_out = gf_atan2(d1.y, d1.x);
      75             :         } else {
      76             :                 *angle_in  = gf_atan2(d1.y, d1.x);
      77             :                 *angle_out = gf_atan2(d2.y, d2.x);
      78             :         }
      79             :         theta = ABS( gf_angle_diff(*angle_in, *angle_out));
      80             :         return ( theta < FT_SMALL_CONIC_THRESHOLD ) ? GF_TRUE : GF_FALSE;
      81             : }
      82             : 
      83             : static void ft_cubic_split( GF_Point2D*  base )
      84             : {
      85             :         Fixed  a, b, c, d;
      86             :         base[6].x = base[3].x;
      87             :         c = base[1].x;
      88             :         d = base[2].x;
      89             :         base[1].x = a = ( base[0].x + c ) / 2;
      90             :         base[5].x = b = ( base[3].x + d ) / 2;
      91             :         c = ( c + d ) / 2;
      92             :         base[2].x = a = ( a + c ) / 2;
      93             :         base[4].x = b = ( b + c ) / 2;
      94             :         base[3].x = ( a + b ) / 2;
      95             : 
      96             :         base[6].y = base[3].y;
      97             :         c = base[1].y;
      98             :         d = base[2].y;
      99             :         base[1].y = a = ( base[0].y + c ) / 2;
     100             :         base[5].y = b = ( base[3].y + d ) / 2;
     101             :         c = ( c + d ) / 2;
     102             :         base[2].y = a = ( a + c ) / 2;
     103             :         base[4].y = b = ( b + c ) / 2;
     104             :         base[3].y = ( a + b ) / 2;
     105             : }
     106             : 
     107             : 
     108             : static Bool ft_cubic_is_small_enough(GF_Point2D *base, Fixed *angle_in, Fixed *angle_mid, Fixed *angle_out)
     109             : {
     110             :         GF_Point2D  d1, d2, d3;
     111             :         Fixed theta1, theta2;
     112             :         s32 close1, close2, close3;
     113             :         d1.x = base[2].x - base[3].x;
     114             :         d1.y = base[2].y - base[3].y;
     115             :         d2.x = base[1].x - base[2].x;
     116             :         d2.y = base[1].y - base[2].y;
     117             :         d3.x = base[0].x - base[1].x;
     118             :         d3.y = base[0].y - base[1].y;
     119             : 
     120             :         close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
     121             :         close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
     122             :         close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
     123             : 
     124             :         if ( close1 || close3 ) {
     125             :                 if ( close2 ) {
     126             :                         /* basically a point */
     127             :                         *angle_in = *angle_out = *angle_mid = 0;
     128             :                 } else if ( close1 ) {
     129             :                         *angle_in  = *angle_mid = gf_atan2( d2.y, d2.x);
     130             :                         *angle_out = gf_atan2( d3.y, d3.x);
     131             :                 }
     132             :                 /* close2 */
     133             :                 else {
     134             :                         *angle_in  = gf_atan2(d1.y, d1.x);
     135             :                         *angle_mid = *angle_out = gf_atan2(d2.y, d2.x);
     136             :                 }
     137             :         }
     138             :         else if ( close2 ) {
     139             :                 *angle_in  = *angle_mid = gf_atan2(d1.y, d1.x);
     140             :                 *angle_out = gf_atan2(d3.y, d3.x);
     141             :         } else {
     142             :                 *angle_in  = gf_atan2(d1.y, d1.x);
     143             :                 *angle_mid = gf_atan2(d2.y, d2.x);
     144             :                 *angle_out = gf_atan2(d3.y, d3.x);
     145             :         }
     146             :         theta1 = ABS( gf_angle_diff( *angle_in,  *angle_mid ) );
     147             :         theta2 = ABS( gf_angle_diff( *angle_mid, *angle_out ) );
     148             :         return ((theta1 < FT_SMALL_CUBIC_THRESHOLD) && (theta2 < FT_SMALL_CUBIC_THRESHOLD )) ? GF_TRUE : GF_FALSE;
     149             : }
     150             : 
     151             : #endif
     152             : 
     153             : /***************************************************************************/
     154             : /***************************************************************************/
     155             : /*****                                                                 *****/
     156             : /*****                       STROKE BORDERS                            *****/
     157             : /*****                                                                 *****/
     158             : /***************************************************************************/
     159             : /***************************************************************************/
     160             : 
     161             : typedef enum
     162             : {
     163             :         FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
     164             :         FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
     165             :         FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
     166             :         FT_STROKE_TAG_END   = 8    /* sub-path end    */
     167             : } FT_StrokeTags;
     168             : 
     169             : 
     170             : typedef struct  FT_StrokeBorderRec_
     171             : {
     172             :         u32 num_points;
     173             :         u32 max_points;
     174             :         GF_Point2D*  points;
     175             :         u8 *tags;
     176             :         Bool movable;
     177             :         /* index of current sub-path start point */
     178             :         s32 start;
     179             :         Bool valid;
     180             : } FT_StrokeBorderRec, *FT_StrokeBorder;
     181             : 
     182             : 
     183     1459543 : static s32 ft_stroke_border_grow(FT_StrokeBorder  border, u32 new_points)
     184             : {
     185     1459543 :         u32 new_max = border->num_points + new_points;
     186     1459543 :         if (new_max > border->max_points) {
     187      123064 :                 u32 cur_max = new_max*2;
     188      123064 :                 border->points = (GF_Point2D *) gf_realloc(border->points, sizeof(GF_Point2D)*cur_max);
     189      123064 :                 border->tags = (u8 *) gf_realloc(border->tags, sizeof(u8)*cur_max);
     190      123064 :                 if (!border->points || !border->tags) return -1;
     191      123064 :                 border->max_points = cur_max;
     192             :         }
     193             :         return 0;
     194             : }
     195             : 
     196       70768 : static void ft_stroke_border_close( FT_StrokeBorder  border )
     197             : {
     198             :         /* don't record empty paths! */
     199       70768 :         if ((border->start <0) || !border->num_points ) return;
     200       35335 :         if ( border->num_points > (u32)border->start ) {
     201       35335 :                 border->tags[border->start] |= FT_STROKE_TAG_BEGIN;
     202       35335 :                 border->tags[border->num_points - 1] |= FT_STROKE_TAG_END;
     203             :         }
     204       35335 :         border->start   = -1;
     205       35335 :         border->movable = GF_FALSE;
     206             : }
     207             : 
     208     3337236 : static s32 ft_stroke_border_lineto( FT_StrokeBorder  border, GF_Point2D*       to, Bool movable )
     209             : {
     210             :         assert(border->start >= 0);
     211             : 
     212     3337236 :         if ( border->movable ) {
     213             :                 /* move last point */
     214     1914228 :                 border->points[border->num_points - 1] = *to;
     215             :         } else {
     216             :                 /* add one point */
     217     1423008 :                 if (ft_stroke_border_grow( border, 1 )==0) {
     218     1423008 :                         GF_Point2D*  vec = border->points + border->num_points;
     219     1423008 :                         u8 *tag = border->tags   + border->num_points;
     220             : 
     221     1423008 :                         vec[0] = *to;
     222     1423008 :                         tag[0] = FT_STROKE_TAG_ON;
     223     1423008 :                         border->num_points += 1;
     224             :                 } else {
     225             :                         return -1;
     226             :                 }
     227             :         }
     228     3337236 :         border->movable = movable;
     229     3337236 :         return 0;
     230             : }
     231             : 
     232             : #if 0 //unused
     233             : static s32 ft_stroke_border_conicto( FT_StrokeBorder  border, GF_Point2D*       control, GF_Point2D*       to )
     234             : {
     235             :         assert( border->start >= 0 );
     236             :         if (ft_stroke_border_grow( border, 2 )==0) {
     237             :                 GF_Point2D*  vec = border->points + border->num_points;
     238             :                 u8 *tag = border->tags   + border->num_points;
     239             : 
     240             :                 vec[0] = *control;
     241             :                 vec[1] = *to;
     242             : 
     243             :                 tag[0] = 0;
     244             :                 tag[1] = FT_STROKE_TAG_ON;
     245             : 
     246             :                 border->num_points += 2;
     247             :         } else {
     248             :                 return -1;
     249             :         }
     250             :         border->movable = GF_FALSE;
     251             :         return 0;
     252             : }
     253             : #endif
     254             : 
     255        1200 : static s32 ft_stroke_border_cubicto( FT_StrokeBorder  border,
     256             :                                      GF_Point2D*       control1,
     257             :                                      GF_Point2D*       control2,
     258             :                                      GF_Point2D*       to )
     259             : {
     260             :         assert( border->start >= 0 );
     261             : 
     262        1200 :         if (!ft_stroke_border_grow( border, 3 )) {
     263        1200 :                 GF_Point2D*  vec = border->points + border->num_points;
     264        1200 :                 u8*    tag = border->tags   + border->num_points;
     265        1200 :                 vec[0] = *control1;
     266        1200 :                 vec[1] = *control2;
     267        1200 :                 vec[2] = *to;
     268             : 
     269        1200 :                 tag[0] = FT_STROKE_TAG_CUBIC;
     270        1200 :                 tag[1] = FT_STROKE_TAG_CUBIC;
     271        1200 :                 tag[2] = FT_STROKE_TAG_ON;
     272             : 
     273        1200 :                 border->num_points += 3;
     274             :         } else {
     275             :                 return -1;
     276             :         }
     277        1200 :         border->movable = GF_FALSE;
     278        1200 :         return 0;
     279             : }
     280             : 
     281             : #define FT_ARC_CUBIC_ANGLE  ( GF_PI / 2 )
     282             : 
     283             : 
     284           1 : static s32 ft_stroke_border_arcto( FT_StrokeBorder  border,
     285             :                                    GF_Point2D*       center,
     286             :                                    Fixed         radius,
     287             :                                    Fixed angle_start,
     288             :                                    Fixed angle_diff )
     289             : {
     290             :         Fixed total, angle, step, rotate, next, theta;
     291             :         GF_Point2D  a, b, a2, b2;
     292             :         Fixed length;
     293             :         /* compute start point */
     294           1 :         a = gf_v2d_from_polar(radius, angle_start );
     295           1 :         a.x += center->x;
     296           1 :         a.y += center->y;
     297             : 
     298             :         total  = angle_diff;
     299             :         angle  = angle_start;
     300           1 :         rotate = ( angle_diff >= 0 ) ? GF_PI2 : -GF_PI2;
     301             : 
     302           4 :         while ( total != 0 ) {
     303             :                 step = total;
     304           2 :                 if ( step > FT_ARC_CUBIC_ANGLE )
     305             :                         step = FT_ARC_CUBIC_ANGLE;
     306           2 :                 else if ( step < -FT_ARC_CUBIC_ANGLE )
     307             :                         step = -FT_ARC_CUBIC_ANGLE;
     308             : 
     309           2 :                 next  = angle + step;
     310             :                 theta = step;
     311           2 :                 if ( theta < 0 )
     312           2 :                         theta = -theta;
     313             : 
     314             : #ifdef GPAC_FIXED_POINT
     315             :                 theta >>= 1;
     316             : #else
     317           2 :                 theta /= 2;
     318             : #endif
     319             : 
     320             :                 /* compute end point */
     321           2 :                 b = gf_v2d_from_polar(radius, next );
     322           2 :                 b.x += center->x;
     323           2 :                 b.y += center->y;
     324             : 
     325             :                 /* compute first and second control points */
     326           2 :                 length = gf_muldiv( radius, gf_sin( theta ) * 4, ( FIX_ONE + gf_cos( theta ) ) * 3 );
     327             : 
     328           2 :                 a2 = gf_v2d_from_polar(length, angle + rotate );
     329           2 :                 a2.x += a.x;
     330           2 :                 a2.y += a.y;
     331             : 
     332           2 :                 b2 = gf_v2d_from_polar(length, next - rotate );
     333           2 :                 b2.x += b.x;
     334           2 :                 b2.y += b.y;
     335             : 
     336             :                 /* add cubic arc */
     337           2 :                 if (ft_stroke_border_cubicto( border, &a2, &b2, &b ) != 0) return -1;
     338             : 
     339             :                 /* process the rest of the arc ?? */
     340           2 :                 a      = b;
     341           2 :                 total -= step;
     342             :                 angle  = next;
     343             :         }
     344             :         return 0;
     345             : }
     346             : 
     347             : 
     348       70670 : static s32 ft_stroke_border_moveto(FT_StrokeBorder  border, GF_Point2D*       to )
     349             : {
     350             :         /* close current open path if any ? */
     351       70670 :         if ( border->start >= 0 )
     352       21517 :                 ft_stroke_border_close( border );
     353             : 
     354       70670 :         border->start   = border->num_points;
     355       70670 :         border->movable = GF_FALSE;
     356       70670 :         return ft_stroke_border_lineto(border, to, GF_FALSE);
     357             : }
     358             : 
     359             : 
     360       27636 : static s32 ft_stroke_border_get_counts(FT_StrokeBorder  border,
     361             :                                        u32 *anum_points,
     362             :                                        u32 *anum_contours )
     363             : {
     364             :         s32 error        = 0;
     365             :         u32 num_points   = 0;
     366             :         u32 num_contours = 0;
     367       27636 :         u32 count      = border->num_points;
     368             :         GF_Point2D *point      = border->points;
     369       27636 :         u8 *tags       = border->tags;
     370             :         s32 in_contour = 0;
     371             : 
     372     1454244 :         for ( ; count > 0; count--, num_points++, point++, tags++ ) {
     373     1426608 :                 if ( tags[0] & FT_STROKE_TAG_BEGIN ) {
     374       35335 :                         if ( in_contour != 0 ) goto Fail;
     375             : 
     376             :                         in_contour = 1;
     377     1391273 :                 } else if ( in_contour == 0 )
     378             :                         goto Fail;
     379             : 
     380     1426608 :                 if ( tags[0] & FT_STROKE_TAG_END ) {
     381       35335 :                         if ( in_contour == 0 )
     382             :                                 goto Fail;
     383             :                         in_contour = 0;
     384       35335 :                         num_contours++;
     385             :                 }
     386             :         }
     387             : 
     388       27636 :         if ( in_contour != 0 )
     389             :                 goto Fail;
     390             : 
     391       27636 :         border->valid = GF_TRUE;
     392             : 
     393       27636 : Exit:
     394       27636 :         *anum_points   = num_points;
     395       27636 :         *anum_contours = num_contours;
     396       27636 :         return error;
     397             : 
     398       27636 : Fail:
     399             :         num_points   = 0;
     400             :         num_contours = 0;
     401             :         error = -1;
     402             :         goto Exit;
     403             : }
     404             : 
     405             : 
     406       13818 : static void ft_stroke_border_export( FT_StrokeBorder  border, GF_Path*      outline )
     407             : {
     408       13818 :         if (!border->num_points) return;
     409             : 
     410             :         /* copy point locations */
     411       13818 :         memcpy(outline->points + outline->n_points, border->points, sizeof(GF_Point2D)*border->num_points);
     412             : 
     413             :         /* copy tags */
     414             :         {
     415       13818 :                 u32 count = border->num_points;
     416       13818 :                 u8*  read  = border->tags;
     417       13818 :                 u8*  write = (u8*)outline->tags + outline->n_points;
     418             : 
     419     1440426 :                 for ( ; count > 0; count--, read++, write++ ) {
     420     1426608 :                         if ( *read & FT_STROKE_TAG_ON )
     421     1424208 :                                 *write = GF_PATH_CURVE_ON;
     422        2400 :                         else if ( *read & FT_STROKE_TAG_CUBIC )
     423        2400 :                                 *write = GF_PATH_CURVE_CUBIC;
     424             :                         else
     425           0 :                                 *write = GF_PATH_CURVE_CONIC;
     426             :                 }
     427             :         }
     428             : 
     429             :         /* copy contours */
     430             :         {
     431       13818 :                 u32 count = border->num_points;
     432       13818 :                 u8 *tags  = border->tags;
     433       13818 :                 u32 *write = outline->contours + outline->n_contours;
     434       13818 :                 u32 idx = outline->n_points;
     435             : 
     436     1440426 :                 for ( ; count > 0; count--, tags++, idx++ ) {
     437     1426608 :                         if ( *tags & FT_STROKE_TAG_END ) {
     438       35335 :                                 *write++ = idx;
     439       35335 :                                 outline->n_contours++;
     440             :                         }
     441             :                 }
     442             :         }
     443       13818 :         outline->n_points = outline->n_points + border->num_points;
     444             : }
     445             : 
     446             : 
     447             : /***************************************************************************/
     448             : /***************************************************************************/
     449             : /*****                                                                 *****/
     450             : /*****                           STROKER                               *****/
     451             : /*****                                                                 *****/
     452             : /***************************************************************************/
     453             : /***************************************************************************/
     454             : 
     455             : #define FT_SIDE_TO_ROTATE( s )   ( GF_PI2 - (s) * GF_PI )
     456             : 
     457             : typedef struct  FT_StrokerRec_
     458             : {
     459             :         Fixed angle_in;
     460             :         Fixed angle_out;
     461             :         GF_Point2D            center;
     462             :         Bool              first_point;
     463             :         Fixed subpath_angle;
     464             :         GF_Point2D            subpath_start;
     465             : 
     466             :         u32 line_cap;
     467             :         u32 line_join;
     468             :         Fixed miter_limit;
     469             :         Fixed radius;
     470             :         Bool              valid;
     471             :         Bool              closing;
     472             :         FT_StrokeBorderRec   borders[2];
     473             : } FT_StrokerRec, FT_Stroker;
     474             : 
     475             : 
     476             : /* creates a circular arc at a corner or cap */
     477           1 : static s32 ft_stroker_arcto( FT_Stroker  *stroker, s32 side )
     478             : {
     479             :         Fixed total, rotate;
     480           1 :         Fixed         radius = stroker->radius;
     481             :         s32 error  = 0;
     482           1 :         FT_StrokeBorder  border = stroker->borders + side;
     483           1 :         rotate = FT_SIDE_TO_ROTATE( side );
     484           1 :         total = gf_angle_diff( stroker->angle_in, stroker->angle_out);
     485           1 :         if ( total == GF_PI ) total = -rotate * 2;
     486           1 :         error = ft_stroke_border_arcto( border,
     487             :                                         &stroker->center,
     488             :                                         radius,
     489           1 :                                         stroker->angle_in + rotate,
     490             :                                         total );
     491           1 :         border->movable = GF_FALSE;
     492           1 :         return error;
     493             : }
     494             : 
     495             : /* adds a cap at the end of an opened path */
     496       42838 : static s32 ft_stroker_cap(FT_Stroker  *stroker, Fixed angle, s32 side)
     497             : {
     498             :         s32 error  = 0;
     499       42838 :         if ( stroker->line_cap == GF_LINE_CAP_ROUND) {
     500             :                 /* OK we cheat a bit here compared to FT original code, and use a rough cubic cap instead of
     501             :                 a circle to deal with arbitrary orientation of regular paths where arc cap is not always properly oriented.
     502             :                 Rather than computing orientation we simply approximate to conic - btw this takes less memory than
     503             :                 exact circle cap since cubics are natively supported - we don't use conic since result is not so good looking*/
     504             :                 GF_Point2D delta, delta2, ctl1, ctl2, end;
     505        1198 :                 Fixed rotate = FT_SIDE_TO_ROTATE( side );
     506        1198 :                 Fixed radius = stroker->radius;
     507        1198 :                 FT_StrokeBorder  border = stroker->borders + side;
     508             : 
     509             : 
     510        1198 :                 delta = gf_v2d_from_polar(radius, angle);
     511        1198 :                 delta.x = 4*delta.x/3;
     512        1198 :                 delta.y = 4*delta.y/3;
     513             : 
     514        1198 :                 delta2 = gf_v2d_from_polar(radius, angle + rotate);
     515        1198 :                 ctl1.x = delta.x + stroker->center.x + delta2.x;
     516        1198 :                 ctl1.y = delta.y + stroker->center.y + delta2.y;
     517             : 
     518        1198 :                 delta2 = gf_v2d_from_polar(radius, angle - rotate);
     519        1198 :                 ctl2.x = delta.x + delta2.x + stroker->center.x;
     520        1198 :                 ctl2.y = delta.y + delta2.y + stroker->center.y;
     521             : 
     522        1198 :                 end.x = delta2.x + stroker->center.x;
     523        1198 :                 end.y = delta2.y + stroker->center.y;
     524             : 
     525        1198 :                 error = ft_stroke_border_cubicto( border, &ctl1, &ctl2, &end);
     526       41640 :         } else if ( stroker->line_cap == GF_LINE_CAP_SQUARE) {
     527             :                 /* add a square cap */
     528             :                 GF_Point2D        delta, delta2;
     529           6 :                 Fixed rotate = FT_SIDE_TO_ROTATE( side );
     530           6 :                 Fixed radius = stroker->radius;
     531           6 :                 FT_StrokeBorder  border = stroker->borders + side;
     532             : 
     533             : 
     534           6 :                 delta2 = gf_v2d_from_polar(radius, angle + rotate);
     535           6 :                 delta = gf_v2d_from_polar(radius, angle);
     536             : 
     537           6 :                 delta.x += stroker->center.x + delta2.x;
     538           6 :                 delta.y += stroker->center.y + delta2.y;
     539             : 
     540           6 :                 error = ft_stroke_border_lineto(border, &delta, GF_FALSE);
     541           6 :                 if ( error )
     542             :                         goto Exit;
     543             : 
     544           6 :                 delta2 = gf_v2d_from_polar(radius, angle - rotate);
     545           6 :                 delta = gf_v2d_from_polar(radius, angle);
     546             : 
     547           6 :                 delta.x += delta2.x + stroker->center.x;
     548           6 :                 delta.y += delta2.y + stroker->center.y;
     549             : 
     550           6 :                 error = ft_stroke_border_lineto(border, &delta, GF_FALSE);
     551       41634 :         } else if ( stroker->line_cap == GF_LINE_CAP_TRIANGLE) {
     552             :                 /* add a triangle cap */
     553             :                 GF_Point2D delta;
     554           6 :                 Fixed radius = stroker->radius;
     555           6 :                 FT_StrokeBorder  border = stroker->borders + side;
     556           6 :                 border->movable = GF_FALSE;
     557           6 :                 delta = gf_v2d_from_polar(radius, angle);
     558           6 :                 delta.x += stroker->center.x;
     559           6 :                 delta.y += stroker->center.y;
     560           6 :                 error = ft_stroke_border_lineto(border, &delta, GF_FALSE);
     561             :         }
     562             : 
     563       84466 : Exit:
     564       42838 :         return error;
     565             : }
     566             : 
     567             : 
     568             : /* process an inside corner, i.e. compute intersection */
     569      632838 : static s32 ft_stroker_inside(FT_Stroker *stroker, s32 side)
     570             : {
     571      632838 :         FT_StrokeBorder  border = stroker->borders + side;
     572             :         Fixed phi, theta, rotate;
     573             :         Fixed length, thcos, sigma;
     574             :         GF_Point2D        delta;
     575             :         s32 error = 0;
     576             : 
     577      632838 :         rotate = FT_SIDE_TO_ROTATE( side );
     578             : 
     579             :         /* compute median angle */
     580      632838 :         theta = gf_angle_diff( stroker->angle_in, stroker->angle_out );
     581      632838 :         if ( theta == GF_PI )
     582             :                 theta = rotate;
     583             :         else
     584      632827 :                 theta = theta / 2;
     585             : 
     586      632838 :         phi = stroker->angle_in + theta;
     587             : 
     588      632838 :         thcos  = gf_cos( theta );
     589      632838 :         sigma  = gf_mulfix( stroker->miter_limit, thcos );
     590             : 
     591      632838 :         if ( sigma < FIX_ONE ) {
     592         829 :                 delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate );
     593         829 :                 delta.x += stroker->center.x;
     594         829 :                 delta.y += stroker->center.y;
     595         829 :                 if (!stroker->closing) border->movable = GF_FALSE;
     596             :         } else {
     597      632009 :                 length = gf_divfix( stroker->radius, thcos );
     598      632009 :                 delta = gf_v2d_from_polar(length, phi + rotate );
     599      632009 :                 delta.x += stroker->center.x;
     600      632009 :                 delta.y += stroker->center.y;
     601             :         }
     602      632838 :         error = ft_stroke_border_lineto(border, &delta, GF_FALSE);
     603      632838 :         return error;
     604             : }
     605             : 
     606             : 
     607             : /* process an outside corner, i.e. compute bevel/miter/round */
     608      646754 : static s32 ft_stroker_outside( FT_Stroker *stroker, s32 side )
     609             : {
     610      646754 :         FT_StrokeBorder  border = stroker->borders + side;
     611             :         s32 error;
     612             :         Fixed rotate;
     613      646754 :         u32 join = stroker->line_join;
     614             : 
     615      646754 :         if ( join == GF_LINE_JOIN_MITER_SVG ) {
     616             :                 Fixed sin_theta, inv_sin_theta;
     617             :                 join = GF_LINE_JOIN_MITER;
     618       57860 :                 sin_theta = gf_sin(gf_angle_diff( stroker->angle_out - GF_PI, stroker->angle_in) / 2 );
     619       57860 :                 if (sin_theta) {
     620       57852 :                         inv_sin_theta = gf_invfix(sin_theta);
     621       57852 :                         if (inv_sin_theta > stroker->miter_limit) join = GF_LINE_JOIN_BEVEL;
     622             :                 } else {
     623             :                         join = GF_LINE_JOIN_BEVEL;
     624             :                 }
     625             :         }
     626             : 
     627      588894 :         if ( join == GF_LINE_JOIN_ROUND ) {
     628           1 :                 error = ft_stroker_arcto( stroker, side );
     629      646753 :         } else if (join == GF_LINE_JOIN_BEVEL) {
     630             :                 GF_Point2D  delta;
     631         469 :                 rotate = FT_SIDE_TO_ROTATE( side );
     632         469 :                 delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate );
     633         469 :                 delta.x += stroker->center.x;
     634         469 :                 delta.y += stroker->center.y;
     635             :                 /*prevent moving current point*/
     636         469 :                 border->movable = GF_FALSE;
     637             :                 /*and add un-movable end point*/
     638         469 :                 error = ft_stroke_border_lineto( border, &delta, GF_FALSE);
     639             :         } else {
     640             :                 /* this is a mitered or beveled corner */
     641      646284 :                 Fixed  sigma, radius = stroker->radius;
     642             :                 Fixed theta, phi;
     643             :                 Fixed thcos;
     644             :                 Bool  miter = GF_TRUE;
     645             : 
     646      646284 :                 rotate = FT_SIDE_TO_ROTATE( side );
     647             : 
     648      646284 :                 theta  = gf_angle_diff( stroker->angle_in, stroker->angle_out );
     649      646284 :                 if ( theta == GF_PI ) {
     650             :                         theta = rotate;
     651           6 :                         phi   = stroker->angle_in;
     652             :                 } else {
     653      646278 :                         theta = theta / 2;
     654      646278 :                         phi   = stroker->angle_in + theta + rotate;
     655             :                 }
     656             : 
     657      646284 :                 thcos = gf_cos( theta );
     658      646284 :                 sigma = gf_mulfix( stroker->miter_limit, thcos );
     659             : 
     660      646284 :                 if ( sigma >= FIX_ONE ) {
     661             :                         miter = GF_FALSE;
     662             :                 }
     663             : 
     664             :                 /* this is a miter (broken angle) */
     665             :                 if ( miter ) {
     666             :                         GF_Point2D  middle, delta;
     667             :                         Fixed   length;
     668             : 
     669             :                         /* compute middle point */
     670         767 :                         middle = gf_v2d_from_polar(gf_mulfix(radius, stroker->miter_limit), phi);
     671         767 :                         middle.x += stroker->center.x;
     672         767 :                         middle.y += stroker->center.y;
     673             : 
     674             :                         /* compute first angle point */
     675         767 :                         length = gf_mulfix(radius, gf_divfix( FIX_ONE - sigma, ABS( gf_sin( theta ) ) ) );
     676             : 
     677         767 :                         delta = gf_v2d_from_polar(length, phi + rotate );
     678         767 :                         delta.x += middle.x;
     679         767 :                         delta.y += middle.y;
     680             : 
     681         767 :                         error = ft_stroke_border_lineto( border, &delta, GF_FALSE );
     682         767 :                         if ( error )
     683             :                                 goto Exit;
     684             : 
     685             :                         /* compute second angle point */
     686         767 :                         delta = gf_v2d_from_polar(length, phi - rotate);
     687         767 :                         delta.x += middle.x;
     688         767 :                         delta.y += middle.y;
     689             : 
     690         767 :                         error = ft_stroke_border_lineto( border, &delta, GF_FALSE );
     691         767 :                         if ( error )
     692             :                                 goto Exit;
     693             : 
     694             :                         /* finally, add a movable end point */
     695         767 :                         delta = gf_v2d_from_polar(radius, stroker->angle_out + rotate );
     696         767 :                         delta.x += stroker->center.x;
     697         767 :                         delta.y += stroker->center.y;
     698             : 
     699         767 :                         error = ft_stroke_border_lineto( border, &delta, GF_TRUE);
     700             :                 }
     701             :                 /* this is a bevel (intersection) */
     702             :                 else {
     703             :                         Fixed   length;
     704             :                         GF_Point2D  delta;
     705             : 
     706             : 
     707     1291034 :                         length = gf_divfix( stroker->radius, thcos );
     708             : 
     709      645517 :                         delta = gf_v2d_from_polar(length, phi );
     710      645517 :                         delta.x += stroker->center.x;
     711      645517 :                         delta.y += stroker->center.y;
     712             : 
     713      645517 :                         error = ft_stroke_border_lineto( border, &delta, GF_FALSE );
     714      645517 :                         if (error) goto Exit;
     715             : 
     716             :                         /* now add end point */
     717      645517 :                         delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate );
     718      645517 :                         delta.x += stroker->center.x;
     719      645517 :                         delta.y += stroker->center.y;
     720             : 
     721      645517 :                         error = ft_stroke_border_lineto( border, &delta, GF_TRUE );
     722             :                 }
     723             :         }
     724      646754 : Exit:
     725      646754 :         return error;
     726             : }
     727             : 
     728             : 
     729             : 
     730      634618 : static s32 ft_stroker_process_corner(FT_Stroker *stroker )
     731             : {
     732             :         s32 error = 0;
     733             :         Fixed turn;
     734             :         s32 inside_side;
     735      634618 :         turn = gf_angle_diff( stroker->angle_in, stroker->angle_out );
     736             : 
     737             :         /* no specific corner processing is required if the turn is 0 */
     738      634618 :         if ( turn == 0 )
     739             :                 goto Exit;
     740             : 
     741             :         /* when we turn to the right, the inside side is 0 */
     742             :         inside_side = 0;
     743             :         /* otherwise, the inside side is 1 */
     744      632838 :         if (turn < 0 )
     745             :                 inside_side = 1;
     746             : 
     747             :         /* process the inside side */
     748      632838 :         error = ft_stroker_inside( stroker, inside_side );
     749      632838 :         if ( error ) goto Exit;
     750             : 
     751             :         /* process the outside side */
     752      632838 :         error = ft_stroker_outside( stroker, 1 - inside_side );
     753             : 
     754      636398 : Exit:
     755      634618 :         return error;
     756             : }
     757             : 
     758             : 
     759             : /* add two points to the left and right borders corresponding to the */
     760             : /* start of the subpath..                                            */
     761       35335 : static s32 ft_stroker_subpath_start( FT_Stroker *stroker, Fixed start_angle )
     762             : {
     763             :         GF_Point2D        delta;
     764             :         GF_Point2D        point;
     765             :         s32 error;
     766             :         FT_StrokeBorder  border;
     767             : 
     768       35335 :         delta = gf_v2d_from_polar(stroker->radius, start_angle + GF_PI2 );
     769             : 
     770       35335 :         point.x = stroker->center.x + delta.x;
     771       35335 :         point.y = stroker->center.y + delta.y;
     772             : 
     773       35335 :         border = stroker->borders;
     774       35335 :         error = ft_stroke_border_moveto( border, &point );
     775       35335 :         if ( error )
     776             :                 goto Exit;
     777             : 
     778       35335 :         point.x = stroker->center.x - delta.x;
     779       35335 :         point.y = stroker->center.y - delta.y;
     780             : 
     781       35335 :         border++;
     782       35335 :         error = ft_stroke_border_moveto( border, &point );
     783             : 
     784             :         /* save angle for last cap */
     785       35335 :         stroker->subpath_angle = start_angle;
     786       35335 :         stroker->first_point   = GF_FALSE;
     787             : 
     788       35335 : Exit:
     789       35335 :         return error;
     790             : }
     791             : 
     792             : 
     793      669959 : static s32 FT_Stroker_LineTo( FT_Stroker *stroker, GF_Point2D*  to, Bool is_last)
     794             : {
     795             :         s32 error = 0;
     796             :         FT_StrokeBorder  border;
     797             :         GF_Point2D        delta;
     798             :         Fixed angle;
     799             :         s32 side;
     800             : 
     801      669959 :         delta.x = to->x - stroker->center.x;
     802      669959 :         delta.y = to->y - stroker->center.y;
     803      669959 :         if (!is_last && !delta.x && !delta.y) return 0;
     804             : 
     805      669953 :         angle = gf_atan2( delta.y, delta.x);
     806      669953 :         delta = gf_v2d_from_polar(stroker->radius, angle + GF_PI2 );
     807             : 
     808             :         /* process corner if necessary */
     809      669953 :         if ( stroker->first_point ) {
     810             :                 /* This is the first segment of a subpath.  We need to     */
     811             :                 /* add a point to each border at their respective starting */
     812             :                 /* point locations.                                        */
     813       35335 :                 error = ft_stroker_subpath_start( stroker, angle );
     814       35335 :                 if ( error )
     815             :                         goto Exit;
     816             :         } else {
     817             :                 /* process the current corner */
     818      634618 :                 stroker->angle_out = angle;
     819      634618 :                 error = ft_stroker_process_corner( stroker );
     820      634618 :                 if ( error )
     821             :                         goto Exit;
     822             :         }
     823             : 
     824             :         /* now add a line segment to both the "inside" and "outside" paths */
     825     2009859 :         for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) {
     826             :                 GF_Point2D  point;
     827     1339906 :                 point.x = to->x + delta.x;
     828     1339906 :                 point.y = to->y + delta.y;
     829             : 
     830     1339906 :                 error = ft_stroke_border_lineto( border, &point, GF_TRUE );
     831     1339906 :                 if ( error )
     832             :                         goto Exit;
     833             : 
     834     1339906 :                 delta.x = -delta.x;
     835     1339906 :                 delta.y = -delta.y;
     836             :         }
     837      669953 :         stroker->angle_in = angle;
     838      669953 :         stroker->center   = *to;
     839             : 
     840           0 : Exit:
     841             :         return error;
     842             : }
     843             : 
     844             : #if 0 //unused
     845             : static s32 FT_Stroker_ConicTo(FT_Stroker *stroker, GF_Point2D*  control, GF_Point2D * to)
     846             : {
     847             :         s32 error = 0;
     848             :         GF_Point2D   bez_stack[34];
     849             :         GF_Point2D*  arc;
     850             :         GF_Point2D*  limit = bez_stack + 30;
     851             :         Fixed start_angle;
     852             :         Bool first_arc = GF_TRUE;
     853             : 
     854             : 
     855             :         arc    = bez_stack;
     856             :         arc[0] = *to;
     857             :         arc[1] = *control;
     858             :         arc[2] = stroker->center;
     859             : 
     860             :         while ( arc >= bez_stack ) {
     861             :                 Fixed angle_in, angle_out;
     862             :                 angle_in = angle_out = 0;  /* remove compiler warnings */
     863             : 
     864             :                 if ( arc < limit                                             &&
     865             :                         !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
     866             :                 {
     867             :                         ft_conic_split( arc );
     868             :                         arc += 2;
     869             :                         continue;
     870             :                 }
     871             : 
     872             :                 if ( first_arc ) {
     873             :                         first_arc = GF_FALSE;
     874             : 
     875             :                         start_angle = angle_in;
     876             : 
     877             :                         /* process corner if necessary */
     878             :                         if ( stroker->first_point )
     879             :                                 error = ft_stroker_subpath_start( stroker, start_angle );
     880             :                         else {
     881             :                                 stroker->angle_out = start_angle;
     882             :                                 error = ft_stroker_process_corner( stroker );
     883             :                         }
     884             :                 }
     885             : 
     886             :                 /* the arc's angle is small enough; we can add it directly to each */
     887             :                 /* border                                                          */
     888             :                 {
     889             :                         GF_Point2D  ctrl, end;
     890             :                         Fixed theta, phi, rotate;
     891             :                         Fixed length;
     892             :                         s32 side;
     893             : 
     894             :                         theta  = gf_angle_diff( angle_in, angle_out ) / 2;
     895             :                         phi    = angle_in + theta;
     896             :                         length = gf_divfix( stroker->radius, gf_cos( theta ) );
     897             : 
     898             :                         for ( side = 0; side <= 1; side++ ) {
     899             :                                 rotate = FT_SIDE_TO_ROTATE( side );
     900             : 
     901             :                                 /* compute control point */
     902             :                                 ctrl = gf_v2d_from_polar(length, phi + rotate );
     903             :                                 ctrl.x += arc[1].x;
     904             :                                 ctrl.y += arc[1].y;
     905             : 
     906             :                                 /* compute end point */
     907             :                                 end = gf_v2d_from_polar(stroker->radius, angle_out + rotate );
     908             :                                 end.x += arc[0].x;
     909             :                                 end.y += arc[0].y;
     910             : 
     911             :                                 error = ft_stroke_border_conicto( stroker->borders + side, &ctrl, &end );
     912             :                                 if ( error )
     913             :                                         goto Exit;
     914             :                         }
     915             :                 }
     916             : 
     917             :                 arc -= 2;
     918             : 
     919             :                 if ( arc < bez_stack )
     920             :                         stroker->angle_in = angle_out;
     921             :         }
     922             : 
     923             :         stroker->center = *to;
     924             : Exit:
     925             :         return error;
     926             : }
     927             : 
     928             : 
     929             : static s32 FT_Stroker_CubicTo(FT_Stroker *stroker,
     930             :                               GF_Point2D*  control1,
     931             :                               GF_Point2D*  control2,
     932             :                               GF_Point2D*  to )
     933             : {
     934             :         s32 error = 0;
     935             :         GF_Point2D   bez_stack[37];
     936             :         GF_Point2D*  arc;
     937             :         GF_Point2D*  limit = bez_stack + 32;
     938             :         Fixed start_angle;
     939             :         Bool     first_arc = GF_TRUE;
     940             : 
     941             :         arc    = bez_stack;
     942             :         arc[0] = *to;
     943             :         arc[1] = *control2;
     944             :         arc[2] = *control1;
     945             :         arc[3] = stroker->center;
     946             : 
     947             :         while ( arc >= bez_stack ) {
     948             :                 Fixed angle_in, angle_mid, angle_out;
     949             :                 /* remove compiler warnings */
     950             :                 angle_in = angle_out = angle_mid = 0;
     951             : 
     952             :                 if (arc < limit &&
     953             :                         !ft_cubic_is_small_enough( arc, &angle_in, &angle_mid, &angle_out ) )
     954             :                 {
     955             :                         ft_cubic_split( arc );
     956             :                         arc += 3;
     957             :                         continue;
     958             :                 }
     959             : 
     960             :                 if ( first_arc ) {
     961             :                         first_arc = GF_FALSE;
     962             : 
     963             :                         /* process corner if necessary */
     964             :                         start_angle = angle_in;
     965             : 
     966             :                         if ( stroker->first_point )
     967             :                                 error = ft_stroker_subpath_start( stroker, start_angle );
     968             :                         else {
     969             :                                 stroker->angle_out = start_angle;
     970             :                                 error = ft_stroker_process_corner( stroker );
     971             :                         }
     972             :                         if ( error )
     973             :                                 goto Exit;
     974             :                 }
     975             : 
     976             :                 /* the arc's angle is small enough; we can add it directly to each */
     977             :                 /* border                                                          */
     978             :                 {
     979             :                         GF_Point2D  ctrl1, ctrl2, end;
     980             :                         Fixed theta1, phi1, theta2, phi2, rotate;
     981             :                         Fixed length1, length2;
     982             :                         s32 side;
     983             : 
     984             : 
     985             :                         theta1  = ABS( angle_mid - angle_in ) / 2;
     986             :                         theta2  = ABS( angle_out - angle_mid ) / 2;
     987             :                         phi1    = (angle_mid + angle_in ) / 2;
     988             :                         phi2    = (angle_mid + angle_out ) / 2;
     989             :                         length1 = gf_divfix( stroker->radius, gf_cos( theta1 ) );
     990             :                         length2 = gf_divfix( stroker->radius, gf_cos(theta2) );
     991             : 
     992             :                         for ( side = 0; side <= 1; side++ ) {
     993             :                                 rotate = FT_SIDE_TO_ROTATE( side );
     994             : 
     995             :                                 /* compute control points */
     996             :                                 ctrl1 = gf_v2d_from_polar(length1, phi1 + rotate );
     997             :                                 ctrl1.x += arc[2].x;
     998             :                                 ctrl1.y += arc[2].y;
     999             : 
    1000             :                                 ctrl2 = gf_v2d_from_polar(length2, phi2 + rotate );
    1001             :                                 ctrl2.x += arc[1].x;
    1002             :                                 ctrl2.y += arc[1].y;
    1003             : 
    1004             :                                 /* compute end point */
    1005             :                                 end = gf_v2d_from_polar(stroker->radius, angle_out + rotate );
    1006             :                                 end.x += arc[0].x;
    1007             :                                 end.y += arc[0].y;
    1008             : 
    1009             :                                 error = ft_stroke_border_cubicto( stroker->borders + side,
    1010             :                                                                   &ctrl1, &ctrl2, &end );
    1011             :                                 if ( error )
    1012             :                                         goto Exit;
    1013             :                         }
    1014             :                 }
    1015             : 
    1016             :                 arc -= 3;
    1017             :                 if ( arc < bez_stack )
    1018             :                         stroker->angle_in = angle_out;
    1019             :         }
    1020             : 
    1021             :         stroker->center = *to;
    1022             : 
    1023             : Exit:
    1024             :         return error;
    1025             : }
    1026             : #endif
    1027             : 
    1028             : 
    1029             : 
    1030             : static s32 FT_Stroker_BeginSubPath(FT_Stroker *stroker, GF_Point2D*  to)
    1031             : {
    1032             :         /* We cannot process the first point, because there is not enough      */
    1033             :         /* information regarding its corner/cap.  The latter will be processed */
    1034             :         /* in the "end_subpath" routine.                                       */
    1035             :         /*                                                                     */
    1036       35335 :         stroker->first_point   = GF_TRUE;
    1037       35335 :         stroker->center        = *to;
    1038             : 
    1039             :         /* record the subpath start point index for each border */
    1040       35335 :         stroker->subpath_start = *to;
    1041             :         return 0;
    1042             : }
    1043             : 
    1044       35335 : static s32 ft_stroker_add_reverse_left( FT_Stroker *stroker, Bool     open )
    1045             : {
    1046       35335 :         FT_StrokeBorder  right  = stroker->borders + 0;
    1047             :         FT_StrokeBorder  left   = stroker->borders + 1;
    1048             :         s32 new_points;
    1049             :         s32 error  = 0;
    1050             : 
    1051       35335 :         if (!left->num_points) return 0;
    1052             : 
    1053             :         assert( left->start >= 0 );
    1054       35335 :         new_points = left->num_points - left->start;
    1055       35335 :         if ( new_points > 0 ) {
    1056       35335 :                 error = ft_stroke_border_grow( right, (u32)new_points );
    1057       35335 :                 if ( error )
    1058             :                         goto Exit;
    1059             : 
    1060             :                 {
    1061       35335 :                         GF_Point2D*  dst_point = right->points + right->num_points;
    1062       35335 :                         u8*    dst_tag   = right->tags   + right->num_points;
    1063       35335 :                         GF_Point2D*  src_point = left->points  + left->num_points - 1;
    1064       35335 :                         u8*    src_tag   = left->tags    + left->num_points - 1;
    1065             : 
    1066      785248 :                         while ( src_point >= left->points + left->start ) {
    1067      714578 :                                 *dst_point = *src_point;
    1068      714578 :                                 *dst_tag   = *src_tag;
    1069             : 
    1070      714578 :                                 if ( open )
    1071      179568 :                                         dst_tag[0] &= ~( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
    1072             :                                 else {
    1073             :                                         /* switch begin/end tags if necessary.. */
    1074      535010 :                                         if ( dst_tag[0] & ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) )
    1075           0 :                                                 dst_tag[0] ^= ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
    1076             :                                 }
    1077             : 
    1078      714578 :                                 src_point--;
    1079      714578 :                                 src_tag--;
    1080      714578 :                                 dst_point++;
    1081      714578 :                                 dst_tag++;
    1082             :                         }
    1083             :                 }
    1084             : 
    1085       35335 :                 left->num_points   = left->start;
    1086       35335 :                 right->num_points += new_points;
    1087             : 
    1088       35335 :                 right->movable = GF_FALSE;
    1089       35335 :                 left->movable  = GF_FALSE;
    1090             :         }
    1091             : 
    1092           0 : Exit:
    1093             :         return error;
    1094             : }
    1095             : 
    1096             : /* there's a lot of magic in this function! */
    1097       35335 : static s32 FT_Stroker_EndSubPath( FT_Stroker *stroker, Bool do_close)
    1098             : {
    1099             :         s32  error  = 0;
    1100       35335 :         FT_StrokeBorder  right = stroker->borders;
    1101       35335 :         if (do_close) {
    1102             :                 Fixed turn;
    1103             :                 s32 inside_side;
    1104             : 
    1105             :                 /* process the corner */
    1106       13916 :                 stroker->angle_out = stroker->subpath_angle;
    1107       13916 :                 turn               = gf_angle_diff(stroker->angle_in, stroker->angle_out );
    1108             : 
    1109             :                 /* no specific corner processing is required if the turn is 0 */
    1110       13916 :                 if ( turn != 0 ) {
    1111             :                         /* when we turn to the right, the inside side is 0 */
    1112             :                         inside_side = 0;
    1113             : 
    1114             :                         /* otherwise, the inside side is 1 */
    1115       13916 :                         if ( turn < 0 ) inside_side = 1;
    1116             : 
    1117             :                         /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE! */
    1118             :                         /* process the inside side                              */
    1119             :                         /* error = ft_stroker_inside( stroker, inside_side );   */
    1120             :                         /* if ( error )                                         */
    1121             :                         /*   goto Exit;                                         */
    1122             : 
    1123             :                         /* process the outside side */
    1124       13916 :                         error = ft_stroker_outside( stroker, 1 - inside_side );
    1125       13916 :                         if ( error )
    1126             :                                 goto Exit;
    1127             :                 }
    1128             : 
    1129       13916 :                 ft_stroker_add_reverse_left(stroker, GF_FALSE);
    1130             :                 /* then end our two subpaths */
    1131       13916 :                 ft_stroke_border_close( stroker->borders + 0 );
    1132       13916 :                 ft_stroke_border_close( stroker->borders + 1 );
    1133             :         } else {
    1134             :                 /* All right, this is an opened path, we need to add a cap between */
    1135             :                 /* right & left, add the reverse of left, then add a final cap     */
    1136             :                 /* between left & right.                                           */
    1137       21419 :                 error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
    1138       21419 :                 if ( error ) goto Exit;
    1139             : 
    1140             :                 /* add reversed points from "left" to "right" */
    1141       21419 :                 error = ft_stroker_add_reverse_left( stroker, GF_TRUE );
    1142       21419 :                 if ( error ) goto Exit;
    1143             : 
    1144             :                 /* now add the final cap */
    1145       21419 :                 stroker->center = stroker->subpath_start;
    1146       21419 :                 error = ft_stroker_cap( stroker,
    1147       21419 :                                         stroker->subpath_angle + GF_PI, 0 );
    1148       21419 :                 if ( error )
    1149             :                         goto Exit;
    1150             : 
    1151             :                 /* Now end the right subpath accordingly.  The left one is */
    1152             :                 /* rewind and doesn't need further processing.             */
    1153       21419 :                 ft_stroke_border_close( right );
    1154             :         }
    1155             : 
    1156       35335 : Exit:
    1157       35335 :         return error;
    1158             : }
    1159             : 
    1160             : 
    1161       13818 : static s32 FT_Stroker_GetCounts( FT_Stroker *stroker, u32 *anum_points, u32 *anum_contours )
    1162             : {
    1163             :         u32 count1, count2, num_points   = 0;
    1164             :         u32 count3, count4, num_contours = 0;
    1165             :         s32 error;
    1166             : 
    1167       13818 :         error = ft_stroke_border_get_counts( stroker->borders + 0, &count1, &count2 );
    1168       13818 :         if ( error ) goto Exit;
    1169       13818 :         error = ft_stroke_border_get_counts( stroker->borders + 1, &count3, &count4 );
    1170       13818 :         if ( error ) goto Exit;
    1171       13818 :         num_points   = count1 + count3;
    1172       13818 :         num_contours = count2 + count4;
    1173             : 
    1174       13818 : Exit:
    1175       13818 :         *anum_points   = num_points;
    1176       13818 :         *anum_contours = num_contours;
    1177       13818 :         return error;
    1178             : }
    1179             : 
    1180             : /*
    1181             : *  The following is very similar to FT_Outline_Decompose, except
    1182             : *  that we do support opened paths, and do not scale the outline.
    1183             : */
    1184       13818 : static s32 FT_Stroker_ParseOutline(FT_Stroker *stroker, GF_Path*  outline)
    1185             : {
    1186             :         GF_Point2D   v_last;
    1187             : #if 0 //unused
    1188             :         GF_Point2D   v_control;
    1189             : #endif
    1190             :         GF_Point2D   v_start;
    1191             :         GF_Point2D*  point;
    1192             :         GF_Point2D*  limit;
    1193             :         u8 *tags;
    1194             :         s32 error;
    1195             :         u32 n;         /* index of contour in outline     */
    1196             :         u32 first;     /* index of first point in contour */
    1197             :         s32 tag;       /* current point's state           */
    1198             : 
    1199       13818 :         if ( !outline || !stroker )
    1200             :                 return -1;
    1201             : 
    1202             :         first = 0;
    1203             : 
    1204       35335 :         for ( n = 0; n < outline->n_contours; n++ ) {
    1205             :                 s32 closed_subpath;
    1206             :                 s32 last;  /* index of last point in contour */
    1207             : 
    1208       35335 :                 last  = outline->contours[n];
    1209       35335 :                 limit = outline->points + last;
    1210             : 
    1211       35335 :                 v_start = outline->points[first];
    1212       35335 :                 v_last  = outline->points[last];
    1213             : 
    1214             : #if 0 //unused
    1215             :                 v_control = v_start;
    1216             : #endif
    1217             : 
    1218             :                 point = outline->points + first;
    1219       35335 :                 tags  = outline->tags  + first;
    1220       35335 :                 tag = tags[0];
    1221             : 
    1222             :                 /* A contour cannot start with a cubic control point! */
    1223       35335 :                 if ( tag == GF_PATH_CURVE_CUBIC )
    1224             :                         goto Invalid_Outline;
    1225             : 
    1226             :                 /* check first point to determine origin */
    1227       35335 :                 if ( tag == GF_PATH_CURVE_CONIC ) {
    1228             :                         /* First point is conic control.  Yes, this happens. */
    1229           0 :                         if ( outline->tags[last] & GF_PATH_CURVE_ON ) {
    1230             :                                 /* start at last point if it is on the curve */
    1231           0 :                                 v_start = v_last;
    1232           0 :                                 limit--;
    1233             :                         } else {
    1234             :                                 /* if both first and last points are conic,         */
    1235             :                                 /* start at their middle and record its position    */
    1236             :                                 /* for closure                                      */
    1237           0 :                                 v_start.x = ( v_start.x + v_last.x ) / 2;
    1238           0 :                                 v_start.y = ( v_start.y + v_last.y ) / 2;
    1239             :                         }
    1240           0 :                         point--;
    1241           0 :                         tags--;
    1242             :                 }
    1243       35335 :                 closed_subpath = (outline->tags[outline->contours[n]]==GF_PATH_CLOSE) ? 1 : 0;
    1244             : 
    1245             :                 error = FT_Stroker_BeginSubPath(stroker, &v_start);
    1246             :                 if ( error )
    1247             :                         goto Exit;
    1248             : 
    1249             :                 /*subpath is a single point, force a lineTo to start for the stroker to compute lineCap*/
    1250       35335 :                 if (point==limit) {
    1251          14 :                         error = FT_Stroker_LineTo(stroker, &v_start, GF_TRUE);
    1252             :                         closed_subpath = 0;
    1253          14 :                         goto Close;
    1254             :                 }
    1255             : 
    1256      705266 :                 while ( point < limit ) {
    1257      669945 :                         point++;
    1258      669945 :                         tags++;
    1259             : 
    1260      669945 :                         tag = tags[0];
    1261      669945 :                         switch ( tag ) {
    1262      669945 :                         case GF_PATH_CURVE_ON:  /* emit a single line_to */
    1263             :                         case GF_PATH_CLOSE:  /* emit a single line_to */
    1264             :                         {
    1265             :                                 GF_Point2D  vec;
    1266      669945 :                                 vec.x = point->x;
    1267      669945 :                                 vec.y = point->y;
    1268             : 
    1269      669945 :                                 error = FT_Stroker_LineTo( stroker, &vec, (point == limit) ? GF_TRUE : GF_FALSE );
    1270      669945 :                                 if ( error )
    1271             :                                         goto Exit;
    1272      669945 :                                 continue;
    1273             :                         }
    1274             : 
    1275             : 
    1276             : #if 0 //unused
    1277             :                         case GF_PATH_CURVE_CONIC:  /* consume conic arcs */
    1278             :                                 v_control.x = point->x;
    1279             :                                 v_control.y = point->y;
    1280             : 
    1281             : Do_Conic:
    1282             :                                 if ( point < limit ) {
    1283             :                                         GF_Point2D  vec;
    1284             :                                         GF_Point2D  v_middle;
    1285             : 
    1286             : 
    1287             :                                         point++;
    1288             :                                         tags++;
    1289             :                                         tag = tags[0];
    1290             : 
    1291             :                                         vec = point[0];
    1292             : 
    1293             :                                         if ( tag & GF_PATH_CURVE_ON) {
    1294             : 
    1295             :                                                 error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
    1296             :                                                 if ( error )
    1297             :                                                         goto Exit;
    1298             :                                                 continue;
    1299             :                                         }
    1300             : 
    1301             :                                         if ( tag != GF_PATH_CURVE_CONIC )
    1302             :                                                 goto Invalid_Outline;
    1303             : 
    1304             :                                         v_middle.x = ( v_control.x + vec.x ) / 2;
    1305             :                                         v_middle.y = ( v_control.y + vec.y ) / 2;
    1306             : 
    1307             :                                         error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
    1308             :                                         if ( error )
    1309             :                                                 goto Exit;
    1310             : 
    1311             :                                         v_control = vec;
    1312             :                                         goto Do_Conic;
    1313             :                                 }
    1314             :                                 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
    1315             :                                 goto Close;
    1316             : 
    1317             :                         default:  /* GF_PATH_CURVE_CUBIC */
    1318             :                         {
    1319             :                                 GF_Point2D  vec1, vec2;
    1320             : 
    1321             :                                 if ( point + 1 > limit                             ||
    1322             :                                         tags[1] != GF_PATH_CURVE_CUBIC )
    1323             :                                         goto Invalid_Outline;
    1324             : 
    1325             :                                 point += 2;
    1326             :                                 tags  += 2;
    1327             : 
    1328             :                                 vec1 = point[-2];
    1329             :                                 vec2 = point[-1];
    1330             : 
    1331             :                                 if ( point <= limit ) {
    1332             :                                         GF_Point2D  vec;
    1333             :                                         vec = point[0];
    1334             : 
    1335             :                                         error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
    1336             :                                         if ( error )
    1337             :                                                 goto Exit;
    1338             :                                         continue;
    1339             :                                 }
    1340             :                                 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
    1341             :                                 goto Close;
    1342             :                         }
    1343             :                         break;
    1344             : #else
    1345           0 :                         default:  /* GF_PATH_CURVE_CUBIC */
    1346           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Path2DStroke] Path is not flatten, cannot strike cubic and quadratic !\n"));
    1347             : #endif
    1348             : 
    1349             :                         }
    1350             :                 }
    1351             : 
    1352       35321 : Close:
    1353       35335 :                 if ( error ) goto Exit;
    1354             : 
    1355       35335 :                 error = FT_Stroker_EndSubPath(stroker, closed_subpath);
    1356       35335 :                 if ( error )
    1357             :                         goto Exit;
    1358             : 
    1359       35335 :                 first = last + 1;
    1360             :         }
    1361             :         return 0;
    1362             : 
    1363           0 : Exit:
    1364             :         return error;
    1365             : 
    1366       13818 : Invalid_Outline:
    1367             :         return -1;
    1368             : }
    1369             : 
    1370             : 
    1371             : 
    1372             : 
    1373             : 
    1374             : 
    1375             : #define GF_PATH_DOT_LEN         1
    1376             : #define GF_PATH_DOT_SPACE       2
    1377             : #define GF_PATH_DASH_LEN        3
    1378             : 
    1379       38771 : static Fixed gf_path_get_dash(GF_PenSettings *pen, u32 dash_slot, u32 *next_slot)
    1380             : {
    1381             :         Fixed ret = 0;
    1382       38771 :         switch (pen->dash) {
    1383       37425 :         case GF_DASH_STYLE_DOT:
    1384       37425 :                 if (dash_slot==0) ret = GF_PATH_DOT_LEN;
    1385       18693 :                 else if (dash_slot==1) ret = GF_PATH_DOT_SPACE;
    1386       37425 :                 *next_slot = (dash_slot + 1) % 2;
    1387       37425 :                 return ret * pen->width;
    1388          18 :         case GF_DASH_STYLE_DASH:
    1389          18 :                 if (dash_slot==0) ret = GF_PATH_DASH_LEN;
    1390           9 :                 else if (dash_slot==1) ret = GF_PATH_DOT_SPACE;
    1391          18 :                 *next_slot = (dash_slot + 1) % 2;
    1392          18 :                 return ret * pen->width;
    1393          15 :         case GF_DASH_STYLE_DASH_DOT:
    1394          15 :                 if (dash_slot==0) ret = GF_PATH_DASH_LEN;
    1395           9 :                 else if (dash_slot==1) ret = GF_PATH_DOT_SPACE;
    1396           6 :                 else if (dash_slot==2) ret = GF_PATH_DOT_LEN;
    1397           3 :                 else if (dash_slot==3) ret = GF_PATH_DOT_SPACE;
    1398          15 :                 *next_slot = (dash_slot + 1) % 4;
    1399          15 :                 return ret * pen->width;
    1400         217 :         case GF_DASH_STYLE_DASH_DASH_DOT:
    1401         217 :                 if (dash_slot==0) ret = GF_PATH_DASH_LEN;
    1402         180 :                 else if (dash_slot==1) ret = GF_PATH_DOT_SPACE;
    1403         143 :                 else if (dash_slot==2) ret = GF_PATH_DASH_LEN;
    1404         106 :                 else if (dash_slot==3) ret = GF_PATH_DOT_SPACE;
    1405          69 :                 else if (dash_slot==4) ret = GF_PATH_DOT_LEN;
    1406          34 :                 else if (dash_slot==5) ret = GF_PATH_DOT_SPACE;
    1407         217 :                 *next_slot = (dash_slot + 1) % 6;
    1408         217 :                 return ret * pen->width;
    1409           0 :         case GF_DASH_STYLE_DASH_DOT_DOT:
    1410           0 :                 if (dash_slot==0) ret = GF_PATH_DASH_LEN;
    1411           0 :                 else if (dash_slot==1) ret = GF_PATH_DOT_SPACE;
    1412           0 :                 else if (dash_slot==2) ret = GF_PATH_DOT_LEN;
    1413           0 :                 else if (dash_slot==3) ret = GF_PATH_DOT_SPACE;
    1414           0 :                 else if (dash_slot==4) ret = GF_PATH_DOT_LEN;
    1415           0 :                 else if (dash_slot==5) ret = GF_PATH_DOT_SPACE;
    1416           0 :                 *next_slot = (dash_slot + 1) % 6;
    1417           0 :                 return ret * pen->width;
    1418        1096 :         case GF_DASH_STYLE_CUSTOM:
    1419             :         case GF_DASH_STYLE_SVG:
    1420        1096 :                 if (!pen->dash_set || !pen->dash_set->num_dash) return 0;
    1421        1096 :                 if (dash_slot>=pen->dash_set->num_dash) dash_slot = 0;
    1422        1096 :                 ret = pen->dash_set->dashes[dash_slot];
    1423        1096 :                 *next_slot = (1 + dash_slot) % pen->dash_set->num_dash;
    1424        1096 :                 if (pen->dash==GF_DASH_STYLE_SVG) return ret;
    1425             :                 /*custom dashes are of type Fixed !!*/
    1426        1096 :                 return gf_mulfix(ret, pen->width);
    1427             : 
    1428           0 :         default:
    1429             :         case GF_DASH_STYLE_PLAIN:
    1430           0 :                 *next_slot = 0;
    1431           0 :                 return 0;
    1432             :         }
    1433             : }
    1434             : 
    1435             : 
    1436             : /* Credits go to Raph Levien for libart / art_vpath_dash */
    1437             : 
    1438             : /* FIXEME - NOT DONE - Merge first and last subpaths when first and last dash segment are joined a closepath. */
    1439          37 : static GF_Err gf_path_mergedashes(GF_Path *gp, u32 start_contour_index)
    1440             : {
    1441             :         u32 i, dash_first_pt, dash_nb_pts;
    1442          37 :         if (start_contour_index) {
    1443           1 :                 dash_nb_pts = gp->contours[start_contour_index] - gp->contours[start_contour_index-1];
    1444           1 :                 dash_first_pt = gp->contours[start_contour_index-1]+1;
    1445             :         } else {
    1446          36 :                 dash_nb_pts = gp->contours[start_contour_index]+1;
    1447             :                 dash_first_pt = 0;
    1448             :         }
    1449             :         /*skip first point of first dash in subpath (same as last point of last dash)*/
    1450          79 :         for (i=1; i<dash_nb_pts; i++) {
    1451          42 :                 GF_Err e = gf_path_add_line_to_vec(gp, &gp->points[dash_first_pt + i]);
    1452          42 :                 if (e) return e;
    1453             :         }
    1454             :         /*remove initial dash*/
    1455          37 :         gp->n_points -= dash_nb_pts;
    1456          37 :         memmove(gp->points + dash_first_pt, gp->points + dash_first_pt + dash_nb_pts, sizeof(GF_Point2D)*(gp->n_points - dash_first_pt));
    1457          37 :         memmove(gp->tags + dash_first_pt, gp->tags + dash_first_pt + dash_nb_pts, sizeof(u8)*(gp->n_points - dash_first_pt));
    1458             : 
    1459       14210 :         for (i=start_contour_index; i<gp->n_contours-1; i++) {
    1460       14136 :                 gp->contours[i] = gp->contours[i+1] - dash_nb_pts;
    1461             :         }
    1462          37 :         gp->n_contours--;
    1463          37 :         gp->contours = (u32 *)gf_realloc(gp->contours, sizeof(u32)*gp->n_contours);
    1464             : 
    1465             :         /*
    1466             :                 gp->points = gf_realloc(gp->points, sizeof(GF_Point2D)*gp->n_points);
    1467             :                 gp->tags = gf_realloc(gp->tags, sizeof(u8)*gp->n_points);
    1468             :                 gp->n_alloc_points = gp->n_points;
    1469             :         */
    1470          37 :         return GF_OK;
    1471             : }
    1472             : 
    1473         691 : static GF_Err evg_dash_subpath(GF_Path *dashed, GF_Point2D *pts, u32 nb_pts, GF_PenSettings *pen, Fixed length_scale)
    1474             : {
    1475             :         Fixed *dists;
    1476             :         Fixed totaldist;
    1477             :         Fixed dash;
    1478             :         Fixed dist;
    1479             :         Fixed dash_dist;
    1480             :         s32 offsetinit;
    1481         691 :         u32 next_offset=0;
    1482             :         s32 toggleinit;
    1483             :         s32 firstindex;
    1484             :         Bool toggle_check;
    1485             :         GF_Err e;
    1486             :         u32 i, start_ind;
    1487             :         Fixed phase;
    1488             :         s32 offset, toggle;
    1489             : 
    1490         691 :         dists = (Fixed *)gf_malloc(sizeof (Fixed) * nb_pts);
    1491         691 :         if (dists == NULL) return GF_OUT_OF_MEM;
    1492             : 
    1493             :         /* initial values */
    1494             :         toggleinit = 1;
    1495             :         offsetinit = 0;
    1496             :         dash_dist = 0;
    1497             : 
    1498         691 :         dash = gf_path_get_dash(pen, offsetinit, &next_offset);
    1499         691 :         if (length_scale) dash = gf_mulfix(dash, length_scale);
    1500             :         firstindex = -1;
    1501             :         toggle_check = GF_FALSE;
    1502             : 
    1503             :         start_ind = 0;
    1504             :         dist = 0;
    1505             : 
    1506             :         /*SVG dashing is different from BIFS one*/
    1507         691 :         if (pen->dash==GF_DASH_STYLE_SVG) {
    1508           0 :                 while (pen->dash_offset>0) {
    1509           0 :                         if (pen->dash_offset - dash < 0) {
    1510           0 :                                 dash -= pen->dash_offset;
    1511           0 :                                 pen->dash_offset = 0;
    1512           0 :                                 break;
    1513             :                         }
    1514           0 :                         pen->dash_offset -= dash;
    1515           0 :                         offsetinit = next_offset;
    1516           0 :                         toggleinit = !toggleinit;
    1517           0 :                         dash = gf_path_get_dash(pen, offsetinit, &next_offset);
    1518           0 :                         if (length_scale) dash = gf_mulfix(dash, length_scale);
    1519             :                 }
    1520           0 :                 if ((pen->dash_offset<0) && pen->dash_set && pen->dash_set->num_dash) {
    1521           0 :                         offsetinit = pen->dash_set->num_dash-1;
    1522           0 :                         dash = gf_path_get_dash(pen, offsetinit, &next_offset);
    1523           0 :                         if (length_scale) dash = gf_mulfix(dash, length_scale);
    1524           0 :                         while (pen->dash_offset<0) {
    1525           0 :                                 toggleinit = !toggleinit;
    1526           0 :                                 if (pen->dash_offset + dash > 0) {
    1527             :                                         dash_dist = dash+pen->dash_offset;
    1528           0 :                                         pen->dash_offset = 0;
    1529           0 :                                         break;
    1530             :                                 }
    1531           0 :                                 pen->dash_offset += dash;
    1532           0 :                                 if (offsetinit) offsetinit --;
    1533           0 :                                 else offsetinit = pen->dash_set->num_dash-1;
    1534           0 :                                 dash = gf_path_get_dash(pen, offsetinit, &next_offset);
    1535           0 :                                 if (length_scale) dash = gf_mulfix(dash, length_scale);
    1536             :                         }
    1537             :                 }
    1538             :         }
    1539             : 
    1540             :         /* calculate line lengths and update offset*/
    1541             :         totaldist = 0;
    1542      121124 :         for (i = 0; i < nb_pts - 1; i++) {
    1543             :                 GF_Point2D diff;
    1544      119742 :                 diff.x = pts[i+1].x - pts[i].x;
    1545      119742 :                 diff.y = pts[i+1].y - pts[i].y;
    1546      119742 :                 dists[i] = gf_v2d_len(&diff);
    1547             : 
    1548      119742 :                 if (pen->dash_offset > dists[i]) {
    1549       31514 :                         pen->dash_offset -= dists[i];
    1550       31514 :                         dists[i] = 0;
    1551             :                 }
    1552       88228 :                 else if (pen->dash_offset) {
    1553             :                         Fixed a, x, y, dx, dy;
    1554             : 
    1555         296 :                         a = gf_divfix(pen->dash_offset, dists[i]);
    1556         296 :                         dx = pts[i + 1].x - pts[i].x;
    1557         296 :                         dy = pts[i + 1].y - pts[i].y;
    1558         296 :                         x = pts[i].x + gf_mulfix(a, dx);
    1559         296 :                         y = pts[i].y + gf_mulfix(a, dy);
    1560         296 :                         e = gf_path_add_move_to(dashed, x, y);
    1561         296 :                         if (e) goto err_exit;
    1562         296 :                         totaldist += dists[i];
    1563         296 :                         dist = pen->dash_offset;
    1564         296 :                         pen->dash_offset = 0;
    1565             :                         start_ind = i;
    1566             :                 } else {
    1567       87932 :                         totaldist += dists[i];
    1568             :                 }
    1569             :         }
    1570         691 :         dash -= dash_dist;
    1571             : 
    1572             :         /* subpath fits within first dash and no offset*/
    1573         691 :         if (!dist && totaldist <= dash) {
    1574          28 :                 if (toggleinit) {
    1575          28 :                         gf_path_add_move_to_vec(dashed, &pts[0]);
    1576        5628 :                         for (i=1; i<nb_pts; i++) {
    1577        5600 :                                 gf_path_add_line_to_vec(dashed, &pts[i]);
    1578             :                         }
    1579             :                 }
    1580          28 :                 gf_free(dists);
    1581          28 :                 return GF_OK;
    1582             :         }
    1583             : 
    1584             :         /* subpath is composed of at least one dash */
    1585             :         phase = 0;
    1586             :         toggle = toggleinit;
    1587             :         i = start_ind;
    1588             : 
    1589         663 :         if (toggle && !dist) {
    1590         367 :                 e = gf_path_add_move_to_vec(dashed, &pts[i]);
    1591         367 :                 if (e) goto err_exit;
    1592         367 :                 firstindex = dashed->n_contours - 1;
    1593             :         }
    1594             : 
    1595      121371 :         while (i < nb_pts - 1) {
    1596             :                 /* dash boundary is next */
    1597      120708 :                 if (dists[i] - dist > dash - phase) {
    1598             :                         Fixed a, x, y, dx, dy;
    1599       38080 :                         dist += dash - phase;
    1600       38080 :                         a = gf_divfix(dist, dists[i]);
    1601       38080 :                         dx = pts[i + 1].x - pts[i].x;
    1602       38080 :                         dy = pts[i + 1].y - pts[i].y;
    1603       38080 :                         x = pts[i].x + gf_mulfix(a, dx);
    1604       38080 :                         y = pts[i].y + gf_mulfix(a, dy);
    1605             : 
    1606       38080 :                         if (!toggle_check || ((x != pts[i].x) || (y != pts[i].y))) {
    1607       38080 :                                 if (toggle) {
    1608       19316 :                                         e = gf_path_add_line_to(dashed, x, y);
    1609       19316 :                                         if (e) goto err_exit;
    1610             :                                 }
    1611             :                                 else {
    1612       18764 :                                         e = gf_path_add_move_to(dashed, x, y);
    1613       18764 :                                         if (e) goto err_exit;
    1614             :                                 }
    1615             :                         }
    1616             : 
    1617             :                         /* advance to next dash */
    1618       38080 :                         toggle = !toggle;
    1619             :                         phase = 0;
    1620       38080 :                         offset = next_offset;
    1621       38080 :                         dash = gf_path_get_dash(pen, offset, &next_offset);
    1622       38080 :                         if (length_scale) dash = gf_mulfix(dash, length_scale);
    1623             :                 }
    1624             :                 /* end of line in subpath is next */
    1625             :                 else {
    1626       82628 :                         phase += dists[i] - dist;
    1627       82628 :                         i ++;
    1628             :                         toggle_check = GF_FALSE;
    1629             :                         dist = 0;
    1630       82628 :                         if (toggle) {
    1631       39704 :                                 e = gf_path_add_line_to_vec(dashed, &pts[i]);
    1632       39704 :                                 if (e) goto err_exit;
    1633             :                                 toggle_check = GF_TRUE;
    1634             : 
    1635       39704 :                                 if ( (firstindex>=0) && (i == (nb_pts - 1) && ((firstindex + 1) != (s32) start_ind ) ))  {
    1636             :                                         /*merge if closed path*/
    1637          43 :                                         if ((pts[0].x==pts[nb_pts-1].x) && (pts[0].y==pts[nb_pts-1].y)) {
    1638          37 :                                                 e = gf_path_mergedashes(dashed, firstindex);
    1639          37 :                                                 if (e) goto err_exit;
    1640             :                                         }
    1641             :                                 }
    1642             :                         }
    1643             :                 }
    1644             :         }
    1645             : 
    1646         663 : err_exit:
    1647             : //      pen->dash_offset = dist;
    1648         663 :         gf_free(dists);
    1649         663 :         return GF_OK;
    1650             : }
    1651             : 
    1652         690 : static GF_Path *gf_path_dash(GF_Path *path, GF_PenSettings *pen)
    1653             : {
    1654             :         u32 i, j, nb_pts;
    1655             :         GF_Point2D *pts;
    1656             :         Fixed length_scale = 0;
    1657         690 :         Fixed dash_off = pen->dash_offset;
    1658         690 :         GF_Path *dashed = gf_path_new();
    1659             : 
    1660             : 
    1661             :         /* calculate line lengths and update offset*/
    1662         690 :         if (pen->path_length) {
    1663             :                 Fixed totaldist = 0;
    1664             :                 nb_pts = 0;
    1665           0 :                 for (i=0; i<path->n_contours; i++) {
    1666           0 :                         pts = &path->points[nb_pts];
    1667           0 :                         nb_pts = 1+path->contours[i] - nb_pts;
    1668             : 
    1669           0 :                         for (j=0; j<nb_pts-1; j++) {
    1670             :                                 GF_Point2D diff;
    1671           0 :                                 diff.x = pts[j+1].x - pts[j].x;
    1672           0 :                                 diff.y = pts[j+1].y - pts[j].y;
    1673           0 :                                 totaldist += gf_v2d_len(&diff);
    1674             :                         }
    1675           0 :                         nb_pts = 1+path->contours[i];
    1676             :                 }
    1677           0 :                 length_scale = gf_divfix(totaldist, pen->path_length);
    1678           0 :                 pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale);
    1679             :         }
    1680             : 
    1681             :         nb_pts = 0;
    1682        1381 :         for (i=0; i<path->n_contours; i++) {
    1683         691 :                 pts = &path->points[nb_pts];
    1684         691 :                 nb_pts = 1+path->contours[i] - nb_pts;
    1685         691 :                 evg_dash_subpath(dashed, pts, nb_pts, pen, length_scale);
    1686         691 :                 nb_pts = 1+path->contours[i];
    1687             : //              if (length_scale) pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale);
    1688             :         }
    1689         690 :         pen->dash_offset = dash_off;
    1690         690 :         dashed->flags |= GF_PATH_FILL_ZERO_NONZERO;
    1691         690 :         return dashed;
    1692             : }
    1693             : 
    1694             : GF_EXPORT
    1695       13818 : GF_Path *gf_path_get_outline(GF_Path *path, GF_PenSettings pen)
    1696             : {
    1697             :         s32 error;
    1698             :         GF_Path *outline;
    1699             :         GF_Path *dashed;
    1700             :         GF_Path *scaled;
    1701             :         FT_Stroker stroker;
    1702       13818 :         if (!path || !pen.width) return NULL;
    1703             : 
    1704             :         memset(&stroker, 0, sizeof(stroker));
    1705       13818 :         stroker.borders[0].start = -1;
    1706       13818 :         stroker.borders[1].start = -1;
    1707       13818 :         stroker.line_cap = pen.cap;
    1708       13818 :         stroker.line_join = pen.join;
    1709       13818 :         stroker.miter_limit = pen.miterLimit;
    1710       13818 :         stroker.radius = pen.width/2;
    1711             : 
    1712       13818 :         gf_path_flatten(path);
    1713             : 
    1714             :         scaled = NULL;
    1715             :         /*if not centered, simply scale path...*/
    1716       13818 :         if (pen.align) {
    1717             :                 Fixed sx, sy;
    1718             :                 GF_Rect bounds;
    1719        1852 :                 gf_path_get_bounds(path, &bounds);
    1720        1852 :                 if (pen.align==GF_PATH_LINE_OUTSIDE) {
    1721           2 :                         sx = gf_divfix(bounds.width+pen.width, bounds.width);
    1722           2 :                         sy = gf_divfix(bounds.height+pen.width, bounds.height);
    1723             :                 } else {
    1724             :                         /*note: this may result in negative scaling, not our pb but the author's one*/
    1725        1850 :                         sx = gf_divfix(bounds.width-pen.width, bounds.width);
    1726        1850 :                         sy = gf_divfix(bounds.height-pen.width, bounds.height);
    1727             :                 }
    1728        1852 :                 if (sx && sy) {
    1729             :                         u32 i;
    1730        1852 :                         scaled = gf_path_clone(path);
    1731       34697 :                         for (i=0; i<scaled->n_points; i++) {
    1732       32845 :                                 scaled->points[i].x = gf_mulfix(scaled->points[i].x, sx);
    1733       32845 :                                 scaled->points[i].y = gf_mulfix(scaled->points[i].y, sy);
    1734             :                         }
    1735             :                         path = scaled;
    1736             :                 }
    1737             :         }
    1738             : 
    1739             :         /*if dashing, first flatten path then dash all segments*/
    1740             :         dashed = NULL;
    1741             :         /*security, seen in some SVG files*/
    1742       13818 :         if (pen.dash_set && (pen.dash_set->num_dash==1) && (pen.dash_set->dashes[0]==0)) pen.dash = GF_DASH_STYLE_PLAIN;
    1743       13818 :         if (pen.dash) {
    1744             :                 GF_Path *flat;
    1745         690 :                 flat = gf_path_get_flatten(path);
    1746         690 :                 if (!flat) return NULL;
    1747         690 :                 dashed = gf_path_dash(flat, &pen);
    1748         690 :                 gf_path_del(flat);
    1749         690 :                 if (!dashed) return NULL;
    1750             :                 path = dashed;
    1751             :         }
    1752             : 
    1753             :         outline = NULL;
    1754       13818 :         error = FT_Stroker_ParseOutline(&stroker, path);
    1755       13818 :         if (!error) {
    1756             :                 u32 nb_pt, nb_cnt;
    1757       13818 :                 error = FT_Stroker_GetCounts(&stroker, &nb_pt, &nb_cnt);
    1758       13818 :                 if (!error) {
    1759       13818 :                         outline = gf_path_new();
    1760       13818 :                         if (nb_pt) {
    1761             :                                 FT_StrokeBorder sborder;
    1762       13818 :                                 outline->points = (GF_Point2D *) gf_malloc(sizeof(GF_Point2D)*nb_pt);
    1763       13818 :                                 outline->tags = (u8 *) gf_malloc(sizeof(u8)*nb_pt);
    1764       13818 :                                 outline->contours = (u32 *) gf_malloc(sizeof(u32)*nb_cnt);
    1765       13818 :                                 outline->n_alloc_points = nb_pt;
    1766             :                                 sborder = &stroker.borders[0];
    1767       13818 :                                 if (sborder->valid ) ft_stroke_border_export(sborder, outline);
    1768             :                                 sborder = &stroker.borders[1];
    1769             :                                 /*if left border is valid this is a closed path, used odd/even rule - we will have issues at recovering
    1770             :                                 segments...*/
    1771       13818 :                                 if (sborder->valid && sborder->num_points) {
    1772           0 :                                         ft_stroke_border_export(sborder, outline);
    1773             :                                 }
    1774             :                                 /*otherwise this is an open path, use zero/non-zero*/
    1775             :                                 else {
    1776       13818 :                                         outline->flags |= GF_PATH_FILL_ZERO_NONZERO;
    1777             :                                 }
    1778             :                         }
    1779       13818 :                         outline->flags |= GF_PATH_BBOX_DIRTY;
    1780             : 
    1781             :                         /*our caps are cubic bezier!!*/
    1782       13818 :                         if ( (path->flags & GF_PATH_FLATTENED) && (pen.cap!=GF_LINE_CAP_ROUND) && (pen.join!=GF_LINE_JOIN_ROUND) )
    1783       13124 :                                 outline->flags |= GF_PATH_FLATTENED;
    1784             :                 }
    1785             :         }
    1786             : 
    1787       13818 :         if (stroker.borders[0].points) gf_free(stroker.borders[0].points);
    1788       13818 :         if (stroker.borders[0].tags) gf_free(stroker.borders[0].tags);
    1789       13818 :         if (stroker.borders[1].points) gf_free(stroker.borders[1].points);
    1790       13818 :         if (stroker.borders[1].tags) gf_free(stroker.borders[1].tags);
    1791             : 
    1792       13818 :         if (dashed) gf_path_del(dashed);
    1793       13818 :         if (scaled) gf_path_del(scaled);
    1794             : 
    1795             :         return outline;
    1796             : }
    1797             : 

Generated by: LCOV version 1.13