Home | History | Annotate | Download | only in base
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftstroke.c                                                             */
      4 /*                                                                         */
      5 /*    FreeType path stroker (body).                                        */
      6 /*                                                                         */
      7 /*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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 <ft2build.h>
     20 #include FT_STROKER_H
     21 #include FT_TRIGONOMETRY_H
     22 #include FT_OUTLINE_H
     23 #include FT_INTERNAL_MEMORY_H
     24 #include FT_INTERNAL_DEBUG_H
     25 #include FT_INTERNAL_OBJECTS_H
     26 
     27 
     28   /* documentation is in ftstroke.h */
     29 
     30   FT_EXPORT_DEF( FT_StrokerBorder )
     31   FT_Outline_GetInsideBorder( FT_Outline*  outline )
     32   {
     33     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
     34 
     35 
     36     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
     37                                         : FT_STROKER_BORDER_LEFT ;
     38   }
     39 
     40 
     41   /* documentation is in ftstroke.h */
     42 
     43   FT_EXPORT_DEF( FT_StrokerBorder )
     44   FT_Outline_GetOutsideBorder( FT_Outline*  outline )
     45   {
     46     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
     47 
     48 
     49     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
     50                                         : FT_STROKER_BORDER_RIGHT ;
     51   }
     52 
     53 
     54  /***************************************************************************/
     55  /***************************************************************************/
     56  /*****                                                                 *****/
     57  /*****                       BEZIER COMPUTATIONS                       *****/
     58  /*****                                                                 *****/
     59  /***************************************************************************/
     60  /***************************************************************************/
     61 
     62 #define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
     63 #define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
     64 #define FT_EPSILON  2
     65 
     66 #define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
     67 
     68 
     69   static FT_Pos
     70   ft_pos_abs( FT_Pos  x )
     71   {
     72     return x >= 0 ? x : -x ;
     73   }
     74 
     75 
     76   static void
     77   ft_conic_split( FT_Vector*  base )
     78   {
     79     FT_Pos  a, b;
     80 
     81 
     82     base[4].x = base[2].x;
     83     b = base[1].x;
     84     a = base[3].x = ( base[2].x + b ) / 2;
     85     b = base[1].x = ( base[0].x + b ) / 2;
     86     base[2].x = ( a + b ) / 2;
     87 
     88     base[4].y = base[2].y;
     89     b = base[1].y;
     90     a = base[3].y = ( base[2].y + b ) / 2;
     91     b = base[1].y = ( base[0].y + b ) / 2;
     92     base[2].y = ( a + b ) / 2;
     93   }
     94 
     95 
     96   static FT_Bool
     97   ft_conic_is_small_enough( FT_Vector*  base,
     98                             FT_Angle   *angle_in,
     99                             FT_Angle   *angle_out )
    100   {
    101     FT_Vector  d1, d2;
    102     FT_Angle   theta;
    103     FT_Int     close1, close2;
    104 
    105 
    106     d1.x = base[1].x - base[2].x;
    107     d1.y = base[1].y - base[2].y;
    108     d2.x = base[0].x - base[1].x;
    109     d2.y = base[0].y - base[1].y;
    110 
    111     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
    112     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
    113 
    114     if ( close1 )
    115     {
    116       if ( close2 )
    117         *angle_in = *angle_out = 0;
    118       else
    119         *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
    120     }
    121     else if ( close2 )
    122     {
    123       *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
    124     }
    125     else
    126     {
    127       *angle_in  = FT_Atan2( d1.x, d1.y );
    128       *angle_out = FT_Atan2( d2.x, d2.y );
    129     }
    130 
    131     theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
    132 
    133     return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
    134   }
    135 
    136 
    137   static void
    138   ft_cubic_split( FT_Vector*  base )
    139   {
    140     FT_Pos  a, b, c, d;
    141 
    142 
    143     base[6].x = base[3].x;
    144     c = base[1].x;
    145     d = base[2].x;
    146     base[1].x = a = ( base[0].x + c ) / 2;
    147     base[5].x = b = ( base[3].x + d ) / 2;
    148     c = ( c + d ) / 2;
    149     base[2].x = a = ( a + c ) / 2;
    150     base[4].x = b = ( b + c ) / 2;
    151     base[3].x = ( a + b ) / 2;
    152 
    153     base[6].y = base[3].y;
    154     c = base[1].y;
    155     d = base[2].y;
    156     base[1].y = a = ( base[0].y + c ) / 2;
    157     base[5].y = b = ( base[3].y + d ) / 2;
    158     c = ( c + d ) / 2;
    159     base[2].y = a = ( a + c ) / 2;
    160     base[4].y = b = ( b + c ) / 2;
    161     base[3].y = ( a + b ) / 2;
    162   }
    163 
    164 
    165   static FT_Bool
    166   ft_cubic_is_small_enough( FT_Vector*  base,
    167                             FT_Angle   *angle_in,
    168                             FT_Angle   *angle_mid,
    169                             FT_Angle   *angle_out )
    170   {
    171     FT_Vector  d1, d2, d3;
    172     FT_Angle   theta1, theta2;
    173     FT_Int     close1, close2, close3;
    174 
    175 
    176     d1.x = base[2].x - base[3].x;
    177     d1.y = base[2].y - base[3].y;
    178     d2.x = base[1].x - base[2].x;
    179     d2.y = base[1].y - base[2].y;
    180     d3.x = base[0].x - base[1].x;
    181     d3.y = base[0].y - base[1].y;
    182 
    183     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
    184     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
    185     close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
    186 
    187     if ( close1 || close3 )
    188     {
    189       if ( close2 )
    190       {
    191         /* basically a point */
    192         *angle_in = *angle_out = *angle_mid = 0;
    193       }
    194       else if ( close1 )
    195       {
    196         *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
    197         *angle_out = FT_Atan2( d3.x, d3.y );
    198       }
    199       else  /* close2 */
    200       {
    201         *angle_in  = FT_Atan2( d1.x, d1.y );
    202         *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
    203       }
    204     }
    205     else if ( close2 )
    206     {
    207       *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
    208       *angle_out = FT_Atan2( d3.x, d3.y );
    209     }
    210     else
    211     {
    212       *angle_in  = FT_Atan2( d1.x, d1.y );
    213       *angle_mid = FT_Atan2( d2.x, d2.y );
    214       *angle_out = FT_Atan2( d3.x, d3.y );
    215     }
    216 
    217     theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
    218     theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
    219 
    220     return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
    221                     theta2 < FT_SMALL_CUBIC_THRESHOLD );
    222   }
    223 
    224 
    225  /***************************************************************************/
    226  /***************************************************************************/
    227  /*****                                                                 *****/
    228  /*****                       STROKE BORDERS                            *****/
    229  /*****                                                                 *****/
    230  /***************************************************************************/
    231  /***************************************************************************/
    232 
    233   typedef enum  FT_StrokeTags_
    234   {
    235     FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
    236     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
    237     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
    238     FT_STROKE_TAG_END   = 8    /* sub-path end    */
    239 
    240   } FT_StrokeTags;
    241 
    242 #define  FT_STROKE_TAG_BEGIN_END  (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
    243 
    244   typedef struct  FT_StrokeBorderRec_
    245   {
    246     FT_UInt     num_points;
    247     FT_UInt     max_points;
    248     FT_Vector*  points;
    249     FT_Byte*    tags;
    250     FT_Bool     movable;
    251     FT_Int      start;    /* index of current sub-path start point */
    252     FT_Memory   memory;
    253     FT_Bool     valid;
    254 
    255   } FT_StrokeBorderRec, *FT_StrokeBorder;
    256 
    257 
    258   static FT_Error
    259   ft_stroke_border_grow( FT_StrokeBorder  border,
    260                          FT_UInt          new_points )
    261   {
    262     FT_UInt   old_max = border->max_points;
    263     FT_UInt   new_max = border->num_points + new_points;
    264     FT_Error  error   = FT_Err_Ok;
    265 
    266 
    267     if ( new_max > old_max )
    268     {
    269       FT_UInt    cur_max = old_max;
    270       FT_Memory  memory  = border->memory;
    271 
    272 
    273       while ( cur_max < new_max )
    274         cur_max += ( cur_max >> 1 ) + 16;
    275 
    276       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
    277            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
    278         goto Exit;
    279 
    280       border->max_points = cur_max;
    281     }
    282 
    283   Exit:
    284     return error;
    285   }
    286 
    287 
    288   static void
    289   ft_stroke_border_close( FT_StrokeBorder  border,
    290                           FT_Bool          reverse )
    291   {
    292     FT_UInt  start = border->start;
    293     FT_UInt  count = border->num_points;
    294 
    295 
    296     FT_ASSERT( border->start >= 0 );
    297 
    298     /* don't record empty paths! */
    299     if ( count <= start + 1U )
    300       border->num_points = start;
    301     else
    302     {
    303       /* copy the last point to the start of this sub-path, since */
    304       /* it contains the `adjusted' starting coordinates          */
    305       border->num_points    = --count;
    306       border->points[start] = border->points[count];
    307 
    308       if ( reverse )
    309       {
    310         /* reverse the points */
    311         {
    312           FT_Vector*  vec1 = border->points + start + 1;
    313           FT_Vector*  vec2 = border->points + count - 1;
    314 
    315 
    316           for ( ; vec1 < vec2; vec1++, vec2-- )
    317           {
    318             FT_Vector  tmp;
    319 
    320 
    321             tmp   = *vec1;
    322             *vec1 = *vec2;
    323             *vec2 = tmp;
    324           }
    325         }
    326 
    327         /* then the tags */
    328         {
    329           FT_Byte*  tag1 = border->tags + start + 1;
    330           FT_Byte*  tag2 = border->tags + count - 1;
    331 
    332 
    333           for ( ; tag1 < tag2; tag1++, tag2-- )
    334           {
    335             FT_Byte  tmp;
    336 
    337 
    338             tmp   = *tag1;
    339             *tag1 = *tag2;
    340             *tag2 = tmp;
    341           }
    342         }
    343       }
    344 
    345       border->tags[start    ] |= FT_STROKE_TAG_BEGIN;
    346       border->tags[count - 1] |= FT_STROKE_TAG_END;
    347     }
    348 
    349     border->start   = -1;
    350     border->movable = FALSE;
    351   }
    352 
    353 
    354   static FT_Error
    355   ft_stroke_border_lineto( FT_StrokeBorder  border,
    356                            FT_Vector*       to,
    357                            FT_Bool          movable )
    358   {
    359     FT_Error  error = FT_Err_Ok;
    360 
    361 
    362     FT_ASSERT( border->start >= 0 );
    363 
    364     if ( border->movable )
    365     {
    366       /* move last point */
    367       border->points[border->num_points - 1] = *to;
    368     }
    369     else
    370     {
    371       /* add one point */
    372       error = ft_stroke_border_grow( border, 1 );
    373       if ( !error )
    374       {
    375         FT_Vector*  vec = border->points + border->num_points;
    376         FT_Byte*    tag = border->tags   + border->num_points;
    377 
    378 
    379         vec[0] = *to;
    380         tag[0] = FT_STROKE_TAG_ON;
    381 
    382         border->num_points += 1;
    383       }
    384     }
    385     border->movable = movable;
    386     return error;
    387   }
    388 
    389 
    390   static FT_Error
    391   ft_stroke_border_conicto( FT_StrokeBorder  border,
    392                             FT_Vector*       control,
    393                             FT_Vector*       to )
    394   {
    395     FT_Error  error;
    396 
    397 
    398     FT_ASSERT( border->start >= 0 );
    399 
    400     error = ft_stroke_border_grow( border, 2 );
    401     if ( !error )
    402     {
    403       FT_Vector*  vec = border->points + border->num_points;
    404       FT_Byte*    tag = border->tags   + border->num_points;
    405 
    406       vec[0] = *control;
    407       vec[1] = *to;
    408 
    409       tag[0] = 0;
    410       tag[1] = FT_STROKE_TAG_ON;
    411 
    412       border->num_points += 2;
    413     }
    414     border->movable = FALSE;
    415     return error;
    416   }
    417 
    418 
    419   static FT_Error
    420   ft_stroke_border_cubicto( FT_StrokeBorder  border,
    421                             FT_Vector*       control1,
    422                             FT_Vector*       control2,
    423                             FT_Vector*       to )
    424   {
    425     FT_Error  error;
    426 
    427 
    428     FT_ASSERT( border->start >= 0 );
    429 
    430     error = ft_stroke_border_grow( border, 3 );
    431     if ( !error )
    432     {
    433       FT_Vector*  vec = border->points + border->num_points;
    434       FT_Byte*    tag = border->tags   + border->num_points;
    435 
    436 
    437       vec[0] = *control1;
    438       vec[1] = *control2;
    439       vec[2] = *to;
    440 
    441       tag[0] = FT_STROKE_TAG_CUBIC;
    442       tag[1] = FT_STROKE_TAG_CUBIC;
    443       tag[2] = FT_STROKE_TAG_ON;
    444 
    445       border->num_points += 3;
    446     }
    447     border->movable = FALSE;
    448     return error;
    449   }
    450 
    451 
    452 #define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
    453 
    454 
    455   static FT_Error
    456   ft_stroke_border_arcto( FT_StrokeBorder  border,
    457                           FT_Vector*       center,
    458                           FT_Fixed         radius,
    459                           FT_Angle         angle_start,
    460                           FT_Angle         angle_diff )
    461   {
    462     FT_Angle   total, angle, step, rotate, next, theta;
    463     FT_Vector  a, b, a2, b2;
    464     FT_Fixed   length;
    465     FT_Error   error = FT_Err_Ok;
    466 
    467 
    468     /* compute start point */
    469     FT_Vector_From_Polar( &a, radius, angle_start );
    470     a.x += center->x;
    471     a.y += center->y;
    472 
    473     total  = angle_diff;
    474     angle  = angle_start;
    475     rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
    476 
    477     while ( total != 0 )
    478     {
    479       step = total;
    480       if ( step > FT_ARC_CUBIC_ANGLE )
    481         step = FT_ARC_CUBIC_ANGLE;
    482 
    483       else if ( step < -FT_ARC_CUBIC_ANGLE )
    484         step = -FT_ARC_CUBIC_ANGLE;
    485 
    486       next  = angle + step;
    487       theta = step;
    488       if ( theta < 0 )
    489         theta = -theta;
    490 
    491       theta >>= 1;
    492 
    493       /* compute end point */
    494       FT_Vector_From_Polar( &b, radius, next );
    495       b.x += center->x;
    496       b.y += center->y;
    497 
    498       /* compute first and second control points */
    499       length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
    500                           ( 0x10000L + FT_Cos( theta ) ) * 3 );
    501 
    502       FT_Vector_From_Polar( &a2, length, angle + rotate );
    503       a2.x += a.x;
    504       a2.y += a.y;
    505 
    506       FT_Vector_From_Polar( &b2, length, next - rotate );
    507       b2.x += b.x;
    508       b2.y += b.y;
    509 
    510       /* add cubic arc */
    511       error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
    512       if ( error )
    513         break;
    514 
    515       /* process the rest of the arc ?? */
    516       a      = b;
    517       total -= step;
    518       angle  = next;
    519     }
    520 
    521     return error;
    522   }
    523 
    524 
    525   static FT_Error
    526   ft_stroke_border_moveto( FT_StrokeBorder  border,
    527                            FT_Vector*       to )
    528   {
    529     /* close current open path if any ? */
    530     if ( border->start >= 0 )
    531       ft_stroke_border_close( border, FALSE );
    532 
    533     border->start   = border->num_points;
    534     border->movable = FALSE;
    535 
    536     return ft_stroke_border_lineto( border, to, FALSE );
    537   }
    538 
    539 
    540   static void
    541   ft_stroke_border_init( FT_StrokeBorder  border,
    542                          FT_Memory        memory )
    543   {
    544     border->memory = memory;
    545     border->points = NULL;
    546     border->tags   = NULL;
    547 
    548     border->num_points = 0;
    549     border->max_points = 0;
    550     border->start      = -1;
    551     border->valid      = FALSE;
    552   }
    553 
    554 
    555   static void
    556   ft_stroke_border_reset( FT_StrokeBorder  border )
    557   {
    558     border->num_points = 0;
    559     border->start      = -1;
    560     border->valid      = FALSE;
    561   }
    562 
    563 
    564   static void
    565   ft_stroke_border_done( FT_StrokeBorder  border )
    566   {
    567     FT_Memory  memory = border->memory;
    568 
    569 
    570     FT_FREE( border->points );
    571     FT_FREE( border->tags );
    572 
    573     border->num_points = 0;
    574     border->max_points = 0;
    575     border->start      = -1;
    576     border->valid      = FALSE;
    577   }
    578 
    579 
    580   static FT_Error
    581   ft_stroke_border_get_counts( FT_StrokeBorder  border,
    582                                FT_UInt         *anum_points,
    583                                FT_UInt         *anum_contours )
    584   {
    585     FT_Error  error        = FT_Err_Ok;
    586     FT_UInt   num_points   = 0;
    587     FT_UInt   num_contours = 0;
    588 
    589     FT_UInt     count      = border->num_points;
    590     FT_Vector*  point      = border->points;
    591     FT_Byte*    tags       = border->tags;
    592     FT_Int      in_contour = 0;
    593 
    594 
    595     for ( ; count > 0; count--, num_points++, point++, tags++ )
    596     {
    597       if ( tags[0] & FT_STROKE_TAG_BEGIN )
    598       {
    599         if ( in_contour != 0 )
    600           goto Fail;
    601 
    602         in_contour = 1;
    603       }
    604       else if ( in_contour == 0 )
    605         goto Fail;
    606 
    607       if ( tags[0] & FT_STROKE_TAG_END )
    608       {
    609         in_contour = 0;
    610         num_contours++;
    611       }
    612     }
    613 
    614     if ( in_contour != 0 )
    615       goto Fail;
    616 
    617     border->valid = TRUE;
    618 
    619   Exit:
    620     *anum_points   = num_points;
    621     *anum_contours = num_contours;
    622     return error;
    623 
    624   Fail:
    625     num_points   = 0;
    626     num_contours = 0;
    627     goto Exit;
    628   }
    629 
    630 
    631   static void
    632   ft_stroke_border_export( FT_StrokeBorder  border,
    633                            FT_Outline*      outline )
    634   {
    635     /* copy point locations */
    636     FT_ARRAY_COPY( outline->points + outline->n_points,
    637                    border->points,
    638                    border->num_points );
    639 
    640     /* copy tags */
    641     {
    642       FT_UInt   count = border->num_points;
    643       FT_Byte*  read  = border->tags;
    644       FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
    645 
    646 
    647       for ( ; count > 0; count--, read++, write++ )
    648       {
    649         if ( *read & FT_STROKE_TAG_ON )
    650           *write = FT_CURVE_TAG_ON;
    651         else if ( *read & FT_STROKE_TAG_CUBIC )
    652           *write = FT_CURVE_TAG_CUBIC;
    653         else
    654           *write = FT_CURVE_TAG_CONIC;
    655       }
    656     }
    657 
    658     /* copy contours */
    659     {
    660       FT_UInt    count = border->num_points;
    661       FT_Byte*   tags  = border->tags;
    662       FT_Short*  write = outline->contours + outline->n_contours;
    663       FT_Short   idx   = (FT_Short)outline->n_points;
    664 
    665 
    666       for ( ; count > 0; count--, tags++, idx++ )
    667       {
    668         if ( *tags & FT_STROKE_TAG_END )
    669         {
    670           *write++ = idx;
    671           outline->n_contours++;
    672         }
    673       }
    674     }
    675 
    676     outline->n_points  = (short)( outline->n_points + border->num_points );
    677 
    678     FT_ASSERT( FT_Outline_Check( outline ) == 0 );
    679   }
    680 
    681 
    682  /***************************************************************************/
    683  /***************************************************************************/
    684  /*****                                                                 *****/
    685  /*****                           STROKER                               *****/
    686  /*****                                                                 *****/
    687  /***************************************************************************/
    688  /***************************************************************************/
    689 
    690 #define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
    691 
    692   typedef struct  FT_StrokerRec_
    693   {
    694     FT_Angle             angle_in;
    695     FT_Angle             angle_out;
    696     FT_Vector            center;
    697     FT_Bool              first_point;
    698     FT_Bool              subpath_open;
    699     FT_Angle             subpath_angle;
    700     FT_Vector            subpath_start;
    701 
    702     FT_Stroker_LineCap   line_cap;
    703     FT_Stroker_LineJoin  line_join;
    704     FT_Fixed             miter_limit;
    705     FT_Fixed             radius;
    706 
    707     FT_Bool              valid;
    708     FT_StrokeBorderRec   borders[2];
    709     FT_Library           library;
    710 
    711   } FT_StrokerRec;
    712 
    713 
    714   /* documentation is in ftstroke.h */
    715 
    716   FT_EXPORT_DEF( FT_Error )
    717   FT_Stroker_New( FT_Library   library,
    718                   FT_Stroker  *astroker )
    719   {
    720     FT_Error    error;
    721     FT_Memory   memory;
    722     FT_Stroker  stroker;
    723 
    724 
    725     if ( !library )
    726       return FT_Err_Invalid_Argument;
    727 
    728     memory = library->memory;
    729 
    730     if ( !FT_NEW( stroker ) )
    731     {
    732       stroker->library = library;
    733 
    734       ft_stroke_border_init( &stroker->borders[0], memory );
    735       ft_stroke_border_init( &stroker->borders[1], memory );
    736     }
    737     *astroker = stroker;
    738     return error;
    739   }
    740 
    741 
    742   /* documentation is in ftstroke.h */
    743 
    744   FT_EXPORT_DEF( void )
    745   FT_Stroker_Set( FT_Stroker           stroker,
    746                   FT_Fixed             radius,
    747                   FT_Stroker_LineCap   line_cap,
    748                   FT_Stroker_LineJoin  line_join,
    749                   FT_Fixed             miter_limit )
    750   {
    751     stroker->radius      = radius;
    752     stroker->line_cap    = line_cap;
    753     stroker->line_join   = line_join;
    754     stroker->miter_limit = miter_limit;
    755 
    756     FT_Stroker_Rewind( stroker );
    757   }
    758 
    759 
    760   /* documentation is in ftstroke.h */
    761 
    762   FT_EXPORT_DEF( void )
    763   FT_Stroker_Rewind( FT_Stroker  stroker )
    764   {
    765     if ( stroker )
    766     {
    767       ft_stroke_border_reset( &stroker->borders[0] );
    768       ft_stroke_border_reset( &stroker->borders[1] );
    769     }
    770   }
    771 
    772 
    773   /* documentation is in ftstroke.h */
    774 
    775   FT_EXPORT_DEF( void )
    776   FT_Stroker_Done( FT_Stroker  stroker )
    777   {
    778     if ( stroker )
    779     {
    780       FT_Memory  memory = stroker->library->memory;
    781 
    782 
    783       ft_stroke_border_done( &stroker->borders[0] );
    784       ft_stroke_border_done( &stroker->borders[1] );
    785 
    786       stroker->library = NULL;
    787       FT_FREE( stroker );
    788     }
    789   }
    790 
    791 
    792   /* creates a circular arc at a corner or cap */
    793   static FT_Error
    794   ft_stroker_arcto( FT_Stroker  stroker,
    795                     FT_Int      side )
    796   {
    797     FT_Angle         total, rotate;
    798     FT_Fixed         radius = stroker->radius;
    799     FT_Error         error  = FT_Err_Ok;
    800     FT_StrokeBorder  border = stroker->borders + side;
    801 
    802 
    803     rotate = FT_SIDE_TO_ROTATE( side );
    804 
    805     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
    806     if ( total == FT_ANGLE_PI )
    807       total = -rotate * 2;
    808 
    809     error = ft_stroke_border_arcto( border,
    810                                     &stroker->center,
    811                                     radius,
    812                                     stroker->angle_in + rotate,
    813                                     total );
    814     border->movable = FALSE;
    815     return error;
    816   }
    817 
    818 
    819   /* adds a cap at the end of an opened path */
    820   static FT_Error
    821   ft_stroker_cap( FT_Stroker  stroker,
    822                   FT_Angle    angle,
    823                   FT_Int      side )
    824   {
    825     FT_Error  error = FT_Err_Ok;
    826 
    827 
    828     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
    829     {
    830       /* add a round cap */
    831       stroker->angle_in  = angle;
    832       stroker->angle_out = angle + FT_ANGLE_PI;
    833       error = ft_stroker_arcto( stroker, side );
    834     }
    835     else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
    836     {
    837       /* add a square cap */
    838       FT_Vector        delta, delta2;
    839       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
    840       FT_Fixed         radius = stroker->radius;
    841       FT_StrokeBorder  border = stroker->borders + side;
    842 
    843 
    844       FT_Vector_From_Polar( &delta2, radius, angle + rotate );
    845       FT_Vector_From_Polar( &delta,  radius, angle );
    846 
    847       delta.x += stroker->center.x + delta2.x;
    848       delta.y += stroker->center.y + delta2.y;
    849 
    850       error = ft_stroke_border_lineto( border, &delta, FALSE );
    851       if ( error )
    852         goto Exit;
    853 
    854       FT_Vector_From_Polar( &delta2, radius, angle - rotate );
    855       FT_Vector_From_Polar( &delta,  radius, angle );
    856 
    857       delta.x += delta2.x + stroker->center.x;
    858       delta.y += delta2.y + stroker->center.y;
    859 
    860       error = ft_stroke_border_lineto( border, &delta, FALSE );
    861     }
    862     else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
    863     {
    864       /* add a butt ending */
    865       FT_Vector        delta;
    866       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
    867       FT_Fixed         radius = stroker->radius;
    868       FT_StrokeBorder  border = stroker->borders + side;
    869 
    870 
    871       FT_Vector_From_Polar( &delta, radius, angle + rotate );
    872 
    873       delta.x += stroker->center.x;
    874       delta.y += stroker->center.y;
    875 
    876       error = ft_stroke_border_lineto( border, &delta, FALSE );
    877       if ( error )
    878         goto Exit;
    879 
    880       FT_Vector_From_Polar( &delta, radius, angle - rotate );
    881 
    882       delta.x += stroker->center.x;
    883       delta.y += stroker->center.y;
    884 
    885       error = ft_stroke_border_lineto( border, &delta, FALSE );
    886     }
    887 
    888   Exit:
    889     return error;
    890   }
    891 
    892 
    893   /* process an inside corner, i.e. compute intersection */
    894   static FT_Error
    895   ft_stroker_inside( FT_Stroker  stroker,
    896                      FT_Int      side)
    897   {
    898     FT_StrokeBorder  border = stroker->borders + side;
    899     FT_Angle         phi, theta, rotate;
    900     FT_Fixed         length, thcos, sigma;
    901     FT_Vector        delta;
    902     FT_Error         error = FT_Err_Ok;
    903 
    904 
    905     rotate = FT_SIDE_TO_ROTATE( side );
    906 
    907     /* compute median angle */
    908     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
    909     if ( theta == FT_ANGLE_PI )
    910       theta = rotate;
    911     else
    912       theta = theta / 2;
    913 
    914     phi = stroker->angle_in + theta;
    915 
    916     thcos = FT_Cos( theta );
    917     sigma = FT_MulFix( stroker->miter_limit, thcos );
    918 
    919     /* TODO: find better criterion to switch off the optimization */
    920     if ( sigma < 0x10000L )
    921     {
    922       FT_Vector_From_Polar( &delta, stroker->radius,
    923                             stroker->angle_out + rotate );
    924       delta.x += stroker->center.x;
    925       delta.y += stroker->center.y;
    926       border->movable = FALSE;
    927     }
    928     else
    929     {
    930       length = FT_DivFix( stroker->radius, thcos );
    931 
    932       FT_Vector_From_Polar( &delta, length, phi + rotate );
    933       delta.x += stroker->center.x;
    934       delta.y += stroker->center.y;
    935     }
    936 
    937     error = ft_stroke_border_lineto( border, &delta, FALSE );
    938 
    939     return error;
    940   }
    941 
    942 
    943   /* process an outside corner, i.e. compute bevel/miter/round */
    944   static FT_Error
    945   ft_stroker_outside( FT_Stroker  stroker,
    946                       FT_Int      side )
    947   {
    948     FT_StrokeBorder  border = stroker->borders + side;
    949     FT_Error         error;
    950     FT_Angle         rotate;
    951 
    952 
    953     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
    954       error = ft_stroker_arcto( stroker, side );
    955     else
    956     {
    957       /* this is a mitered or beveled corner */
    958       FT_Fixed  sigma, radius = stroker->radius;
    959       FT_Angle  theta, phi;
    960       FT_Fixed  thcos;
    961       FT_Bool   miter;
    962 
    963 
    964       rotate = FT_SIDE_TO_ROTATE( side );
    965       miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
    966 
    967       theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
    968       if ( theta == FT_ANGLE_PI )
    969       {
    970         theta = rotate;
    971         phi   = stroker->angle_in;
    972       }
    973       else
    974       {
    975         theta = theta / 2;
    976         phi   = stroker->angle_in + theta + rotate;
    977       }
    978 
    979       thcos = FT_Cos( theta );
    980       sigma = FT_MulFix( stroker->miter_limit, thcos );
    981 
    982       /* FT_Sin(x) = 0 for x <= 57 */
    983       if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
    984         miter = FALSE;
    985 
    986       if ( miter )  /* this is a miter (broken angle) */
    987       {
    988         FT_Vector  middle, delta;
    989         FT_Fixed   length;
    990 
    991 
    992         /* compute middle point */
    993         FT_Vector_From_Polar( &middle,
    994                               FT_MulFix( radius, stroker->miter_limit ),
    995                               phi );
    996         middle.x += stroker->center.x;
    997         middle.y += stroker->center.y;
    998 
    999         /* compute first angle point */
   1000         length = FT_MulFix( radius,
   1001                             FT_DivFix( 0x10000L - sigma,
   1002                                        ft_pos_abs( FT_Sin( theta ) ) ) );
   1003 
   1004         FT_Vector_From_Polar( &delta, length, phi + rotate );
   1005         delta.x += middle.x;
   1006         delta.y += middle.y;
   1007 
   1008         error = ft_stroke_border_lineto( border, &delta, FALSE );
   1009         if ( error )
   1010           goto Exit;
   1011 
   1012         /* compute second angle point */
   1013         FT_Vector_From_Polar( &delta, length, phi - rotate );
   1014         delta.x += middle.x;
   1015         delta.y += middle.y;
   1016 
   1017         error = ft_stroke_border_lineto( border, &delta, FALSE );
   1018         if ( error )
   1019           goto Exit;
   1020 
   1021         /* finally, add a movable end point */
   1022         FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
   1023         delta.x += stroker->center.x;
   1024         delta.y += stroker->center.y;
   1025 
   1026         error = ft_stroke_border_lineto( border, &delta, TRUE );
   1027       }
   1028 
   1029       else /* this is a bevel (intersection) */
   1030       {
   1031         FT_Fixed   length;
   1032         FT_Vector  delta;
   1033 
   1034 
   1035         length = FT_DivFix( stroker->radius, thcos );
   1036 
   1037         FT_Vector_From_Polar( &delta, length, phi );
   1038         delta.x += stroker->center.x;
   1039         delta.y += stroker->center.y;
   1040 
   1041         error = ft_stroke_border_lineto( border, &delta, FALSE );
   1042         if ( error )
   1043           goto Exit;
   1044 
   1045         /* now add end point */
   1046         FT_Vector_From_Polar( &delta, stroker->radius,
   1047                               stroker->angle_out + rotate );
   1048         delta.x += stroker->center.x;
   1049         delta.y += stroker->center.y;
   1050 
   1051         error = ft_stroke_border_lineto( border, &delta, TRUE );
   1052       }
   1053     }
   1054 
   1055   Exit:
   1056     return error;
   1057   }
   1058 
   1059 
   1060   static FT_Error
   1061   ft_stroker_process_corner( FT_Stroker  stroker )
   1062   {
   1063     FT_Error  error = FT_Err_Ok;
   1064     FT_Angle  turn;
   1065     FT_Int    inside_side;
   1066 
   1067 
   1068     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
   1069 
   1070     /* no specific corner processing is required if the turn is 0 */
   1071     if ( turn == 0 )
   1072       goto Exit;
   1073 
   1074     /* when we turn to the right, the inside side is 0 */
   1075     inside_side = 0;
   1076 
   1077     /* otherwise, the inside side is 1 */
   1078     if ( turn < 0 )
   1079       inside_side = 1;
   1080 
   1081     /* process the inside side */
   1082     error = ft_stroker_inside( stroker, inside_side );
   1083     if ( error )
   1084       goto Exit;
   1085 
   1086     /* process the outside side */
   1087     error = ft_stroker_outside( stroker, 1 - inside_side );
   1088 
   1089   Exit:
   1090     return error;
   1091   }
   1092 
   1093 
   1094   /* add two points to the left and right borders corresponding to the */
   1095   /* start of the subpath                                              */
   1096   static FT_Error
   1097   ft_stroker_subpath_start( FT_Stroker  stroker,
   1098                             FT_Angle    start_angle )
   1099   {
   1100     FT_Vector        delta;
   1101     FT_Vector        point;
   1102     FT_Error         error;
   1103     FT_StrokeBorder  border;
   1104 
   1105 
   1106     FT_Vector_From_Polar( &delta, stroker->radius,
   1107                           start_angle + FT_ANGLE_PI2 );
   1108 
   1109     point.x = stroker->center.x + delta.x;
   1110     point.y = stroker->center.y + delta.y;
   1111 
   1112     border = stroker->borders;
   1113     error = ft_stroke_border_moveto( border, &point );
   1114     if ( error )
   1115       goto Exit;
   1116 
   1117     point.x = stroker->center.x - delta.x;
   1118     point.y = stroker->center.y - delta.y;
   1119 
   1120     border++;
   1121     error = ft_stroke_border_moveto( border, &point );
   1122 
   1123     /* save angle for last cap */
   1124     stroker->subpath_angle = start_angle;
   1125     stroker->first_point   = FALSE;
   1126 
   1127   Exit:
   1128     return error;
   1129   }
   1130 
   1131 
   1132   /* documentation is in ftstroke.h */
   1133 
   1134   FT_EXPORT_DEF( FT_Error )
   1135   FT_Stroker_LineTo( FT_Stroker  stroker,
   1136                      FT_Vector*  to )
   1137   {
   1138     FT_Error         error = FT_Err_Ok;
   1139     FT_StrokeBorder  border;
   1140     FT_Vector        delta;
   1141     FT_Angle         angle;
   1142     FT_Int           side;
   1143 
   1144     delta.x = to->x - stroker->center.x;
   1145     delta.y = to->y - stroker->center.y;
   1146 
   1147     angle = FT_Atan2( delta.x, delta.y );
   1148     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
   1149 
   1150     /* process corner if necessary */
   1151     if ( stroker->first_point )
   1152     {
   1153       /* This is the first segment of a subpath.  We need to     */
   1154       /* add a point to each border at their respective starting */
   1155       /* point locations.                                        */
   1156       error = ft_stroker_subpath_start( stroker, angle );
   1157       if ( error )
   1158         goto Exit;
   1159     }
   1160     else
   1161     {
   1162       /* process the current corner */
   1163       stroker->angle_out = angle;
   1164       error = ft_stroker_process_corner( stroker );
   1165       if ( error )
   1166         goto Exit;
   1167     }
   1168 
   1169     /* now add a line segment to both the `inside' and `outside' paths */
   1170 
   1171     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
   1172     {
   1173       FT_Vector  point;
   1174 
   1175 
   1176       point.x = to->x + delta.x;
   1177       point.y = to->y + delta.y;
   1178 
   1179       error = ft_stroke_border_lineto( border, &point, TRUE );
   1180       if ( error )
   1181         goto Exit;
   1182 
   1183       delta.x = -delta.x;
   1184       delta.y = -delta.y;
   1185     }
   1186 
   1187     stroker->angle_in = angle;
   1188     stroker->center   = *to;
   1189 
   1190   Exit:
   1191     return error;
   1192   }
   1193 
   1194 
   1195   /* documentation is in ftstroke.h */
   1196 
   1197   FT_EXPORT_DEF( FT_Error )
   1198   FT_Stroker_ConicTo( FT_Stroker  stroker,
   1199                       FT_Vector*  control,
   1200                       FT_Vector*  to )
   1201   {
   1202     FT_Error    error = FT_Err_Ok;
   1203     FT_Vector   bez_stack[34];
   1204     FT_Vector*  arc;
   1205     FT_Vector*  limit = bez_stack + 30;
   1206     FT_Angle    start_angle;
   1207     FT_Bool     first_arc = TRUE;
   1208 
   1209 
   1210     arc    = bez_stack;
   1211     arc[0] = *to;
   1212     arc[1] = *control;
   1213     arc[2] = stroker->center;
   1214 
   1215     while ( arc >= bez_stack )
   1216     {
   1217       FT_Angle  angle_in, angle_out;
   1218 
   1219 
   1220       angle_in = angle_out = 0;  /* remove compiler warnings */
   1221 
   1222       if ( arc < limit                                             &&
   1223            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
   1224       {
   1225         ft_conic_split( arc );
   1226         arc += 2;
   1227         continue;
   1228       }
   1229 
   1230       if ( first_arc )
   1231       {
   1232         first_arc = FALSE;
   1233 
   1234         start_angle = angle_in;
   1235 
   1236         /* process corner if necessary */
   1237         if ( stroker->first_point )
   1238           error = ft_stroker_subpath_start( stroker, start_angle );
   1239         else
   1240         {
   1241           stroker->angle_out = start_angle;
   1242           error = ft_stroker_process_corner( stroker );
   1243         }
   1244       }
   1245 
   1246       /* the arc's angle is small enough; we can add it directly to each */
   1247       /* border                                                          */
   1248       {
   1249         FT_Vector  ctrl, end;
   1250         FT_Angle   theta, phi, rotate;
   1251         FT_Fixed   length;
   1252         FT_Int     side;
   1253 
   1254 
   1255         theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
   1256         phi    = angle_in + theta;
   1257         length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
   1258 
   1259         for ( side = 0; side <= 1; side++ )
   1260         {
   1261           rotate = FT_SIDE_TO_ROTATE( side );
   1262 
   1263           /* compute control point */
   1264           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
   1265           ctrl.x += arc[1].x;
   1266           ctrl.y += arc[1].y;
   1267 
   1268           /* compute end point */
   1269           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
   1270           end.x += arc[0].x;
   1271           end.y += arc[0].y;
   1272 
   1273           error = ft_stroke_border_conicto( stroker->borders + side,
   1274                                             &ctrl, &end );
   1275           if ( error )
   1276             goto Exit;
   1277         }
   1278       }
   1279 
   1280       arc -= 2;
   1281 
   1282       if ( arc < bez_stack )
   1283         stroker->angle_in = angle_out;
   1284     }
   1285 
   1286     stroker->center = *to;
   1287 
   1288   Exit:
   1289     return error;
   1290   }
   1291 
   1292 
   1293   /* documentation is in ftstroke.h */
   1294 
   1295   FT_EXPORT_DEF( FT_Error )
   1296   FT_Stroker_CubicTo( FT_Stroker  stroker,
   1297                       FT_Vector*  control1,
   1298                       FT_Vector*  control2,
   1299                       FT_Vector*  to )
   1300   {
   1301     FT_Error    error = FT_Err_Ok;
   1302     FT_Vector   bez_stack[37];
   1303     FT_Vector*  arc;
   1304     FT_Vector*  limit = bez_stack + 32;
   1305     FT_Angle    start_angle;
   1306     FT_Bool     first_arc = TRUE;
   1307 
   1308 
   1309     arc    = bez_stack;
   1310     arc[0] = *to;
   1311     arc[1] = *control2;
   1312     arc[2] = *control1;
   1313     arc[3] = stroker->center;
   1314 
   1315     while ( arc >= bez_stack )
   1316     {
   1317       FT_Angle  angle_in, angle_mid, angle_out;
   1318 
   1319 
   1320       /* remove compiler warnings */
   1321       angle_in = angle_out = angle_mid = 0;
   1322 
   1323       if ( arc < limit                                         &&
   1324            !ft_cubic_is_small_enough( arc, &angle_in,
   1325                                       &angle_mid, &angle_out ) )
   1326       {
   1327         ft_cubic_split( arc );
   1328         arc += 3;
   1329         continue;
   1330       }
   1331 
   1332       if ( first_arc )
   1333       {
   1334         first_arc = FALSE;
   1335 
   1336         /* process corner if necessary */
   1337         start_angle = angle_in;
   1338 
   1339         if ( stroker->first_point )
   1340           error = ft_stroker_subpath_start( stroker, start_angle );
   1341         else
   1342         {
   1343           stroker->angle_out = start_angle;
   1344           error = ft_stroker_process_corner( stroker );
   1345         }
   1346         if ( error )
   1347           goto Exit;
   1348       }
   1349 
   1350       /* the arc's angle is small enough; we can add it directly to each */
   1351       /* border                                                          */
   1352       {
   1353         FT_Vector  ctrl1, ctrl2, end;
   1354         FT_Angle   theta1, phi1, theta2, phi2, rotate;
   1355         FT_Fixed   length1, length2;
   1356         FT_Int     side;
   1357 
   1358 
   1359         theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
   1360         theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
   1361         phi1    = (angle_mid + angle_in ) / 2;
   1362         phi2    = (angle_mid + angle_out ) / 2;
   1363         length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
   1364         length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
   1365 
   1366         for ( side = 0; side <= 1; side++ )
   1367         {
   1368           rotate = FT_SIDE_TO_ROTATE( side );
   1369 
   1370           /* compute control points */
   1371           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
   1372           ctrl1.x += arc[2].x;
   1373           ctrl1.y += arc[2].y;
   1374 
   1375           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
   1376           ctrl2.x += arc[1].x;
   1377           ctrl2.y += arc[1].y;
   1378 
   1379           /* compute end point */
   1380           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
   1381           end.x += arc[0].x;
   1382           end.y += arc[0].y;
   1383 
   1384           error = ft_stroke_border_cubicto( stroker->borders + side,
   1385                                             &ctrl1, &ctrl2, &end );
   1386           if ( error )
   1387             goto Exit;
   1388         }
   1389       }
   1390 
   1391       arc -= 3;
   1392       if ( arc < bez_stack )
   1393         stroker->angle_in = angle_out;
   1394     }
   1395 
   1396     stroker->center = *to;
   1397 
   1398   Exit:
   1399     return error;
   1400   }
   1401 
   1402 
   1403   /* documentation is in ftstroke.h */
   1404 
   1405   FT_EXPORT_DEF( FT_Error )
   1406   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
   1407                            FT_Vector*  to,
   1408                            FT_Bool     open )
   1409   {
   1410     /* We cannot process the first point, because there is not enough      */
   1411     /* information regarding its corner/cap.  The latter will be processed */
   1412     /* in the `FT_Stroker_EndSubPath' routine.                             */
   1413     /*                                                                     */
   1414     stroker->first_point  = TRUE;
   1415     stroker->center       = *to;
   1416     stroker->subpath_open = open;
   1417 
   1418     /* record the subpath start point for each border */
   1419     stroker->subpath_start = *to;
   1420 
   1421     return FT_Err_Ok;
   1422   }
   1423 
   1424 
   1425   static FT_Error
   1426   ft_stroker_add_reverse_left( FT_Stroker  stroker,
   1427                                FT_Bool     open )
   1428   {
   1429     FT_StrokeBorder  right = stroker->borders + 0;
   1430     FT_StrokeBorder  left  = stroker->borders + 1;
   1431     FT_Int           new_points;
   1432     FT_Error         error = FT_Err_Ok;
   1433 
   1434 
   1435     FT_ASSERT( left->start >= 0 );
   1436 
   1437     new_points = left->num_points - left->start;
   1438     if ( new_points > 0 )
   1439     {
   1440       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
   1441       if ( error )
   1442         goto Exit;
   1443 
   1444       {
   1445         FT_Vector*  dst_point = right->points + right->num_points;
   1446         FT_Byte*    dst_tag   = right->tags   + right->num_points;
   1447         FT_Vector*  src_point = left->points  + left->num_points - 1;
   1448         FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
   1449 
   1450         while ( src_point >= left->points + left->start )
   1451         {
   1452           *dst_point = *src_point;
   1453           *dst_tag   = *src_tag;
   1454 
   1455           if ( open )
   1456             dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
   1457           else
   1458           {
   1459             FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
   1460 
   1461 
   1462             /* switch begin/end tags if necessary */
   1463             if ( ttag == FT_STROKE_TAG_BEGIN ||
   1464                  ttag == FT_STROKE_TAG_END   )
   1465               dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
   1466 
   1467           }
   1468 
   1469           src_point--;
   1470           src_tag--;
   1471           dst_point++;
   1472           dst_tag++;
   1473         }
   1474       }
   1475 
   1476       left->num_points   = left->start;
   1477       right->num_points += new_points;
   1478 
   1479       right->movable = FALSE;
   1480       left->movable  = FALSE;
   1481     }
   1482 
   1483   Exit:
   1484     return error;
   1485   }
   1486 
   1487 
   1488   /* documentation is in ftstroke.h */
   1489 
   1490   /* there's a lot of magic in this function! */
   1491   FT_EXPORT_DEF( FT_Error )
   1492   FT_Stroker_EndSubPath( FT_Stroker  stroker )
   1493   {
   1494     FT_Error  error = FT_Err_Ok;
   1495 
   1496 
   1497     if ( stroker->subpath_open )
   1498     {
   1499       FT_StrokeBorder  right = stroker->borders;
   1500 
   1501       /* All right, this is an opened path, we need to add a cap between */
   1502       /* right & left, add the reverse of left, then add a final cap     */
   1503       /* between left & right.                                           */
   1504       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
   1505       if ( error )
   1506         goto Exit;
   1507 
   1508       /* add reversed points from `left' to `right' */
   1509       error = ft_stroker_add_reverse_left( stroker, TRUE );
   1510       if ( error )
   1511         goto Exit;
   1512 
   1513       /* now add the final cap */
   1514       stroker->center = stroker->subpath_start;
   1515       error = ft_stroker_cap( stroker,
   1516                               stroker->subpath_angle + FT_ANGLE_PI, 0 );
   1517       if ( error )
   1518         goto Exit;
   1519 
   1520       /* Now end the right subpath accordingly.  The left one is */
   1521       /* rewind and doesn't need further processing.             */
   1522       ft_stroke_border_close( right, FALSE );
   1523     }
   1524     else
   1525     {
   1526       FT_Angle  turn;
   1527       FT_Int    inside_side;
   1528 
   1529       /* close the path if needed */
   1530       if ( stroker->center.x != stroker->subpath_start.x ||
   1531            stroker->center.y != stroker->subpath_start.y )
   1532       {
   1533         error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
   1534         if ( error )
   1535           goto Exit;
   1536       }
   1537 
   1538       /* process the corner */
   1539       stroker->angle_out = stroker->subpath_angle;
   1540       turn               = FT_Angle_Diff( stroker->angle_in,
   1541                                           stroker->angle_out );
   1542 
   1543       /* no specific corner processing is required if the turn is 0 */
   1544       if ( turn != 0 )
   1545       {
   1546         /* when we turn to the right, the inside side is 0 */
   1547         inside_side = 0;
   1548 
   1549         /* otherwise, the inside side is 1 */
   1550         if ( turn < 0 )
   1551           inside_side = 1;
   1552 
   1553         error = ft_stroker_inside( stroker, inside_side );
   1554         if ( error )
   1555           goto Exit;
   1556 
   1557         /* process the outside side */
   1558         error = ft_stroker_outside( stroker, 1 - inside_side );
   1559         if ( error )
   1560           goto Exit;
   1561       }
   1562 
   1563       /* then end our two subpaths */
   1564       ft_stroke_border_close( stroker->borders + 0, TRUE );
   1565       ft_stroke_border_close( stroker->borders + 1, FALSE );
   1566     }
   1567 
   1568   Exit:
   1569     return error;
   1570   }
   1571 
   1572 
   1573   /* documentation is in ftstroke.h */
   1574 
   1575   FT_EXPORT_DEF( FT_Error )
   1576   FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
   1577                               FT_StrokerBorder  border,
   1578                               FT_UInt          *anum_points,
   1579                               FT_UInt          *anum_contours )
   1580   {
   1581     FT_UInt   num_points = 0, num_contours = 0;
   1582     FT_Error  error;
   1583 
   1584 
   1585     if ( !stroker || border > 1 )
   1586     {
   1587       error = FT_Err_Invalid_Argument;
   1588       goto Exit;
   1589     }
   1590 
   1591     error = ft_stroke_border_get_counts( stroker->borders + border,
   1592                                          &num_points, &num_contours );
   1593   Exit:
   1594     if ( anum_points )
   1595       *anum_points = num_points;
   1596 
   1597     if ( anum_contours )
   1598       *anum_contours = num_contours;
   1599 
   1600     return error;
   1601   }
   1602 
   1603 
   1604   /* documentation is in ftstroke.h */
   1605 
   1606   FT_EXPORT_DEF( FT_Error )
   1607   FT_Stroker_GetCounts( FT_Stroker  stroker,
   1608                         FT_UInt    *anum_points,
   1609                         FT_UInt    *anum_contours )
   1610   {
   1611     FT_UInt   count1, count2, num_points   = 0;
   1612     FT_UInt   count3, count4, num_contours = 0;
   1613     FT_Error  error;
   1614 
   1615 
   1616     error = ft_stroke_border_get_counts( stroker->borders + 0,
   1617                                          &count1, &count2 );
   1618     if ( error )
   1619       goto Exit;
   1620 
   1621     error = ft_stroke_border_get_counts( stroker->borders + 1,
   1622                                          &count3, &count4 );
   1623     if ( error )
   1624       goto Exit;
   1625 
   1626     num_points   = count1 + count3;
   1627     num_contours = count2 + count4;
   1628 
   1629   Exit:
   1630     *anum_points   = num_points;
   1631     *anum_contours = num_contours;
   1632     return error;
   1633   }
   1634 
   1635 
   1636   /* documentation is in ftstroke.h */
   1637 
   1638   FT_EXPORT_DEF( void )
   1639   FT_Stroker_ExportBorder( FT_Stroker        stroker,
   1640                            FT_StrokerBorder  border,
   1641                            FT_Outline*       outline )
   1642   {
   1643     if ( border == FT_STROKER_BORDER_LEFT  ||
   1644          border == FT_STROKER_BORDER_RIGHT )
   1645     {
   1646       FT_StrokeBorder  sborder = & stroker->borders[border];
   1647 
   1648 
   1649       if ( sborder->valid )
   1650         ft_stroke_border_export( sborder, outline );
   1651     }
   1652   }
   1653 
   1654 
   1655   /* documentation is in ftstroke.h */
   1656 
   1657   FT_EXPORT_DEF( void )
   1658   FT_Stroker_Export( FT_Stroker   stroker,
   1659                      FT_Outline*  outline )
   1660   {
   1661     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
   1662     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
   1663   }
   1664 
   1665 
   1666   /* documentation is in ftstroke.h */
   1667 
   1668   /*
   1669    *  The following is very similar to FT_Outline_Decompose, except
   1670    *  that we do support opened paths, and do not scale the outline.
   1671    */
   1672   FT_EXPORT_DEF( FT_Error )
   1673   FT_Stroker_ParseOutline( FT_Stroker   stroker,
   1674                            FT_Outline*  outline,
   1675                            FT_Bool      opened )
   1676   {
   1677     FT_Vector   v_last;
   1678     FT_Vector   v_control;
   1679     FT_Vector   v_start;
   1680 
   1681     FT_Vector*  point;
   1682     FT_Vector*  limit;
   1683     char*       tags;
   1684 
   1685     FT_Error    error;
   1686 
   1687     FT_Int   n;         /* index of contour in outline     */
   1688     FT_UInt  first;     /* index of first point in contour */
   1689     FT_Int   tag;       /* current point's state           */
   1690 
   1691 
   1692     if ( !outline || !stroker )
   1693       return FT_Err_Invalid_Argument;
   1694 
   1695     FT_Stroker_Rewind( stroker );
   1696 
   1697     first = 0;
   1698 
   1699     for ( n = 0; n < outline->n_contours; n++ )
   1700     {
   1701       FT_UInt  last;  /* index of last point in contour */
   1702 
   1703 
   1704       last  = outline->contours[n];
   1705       limit = outline->points + last;
   1706 
   1707       /* skip empty points; we don't stroke these */
   1708       if ( last <= first )
   1709       {
   1710         first = last + 1;
   1711         continue;
   1712       }
   1713 
   1714       v_start = outline->points[first];
   1715       v_last  = outline->points[last];
   1716 
   1717       v_control = v_start;
   1718 
   1719       point = outline->points + first;
   1720       tags  = outline->tags   + first;
   1721       tag   = FT_CURVE_TAG( tags[0] );
   1722 
   1723       /* A contour cannot start with a cubic control point! */
   1724       if ( tag == FT_CURVE_TAG_CUBIC )
   1725         goto Invalid_Outline;
   1726 
   1727       /* check first point to determine origin */
   1728       if ( tag == FT_CURVE_TAG_CONIC )
   1729       {
   1730         /* First point is conic control.  Yes, this happens. */
   1731         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
   1732         {
   1733           /* start at last point if it is on the curve */
   1734           v_start = v_last;
   1735           limit--;
   1736         }
   1737         else
   1738         {
   1739           /* if both first and last points are conic, */
   1740           /* start at their middle                    */
   1741           v_start.x = ( v_start.x + v_last.x ) / 2;
   1742           v_start.y = ( v_start.y + v_last.y ) / 2;
   1743         }
   1744         point--;
   1745         tags--;
   1746       }
   1747 
   1748       error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
   1749       if ( error )
   1750         goto Exit;
   1751 
   1752       while ( point < limit )
   1753       {
   1754         point++;
   1755         tags++;
   1756 
   1757         tag = FT_CURVE_TAG( tags[0] );
   1758         switch ( tag )
   1759         {
   1760         case FT_CURVE_TAG_ON:  /* emit a single line_to */
   1761           {
   1762             FT_Vector  vec;
   1763 
   1764 
   1765             vec.x = point->x;
   1766             vec.y = point->y;
   1767 
   1768             error = FT_Stroker_LineTo( stroker, &vec );
   1769             if ( error )
   1770               goto Exit;
   1771             continue;
   1772           }
   1773 
   1774         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
   1775           v_control.x = point->x;
   1776           v_control.y = point->y;
   1777 
   1778         Do_Conic:
   1779           if ( point < limit )
   1780           {
   1781             FT_Vector  vec;
   1782             FT_Vector  v_middle;
   1783 
   1784 
   1785             point++;
   1786             tags++;
   1787             tag = FT_CURVE_TAG( tags[0] );
   1788 
   1789             vec = point[0];
   1790 
   1791             if ( tag == FT_CURVE_TAG_ON )
   1792             {
   1793               error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
   1794               if ( error )
   1795                 goto Exit;
   1796               continue;
   1797             }
   1798 
   1799             if ( tag != FT_CURVE_TAG_CONIC )
   1800               goto Invalid_Outline;
   1801 
   1802             v_middle.x = ( v_control.x + vec.x ) / 2;
   1803             v_middle.y = ( v_control.y + vec.y ) / 2;
   1804 
   1805             error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
   1806             if ( error )
   1807               goto Exit;
   1808 
   1809             v_control = vec;
   1810             goto Do_Conic;
   1811           }
   1812 
   1813           error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
   1814           goto Close;
   1815 
   1816         default:  /* FT_CURVE_TAG_CUBIC */
   1817           {
   1818             FT_Vector  vec1, vec2;
   1819 
   1820 
   1821             if ( point + 1 > limit                             ||
   1822                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
   1823               goto Invalid_Outline;
   1824 
   1825             point += 2;
   1826             tags  += 2;
   1827 
   1828             vec1 = point[-2];
   1829             vec2 = point[-1];
   1830 
   1831             if ( point <= limit )
   1832             {
   1833               FT_Vector  vec;
   1834 
   1835 
   1836               vec = point[0];
   1837 
   1838               error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
   1839               if ( error )
   1840                 goto Exit;
   1841               continue;
   1842             }
   1843 
   1844             error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
   1845             goto Close;
   1846           }
   1847         }
   1848       }
   1849 
   1850     Close:
   1851       if ( error )
   1852         goto Exit;
   1853 
   1854       error = FT_Stroker_EndSubPath( stroker );
   1855       if ( error )
   1856         goto Exit;
   1857 
   1858       first = last + 1;
   1859     }
   1860 
   1861     return FT_Err_Ok;
   1862 
   1863   Exit:
   1864     return error;
   1865 
   1866   Invalid_Outline:
   1867     return FT_Err_Invalid_Outline;
   1868   }
   1869 
   1870 /* declare an extern to access ft_outline_glyph_class global allocated
   1871    in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access
   1872    it when FT_CONFIG_OPTION_PIC is defined */
   1873 #ifndef FT_CONFIG_OPTION_PIC
   1874   extern const FT_Glyph_Class  ft_outline_glyph_class;
   1875 #endif
   1876 #include "basepic.h"
   1877 
   1878 
   1879   /* documentation is in ftstroke.h */
   1880 
   1881   FT_EXPORT_DEF( FT_Error )
   1882   FT_Glyph_Stroke( FT_Glyph    *pglyph,
   1883                    FT_Stroker   stroker,
   1884                    FT_Bool      destroy )
   1885   {
   1886     FT_Error  error = FT_Err_Invalid_Argument;
   1887     FT_Glyph  glyph = NULL;
   1888     FT_Library library = stroker->library;
   1889     FT_UNUSED(library);
   1890 
   1891     if ( pglyph == NULL )
   1892       goto Exit;
   1893 
   1894     glyph = *pglyph;
   1895     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
   1896       goto Exit;
   1897 
   1898     {
   1899       FT_Glyph  copy;
   1900 
   1901 
   1902       error = FT_Glyph_Copy( glyph, &copy );
   1903       if ( error )
   1904         goto Exit;
   1905 
   1906       glyph = copy;
   1907     }
   1908 
   1909     {
   1910       FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
   1911       FT_Outline*      outline = &oglyph->outline;
   1912       FT_UInt          num_points, num_contours;
   1913 
   1914 
   1915       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
   1916       if ( error )
   1917         goto Fail;
   1918 
   1919       (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
   1920 
   1921       FT_Outline_Done( glyph->library, outline );
   1922 
   1923       error = FT_Outline_New( glyph->library,
   1924                               num_points, num_contours, outline );
   1925       if ( error )
   1926         goto Fail;
   1927 
   1928       outline->n_points   = 0;
   1929       outline->n_contours = 0;
   1930 
   1931       FT_Stroker_Export( stroker, outline );
   1932     }
   1933 
   1934     if ( destroy )
   1935       FT_Done_Glyph( *pglyph );
   1936 
   1937     *pglyph = glyph;
   1938     goto Exit;
   1939 
   1940   Fail:
   1941     FT_Done_Glyph( glyph );
   1942     glyph = NULL;
   1943 
   1944     if ( !destroy )
   1945       *pglyph = NULL;
   1946 
   1947   Exit:
   1948     return error;
   1949   }
   1950 
   1951 
   1952   /* documentation is in ftstroke.h */
   1953 
   1954   FT_EXPORT_DEF( FT_Error )
   1955   FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
   1956                          FT_Stroker   stroker,
   1957                          FT_Bool      inside,
   1958                          FT_Bool      destroy )
   1959   {
   1960     FT_Error  error = FT_Err_Invalid_Argument;
   1961     FT_Glyph  glyph = NULL;
   1962     FT_Library library = stroker->library;
   1963     FT_UNUSED(library);
   1964 
   1965     if ( pglyph == NULL )
   1966       goto Exit;
   1967 
   1968     glyph = *pglyph;
   1969     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
   1970       goto Exit;
   1971 
   1972     {
   1973       FT_Glyph  copy;
   1974 
   1975 
   1976       error = FT_Glyph_Copy( glyph, &copy );
   1977       if ( error )
   1978         goto Exit;
   1979 
   1980       glyph = copy;
   1981     }
   1982 
   1983     {
   1984       FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
   1985       FT_StrokerBorder  border;
   1986       FT_Outline*       outline = &oglyph->outline;
   1987       FT_UInt           num_points, num_contours;
   1988 
   1989 
   1990       border = FT_Outline_GetOutsideBorder( outline );
   1991       if ( inside )
   1992       {
   1993         if ( border == FT_STROKER_BORDER_LEFT )
   1994           border = FT_STROKER_BORDER_RIGHT;
   1995         else
   1996           border = FT_STROKER_BORDER_LEFT;
   1997       }
   1998 
   1999       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
   2000       if ( error )
   2001         goto Fail;
   2002 
   2003       (void)FT_Stroker_GetBorderCounts( stroker, border,
   2004                                         &num_points, &num_contours );
   2005 
   2006       FT_Outline_Done( glyph->library, outline );
   2007 
   2008       error = FT_Outline_New( glyph->library,
   2009                               num_points,
   2010                               num_contours,
   2011                               outline );
   2012       if ( error )
   2013         goto Fail;
   2014 
   2015       outline->n_points   = 0;
   2016       outline->n_contours = 0;
   2017 
   2018       FT_Stroker_ExportBorder( stroker, border, outline );
   2019     }
   2020 
   2021     if ( destroy )
   2022       FT_Done_Glyph( *pglyph );
   2023 
   2024     *pglyph = glyph;
   2025     goto Exit;
   2026 
   2027   Fail:
   2028     FT_Done_Glyph( glyph );
   2029     glyph = NULL;
   2030 
   2031     if ( !destroy )
   2032       *pglyph = NULL;
   2033 
   2034   Exit:
   2035     return error;
   2036   }
   2037 
   2038 
   2039 /* END */
   2040