tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ftstroke.c (64777B)


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