misc/libfreetype/src/base/ftstroke.c
changeset 9372 915436ff64ab
parent 9371 f3840de881bd
child 9373 b769a8e38cbd
equal deleted inserted replaced
9371:f3840de881bd 9372:915436ff64ab
     1 /***************************************************************************/
       
     2 /*                                                                         */
       
     3 /*  ftstroke.c                                                             */
       
     4 /*                                                                         */
       
     5 /*    FreeType path stroker (body).                                        */
       
     6 /*                                                                         */
       
     7 /*  Copyright 2002-2006, 2008-2011 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_StrokeBorderRec   borders[2];
       
   708     FT_Library           library;
       
   709 
       
   710   } FT_StrokerRec;
       
   711 
       
   712 
       
   713   /* documentation is in ftstroke.h */
       
   714 
       
   715   FT_EXPORT_DEF( FT_Error )
       
   716   FT_Stroker_New( FT_Library   library,
       
   717                   FT_Stroker  *astroker )
       
   718   {
       
   719     FT_Error    error;
       
   720     FT_Memory   memory;
       
   721     FT_Stroker  stroker;
       
   722 
       
   723 
       
   724     if ( !library )
       
   725       return FT_Err_Invalid_Argument;
       
   726 
       
   727     memory = library->memory;
       
   728 
       
   729     if ( !FT_NEW( stroker ) )
       
   730     {
       
   731       stroker->library = library;
       
   732 
       
   733       ft_stroke_border_init( &stroker->borders[0], memory );
       
   734       ft_stroke_border_init( &stroker->borders[1], memory );
       
   735     }
       
   736     *astroker = stroker;
       
   737     return error;
       
   738   }
       
   739 
       
   740 
       
   741   /* documentation is in ftstroke.h */
       
   742 
       
   743   FT_EXPORT_DEF( void )
       
   744   FT_Stroker_Set( FT_Stroker           stroker,
       
   745                   FT_Fixed             radius,
       
   746                   FT_Stroker_LineCap   line_cap,
       
   747                   FT_Stroker_LineJoin  line_join,
       
   748                   FT_Fixed             miter_limit )
       
   749   {
       
   750     stroker->radius      = radius;
       
   751     stroker->line_cap    = line_cap;
       
   752     stroker->line_join   = line_join;
       
   753     stroker->miter_limit = miter_limit;
       
   754 
       
   755     FT_Stroker_Rewind( stroker );
       
   756   }
       
   757 
       
   758 
       
   759   /* documentation is in ftstroke.h */
       
   760 
       
   761   FT_EXPORT_DEF( void )
       
   762   FT_Stroker_Rewind( FT_Stroker  stroker )
       
   763   {
       
   764     if ( stroker )
       
   765     {
       
   766       ft_stroke_border_reset( &stroker->borders[0] );
       
   767       ft_stroke_border_reset( &stroker->borders[1] );
       
   768     }
       
   769   }
       
   770 
       
   771 
       
   772   /* documentation is in ftstroke.h */
       
   773 
       
   774   FT_EXPORT_DEF( void )
       
   775   FT_Stroker_Done( FT_Stroker  stroker )
       
   776   {
       
   777     if ( stroker )
       
   778     {
       
   779       FT_Memory  memory = stroker->library->memory;
       
   780 
       
   781 
       
   782       ft_stroke_border_done( &stroker->borders[0] );
       
   783       ft_stroke_border_done( &stroker->borders[1] );
       
   784 
       
   785       stroker->library = NULL;
       
   786       FT_FREE( stroker );
       
   787     }
       
   788   }
       
   789 
       
   790 
       
   791   /* creates a circular arc at a corner or cap */
       
   792   static FT_Error
       
   793   ft_stroker_arcto( FT_Stroker  stroker,
       
   794                     FT_Int      side )
       
   795   {
       
   796     FT_Angle         total, rotate;
       
   797     FT_Fixed         radius = stroker->radius;
       
   798     FT_Error         error  = FT_Err_Ok;
       
   799     FT_StrokeBorder  border = stroker->borders + side;
       
   800 
       
   801 
       
   802     rotate = FT_SIDE_TO_ROTATE( side );
       
   803 
       
   804     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
       
   805     if ( total == FT_ANGLE_PI )
       
   806       total = -rotate * 2;
       
   807 
       
   808     error = ft_stroke_border_arcto( border,
       
   809                                     &stroker->center,
       
   810                                     radius,
       
   811                                     stroker->angle_in + rotate,
       
   812                                     total );
       
   813     border->movable = FALSE;
       
   814     return error;
       
   815   }
       
   816 
       
   817 
       
   818   /* adds a cap at the end of an opened path */
       
   819   static FT_Error
       
   820   ft_stroker_cap( FT_Stroker  stroker,
       
   821                   FT_Angle    angle,
       
   822                   FT_Int      side )
       
   823   {
       
   824     FT_Error  error = FT_Err_Ok;
       
   825 
       
   826 
       
   827     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
       
   828     {
       
   829       /* add a round cap */
       
   830       stroker->angle_in  = angle;
       
   831       stroker->angle_out = angle + FT_ANGLE_PI;
       
   832       error = ft_stroker_arcto( stroker, side );
       
   833     }
       
   834     else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
       
   835     {
       
   836       /* add a square cap */
       
   837       FT_Vector        delta, delta2;
       
   838       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
       
   839       FT_Fixed         radius = stroker->radius;
       
   840       FT_StrokeBorder  border = stroker->borders + side;
       
   841 
       
   842 
       
   843       FT_Vector_From_Polar( &delta2, radius, angle + rotate );
       
   844       FT_Vector_From_Polar( &delta,  radius, angle );
       
   845 
       
   846       delta.x += stroker->center.x + delta2.x;
       
   847       delta.y += stroker->center.y + delta2.y;
       
   848 
       
   849       error = ft_stroke_border_lineto( border, &delta, FALSE );
       
   850       if ( error )
       
   851         goto Exit;
       
   852 
       
   853       FT_Vector_From_Polar( &delta2, radius, angle - rotate );
       
   854       FT_Vector_From_Polar( &delta,  radius, angle );
       
   855 
       
   856       delta.x += delta2.x + stroker->center.x;
       
   857       delta.y += delta2.y + stroker->center.y;
       
   858 
       
   859       error = ft_stroke_border_lineto( border, &delta, FALSE );
       
   860     }
       
   861     else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
       
   862     {
       
   863       /* add a butt ending */
       
   864       FT_Vector        delta;
       
   865       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
       
   866       FT_Fixed         radius = stroker->radius;
       
   867       FT_StrokeBorder  border = stroker->borders + side;
       
   868 
       
   869 
       
   870       FT_Vector_From_Polar( &delta, radius, angle + rotate );
       
   871 
       
   872       delta.x += stroker->center.x;
       
   873       delta.y += stroker->center.y;
       
   874 
       
   875       error = ft_stroke_border_lineto( border, &delta, FALSE );
       
   876       if ( error )
       
   877         goto Exit;
       
   878 
       
   879       FT_Vector_From_Polar( &delta, radius, angle - rotate );
       
   880 
       
   881       delta.x += stroker->center.x;
       
   882       delta.y += stroker->center.y;
       
   883 
       
   884       error = ft_stroke_border_lineto( border, &delta, FALSE );   
       
   885     }
       
   886 
       
   887   Exit:
       
   888     return error;
       
   889   }
       
   890 
       
   891 
       
   892   /* process an inside corner, i.e. compute intersection */
       
   893   static FT_Error
       
   894   ft_stroker_inside( FT_Stroker  stroker,
       
   895                      FT_Int      side)
       
   896   {
       
   897     FT_StrokeBorder  border = stroker->borders + side;
       
   898     FT_Angle         phi, theta, rotate;
       
   899     FT_Fixed         length, thcos, sigma;
       
   900     FT_Vector        delta;
       
   901     FT_Error         error = FT_Err_Ok;
       
   902 
       
   903 
       
   904     rotate = FT_SIDE_TO_ROTATE( side );
       
   905 
       
   906     /* compute median angle */
       
   907     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
       
   908     if ( theta == FT_ANGLE_PI )
       
   909       theta = rotate;
       
   910     else
       
   911       theta = theta / 2;
       
   912 
       
   913     phi = stroker->angle_in + theta;
       
   914 
       
   915     thcos = FT_Cos( theta );
       
   916     sigma = FT_MulFix( stroker->miter_limit, thcos );
       
   917 
       
   918     /* TODO: find better criterion to switch off the optimization */
       
   919     if ( sigma < 0x10000L )
       
   920     {
       
   921       FT_Vector_From_Polar( &delta, stroker->radius,
       
   922                             stroker->angle_out + rotate );
       
   923       delta.x += stroker->center.x;
       
   924       delta.y += stroker->center.y;
       
   925       border->movable = FALSE;
       
   926     }
       
   927     else
       
   928     {
       
   929       length = FT_DivFix( stroker->radius, thcos );
       
   930 
       
   931       FT_Vector_From_Polar( &delta, length, phi + rotate );
       
   932       delta.x += stroker->center.x;
       
   933       delta.y += stroker->center.y;
       
   934     }
       
   935 
       
   936     error = ft_stroke_border_lineto( border, &delta, FALSE );
       
   937 
       
   938     return error;
       
   939   }
       
   940 
       
   941 
       
   942   /* process an outside corner, i.e. compute bevel/miter/round */
       
   943   static FT_Error
       
   944   ft_stroker_outside( FT_Stroker  stroker,
       
   945                       FT_Int      side )
       
   946   {
       
   947     FT_StrokeBorder  border = stroker->borders + side;
       
   948     FT_Error         error;
       
   949     FT_Angle         rotate;
       
   950 
       
   951 
       
   952     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
       
   953       error = ft_stroker_arcto( stroker, side );
       
   954     else
       
   955     {
       
   956       /* this is a mitered or beveled corner */
       
   957       FT_Fixed  sigma, radius = stroker->radius;
       
   958       FT_Angle  theta, phi;
       
   959       FT_Fixed  thcos;
       
   960       FT_Bool   miter;
       
   961 
       
   962 
       
   963       rotate = FT_SIDE_TO_ROTATE( side );
       
   964       miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
       
   965 
       
   966       theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
       
   967       if ( theta == FT_ANGLE_PI )
       
   968       {
       
   969         theta = rotate;
       
   970         phi   = stroker->angle_in;
       
   971       }
       
   972       else
       
   973       {
       
   974         theta = theta / 2;
       
   975         phi   = stroker->angle_in + theta + rotate;
       
   976       }
       
   977 
       
   978       thcos = FT_Cos( theta );
       
   979       sigma = FT_MulFix( stroker->miter_limit, thcos );
       
   980 
       
   981       /* FT_Sin(x) = 0 for x <= 57 */
       
   982       if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
       
   983         miter = FALSE;
       
   984 
       
   985       if ( miter )  /* this is a miter (broken angle) */
       
   986       {
       
   987         FT_Vector  middle, delta;
       
   988         FT_Fixed   length;
       
   989 
       
   990 
       
   991         /* compute middle point */
       
   992         FT_Vector_From_Polar( &middle,
       
   993                               FT_MulFix( radius, stroker->miter_limit ),
       
   994                               phi );
       
   995         middle.x += stroker->center.x;
       
   996         middle.y += stroker->center.y;
       
   997 
       
   998         /* compute first angle point */
       
   999         length = FT_MulFix( radius,
       
  1000                             FT_DivFix( 0x10000L - sigma,
       
  1001                                        ft_pos_abs( FT_Sin( theta ) ) ) );
       
  1002 
       
  1003         FT_Vector_From_Polar( &delta, length, phi + rotate );
       
  1004         delta.x += middle.x;
       
  1005         delta.y += middle.y;
       
  1006 
       
  1007         error = ft_stroke_border_lineto( border, &delta, FALSE );
       
  1008         if ( error )
       
  1009           goto Exit;
       
  1010 
       
  1011         /* compute second angle point */
       
  1012         FT_Vector_From_Polar( &delta, length, phi - rotate );
       
  1013         delta.x += middle.x;
       
  1014         delta.y += middle.y;
       
  1015 
       
  1016         error = ft_stroke_border_lineto( border, &delta, FALSE );
       
  1017         if ( error )
       
  1018           goto Exit;
       
  1019 
       
  1020         /* finally, add a movable end point */
       
  1021         FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
       
  1022         delta.x += stroker->center.x;
       
  1023         delta.y += stroker->center.y;
       
  1024 
       
  1025         error = ft_stroke_border_lineto( border, &delta, TRUE );
       
  1026       }
       
  1027 
       
  1028       else /* this is a bevel (intersection) */
       
  1029       {
       
  1030         FT_Fixed   length;
       
  1031         FT_Vector  delta;
       
  1032 
       
  1033 
       
  1034         length = FT_DivFix( stroker->radius, thcos );
       
  1035 
       
  1036         FT_Vector_From_Polar( &delta, length, phi );
       
  1037         delta.x += stroker->center.x;
       
  1038         delta.y += stroker->center.y;
       
  1039 
       
  1040         error = ft_stroke_border_lineto( border, &delta, FALSE );
       
  1041         if ( error )
       
  1042           goto Exit;
       
  1043 
       
  1044         /* now add end point */
       
  1045         FT_Vector_From_Polar( &delta, stroker->radius,
       
  1046                               stroker->angle_out + rotate );
       
  1047         delta.x += stroker->center.x;
       
  1048         delta.y += stroker->center.y;
       
  1049 
       
  1050         error = ft_stroke_border_lineto( border, &delta, TRUE );
       
  1051       }
       
  1052     }
       
  1053 
       
  1054   Exit:
       
  1055     return error;
       
  1056   }
       
  1057 
       
  1058 
       
  1059   static FT_Error
       
  1060   ft_stroker_process_corner( FT_Stroker  stroker )
       
  1061   {
       
  1062     FT_Error  error = FT_Err_Ok;
       
  1063     FT_Angle  turn;
       
  1064     FT_Int    inside_side;
       
  1065 
       
  1066 
       
  1067     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
       
  1068 
       
  1069     /* no specific corner processing is required if the turn is 0 */
       
  1070     if ( turn == 0 )
       
  1071       goto Exit;
       
  1072 
       
  1073     /* when we turn to the right, the inside side is 0 */
       
  1074     inside_side = 0;
       
  1075 
       
  1076     /* otherwise, the inside side is 1 */
       
  1077     if ( turn < 0 )
       
  1078       inside_side = 1;
       
  1079 
       
  1080     /* process the inside side */
       
  1081     error = ft_stroker_inside( stroker, inside_side );
       
  1082     if ( error )
       
  1083       goto Exit;
       
  1084 
       
  1085     /* process the outside side */
       
  1086     error = ft_stroker_outside( stroker, 1 - inside_side );
       
  1087 
       
  1088   Exit:
       
  1089     return error;
       
  1090   }
       
  1091 
       
  1092 
       
  1093   /* add two points to the left and right borders corresponding to the */
       
  1094   /* start of the subpath                                              */
       
  1095   static FT_Error
       
  1096   ft_stroker_subpath_start( FT_Stroker  stroker,
       
  1097                             FT_Angle    start_angle )
       
  1098   {
       
  1099     FT_Vector        delta;
       
  1100     FT_Vector        point;
       
  1101     FT_Error         error;
       
  1102     FT_StrokeBorder  border;
       
  1103 
       
  1104 
       
  1105     FT_Vector_From_Polar( &delta, stroker->radius,
       
  1106                           start_angle + FT_ANGLE_PI2 );
       
  1107 
       
  1108     point.x = stroker->center.x + delta.x;
       
  1109     point.y = stroker->center.y + delta.y;
       
  1110 
       
  1111     border = stroker->borders;
       
  1112     error = ft_stroke_border_moveto( border, &point );
       
  1113     if ( error )
       
  1114       goto Exit;
       
  1115 
       
  1116     point.x = stroker->center.x - delta.x;
       
  1117     point.y = stroker->center.y - delta.y;
       
  1118 
       
  1119     border++;
       
  1120     error = ft_stroke_border_moveto( border, &point );
       
  1121 
       
  1122     /* save angle for last cap */
       
  1123     stroker->subpath_angle = start_angle;
       
  1124     stroker->first_point   = FALSE;
       
  1125 
       
  1126   Exit:
       
  1127     return error;
       
  1128   }
       
  1129 
       
  1130 
       
  1131   /* documentation is in ftstroke.h */
       
  1132 
       
  1133   FT_EXPORT_DEF( FT_Error )
       
  1134   FT_Stroker_LineTo( FT_Stroker  stroker,
       
  1135                      FT_Vector*  to )
       
  1136   {
       
  1137     FT_Error         error = FT_Err_Ok;
       
  1138     FT_StrokeBorder  border;
       
  1139     FT_Vector        delta;
       
  1140     FT_Angle         angle;
       
  1141     FT_Int           side;
       
  1142 
       
  1143     delta.x = to->x - stroker->center.x;
       
  1144     delta.y = to->y - stroker->center.y;
       
  1145 
       
  1146     angle = FT_Atan2( delta.x, delta.y );
       
  1147     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
       
  1148 
       
  1149     /* process corner if necessary */
       
  1150     if ( stroker->first_point )
       
  1151     {
       
  1152       /* This is the first segment of a subpath.  We need to     */
       
  1153       /* add a point to each border at their respective starting */
       
  1154       /* point locations.                                        */
       
  1155       error = ft_stroker_subpath_start( stroker, angle );
       
  1156       if ( error )
       
  1157         goto Exit;
       
  1158     }
       
  1159     else
       
  1160     {
       
  1161       /* process the current corner */
       
  1162       stroker->angle_out = angle;
       
  1163       error = ft_stroker_process_corner( stroker );
       
  1164       if ( error )
       
  1165         goto Exit;
       
  1166     }
       
  1167 
       
  1168     /* now add a line segment to both the `inside' and `outside' paths */
       
  1169 
       
  1170     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
       
  1171     {
       
  1172       FT_Vector  point;
       
  1173 
       
  1174 
       
  1175       point.x = to->x + delta.x;
       
  1176       point.y = to->y + delta.y;
       
  1177 
       
  1178       error = ft_stroke_border_lineto( border, &point, TRUE );
       
  1179       if ( error )
       
  1180         goto Exit;
       
  1181 
       
  1182       delta.x = -delta.x;
       
  1183       delta.y = -delta.y;
       
  1184     }
       
  1185 
       
  1186     stroker->angle_in = angle;
       
  1187     stroker->center   = *to;
       
  1188 
       
  1189   Exit:
       
  1190     return error;
       
  1191   }
       
  1192 
       
  1193 
       
  1194   /* documentation is in ftstroke.h */
       
  1195 
       
  1196   FT_EXPORT_DEF( FT_Error )
       
  1197   FT_Stroker_ConicTo( FT_Stroker  stroker,
       
  1198                       FT_Vector*  control,
       
  1199                       FT_Vector*  to )
       
  1200   {
       
  1201     FT_Error    error = FT_Err_Ok;
       
  1202     FT_Vector   bez_stack[34];
       
  1203     FT_Vector*  arc;
       
  1204     FT_Vector*  limit = bez_stack + 30;
       
  1205     FT_Angle    start_angle;
       
  1206     FT_Bool     first_arc = TRUE;
       
  1207 
       
  1208 
       
  1209     arc    = bez_stack;
       
  1210     arc[0] = *to;
       
  1211     arc[1] = *control;
       
  1212     arc[2] = stroker->center;
       
  1213 
       
  1214     while ( arc >= bez_stack )
       
  1215     {
       
  1216       FT_Angle  angle_in, angle_out;
       
  1217 
       
  1218 
       
  1219       angle_in = angle_out = 0;  /* remove compiler warnings */
       
  1220 
       
  1221       if ( arc < limit                                             &&
       
  1222            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
       
  1223       {
       
  1224         ft_conic_split( arc );
       
  1225         arc += 2;
       
  1226         continue;
       
  1227       }
       
  1228 
       
  1229       if ( first_arc )
       
  1230       {
       
  1231         first_arc = FALSE;
       
  1232 
       
  1233         start_angle = angle_in;
       
  1234 
       
  1235         /* process corner if necessary */
       
  1236         if ( stroker->first_point )
       
  1237           error = ft_stroker_subpath_start( stroker, start_angle );
       
  1238         else
       
  1239         {
       
  1240           stroker->angle_out = start_angle;
       
  1241           error = ft_stroker_process_corner( stroker );
       
  1242         }
       
  1243       }
       
  1244 
       
  1245       /* the arc's angle is small enough; we can add it directly to each */
       
  1246       /* border                                                          */
       
  1247       {
       
  1248         FT_Vector  ctrl, end;
       
  1249         FT_Angle   theta, phi, rotate;
       
  1250         FT_Fixed   length;
       
  1251         FT_Int     side;
       
  1252 
       
  1253 
       
  1254         theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
       
  1255         phi    = angle_in + theta;
       
  1256         length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
       
  1257 
       
  1258         for ( side = 0; side <= 1; side++ )
       
  1259         {
       
  1260           rotate = FT_SIDE_TO_ROTATE( side );
       
  1261 
       
  1262           /* compute control point */
       
  1263           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
       
  1264           ctrl.x += arc[1].x;
       
  1265           ctrl.y += arc[1].y;
       
  1266 
       
  1267           /* compute end point */
       
  1268           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
       
  1269           end.x += arc[0].x;
       
  1270           end.y += arc[0].y;
       
  1271 
       
  1272           error = ft_stroke_border_conicto( stroker->borders + side,
       
  1273                                             &ctrl, &end );
       
  1274           if ( error )
       
  1275             goto Exit;
       
  1276         }
       
  1277       }
       
  1278 
       
  1279       arc -= 2;
       
  1280 
       
  1281       if ( arc < bez_stack )
       
  1282         stroker->angle_in = angle_out;
       
  1283     }
       
  1284 
       
  1285     stroker->center = *to;
       
  1286 
       
  1287   Exit:
       
  1288     return error;
       
  1289   }
       
  1290 
       
  1291 
       
  1292   /* documentation is in ftstroke.h */
       
  1293 
       
  1294   FT_EXPORT_DEF( FT_Error )
       
  1295   FT_Stroker_CubicTo( FT_Stroker  stroker,
       
  1296                       FT_Vector*  control1,
       
  1297                       FT_Vector*  control2,
       
  1298                       FT_Vector*  to )
       
  1299   {
       
  1300     FT_Error    error = FT_Err_Ok;
       
  1301     FT_Vector   bez_stack[37];
       
  1302     FT_Vector*  arc;
       
  1303     FT_Vector*  limit = bez_stack + 32;
       
  1304     FT_Angle    start_angle;
       
  1305     FT_Bool     first_arc = TRUE;
       
  1306 
       
  1307 
       
  1308     arc    = bez_stack;
       
  1309     arc[0] = *to;
       
  1310     arc[1] = *control2;
       
  1311     arc[2] = *control1;
       
  1312     arc[3] = stroker->center;
       
  1313 
       
  1314     while ( arc >= bez_stack )
       
  1315     {
       
  1316       FT_Angle  angle_in, angle_mid, angle_out;
       
  1317 
       
  1318 
       
  1319       /* remove compiler warnings */
       
  1320       angle_in = angle_out = angle_mid = 0;
       
  1321 
       
  1322       if ( arc < limit                                         &&
       
  1323            !ft_cubic_is_small_enough( arc, &angle_in,
       
  1324                                       &angle_mid, &angle_out ) )
       
  1325       {
       
  1326         ft_cubic_split( arc );
       
  1327         arc += 3;
       
  1328         continue;
       
  1329       }
       
  1330 
       
  1331       if ( first_arc )
       
  1332       {
       
  1333         first_arc = FALSE;
       
  1334 
       
  1335         /* process corner if necessary */
       
  1336         start_angle = angle_in;
       
  1337 
       
  1338         if ( stroker->first_point )
       
  1339           error = ft_stroker_subpath_start( stroker, start_angle );
       
  1340         else
       
  1341         {
       
  1342           stroker->angle_out = start_angle;
       
  1343           error = ft_stroker_process_corner( stroker );
       
  1344         }
       
  1345         if ( error )
       
  1346           goto Exit;
       
  1347       }
       
  1348 
       
  1349       /* the arc's angle is small enough; we can add it directly to each */
       
  1350       /* border                                                          */
       
  1351       {
       
  1352         FT_Vector  ctrl1, ctrl2, end;
       
  1353         FT_Angle   theta1, phi1, theta2, phi2, rotate;
       
  1354         FT_Fixed   length1, length2;
       
  1355         FT_Int     side;
       
  1356 
       
  1357 
       
  1358         theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
       
  1359         theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
       
  1360         phi1    = (angle_mid + angle_in ) / 2;
       
  1361         phi2    = (angle_mid + angle_out ) / 2;
       
  1362         length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
       
  1363         length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
       
  1364 
       
  1365         for ( side = 0; side <= 1; side++ )
       
  1366         {
       
  1367           rotate = FT_SIDE_TO_ROTATE( side );
       
  1368 
       
  1369           /* compute control points */
       
  1370           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
       
  1371           ctrl1.x += arc[2].x;
       
  1372           ctrl1.y += arc[2].y;
       
  1373 
       
  1374           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
       
  1375           ctrl2.x += arc[1].x;
       
  1376           ctrl2.y += arc[1].y;
       
  1377 
       
  1378           /* compute end point */
       
  1379           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
       
  1380           end.x += arc[0].x;
       
  1381           end.y += arc[0].y;
       
  1382 
       
  1383           error = ft_stroke_border_cubicto( stroker->borders + side,
       
  1384                                             &ctrl1, &ctrl2, &end );
       
  1385           if ( error )
       
  1386             goto Exit;
       
  1387         }
       
  1388       }
       
  1389 
       
  1390       arc -= 3;
       
  1391       if ( arc < bez_stack )
       
  1392         stroker->angle_in = angle_out;
       
  1393     }
       
  1394 
       
  1395     stroker->center = *to;
       
  1396 
       
  1397   Exit:
       
  1398     return error;
       
  1399   }
       
  1400 
       
  1401 
       
  1402   /* documentation is in ftstroke.h */
       
  1403 
       
  1404   FT_EXPORT_DEF( FT_Error )
       
  1405   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
       
  1406                            FT_Vector*  to,
       
  1407                            FT_Bool     open )
       
  1408   {
       
  1409     /* We cannot process the first point, because there is not enough      */
       
  1410     /* information regarding its corner/cap.  The latter will be processed */
       
  1411     /* in the `FT_Stroker_EndSubPath' routine.                             */
       
  1412     /*                                                                     */
       
  1413     stroker->first_point  = TRUE;
       
  1414     stroker->center       = *to;
       
  1415     stroker->subpath_open = open;
       
  1416 
       
  1417     /* record the subpath start point for each border */
       
  1418     stroker->subpath_start = *to;
       
  1419 
       
  1420     return FT_Err_Ok;
       
  1421   }
       
  1422 
       
  1423 
       
  1424   static FT_Error
       
  1425   ft_stroker_add_reverse_left( FT_Stroker  stroker,
       
  1426                                FT_Bool     open )
       
  1427   {
       
  1428     FT_StrokeBorder  right = stroker->borders + 0;
       
  1429     FT_StrokeBorder  left  = stroker->borders + 1;
       
  1430     FT_Int           new_points;
       
  1431     FT_Error         error = FT_Err_Ok;
       
  1432 
       
  1433 
       
  1434     FT_ASSERT( left->start >= 0 );
       
  1435 
       
  1436     new_points = left->num_points - left->start;
       
  1437     if ( new_points > 0 )
       
  1438     {
       
  1439       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
       
  1440       if ( error )
       
  1441         goto Exit;
       
  1442 
       
  1443       {
       
  1444         FT_Vector*  dst_point = right->points + right->num_points;
       
  1445         FT_Byte*    dst_tag   = right->tags   + right->num_points;
       
  1446         FT_Vector*  src_point = left->points  + left->num_points - 1;
       
  1447         FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
       
  1448 
       
  1449         while ( src_point >= left->points + left->start )
       
  1450         {
       
  1451           *dst_point = *src_point;
       
  1452           *dst_tag   = *src_tag;
       
  1453 
       
  1454           if ( open )
       
  1455             dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
       
  1456           else
       
  1457           {
       
  1458             FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
       
  1459 
       
  1460 
       
  1461             /* switch begin/end tags if necessary */
       
  1462             if ( ttag == FT_STROKE_TAG_BEGIN ||
       
  1463                  ttag == FT_STROKE_TAG_END   )
       
  1464               dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
       
  1465 
       
  1466           }
       
  1467 
       
  1468           src_point--;
       
  1469           src_tag--;
       
  1470           dst_point++;
       
  1471           dst_tag++;
       
  1472         }
       
  1473       }
       
  1474 
       
  1475       left->num_points   = left->start;
       
  1476       right->num_points += new_points;
       
  1477 
       
  1478       right->movable = FALSE;
       
  1479       left->movable  = FALSE;
       
  1480     }
       
  1481 
       
  1482   Exit:
       
  1483     return error;
       
  1484   }
       
  1485 
       
  1486 
       
  1487   /* documentation is in ftstroke.h */
       
  1488 
       
  1489   /* there's a lot of magic in this function! */
       
  1490   FT_EXPORT_DEF( FT_Error )
       
  1491   FT_Stroker_EndSubPath( FT_Stroker  stroker )
       
  1492   {
       
  1493     FT_Error  error = FT_Err_Ok;
       
  1494 
       
  1495 
       
  1496     if ( stroker->subpath_open )
       
  1497     {
       
  1498       FT_StrokeBorder  right = stroker->borders;
       
  1499 
       
  1500       /* All right, this is an opened path, we need to add a cap between */
       
  1501       /* right & left, add the reverse of left, then add a final cap     */
       
  1502       /* between left & right.                                           */
       
  1503       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
       
  1504       if ( error )
       
  1505         goto Exit;
       
  1506 
       
  1507       /* add reversed points from `left' to `right' */
       
  1508       error = ft_stroker_add_reverse_left( stroker, TRUE );
       
  1509       if ( error )
       
  1510         goto Exit;
       
  1511 
       
  1512       /* now add the final cap */
       
  1513       stroker->center = stroker->subpath_start;
       
  1514       error = ft_stroker_cap( stroker,
       
  1515                               stroker->subpath_angle + FT_ANGLE_PI, 0 );
       
  1516       if ( error )
       
  1517         goto Exit;
       
  1518 
       
  1519       /* Now end the right subpath accordingly.  The left one is */
       
  1520       /* rewind and doesn't need further processing.             */
       
  1521       ft_stroke_border_close( right, FALSE );
       
  1522     }
       
  1523     else
       
  1524     {
       
  1525       FT_Angle  turn;
       
  1526       FT_Int    inside_side;
       
  1527 
       
  1528       /* close the path if needed */
       
  1529       if ( stroker->center.x != stroker->subpath_start.x ||
       
  1530            stroker->center.y != stroker->subpath_start.y )
       
  1531       {
       
  1532         error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
       
  1533         if ( error )
       
  1534           goto Exit;
       
  1535       }
       
  1536 
       
  1537       /* process the corner */
       
  1538       stroker->angle_out = stroker->subpath_angle;
       
  1539       turn               = FT_Angle_Diff( stroker->angle_in,
       
  1540                                           stroker->angle_out );
       
  1541 
       
  1542       /* no specific corner processing is required if the turn is 0 */
       
  1543       if ( turn != 0 )
       
  1544       {
       
  1545         /* when we turn to the right, the inside side is 0 */
       
  1546         inside_side = 0;
       
  1547 
       
  1548         /* otherwise, the inside side is 1 */
       
  1549         if ( turn < 0 )
       
  1550           inside_side = 1;
       
  1551 
       
  1552         error = ft_stroker_inside( stroker, inside_side );
       
  1553         if ( error )
       
  1554           goto Exit;
       
  1555 
       
  1556         /* process the outside side */
       
  1557         error = ft_stroker_outside( stroker, 1 - inside_side );
       
  1558         if ( error )
       
  1559           goto Exit;
       
  1560       }
       
  1561 
       
  1562       /* then end our two subpaths */
       
  1563       ft_stroke_border_close( stroker->borders + 0, TRUE );
       
  1564       ft_stroke_border_close( stroker->borders + 1, FALSE );
       
  1565     }
       
  1566 
       
  1567   Exit:
       
  1568     return error;
       
  1569   }
       
  1570 
       
  1571 
       
  1572   /* documentation is in ftstroke.h */
       
  1573 
       
  1574   FT_EXPORT_DEF( FT_Error )
       
  1575   FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
       
  1576                               FT_StrokerBorder  border,
       
  1577                               FT_UInt          *anum_points,
       
  1578                               FT_UInt          *anum_contours )
       
  1579   {
       
  1580     FT_UInt   num_points = 0, num_contours = 0;
       
  1581     FT_Error  error;
       
  1582 
       
  1583 
       
  1584     if ( !stroker || border > 1 )
       
  1585     {
       
  1586       error = FT_Err_Invalid_Argument;
       
  1587       goto Exit;
       
  1588     }
       
  1589 
       
  1590     error = ft_stroke_border_get_counts( stroker->borders + border,
       
  1591                                          &num_points, &num_contours );
       
  1592   Exit:
       
  1593     if ( anum_points )
       
  1594       *anum_points = num_points;
       
  1595 
       
  1596     if ( anum_contours )
       
  1597       *anum_contours = num_contours;
       
  1598 
       
  1599     return error;
       
  1600   }
       
  1601 
       
  1602 
       
  1603   /* documentation is in ftstroke.h */
       
  1604 
       
  1605   FT_EXPORT_DEF( FT_Error )
       
  1606   FT_Stroker_GetCounts( FT_Stroker  stroker,
       
  1607                         FT_UInt    *anum_points,
       
  1608                         FT_UInt    *anum_contours )
       
  1609   {
       
  1610     FT_UInt   count1, count2, num_points   = 0;
       
  1611     FT_UInt   count3, count4, num_contours = 0;
       
  1612     FT_Error  error;
       
  1613 
       
  1614 
       
  1615     error = ft_stroke_border_get_counts( stroker->borders + 0,
       
  1616                                          &count1, &count2 );
       
  1617     if ( error )
       
  1618       goto Exit;
       
  1619 
       
  1620     error = ft_stroke_border_get_counts( stroker->borders + 1,
       
  1621                                          &count3, &count4 );
       
  1622     if ( error )
       
  1623       goto Exit;
       
  1624 
       
  1625     num_points   = count1 + count3;
       
  1626     num_contours = count2 + count4;
       
  1627 
       
  1628   Exit:
       
  1629     *anum_points   = num_points;
       
  1630     *anum_contours = num_contours;
       
  1631     return error;
       
  1632   }
       
  1633 
       
  1634 
       
  1635   /* documentation is in ftstroke.h */
       
  1636 
       
  1637   FT_EXPORT_DEF( void )
       
  1638   FT_Stroker_ExportBorder( FT_Stroker        stroker,
       
  1639                            FT_StrokerBorder  border,
       
  1640                            FT_Outline*       outline )
       
  1641   {
       
  1642     if ( border == FT_STROKER_BORDER_LEFT  ||
       
  1643          border == FT_STROKER_BORDER_RIGHT )
       
  1644     {
       
  1645       FT_StrokeBorder  sborder = & stroker->borders[border];
       
  1646 
       
  1647 
       
  1648       if ( sborder->valid )
       
  1649         ft_stroke_border_export( sborder, outline );
       
  1650     }
       
  1651   }
       
  1652 
       
  1653 
       
  1654   /* documentation is in ftstroke.h */
       
  1655 
       
  1656   FT_EXPORT_DEF( void )
       
  1657   FT_Stroker_Export( FT_Stroker   stroker,
       
  1658                      FT_Outline*  outline )
       
  1659   {
       
  1660     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
       
  1661     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
       
  1662   }
       
  1663 
       
  1664 
       
  1665   /* documentation is in ftstroke.h */
       
  1666 
       
  1667   /*
       
  1668    *  The following is very similar to FT_Outline_Decompose, except
       
  1669    *  that we do support opened paths, and do not scale the outline.
       
  1670    */
       
  1671   FT_EXPORT_DEF( FT_Error )
       
  1672   FT_Stroker_ParseOutline( FT_Stroker   stroker,
       
  1673                            FT_Outline*  outline,
       
  1674                            FT_Bool      opened )
       
  1675   {
       
  1676     FT_Vector   v_last;
       
  1677     FT_Vector   v_control;
       
  1678     FT_Vector   v_start;
       
  1679 
       
  1680     FT_Vector*  point;
       
  1681     FT_Vector*  limit;
       
  1682     char*       tags;
       
  1683 
       
  1684     FT_Error    error;
       
  1685 
       
  1686     FT_Int   n;         /* index of contour in outline     */
       
  1687     FT_UInt  first;     /* index of first point in contour */
       
  1688     FT_Int   tag;       /* current point's state           */
       
  1689 
       
  1690 
       
  1691     if ( !outline || !stroker )
       
  1692       return FT_Err_Invalid_Argument;
       
  1693 
       
  1694     FT_Stroker_Rewind( stroker );
       
  1695 
       
  1696     first = 0;
       
  1697 
       
  1698     for ( n = 0; n < outline->n_contours; n++ )
       
  1699     {
       
  1700       FT_UInt  last;  /* index of last point in contour */
       
  1701 
       
  1702 
       
  1703       last  = outline->contours[n];
       
  1704       limit = outline->points + last;
       
  1705 
       
  1706       /* skip empty points; we don't stroke these */
       
  1707       if ( last <= first )
       
  1708       {
       
  1709         first = last + 1;
       
  1710         continue;
       
  1711       }
       
  1712 
       
  1713       v_start = outline->points[first];
       
  1714       v_last  = outline->points[last];
       
  1715 
       
  1716       v_control = v_start;
       
  1717 
       
  1718       point = outline->points + first;
       
  1719       tags  = outline->tags   + first;
       
  1720       tag   = FT_CURVE_TAG( tags[0] );
       
  1721 
       
  1722       /* A contour cannot start with a cubic control point! */
       
  1723       if ( tag == FT_CURVE_TAG_CUBIC )
       
  1724         goto Invalid_Outline;
       
  1725 
       
  1726       /* check first point to determine origin */
       
  1727       if ( tag == FT_CURVE_TAG_CONIC )
       
  1728       {
       
  1729         /* First point is conic control.  Yes, this happens. */
       
  1730         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
       
  1731         {
       
  1732           /* start at last point if it is on the curve */
       
  1733           v_start = v_last;
       
  1734           limit--;
       
  1735         }
       
  1736         else
       
  1737         {
       
  1738           /* if both first and last points are conic, */
       
  1739           /* start at their middle                    */
       
  1740           v_start.x = ( v_start.x + v_last.x ) / 2;
       
  1741           v_start.y = ( v_start.y + v_last.y ) / 2;
       
  1742         }
       
  1743         point--;
       
  1744         tags--;
       
  1745       }
       
  1746 
       
  1747       error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
       
  1748       if ( error )
       
  1749         goto Exit;
       
  1750 
       
  1751       while ( point < limit )
       
  1752       {
       
  1753         point++;
       
  1754         tags++;
       
  1755 
       
  1756         tag = FT_CURVE_TAG( tags[0] );
       
  1757         switch ( tag )
       
  1758         {
       
  1759         case FT_CURVE_TAG_ON:  /* emit a single line_to */
       
  1760           {
       
  1761             FT_Vector  vec;
       
  1762 
       
  1763 
       
  1764             vec.x = point->x;
       
  1765             vec.y = point->y;
       
  1766 
       
  1767             error = FT_Stroker_LineTo( stroker, &vec );
       
  1768             if ( error )
       
  1769               goto Exit;
       
  1770             continue;
       
  1771           }
       
  1772 
       
  1773         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
       
  1774           v_control.x = point->x;
       
  1775           v_control.y = point->y;
       
  1776 
       
  1777         Do_Conic:
       
  1778           if ( point < limit )
       
  1779           {
       
  1780             FT_Vector  vec;
       
  1781             FT_Vector  v_middle;
       
  1782 
       
  1783 
       
  1784             point++;
       
  1785             tags++;
       
  1786             tag = FT_CURVE_TAG( tags[0] );
       
  1787 
       
  1788             vec = point[0];
       
  1789 
       
  1790             if ( tag == FT_CURVE_TAG_ON )
       
  1791             {
       
  1792               error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
       
  1793               if ( error )
       
  1794                 goto Exit;
       
  1795               continue;
       
  1796             }
       
  1797 
       
  1798             if ( tag != FT_CURVE_TAG_CONIC )
       
  1799               goto Invalid_Outline;
       
  1800 
       
  1801             v_middle.x = ( v_control.x + vec.x ) / 2;
       
  1802             v_middle.y = ( v_control.y + vec.y ) / 2;
       
  1803 
       
  1804             error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
       
  1805             if ( error )
       
  1806               goto Exit;
       
  1807 
       
  1808             v_control = vec;
       
  1809             goto Do_Conic;
       
  1810           }
       
  1811 
       
  1812           error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
       
  1813           goto Close;
       
  1814 
       
  1815         default:  /* FT_CURVE_TAG_CUBIC */
       
  1816           {
       
  1817             FT_Vector  vec1, vec2;
       
  1818 
       
  1819 
       
  1820             if ( point + 1 > limit                             ||
       
  1821                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
       
  1822               goto Invalid_Outline;
       
  1823 
       
  1824             point += 2;
       
  1825             tags  += 2;
       
  1826 
       
  1827             vec1 = point[-2];
       
  1828             vec2 = point[-1];
       
  1829 
       
  1830             if ( point <= limit )
       
  1831             {
       
  1832               FT_Vector  vec;
       
  1833 
       
  1834 
       
  1835               vec = point[0];
       
  1836 
       
  1837               error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
       
  1838               if ( error )
       
  1839                 goto Exit;
       
  1840               continue;
       
  1841             }
       
  1842 
       
  1843             error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
       
  1844             goto Close;
       
  1845           }
       
  1846         }
       
  1847       }
       
  1848 
       
  1849     Close:
       
  1850       if ( error )
       
  1851         goto Exit;
       
  1852 
       
  1853       error = FT_Stroker_EndSubPath( stroker );
       
  1854       if ( error )
       
  1855         goto Exit;
       
  1856 
       
  1857       first = last + 1;
       
  1858     }
       
  1859 
       
  1860     return FT_Err_Ok;
       
  1861 
       
  1862   Exit:
       
  1863     return error;
       
  1864 
       
  1865   Invalid_Outline:
       
  1866     return FT_Err_Invalid_Outline;
       
  1867   }
       
  1868 
       
  1869 /* declare an extern to access ft_outline_glyph_class global allocated 
       
  1870    in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access 
       
  1871    it when FT_CONFIG_OPTION_PIC is defined */
       
  1872 #ifndef FT_CONFIG_OPTION_PIC
       
  1873   extern const FT_Glyph_Class  ft_outline_glyph_class;
       
  1874 #endif
       
  1875 #include "basepic.h"
       
  1876 
       
  1877 
       
  1878   /* documentation is in ftstroke.h */
       
  1879 
       
  1880   FT_EXPORT_DEF( FT_Error )
       
  1881   FT_Glyph_Stroke( FT_Glyph    *pglyph,
       
  1882                    FT_Stroker   stroker,
       
  1883                    FT_Bool      destroy )
       
  1884   {
       
  1885     FT_Error  error = FT_Err_Invalid_Argument;
       
  1886     FT_Glyph  glyph = NULL;
       
  1887     FT_Library library = stroker->library;
       
  1888     FT_UNUSED(library);
       
  1889 
       
  1890     if ( pglyph == NULL )
       
  1891       goto Exit;
       
  1892 
       
  1893     glyph = *pglyph;
       
  1894     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
       
  1895       goto Exit;
       
  1896 
       
  1897     {
       
  1898       FT_Glyph  copy;
       
  1899 
       
  1900 
       
  1901       error = FT_Glyph_Copy( glyph, &copy );
       
  1902       if ( error )
       
  1903         goto Exit;
       
  1904 
       
  1905       glyph = copy;
       
  1906     }
       
  1907 
       
  1908     {
       
  1909       FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
       
  1910       FT_Outline*      outline = &oglyph->outline;
       
  1911       FT_UInt          num_points, num_contours;
       
  1912 
       
  1913 
       
  1914       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
       
  1915       if ( error )
       
  1916         goto Fail;
       
  1917 
       
  1918       (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
       
  1919 
       
  1920       FT_Outline_Done( glyph->library, outline );
       
  1921 
       
  1922       error = FT_Outline_New( glyph->library,
       
  1923                               num_points, num_contours, outline );
       
  1924       if ( error )
       
  1925         goto Fail;
       
  1926 
       
  1927       outline->n_points   = 0;
       
  1928       outline->n_contours = 0;
       
  1929 
       
  1930       FT_Stroker_Export( stroker, outline );
       
  1931     }
       
  1932 
       
  1933     if ( destroy )
       
  1934       FT_Done_Glyph( *pglyph );
       
  1935 
       
  1936     *pglyph = glyph;
       
  1937     goto Exit;
       
  1938 
       
  1939   Fail:
       
  1940     FT_Done_Glyph( glyph );
       
  1941     glyph = NULL;
       
  1942 
       
  1943     if ( !destroy )
       
  1944       *pglyph = NULL;
       
  1945 
       
  1946   Exit:
       
  1947     return error;
       
  1948   }
       
  1949 
       
  1950 
       
  1951   /* documentation is in ftstroke.h */
       
  1952 
       
  1953   FT_EXPORT_DEF( FT_Error )
       
  1954   FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
       
  1955                          FT_Stroker   stroker,
       
  1956                          FT_Bool      inside,
       
  1957                          FT_Bool      destroy )
       
  1958   {
       
  1959     FT_Error  error = FT_Err_Invalid_Argument;
       
  1960     FT_Glyph  glyph = NULL;
       
  1961     FT_Library library = stroker->library;
       
  1962     FT_UNUSED(library);
       
  1963 
       
  1964     if ( pglyph == NULL )
       
  1965       goto Exit;
       
  1966 
       
  1967     glyph = *pglyph;
       
  1968     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
       
  1969       goto Exit;
       
  1970 
       
  1971     {
       
  1972       FT_Glyph  copy;
       
  1973 
       
  1974 
       
  1975       error = FT_Glyph_Copy( glyph, &copy );
       
  1976       if ( error )
       
  1977         goto Exit;
       
  1978 
       
  1979       glyph = copy;
       
  1980     }
       
  1981 
       
  1982     {
       
  1983       FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
       
  1984       FT_StrokerBorder  border;
       
  1985       FT_Outline*       outline = &oglyph->outline;
       
  1986       FT_UInt           num_points, num_contours;
       
  1987 
       
  1988 
       
  1989       border = FT_Outline_GetOutsideBorder( outline );
       
  1990       if ( inside )
       
  1991       {
       
  1992         if ( border == FT_STROKER_BORDER_LEFT )
       
  1993           border = FT_STROKER_BORDER_RIGHT;
       
  1994         else
       
  1995           border = FT_STROKER_BORDER_LEFT;
       
  1996       }
       
  1997 
       
  1998       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
       
  1999       if ( error )
       
  2000         goto Fail;
       
  2001 
       
  2002       (void)FT_Stroker_GetBorderCounts( stroker, border,
       
  2003                                         &num_points, &num_contours );
       
  2004 
       
  2005       FT_Outline_Done( glyph->library, outline );
       
  2006 
       
  2007       error = FT_Outline_New( glyph->library,
       
  2008                               num_points,
       
  2009                               num_contours,
       
  2010                               outline );
       
  2011       if ( error )
       
  2012         goto Fail;
       
  2013 
       
  2014       outline->n_points   = 0;
       
  2015       outline->n_contours = 0;
       
  2016 
       
  2017       FT_Stroker_ExportBorder( stroker, border, outline );
       
  2018     }
       
  2019 
       
  2020     if ( destroy )
       
  2021       FT_Done_Glyph( *pglyph );
       
  2022 
       
  2023     *pglyph = glyph;
       
  2024     goto Exit;
       
  2025 
       
  2026   Fail:
       
  2027     FT_Done_Glyph( glyph );
       
  2028     glyph = NULL;
       
  2029 
       
  2030     if ( !destroy )
       
  2031       *pglyph = NULL;
       
  2032 
       
  2033   Exit:
       
  2034     return error;
       
  2035   }
       
  2036 
       
  2037 
       
  2038 /* END */