ftstroke.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  ftstroke.c                                                             */
00004 /*                                                                         */
00005 /*    FreeType path stroker (body).                                        */
00006 /*                                                                         */
00007 /*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 by            */
00008 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00009 /*                                                                         */
00010 /*  This file is part of the FreeType project, and may only be used,       */
00011 /*  modified, and distributed under the terms of the FreeType project      */
00012 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00013 /*  this file you indicate that you have read the license and              */
00014 /*  understand and accept it fully.                                        */
00015 /*                                                                         */
00016 /***************************************************************************/
00017 
00018 
00019 #include <ft2build.h>
00020 #include FT_STROKER_H
00021 #include FT_TRIGONOMETRY_H
00022 #include FT_OUTLINE_H
00023 #include FT_INTERNAL_MEMORY_H
00024 #include FT_INTERNAL_DEBUG_H
00025 #include FT_INTERNAL_OBJECTS_H
00026 
00027 
00028   /* documentation is in ftstroke.h */
00029 
00030   FT_EXPORT_DEF( FT_StrokerBorder )
00031   FT_Outline_GetInsideBorder( FT_Outline*  outline )
00032   {
00033     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
00034 
00035 
00036     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
00037                                         : FT_STROKER_BORDER_LEFT ;
00038   }
00039 
00040 
00041   /* documentation is in ftstroke.h */
00042 
00043   FT_EXPORT_DEF( FT_StrokerBorder )
00044   FT_Outline_GetOutsideBorder( FT_Outline*  outline )
00045   {
00046     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
00047 
00048 
00049     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
00050                                         : FT_STROKER_BORDER_RIGHT ;
00051   }
00052 
00053 
00054  /***************************************************************************/
00055  /***************************************************************************/
00056  /*****                                                                 *****/
00057  /*****                       BEZIER COMPUTATIONS                       *****/
00058  /*****                                                                 *****/
00059  /***************************************************************************/
00060  /***************************************************************************/
00061 
00062 #define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
00063 #define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
00064 #define FT_EPSILON  2
00065 
00066 #define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
00067 
00068 
00069   static FT_Pos
00070   ft_pos_abs( FT_Pos  x )
00071   {
00072     return x >= 0 ? x : -x ;
00073   }
00074 
00075 
00076   static void
00077   ft_conic_split( FT_Vector*  base )
00078   {
00079     FT_Pos  a, b;
00080 
00081 
00082     base[4].x = base[2].x;
00083     b = base[1].x;
00084     a = base[3].x = ( base[2].x + b ) / 2;
00085     b = base[1].x = ( base[0].x + b ) / 2;
00086     base[2].x = ( a + b ) / 2;
00087 
00088     base[4].y = base[2].y;
00089     b = base[1].y;
00090     a = base[3].y = ( base[2].y + b ) / 2;
00091     b = base[1].y = ( base[0].y + b ) / 2;
00092     base[2].y = ( a + b ) / 2;
00093   }
00094 
00095 
00096   static FT_Bool
00097   ft_conic_is_small_enough( FT_Vector*  base,
00098                             FT_Angle   *angle_in,
00099                             FT_Angle   *angle_out )
00100   {
00101     FT_Vector  d1, d2;
00102     FT_Angle   theta;
00103     FT_Int     close1, close2;
00104 
00105 
00106     d1.x = base[1].x - base[2].x;
00107     d1.y = base[1].y - base[2].y;
00108     d2.x = base[0].x - base[1].x;
00109     d2.y = base[0].y - base[1].y;
00110 
00111     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
00112     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
00113 
00114     if ( close1 )
00115     {
00116       if ( close2 )
00117         *angle_in = *angle_out = 0;
00118       else
00119         *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
00120     }
00121     else if ( close2 )
00122     {
00123       *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
00124     }
00125     else
00126     {
00127       *angle_in  = FT_Atan2( d1.x, d1.y );
00128       *angle_out = FT_Atan2( d2.x, d2.y );
00129     }
00130 
00131     theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
00132 
00133     return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
00134   }
00135 
00136 
00137   static void
00138   ft_cubic_split( FT_Vector*  base )
00139   {
00140     FT_Pos  a, b, c, d;
00141 
00142 
00143     base[6].x = base[3].x;
00144     c = base[1].x;
00145     d = base[2].x;
00146     base[1].x = a = ( base[0].x + c ) / 2;
00147     base[5].x = b = ( base[3].x + d ) / 2;
00148     c = ( c + d ) / 2;
00149     base[2].x = a = ( a + c ) / 2;
00150     base[4].x = b = ( b + c ) / 2;
00151     base[3].x = ( a + b ) / 2;
00152 
00153     base[6].y = base[3].y;
00154     c = base[1].y;
00155     d = base[2].y;
00156     base[1].y = a = ( base[0].y + c ) / 2;
00157     base[5].y = b = ( base[3].y + d ) / 2;
00158     c = ( c + d ) / 2;
00159     base[2].y = a = ( a + c ) / 2;
00160     base[4].y = b = ( b + c ) / 2;
00161     base[3].y = ( a + b ) / 2;
00162   }
00163 
00164 
00165   static FT_Bool
00166   ft_cubic_is_small_enough( FT_Vector*  base,
00167                             FT_Angle   *angle_in,
00168                             FT_Angle   *angle_mid,
00169                             FT_Angle   *angle_out )
00170   {
00171     FT_Vector  d1, d2, d3;
00172     FT_Angle   theta1, theta2;
00173     FT_Int     close1, close2, close3;
00174 
00175 
00176     d1.x = base[2].x - base[3].x;
00177     d1.y = base[2].y - base[3].y;
00178     d2.x = base[1].x - base[2].x;
00179     d2.y = base[1].y - base[2].y;
00180     d3.x = base[0].x - base[1].x;
00181     d3.y = base[0].y - base[1].y;
00182 
00183     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
00184     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
00185     close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
00186 
00187     if ( close1 || close3 )
00188     {
00189       if ( close2 )
00190       {
00191         /* basically a point */
00192         *angle_in = *angle_out = *angle_mid = 0;
00193       }
00194       else if ( close1 )
00195       {
00196         *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
00197         *angle_out = FT_Atan2( d3.x, d3.y );
00198       }
00199       else  /* close2 */
00200       {
00201         *angle_in  = FT_Atan2( d1.x, d1.y );
00202         *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
00203       }
00204     }
00205     else if ( close2 )
00206     {
00207       *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
00208       *angle_out = FT_Atan2( d3.x, d3.y );
00209     }
00210     else
00211     {
00212       *angle_in  = FT_Atan2( d1.x, d1.y );
00213       *angle_mid = FT_Atan2( d2.x, d2.y );
00214       *angle_out = FT_Atan2( d3.x, d3.y );
00215     }
00216 
00217     theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
00218     theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
00219 
00220     return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
00221                     theta2 < FT_SMALL_CUBIC_THRESHOLD );
00222   }
00223 
00224 
00225  /***************************************************************************/
00226  /***************************************************************************/
00227  /*****                                                                 *****/
00228  /*****                       STROKE BORDERS                            *****/
00229  /*****                                                                 *****/
00230  /***************************************************************************/
00231  /***************************************************************************/
00232 
00233   typedef enum  FT_StrokeTags_
00234   {
00235     FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
00236     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
00237     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
00238     FT_STROKE_TAG_END   = 8    /* sub-path end    */
00239 
00240   } FT_StrokeTags;
00241 
00242 #define  FT_STROKE_TAG_BEGIN_END  (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
00243 
00244   typedef struct  FT_StrokeBorderRec_
00245   {
00246     FT_UInt     num_points;
00247     FT_UInt     max_points;
00248     FT_Vector*  points;
00249     FT_Byte*    tags;
00250     FT_Bool     movable;
00251     FT_Int      start;    /* index of current sub-path start point */
00252     FT_Memory   memory;
00253     FT_Bool     valid;
00254 
00255   } FT_StrokeBorderRec, *FT_StrokeBorder;
00256 
00257 
00258   static FT_Error
00259   ft_stroke_border_grow( FT_StrokeBorder  border,
00260                          FT_UInt          new_points )
00261   {
00262     FT_UInt   old_max = border->max_points;
00263     FT_UInt   new_max = border->num_points + new_points;
00264     FT_Error  error   = FT_Err_Ok;
00265 
00266 
00267     if ( new_max > old_max )
00268     {
00269       FT_UInt    cur_max = old_max;
00270       FT_Memory  memory  = border->memory;
00271 
00272 
00273       while ( cur_max < new_max )
00274         cur_max += ( cur_max >> 1 ) + 16;
00275 
00276       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
00277            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
00278         goto Exit;
00279 
00280       border->max_points = cur_max;
00281     }
00282 
00283   Exit:
00284     return error;
00285   }
00286 
00287 
00288   static void
00289   ft_stroke_border_close( FT_StrokeBorder  border,
00290                           FT_Bool          reverse )
00291   {
00292     FT_UInt  start = border->start;
00293     FT_UInt  count = border->num_points;
00294 
00295 
00296     FT_ASSERT( border->start >= 0 );
00297 
00298     /* don't record empty paths! */
00299     if ( count <= start + 1U )
00300       border->num_points = start;
00301     else
00302     {
00303       /* copy the last point to the start of this sub-path, since */
00304       /* it contains the `adjusted' starting coordinates          */
00305       border->num_points    = --count;
00306       border->points[start] = border->points[count];
00307 
00308       if ( reverse )
00309       {
00310         /* reverse the points */
00311         {
00312           FT_Vector*  vec1 = border->points + start + 1;
00313           FT_Vector*  vec2 = border->points + count - 1;
00314 
00315 
00316           for ( ; vec1 < vec2; vec1++, vec2-- )
00317           {
00318             FT_Vector  tmp;
00319 
00320 
00321             tmp   = *vec1;
00322             *vec1 = *vec2;
00323             *vec2 = tmp;
00324           }
00325         }
00326 
00327         /* then the tags */
00328         {
00329           FT_Byte*  tag1 = border->tags + start + 1;
00330           FT_Byte*  tag2 = border->tags + count - 1;
00331 
00332 
00333           for ( ; tag1 < tag2; tag1++, tag2-- )
00334           {
00335             FT_Byte  tmp;
00336 
00337 
00338             tmp   = *tag1;
00339             *tag1 = *tag2;
00340             *tag2 = tmp;
00341           }
00342         }
00343       }
00344 
00345       border->tags[start    ] |= FT_STROKE_TAG_BEGIN;
00346       border->tags[count - 1] |= FT_STROKE_TAG_END;
00347     }
00348 
00349     border->start   = -1;
00350     border->movable = FALSE;
00351   }
00352 
00353 
00354   static FT_Error
00355   ft_stroke_border_lineto( FT_StrokeBorder  border,
00356                            FT_Vector*       to,
00357                            FT_Bool          movable )
00358   {
00359     FT_Error  error = FT_Err_Ok;
00360 
00361 
00362     FT_ASSERT( border->start >= 0 );
00363 
00364     if ( border->movable )
00365     {
00366       /* move last point */
00367       border->points[border->num_points - 1] = *to;
00368     }
00369     else
00370     {
00371       /* add one point */
00372       error = ft_stroke_border_grow( border, 1 );
00373       if ( !error )
00374       {
00375         FT_Vector*  vec = border->points + border->num_points;
00376         FT_Byte*    tag = border->tags   + border->num_points;
00377 
00378 
00379         vec[0] = *to;
00380         tag[0] = FT_STROKE_TAG_ON;
00381 
00382         border->num_points += 1;
00383       }
00384     }
00385     border->movable = movable;
00386     return error;
00387   }
00388 
00389 
00390   static FT_Error
00391   ft_stroke_border_conicto( FT_StrokeBorder  border,
00392                             FT_Vector*       control,
00393                             FT_Vector*       to )
00394   {
00395     FT_Error  error;
00396 
00397 
00398     FT_ASSERT( border->start >= 0 );
00399 
00400     error = ft_stroke_border_grow( border, 2 );
00401     if ( !error )
00402     {
00403       FT_Vector*  vec = border->points + border->num_points;
00404       FT_Byte*    tag = border->tags   + border->num_points;
00405 
00406       vec[0] = *control;
00407       vec[1] = *to;
00408 
00409       tag[0] = 0;
00410       tag[1] = FT_STROKE_TAG_ON;
00411 
00412       border->num_points += 2;
00413     }
00414     border->movable = FALSE;
00415     return error;
00416   }
00417 
00418 
00419   static FT_Error
00420   ft_stroke_border_cubicto( FT_StrokeBorder  border,
00421                             FT_Vector*       control1,
00422                             FT_Vector*       control2,
00423                             FT_Vector*       to )
00424   {
00425     FT_Error  error;
00426 
00427 
00428     FT_ASSERT( border->start >= 0 );
00429 
00430     error = ft_stroke_border_grow( border, 3 );
00431     if ( !error )
00432     {
00433       FT_Vector*  vec = border->points + border->num_points;
00434       FT_Byte*    tag = border->tags   + border->num_points;
00435 
00436 
00437       vec[0] = *control1;
00438       vec[1] = *control2;
00439       vec[2] = *to;
00440 
00441       tag[0] = FT_STROKE_TAG_CUBIC;
00442       tag[1] = FT_STROKE_TAG_CUBIC;
00443       tag[2] = FT_STROKE_TAG_ON;
00444 
00445       border->num_points += 3;
00446     }
00447     border->movable = FALSE;
00448     return error;
00449   }
00450 
00451 
00452 #define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
00453 
00454 
00455   static FT_Error
00456   ft_stroke_border_arcto( FT_StrokeBorder  border,
00457                           FT_Vector*       center,
00458                           FT_Fixed         radius,
00459                           FT_Angle         angle_start,
00460                           FT_Angle         angle_diff )
00461   {
00462     FT_Angle   total, angle, step, rotate, next, theta;
00463     FT_Vector  a, b, a2, b2;
00464     FT_Fixed   length;
00465     FT_Error   error = FT_Err_Ok;
00466 
00467 
00468     /* compute start point */
00469     FT_Vector_From_Polar( &a, radius, angle_start );
00470     a.x += center->x;
00471     a.y += center->y;
00472 
00473     total  = angle_diff;
00474     angle  = angle_start;
00475     rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
00476 
00477     while ( total != 0 )
00478     {
00479       step = total;
00480       if ( step > FT_ARC_CUBIC_ANGLE )
00481         step = FT_ARC_CUBIC_ANGLE;
00482 
00483       else if ( step < -FT_ARC_CUBIC_ANGLE )
00484         step = -FT_ARC_CUBIC_ANGLE;
00485 
00486       next  = angle + step;
00487       theta = step;
00488       if ( theta < 0 )
00489         theta = -theta;
00490 
00491       theta >>= 1;
00492 
00493       /* compute end point */
00494       FT_Vector_From_Polar( &b, radius, next );
00495       b.x += center->x;
00496       b.y += center->y;
00497 
00498       /* compute first and second control points */
00499       length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
00500                           ( 0x10000L + FT_Cos( theta ) ) * 3 );
00501 
00502       FT_Vector_From_Polar( &a2, length, angle + rotate );
00503       a2.x += a.x;
00504       a2.y += a.y;
00505 
00506       FT_Vector_From_Polar( &b2, length, next - rotate );
00507       b2.x += b.x;
00508       b2.y += b.y;
00509 
00510       /* add cubic arc */
00511       error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
00512       if ( error )
00513         break;
00514 
00515       /* process the rest of the arc ?? */
00516       a      = b;
00517       total -= step;
00518       angle  = next;
00519     }
00520 
00521     return error;
00522   }
00523 
00524 
00525   static FT_Error
00526   ft_stroke_border_moveto( FT_StrokeBorder  border,
00527                            FT_Vector*       to )
00528   {
00529     /* close current open path if any ? */
00530     if ( border->start >= 0 )
00531       ft_stroke_border_close( border, FALSE );
00532 
00533     border->start   = border->num_points;
00534     border->movable = FALSE;
00535 
00536     return ft_stroke_border_lineto( border, to, FALSE );
00537   }
00538 
00539 
00540   static void
00541   ft_stroke_border_init( FT_StrokeBorder  border,
00542                          FT_Memory        memory )
00543   {
00544     border->memory = memory;
00545     border->points = NULL;
00546     border->tags   = NULL;
00547 
00548     border->num_points = 0;
00549     border->max_points = 0;
00550     border->start      = -1;
00551     border->valid      = FALSE;
00552   }
00553 
00554 
00555   static void
00556   ft_stroke_border_reset( FT_StrokeBorder  border )
00557   {
00558     border->num_points = 0;
00559     border->start      = -1;
00560     border->valid      = FALSE;
00561   }
00562 
00563 
00564   static void
00565   ft_stroke_border_done( FT_StrokeBorder  border )
00566   {
00567     FT_Memory  memory = border->memory;
00568 
00569 
00570     FT_FREE( border->points );
00571     FT_FREE( border->tags );
00572 
00573     border->num_points = 0;
00574     border->max_points = 0;
00575     border->start      = -1;
00576     border->valid      = FALSE;
00577   }
00578 
00579 
00580   static FT_Error
00581   ft_stroke_border_get_counts( FT_StrokeBorder  border,
00582                                FT_UInt         *anum_points,
00583                                FT_UInt         *anum_contours )
00584   {
00585     FT_Error  error        = FT_Err_Ok;
00586     FT_UInt   num_points   = 0;
00587     FT_UInt   num_contours = 0;
00588 
00589     FT_UInt     count      = border->num_points;
00590     FT_Vector*  point      = border->points;
00591     FT_Byte*    tags       = border->tags;
00592     FT_Int      in_contour = 0;
00593 
00594 
00595     for ( ; count > 0; count--, num_points++, point++, tags++ )
00596     {
00597       if ( tags[0] & FT_STROKE_TAG_BEGIN )
00598       {
00599         if ( in_contour != 0 )
00600           goto Fail;
00601 
00602         in_contour = 1;
00603       }
00604       else if ( in_contour == 0 )
00605         goto Fail;
00606 
00607       if ( tags[0] & FT_STROKE_TAG_END )
00608       {
00609         in_contour = 0;
00610         num_contours++;
00611       }
00612     }
00613 
00614     if ( in_contour != 0 )
00615       goto Fail;
00616 
00617     border->valid = TRUE;
00618 
00619   Exit:
00620     *anum_points   = num_points;
00621     *anum_contours = num_contours;
00622     return error;
00623 
00624   Fail:
00625     num_points   = 0;
00626     num_contours = 0;
00627     goto Exit;
00628   }
00629 
00630 
00631   static void
00632   ft_stroke_border_export( FT_StrokeBorder  border,
00633                            FT_Outline*      outline )
00634   {
00635     /* copy point locations */
00636     FT_ARRAY_COPY( outline->points + outline->n_points,
00637                    border->points,
00638                    border->num_points );
00639 
00640     /* copy tags */
00641     {
00642       FT_UInt   count = border->num_points;
00643       FT_Byte*  read  = border->tags;
00644       FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
00645 
00646 
00647       for ( ; count > 0; count--, read++, write++ )
00648       {
00649         if ( *read & FT_STROKE_TAG_ON )
00650           *write = FT_CURVE_TAG_ON;
00651         else if ( *read & FT_STROKE_TAG_CUBIC )
00652           *write = FT_CURVE_TAG_CUBIC;
00653         else
00654           *write = FT_CURVE_TAG_CONIC;
00655       }
00656     }
00657 
00658     /* copy contours */
00659     {
00660       FT_UInt    count = border->num_points;
00661       FT_Byte*   tags  = border->tags;
00662       FT_Short*  write = outline->contours + outline->n_contours;
00663       FT_Short   idx   = (FT_Short)outline->n_points;
00664 
00665 
00666       for ( ; count > 0; count--, tags++, idx++ )
00667       {
00668         if ( *tags & FT_STROKE_TAG_END )
00669         {
00670           *write++ = idx;
00671           outline->n_contours++;
00672         }
00673       }
00674     }
00675 
00676     outline->n_points  = (short)( outline->n_points + border->num_points );
00677 
00678     FT_ASSERT( FT_Outline_Check( outline ) == 0 );
00679   }
00680 
00681 
00682  /***************************************************************************/
00683  /***************************************************************************/
00684  /*****                                                                 *****/
00685  /*****                           STROKER                               *****/
00686  /*****                                                                 *****/
00687  /***************************************************************************/
00688  /***************************************************************************/
00689 
00690 #define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
00691 
00692   typedef struct  FT_StrokerRec_
00693   {
00694     FT_Angle             angle_in;
00695     FT_Angle             angle_out;
00696     FT_Vector            center;
00697     FT_Bool              first_point;
00698     FT_Bool              subpath_open;
00699     FT_Angle             subpath_angle;
00700     FT_Vector            subpath_start;
00701 
00702     FT_Stroker_LineCap   line_cap;
00703     FT_Stroker_LineJoin  line_join;
00704     FT_Fixed             miter_limit;
00705     FT_Fixed             radius;
00706 
00707     FT_Bool              valid;
00708     FT_StrokeBorderRec   borders[2];
00709     FT_Library           library;
00710 
00711   } FT_StrokerRec;
00712 
00713 
00714   /* documentation is in ftstroke.h */
00715 
00716   FT_EXPORT_DEF( FT_Error )
00717   FT_Stroker_New( FT_Library   library,
00718                   FT_Stroker  *astroker )
00719   {
00720     FT_Error    error;
00721     FT_Memory   memory;
00722     FT_Stroker  stroker;
00723 
00724 
00725     if ( !library )
00726       return FT_Err_Invalid_Argument;
00727 
00728     memory = library->memory;
00729 
00730     if ( !FT_NEW( stroker ) )
00731     {
00732       stroker->library = library;
00733 
00734       ft_stroke_border_init( &stroker->borders[0], memory );
00735       ft_stroke_border_init( &stroker->borders[1], memory );
00736     }
00737     *astroker = stroker;
00738     return error;
00739   }
00740 
00741 
00742   /* documentation is in ftstroke.h */
00743 
00744   FT_EXPORT_DEF( void )
00745   FT_Stroker_Set( FT_Stroker           stroker,
00746                   FT_Fixed             radius,
00747                   FT_Stroker_LineCap   line_cap,
00748                   FT_Stroker_LineJoin  line_join,
00749                   FT_Fixed             miter_limit )
00750   {
00751     stroker->radius      = radius;
00752     stroker->line_cap    = line_cap;
00753     stroker->line_join   = line_join;
00754     stroker->miter_limit = miter_limit;
00755 
00756     FT_Stroker_Rewind( stroker );
00757   }
00758 
00759 
00760   /* documentation is in ftstroke.h */
00761 
00762   FT_EXPORT_DEF( void )
00763   FT_Stroker_Rewind( FT_Stroker  stroker )
00764   {
00765     if ( stroker )
00766     {
00767       ft_stroke_border_reset( &stroker->borders[0] );
00768       ft_stroke_border_reset( &stroker->borders[1] );
00769     }
00770   }
00771 
00772 
00773   /* documentation is in ftstroke.h */
00774 
00775   FT_EXPORT_DEF( void )
00776   FT_Stroker_Done( FT_Stroker  stroker )
00777   {
00778     if ( stroker )
00779     {
00780       FT_Memory  memory = stroker->library->memory;
00781 
00782 
00783       ft_stroke_border_done( &stroker->borders[0] );
00784       ft_stroke_border_done( &stroker->borders[1] );
00785 
00786       stroker->library = NULL;
00787       FT_FREE( stroker );
00788     }
00789   }
00790 
00791 
00792   /* creates a circular arc at a corner or cap */
00793   static FT_Error
00794   ft_stroker_arcto( FT_Stroker  stroker,
00795                     FT_Int      side )
00796   {
00797     FT_Angle         total, rotate;
00798     FT_Fixed         radius = stroker->radius;
00799     FT_Error         error  = FT_Err_Ok;
00800     FT_StrokeBorder  border = stroker->borders + side;
00801 
00802 
00803     rotate = FT_SIDE_TO_ROTATE( side );
00804 
00805     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00806     if ( total == FT_ANGLE_PI )
00807       total = -rotate * 2;
00808 
00809     error = ft_stroke_border_arcto( border,
00810                                     &stroker->center,
00811                                     radius,
00812                                     stroker->angle_in + rotate,
00813                                     total );
00814     border->movable = FALSE;
00815     return error;
00816   }
00817 
00818 
00819   /* adds a cap at the end of an opened path */
00820   static FT_Error
00821   ft_stroker_cap( FT_Stroker  stroker,
00822                   FT_Angle    angle,
00823                   FT_Int      side )
00824   {
00825     FT_Error  error = FT_Err_Ok;
00826 
00827 
00828     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
00829     {
00830       /* add a round cap */
00831       stroker->angle_in  = angle;
00832       stroker->angle_out = angle + FT_ANGLE_PI;
00833       error = ft_stroker_arcto( stroker, side );
00834     }
00835     else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
00836     {
00837       /* add a square cap */
00838       FT_Vector        delta, delta2;
00839       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
00840       FT_Fixed         radius = stroker->radius;
00841       FT_StrokeBorder  border = stroker->borders + side;
00842 
00843 
00844       FT_Vector_From_Polar( &delta2, radius, angle + rotate );
00845       FT_Vector_From_Polar( &delta,  radius, angle );
00846 
00847       delta.x += stroker->center.x + delta2.x;
00848       delta.y += stroker->center.y + delta2.y;
00849 
00850       error = ft_stroke_border_lineto( border, &delta, FALSE );
00851       if ( error )
00852         goto Exit;
00853 
00854       FT_Vector_From_Polar( &delta2, radius, angle - rotate );
00855       FT_Vector_From_Polar( &delta,  radius, angle );
00856 
00857       delta.x += delta2.x + stroker->center.x;
00858       delta.y += delta2.y + stroker->center.y;
00859 
00860       error = ft_stroke_border_lineto( border, &delta, FALSE );
00861     }
00862     else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
00863     {
00864       /* add a butt ending */
00865       FT_Vector        delta;
00866       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
00867       FT_Fixed         radius = stroker->radius;
00868       FT_StrokeBorder  border = stroker->borders + side;
00869 
00870 
00871       FT_Vector_From_Polar( &delta, radius, angle + rotate );
00872 
00873       delta.x += stroker->center.x;
00874       delta.y += stroker->center.y;
00875 
00876       error = ft_stroke_border_lineto( border, &delta, FALSE );
00877       if ( error )
00878         goto Exit;
00879 
00880       FT_Vector_From_Polar( &delta, radius, angle - rotate );
00881 
00882       delta.x += stroker->center.x;
00883       delta.y += stroker->center.y;
00884 
00885       error = ft_stroke_border_lineto( border, &delta, FALSE );   
00886     }
00887 
00888   Exit:
00889     return error;
00890   }
00891 
00892 
00893   /* process an inside corner, i.e. compute intersection */
00894   static FT_Error
00895   ft_stroker_inside( FT_Stroker  stroker,
00896                      FT_Int      side)
00897   {
00898     FT_StrokeBorder  border = stroker->borders + side;
00899     FT_Angle         phi, theta, rotate;
00900     FT_Fixed         length, thcos, sigma;
00901     FT_Vector        delta;
00902     FT_Error         error = FT_Err_Ok;
00903 
00904 
00905     rotate = FT_SIDE_TO_ROTATE( side );
00906 
00907     /* compute median angle */
00908     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00909     if ( theta == FT_ANGLE_PI )
00910       theta = rotate;
00911     else
00912       theta = theta / 2;
00913 
00914     phi = stroker->angle_in + theta;
00915 
00916     thcos = FT_Cos( theta );
00917     sigma = FT_MulFix( stroker->miter_limit, thcos );
00918 
00919     /* TODO: find better criterion to switch off the optimization */
00920     if ( sigma < 0x10000L )
00921     {
00922       FT_Vector_From_Polar( &delta, stroker->radius,
00923                             stroker->angle_out + rotate );
00924       delta.x += stroker->center.x;
00925       delta.y += stroker->center.y;
00926       border->movable = FALSE;
00927     }
00928     else
00929     {
00930       length = FT_DivFix( stroker->radius, thcos );
00931 
00932       FT_Vector_From_Polar( &delta, length, phi + rotate );
00933       delta.x += stroker->center.x;
00934       delta.y += stroker->center.y;
00935     }
00936 
00937     error = ft_stroke_border_lineto( border, &delta, FALSE );
00938 
00939     return error;
00940   }
00941 
00942 
00943   /* process an outside corner, i.e. compute bevel/miter/round */
00944   static FT_Error
00945   ft_stroker_outside( FT_Stroker  stroker,
00946                       FT_Int      side )
00947   {
00948     FT_StrokeBorder  border = stroker->borders + side;
00949     FT_Error         error;
00950     FT_Angle         rotate;
00951 
00952 
00953     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
00954       error = ft_stroker_arcto( stroker, side );
00955     else
00956     {
00957       /* this is a mitered or beveled corner */
00958       FT_Fixed  sigma, radius = stroker->radius;
00959       FT_Angle  theta, phi;
00960       FT_Fixed  thcos;
00961       FT_Bool   miter;
00962 
00963 
00964       rotate = FT_SIDE_TO_ROTATE( side );
00965       miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
00966 
00967       theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00968       if ( theta == FT_ANGLE_PI )
00969       {
00970         theta = rotate;
00971         phi   = stroker->angle_in;
00972       }
00973       else
00974       {
00975         theta = theta / 2;
00976         phi   = stroker->angle_in + theta + rotate;
00977       }
00978 
00979       thcos = FT_Cos( theta );
00980       sigma = FT_MulFix( stroker->miter_limit, thcos );
00981 
00982       /* FT_Sin(x) = 0 for x <= 57 */
00983       if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
00984         miter = FALSE;
00985 
00986       if ( miter )  /* this is a miter (broken angle) */
00987       {
00988         FT_Vector  middle, delta;
00989         FT_Fixed   length;
00990 
00991 
00992         /* compute middle point */
00993         FT_Vector_From_Polar( &middle,
00994                               FT_MulFix( radius, stroker->miter_limit ),
00995                               phi );
00996         middle.x += stroker->center.x;
00997         middle.y += stroker->center.y;
00998 
00999         /* compute first angle point */
01000         length = FT_MulFix( radius,
01001                             FT_DivFix( 0x10000L - sigma,
01002                                        ft_pos_abs( FT_Sin( theta ) ) ) );
01003 
01004         FT_Vector_From_Polar( &delta, length, phi + rotate );
01005         delta.x += middle.x;
01006         delta.y += middle.y;
01007 
01008         error = ft_stroke_border_lineto( border, &delta, FALSE );
01009         if ( error )
01010           goto Exit;
01011 
01012         /* compute second angle point */
01013         FT_Vector_From_Polar( &delta, length, phi - rotate );
01014         delta.x += middle.x;
01015         delta.y += middle.y;
01016 
01017         error = ft_stroke_border_lineto( border, &delta, FALSE );
01018         if ( error )
01019           goto Exit;
01020 
01021         /* finally, add a movable end point */
01022         FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
01023         delta.x += stroker->center.x;
01024         delta.y += stroker->center.y;
01025 
01026         error = ft_stroke_border_lineto( border, &delta, TRUE );
01027       }
01028 
01029       else /* this is a bevel (intersection) */
01030       {
01031         FT_Fixed   length;
01032         FT_Vector  delta;
01033 
01034 
01035         length = FT_DivFix( stroker->radius, thcos );
01036 
01037         FT_Vector_From_Polar( &delta, length, phi );
01038         delta.x += stroker->center.x;
01039         delta.y += stroker->center.y;
01040 
01041         error = ft_stroke_border_lineto( border, &delta, FALSE );
01042         if ( error )
01043           goto Exit;
01044 
01045         /* now add end point */
01046         FT_Vector_From_Polar( &delta, stroker->radius,
01047                               stroker->angle_out + rotate );
01048         delta.x += stroker->center.x;
01049         delta.y += stroker->center.y;
01050 
01051         error = ft_stroke_border_lineto( border, &delta, TRUE );
01052       }
01053     }
01054 
01055   Exit:
01056     return error;
01057   }
01058 
01059 
01060   static FT_Error
01061   ft_stroker_process_corner( FT_Stroker  stroker )
01062   {
01063     FT_Error  error = FT_Err_Ok;
01064     FT_Angle  turn;
01065     FT_Int    inside_side;
01066 
01067 
01068     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
01069 
01070     /* no specific corner processing is required if the turn is 0 */
01071     if ( turn == 0 )
01072       goto Exit;
01073 
01074     /* when we turn to the right, the inside side is 0 */
01075     inside_side = 0;
01076 
01077     /* otherwise, the inside side is 1 */
01078     if ( turn < 0 )
01079       inside_side = 1;
01080 
01081     /* process the inside side */
01082     error = ft_stroker_inside( stroker, inside_side );
01083     if ( error )
01084       goto Exit;
01085 
01086     /* process the outside side */
01087     error = ft_stroker_outside( stroker, 1 - inside_side );
01088 
01089   Exit:
01090     return error;
01091   }
01092 
01093 
01094   /* add two points to the left and right borders corresponding to the */
01095   /* start of the subpath                                              */
01096   static FT_Error
01097   ft_stroker_subpath_start( FT_Stroker  stroker,
01098                             FT_Angle    start_angle )
01099   {
01100     FT_Vector        delta;
01101     FT_Vector        point;
01102     FT_Error         error;
01103     FT_StrokeBorder  border;
01104 
01105 
01106     FT_Vector_From_Polar( &delta, stroker->radius,
01107                           start_angle + FT_ANGLE_PI2 );
01108 
01109     point.x = stroker->center.x + delta.x;
01110     point.y = stroker->center.y + delta.y;
01111 
01112     border = stroker->borders;
01113     error = ft_stroke_border_moveto( border, &point );
01114     if ( error )
01115       goto Exit;
01116 
01117     point.x = stroker->center.x - delta.x;
01118     point.y = stroker->center.y - delta.y;
01119 
01120     border++;
01121     error = ft_stroke_border_moveto( border, &point );
01122 
01123     /* save angle for last cap */
01124     stroker->subpath_angle = start_angle;
01125     stroker->first_point   = FALSE;
01126 
01127   Exit:
01128     return error;
01129   }
01130 
01131 
01132   /* documentation is in ftstroke.h */
01133 
01134   FT_EXPORT_DEF( FT_Error )
01135   FT_Stroker_LineTo( FT_Stroker  stroker,
01136                      FT_Vector*  to )
01137   {
01138     FT_Error         error = FT_Err_Ok;
01139     FT_StrokeBorder  border;
01140     FT_Vector        delta;
01141     FT_Angle         angle;
01142     FT_Int           side;
01143 
01144     delta.x = to->x - stroker->center.x;
01145     delta.y = to->y - stroker->center.y;
01146 
01147     angle = FT_Atan2( delta.x, delta.y );
01148     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
01149 
01150     /* process corner if necessary */
01151     if ( stroker->first_point )
01152     {
01153       /* This is the first segment of a subpath.  We need to     */
01154       /* add a point to each border at their respective starting */
01155       /* point locations.                                        */
01156       error = ft_stroker_subpath_start( stroker, angle );
01157       if ( error )
01158         goto Exit;
01159     }
01160     else
01161     {
01162       /* process the current corner */
01163       stroker->angle_out = angle;
01164       error = ft_stroker_process_corner( stroker );
01165       if ( error )
01166         goto Exit;
01167     }
01168 
01169     /* now add a line segment to both the `inside' and `outside' paths */
01170 
01171     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
01172     {
01173       FT_Vector  point;
01174 
01175 
01176       point.x = to->x + delta.x;
01177       point.y = to->y + delta.y;
01178 
01179       error = ft_stroke_border_lineto( border, &point, TRUE );
01180       if ( error )
01181         goto Exit;
01182 
01183       delta.x = -delta.x;
01184       delta.y = -delta.y;
01185     }
01186 
01187     stroker->angle_in = angle;
01188     stroker->center   = *to;
01189 
01190   Exit:
01191     return error;
01192   }
01193 
01194 
01195   /* documentation is in ftstroke.h */
01196 
01197   FT_EXPORT_DEF( FT_Error )
01198   FT_Stroker_ConicTo( FT_Stroker  stroker,
01199                       FT_Vector*  control,
01200                       FT_Vector*  to )
01201   {
01202     FT_Error    error = FT_Err_Ok;
01203     FT_Vector   bez_stack[34];
01204     FT_Vector*  arc;
01205     FT_Vector*  limit = bez_stack + 30;
01206     FT_Angle    start_angle;
01207     FT_Bool     first_arc = TRUE;
01208 
01209 
01210     arc    = bez_stack;
01211     arc[0] = *to;
01212     arc[1] = *control;
01213     arc[2] = stroker->center;
01214 
01215     while ( arc >= bez_stack )
01216     {
01217       FT_Angle  angle_in, angle_out;
01218 
01219 
01220       angle_in = angle_out = 0;  /* remove compiler warnings */
01221 
01222       if ( arc < limit                                             &&
01223            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
01224       {
01225         ft_conic_split( arc );
01226         arc += 2;
01227         continue;
01228       }
01229 
01230       if ( first_arc )
01231       {
01232         first_arc = FALSE;
01233 
01234         start_angle = angle_in;
01235 
01236         /* process corner if necessary */
01237         if ( stroker->first_point )
01238           error = ft_stroker_subpath_start( stroker, start_angle );
01239         else
01240         {
01241           stroker->angle_out = start_angle;
01242           error = ft_stroker_process_corner( stroker );
01243         }
01244       }
01245 
01246       /* the arc's angle is small enough; we can add it directly to each */
01247       /* border                                                          */
01248       {
01249         FT_Vector  ctrl, end;
01250         FT_Angle   theta, phi, rotate;
01251         FT_Fixed   length;
01252         FT_Int     side;
01253 
01254 
01255         theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
01256         phi    = angle_in + theta;
01257         length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
01258 
01259         for ( side = 0; side <= 1; side++ )
01260         {
01261           rotate = FT_SIDE_TO_ROTATE( side );
01262 
01263           /* compute control point */
01264           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
01265           ctrl.x += arc[1].x;
01266           ctrl.y += arc[1].y;
01267 
01268           /* compute end point */
01269           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
01270           end.x += arc[0].x;
01271           end.y += arc[0].y;
01272 
01273           error = ft_stroke_border_conicto( stroker->borders + side,
01274                                             &ctrl, &end );
01275           if ( error )
01276             goto Exit;
01277         }
01278       }
01279 
01280       arc -= 2;
01281 
01282       if ( arc < bez_stack )
01283         stroker->angle_in = angle_out;
01284     }
01285 
01286     stroker->center = *to;
01287 
01288   Exit:
01289     return error;
01290   }
01291 
01292 
01293   /* documentation is in ftstroke.h */
01294 
01295   FT_EXPORT_DEF( FT_Error )
01296   FT_Stroker_CubicTo( FT_Stroker  stroker,
01297                       FT_Vector*  control1,
01298                       FT_Vector*  control2,
01299                       FT_Vector*  to )
01300   {
01301     FT_Error    error = FT_Err_Ok;
01302     FT_Vector   bez_stack[37];
01303     FT_Vector*  arc;
01304     FT_Vector*  limit = bez_stack + 32;
01305     FT_Angle    start_angle;
01306     FT_Bool     first_arc = TRUE;
01307 
01308 
01309     arc    = bez_stack;
01310     arc[0] = *to;
01311     arc[1] = *control2;
01312     arc[2] = *control1;
01313     arc[3] = stroker->center;
01314 
01315     while ( arc >= bez_stack )
01316     {
01317       FT_Angle  angle_in, angle_mid, angle_out;
01318 
01319 
01320       /* remove compiler warnings */
01321       angle_in = angle_out = angle_mid = 0;
01322 
01323       if ( arc < limit                                         &&
01324            !ft_cubic_is_small_enough( arc, &angle_in,
01325                                       &angle_mid, &angle_out ) )
01326       {
01327         ft_cubic_split( arc );
01328         arc += 3;
01329         continue;
01330       }
01331 
01332       if ( first_arc )
01333       {
01334         first_arc = FALSE;
01335 
01336         /* process corner if necessary */
01337         start_angle = angle_in;
01338 
01339         if ( stroker->first_point )
01340           error = ft_stroker_subpath_start( stroker, start_angle );
01341         else
01342         {
01343           stroker->angle_out = start_angle;
01344           error = ft_stroker_process_corner( stroker );
01345         }
01346         if ( error )
01347           goto Exit;
01348       }
01349 
01350       /* the arc's angle is small enough; we can add it directly to each */
01351       /* border                                                          */
01352       {
01353         FT_Vector  ctrl1, ctrl2, end;
01354         FT_Angle   theta1, phi1, theta2, phi2, rotate;
01355         FT_Fixed   length1, length2;
01356         FT_Int     side;
01357 
01358 
01359         theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
01360         theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
01361         phi1    = (angle_mid + angle_in ) / 2;
01362         phi2    = (angle_mid + angle_out ) / 2;
01363         length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
01364         length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
01365 
01366         for ( side = 0; side <= 1; side++ )
01367         {
01368           rotate = FT_SIDE_TO_ROTATE( side );
01369 
01370           /* compute control points */
01371           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
01372           ctrl1.x += arc[2].x;
01373           ctrl1.y += arc[2].y;
01374 
01375           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
01376           ctrl2.x += arc[1].x;
01377           ctrl2.y += arc[1].y;
01378 
01379           /* compute end point */
01380           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
01381           end.x += arc[0].x;
01382           end.y += arc[0].y;
01383 
01384           error = ft_stroke_border_cubicto( stroker->borders + side,
01385                                             &ctrl1, &ctrl2, &end );
01386           if ( error )
01387             goto Exit;
01388         }
01389       }
01390 
01391       arc -= 3;
01392       if ( arc < bez_stack )
01393         stroker->angle_in = angle_out;
01394     }
01395 
01396     stroker->center = *to;
01397 
01398   Exit:
01399     return error;
01400   }
01401 
01402 
01403   /* documentation is in ftstroke.h */
01404 
01405   FT_EXPORT_DEF( FT_Error )
01406   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
01407                            FT_Vector*  to,
01408                            FT_Bool     open )
01409   {
01410     /* We cannot process the first point, because there is not enough      */
01411     /* information regarding its corner/cap.  The latter will be processed */
01412     /* in the `FT_Stroker_EndSubPath' routine.                             */
01413     /*                                                                     */
01414     stroker->first_point  = TRUE;
01415     stroker->center       = *to;
01416     stroker->subpath_open = open;
01417 
01418     /* record the subpath start point for each border */
01419     stroker->subpath_start = *to;
01420 
01421     return FT_Err_Ok;
01422   }
01423 
01424 
01425   static FT_Error
01426   ft_stroker_add_reverse_left( FT_Stroker  stroker,
01427                                FT_Bool     open )
01428   {
01429     FT_StrokeBorder  right = stroker->borders + 0;
01430     FT_StrokeBorder  left  = stroker->borders + 1;
01431     FT_Int           new_points;
01432     FT_Error         error = FT_Err_Ok;
01433 
01434 
01435     FT_ASSERT( left->start >= 0 );
01436 
01437     new_points = left->num_points - left->start;
01438     if ( new_points > 0 )
01439     {
01440       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
01441       if ( error )
01442         goto Exit;
01443 
01444       {
01445         FT_Vector*  dst_point = right->points + right->num_points;
01446         FT_Byte*    dst_tag   = right->tags   + right->num_points;
01447         FT_Vector*  src_point = left->points  + left->num_points - 1;
01448         FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
01449 
01450         while ( src_point >= left->points + left->start )
01451         {
01452           *dst_point = *src_point;
01453           *dst_tag   = *src_tag;
01454 
01455           if ( open )
01456             dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
01457           else
01458           {
01459             FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
01460 
01461 
01462             /* switch begin/end tags if necessary */
01463             if ( ttag == FT_STROKE_TAG_BEGIN ||
01464                  ttag == FT_STROKE_TAG_END   )
01465               dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
01466 
01467           }
01468 
01469           src_point--;
01470           src_tag--;
01471           dst_point++;
01472           dst_tag++;
01473         }
01474       }
01475 
01476       left->num_points   = left->start;
01477       right->num_points += new_points;
01478 
01479       right->movable = FALSE;
01480       left->movable  = FALSE;
01481     }
01482 
01483   Exit:
01484     return error;
01485   }
01486 
01487 
01488   /* documentation is in ftstroke.h */
01489 
01490   /* there's a lot of magic in this function! */
01491   FT_EXPORT_DEF( FT_Error )
01492   FT_Stroker_EndSubPath( FT_Stroker  stroker )
01493   {
01494     FT_Error  error = FT_Err_Ok;
01495 
01496 
01497     if ( stroker->subpath_open )
01498     {
01499       FT_StrokeBorder  right = stroker->borders;
01500 
01501       /* All right, this is an opened path, we need to add a cap between */
01502       /* right & left, add the reverse of left, then add a final cap     */
01503       /* between left & right.                                           */
01504       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
01505       if ( error )
01506         goto Exit;
01507 
01508       /* add reversed points from `left' to `right' */
01509       error = ft_stroker_add_reverse_left( stroker, TRUE );
01510       if ( error )
01511         goto Exit;
01512 
01513       /* now add the final cap */
01514       stroker->center = stroker->subpath_start;
01515       error = ft_stroker_cap( stroker,
01516                               stroker->subpath_angle + FT_ANGLE_PI, 0 );
01517       if ( error )
01518         goto Exit;
01519 
01520       /* Now end the right subpath accordingly.  The left one is */
01521       /* rewind and doesn't need further processing.             */
01522       ft_stroke_border_close( right, FALSE );
01523     }
01524     else
01525     {
01526       FT_Angle  turn;
01527       FT_Int    inside_side;
01528 
01529       /* close the path if needed */
01530       if ( stroker->center.x != stroker->subpath_start.x ||
01531            stroker->center.y != stroker->subpath_start.y )
01532       {
01533         error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
01534         if ( error )
01535           goto Exit;
01536       }
01537 
01538       /* process the corner */
01539       stroker->angle_out = stroker->subpath_angle;
01540       turn               = FT_Angle_Diff( stroker->angle_in,
01541                                           stroker->angle_out );
01542 
01543       /* no specific corner processing is required if the turn is 0 */
01544       if ( turn != 0 )
01545       {
01546         /* when we turn to the right, the inside side is 0 */
01547         inside_side = 0;
01548 
01549         /* otherwise, the inside side is 1 */
01550         if ( turn < 0 )
01551           inside_side = 1;
01552 
01553         error = ft_stroker_inside( stroker, inside_side );
01554         if ( error )
01555           goto Exit;
01556 
01557         /* process the outside side */
01558         error = ft_stroker_outside( stroker, 1 - inside_side );
01559         if ( error )
01560           goto Exit;
01561       }
01562 
01563       /* then end our two subpaths */
01564       ft_stroke_border_close( stroker->borders + 0, TRUE );
01565       ft_stroke_border_close( stroker->borders + 1, FALSE );
01566     }
01567 
01568   Exit:
01569     return error;
01570   }
01571 
01572 
01573   /* documentation is in ftstroke.h */
01574 
01575   FT_EXPORT_DEF( FT_Error )
01576   FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
01577                               FT_StrokerBorder  border,
01578                               FT_UInt          *anum_points,
01579                               FT_UInt          *anum_contours )
01580   {
01581     FT_UInt   num_points = 0, num_contours = 0;
01582     FT_Error  error;
01583 
01584 
01585     if ( !stroker || border > 1 )
01586     {
01587       error = FT_Err_Invalid_Argument;
01588       goto Exit;
01589     }
01590 
01591     error = ft_stroke_border_get_counts( stroker->borders + border,
01592                                          &num_points, &num_contours );
01593   Exit:
01594     if ( anum_points )
01595       *anum_points = num_points;
01596 
01597     if ( anum_contours )
01598       *anum_contours = num_contours;
01599 
01600     return error;
01601   }
01602 
01603 
01604   /* documentation is in ftstroke.h */
01605 
01606   FT_EXPORT_DEF( FT_Error )
01607   FT_Stroker_GetCounts( FT_Stroker  stroker,
01608                         FT_UInt    *anum_points,
01609                         FT_UInt    *anum_contours )
01610   {
01611     FT_UInt   count1, count2, num_points   = 0;
01612     FT_UInt   count3, count4, num_contours = 0;
01613     FT_Error  error;
01614 
01615 
01616     error = ft_stroke_border_get_counts( stroker->borders + 0,
01617                                          &count1, &count2 );
01618     if ( error )
01619       goto Exit;
01620 
01621     error = ft_stroke_border_get_counts( stroker->borders + 1,
01622                                          &count3, &count4 );
01623     if ( error )
01624       goto Exit;
01625 
01626     num_points   = count1 + count3;
01627     num_contours = count2 + count4;
01628 
01629   Exit:
01630     *anum_points   = num_points;
01631     *anum_contours = num_contours;
01632     return error;
01633   }
01634 
01635 
01636   /* documentation is in ftstroke.h */
01637 
01638   FT_EXPORT_DEF( void )
01639   FT_Stroker_ExportBorder( FT_Stroker        stroker,
01640                            FT_StrokerBorder  border,
01641                            FT_Outline*       outline )
01642   {
01643     if ( border == FT_STROKER_BORDER_LEFT  ||
01644          border == FT_STROKER_BORDER_RIGHT )
01645     {
01646       FT_StrokeBorder  sborder = & stroker->borders[border];
01647 
01648 
01649       if ( sborder->valid )
01650         ft_stroke_border_export( sborder, outline );
01651     }
01652   }
01653 
01654 
01655   /* documentation is in ftstroke.h */
01656 
01657   FT_EXPORT_DEF( void )
01658   FT_Stroker_Export( FT_Stroker   stroker,
01659                      FT_Outline*  outline )
01660   {
01661     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
01662     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
01663   }
01664 
01665 
01666   /* documentation is in ftstroke.h */
01667 
01668   /*
01669    *  The following is very similar to FT_Outline_Decompose, except
01670    *  that we do support opened paths, and do not scale the outline.
01671    */
01672   FT_EXPORT_DEF( FT_Error )
01673   FT_Stroker_ParseOutline( FT_Stroker   stroker,
01674                            FT_Outline*  outline,
01675                            FT_Bool      opened )
01676   {
01677     FT_Vector   v_last;
01678     FT_Vector   v_control;
01679     FT_Vector   v_start;
01680 
01681     FT_Vector*  point;
01682     FT_Vector*  limit;
01683     char*       tags;
01684 
01685     FT_Error    error;
01686 
01687     FT_Int   n;         /* index of contour in outline     */
01688     FT_UInt  first;     /* index of first point in contour */
01689     FT_Int   tag;       /* current point's state           */
01690 
01691 
01692     if ( !outline || !stroker )
01693       return FT_Err_Invalid_Argument;
01694 
01695     FT_Stroker_Rewind( stroker );
01696 
01697     first = 0;
01698 
01699     for ( n = 0; n < outline->n_contours; n++ )
01700     {
01701       FT_UInt  last;  /* index of last point in contour */
01702 
01703 
01704       last  = outline->contours[n];
01705       limit = outline->points + last;
01706 
01707       /* skip empty points; we don't stroke these */
01708       if ( last <= first )
01709       {
01710         first = last + 1;
01711         continue;
01712       }
01713 
01714       v_start = outline->points[first];
01715       v_last  = outline->points[last];
01716 
01717       v_control = v_start;
01718 
01719       point = outline->points + first;
01720       tags  = outline->tags   + first;
01721       tag   = FT_CURVE_TAG( tags[0] );
01722 
01723       /* A contour cannot start with a cubic control point! */
01724       if ( tag == FT_CURVE_TAG_CUBIC )
01725         goto Invalid_Outline;
01726 
01727       /* check first point to determine origin */
01728       if ( tag == FT_CURVE_TAG_CONIC )
01729       {
01730         /* First point is conic control.  Yes, this happens. */
01731         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
01732         {
01733           /* start at last point if it is on the curve */
01734           v_start = v_last;
01735           limit--;
01736         }
01737         else
01738         {
01739           /* if both first and last points are conic, */
01740           /* start at their middle                    */
01741           v_start.x = ( v_start.x + v_last.x ) / 2;
01742           v_start.y = ( v_start.y + v_last.y ) / 2;
01743         }
01744         point--;
01745         tags--;
01746       }
01747 
01748       error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
01749       if ( error )
01750         goto Exit;
01751 
01752       while ( point < limit )
01753       {
01754         point++;
01755         tags++;
01756 
01757         tag = FT_CURVE_TAG( tags[0] );
01758         switch ( tag )
01759         {
01760         case FT_CURVE_TAG_ON:  /* emit a single line_to */
01761           {
01762             FT_Vector  vec;
01763 
01764 
01765             vec.x = point->x;
01766             vec.y = point->y;
01767 
01768             error = FT_Stroker_LineTo( stroker, &vec );
01769             if ( error )
01770               goto Exit;
01771             continue;
01772           }
01773 
01774         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
01775           v_control.x = point->x;
01776           v_control.y = point->y;
01777 
01778         Do_Conic:
01779           if ( point < limit )
01780           {
01781             FT_Vector  vec;
01782             FT_Vector  v_middle;
01783 
01784 
01785             point++;
01786             tags++;
01787             tag = FT_CURVE_TAG( tags[0] );
01788 
01789             vec = point[0];
01790 
01791             if ( tag == FT_CURVE_TAG_ON )
01792             {
01793               error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
01794               if ( error )
01795                 goto Exit;
01796               continue;
01797             }
01798 
01799             if ( tag != FT_CURVE_TAG_CONIC )
01800               goto Invalid_Outline;
01801 
01802             v_middle.x = ( v_control.x + vec.x ) / 2;
01803             v_middle.y = ( v_control.y + vec.y ) / 2;
01804 
01805             error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
01806             if ( error )
01807               goto Exit;
01808 
01809             v_control = vec;
01810             goto Do_Conic;
01811           }
01812 
01813           error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
01814           goto Close;
01815 
01816         default:  /* FT_CURVE_TAG_CUBIC */
01817           {
01818             FT_Vector  vec1, vec2;
01819 
01820 
01821             if ( point + 1 > limit                             ||
01822                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
01823               goto Invalid_Outline;
01824 
01825             point += 2;
01826             tags  += 2;
01827 
01828             vec1 = point[-2];
01829             vec2 = point[-1];
01830 
01831             if ( point <= limit )
01832             {
01833               FT_Vector  vec;
01834 
01835 
01836               vec = point[0];
01837 
01838               error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
01839               if ( error )
01840                 goto Exit;
01841               continue;
01842             }
01843 
01844             error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
01845             goto Close;
01846           }
01847         }
01848       }
01849 
01850     Close:
01851       if ( error )
01852         goto Exit;
01853 
01854       error = FT_Stroker_EndSubPath( stroker );
01855       if ( error )
01856         goto Exit;
01857 
01858       first = last + 1;
01859     }
01860 
01861     return FT_Err_Ok;
01862 
01863   Exit:
01864     return error;
01865 
01866   Invalid_Outline:
01867     return FT_Err_Invalid_Outline;
01868   }
01869 
01870 /* declare an extern to access ft_outline_glyph_class global allocated 
01871    in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access 
01872    it when FT_CONFIG_OPTION_PIC is defined */
01873 #ifndef FT_CONFIG_OPTION_PIC
01874   extern const FT_Glyph_Class  ft_outline_glyph_class;
01875 #endif
01876 #include "basepic.h"
01877 
01878 
01879   /* documentation is in ftstroke.h */
01880 
01881   FT_EXPORT_DEF( FT_Error )
01882   FT_Glyph_Stroke( FT_Glyph    *pglyph,
01883                    FT_Stroker   stroker,
01884                    FT_Bool      destroy )
01885   {
01886     FT_Error  error = FT_Err_Invalid_Argument;
01887     FT_Glyph  glyph = NULL;
01888     FT_Library library = stroker->library;
01889     FT_UNUSED(library);
01890 
01891     if ( pglyph == NULL )
01892       goto Exit;
01893 
01894     glyph = *pglyph;
01895     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
01896       goto Exit;
01897 
01898     {
01899       FT_Glyph  copy;
01900 
01901 
01902       error = FT_Glyph_Copy( glyph, &copy );
01903       if ( error )
01904         goto Exit;
01905 
01906       glyph = copy;
01907     }
01908 
01909     {
01910       FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
01911       FT_Outline*      outline = &oglyph->outline;
01912       FT_UInt          num_points, num_contours;
01913 
01914 
01915       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
01916       if ( error )
01917         goto Fail;
01918 
01919       (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
01920 
01921       FT_Outline_Done( glyph->library, outline );
01922 
01923       error = FT_Outline_New( glyph->library,
01924                               num_points, num_contours, outline );
01925       if ( error )
01926         goto Fail;
01927 
01928       outline->n_points   = 0;
01929       outline->n_contours = 0;
01930 
01931       FT_Stroker_Export( stroker, outline );
01932     }
01933 
01934     if ( destroy )
01935       FT_Done_Glyph( *pglyph );
01936 
01937     *pglyph = glyph;
01938     goto Exit;
01939 
01940   Fail:
01941     FT_Done_Glyph( glyph );
01942     glyph = NULL;
01943 
01944     if ( !destroy )
01945       *pglyph = NULL;
01946 
01947   Exit:
01948     return error;
01949   }
01950 
01951 
01952   /* documentation is in ftstroke.h */
01953 
01954   FT_EXPORT_DEF( FT_Error )
01955   FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
01956                          FT_Stroker   stroker,
01957                          FT_Bool      inside,
01958                          FT_Bool      destroy )
01959   {
01960     FT_Error  error = FT_Err_Invalid_Argument;
01961     FT_Glyph  glyph = NULL;
01962     FT_Library library = stroker->library;
01963     FT_UNUSED(library);
01964 
01965     if ( pglyph == NULL )
01966       goto Exit;
01967 
01968     glyph = *pglyph;
01969     if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
01970       goto Exit;
01971 
01972     {
01973       FT_Glyph  copy;
01974 
01975 
01976       error = FT_Glyph_Copy( glyph, &copy );
01977       if ( error )
01978         goto Exit;
01979 
01980       glyph = copy;
01981     }
01982 
01983     {
01984       FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
01985       FT_StrokerBorder  border;
01986       FT_Outline*       outline = &oglyph->outline;
01987       FT_UInt           num_points, num_contours;
01988 
01989 
01990       border = FT_Outline_GetOutsideBorder( outline );
01991       if ( inside )
01992       {
01993         if ( border == FT_STROKER_BORDER_LEFT )
01994           border = FT_STROKER_BORDER_RIGHT;
01995         else
01996           border = FT_STROKER_BORDER_LEFT;
01997       }
01998 
01999       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
02000       if ( error )
02001         goto Fail;
02002 
02003       (void)FT_Stroker_GetBorderCounts( stroker, border,
02004                                         &num_points, &num_contours );
02005 
02006       FT_Outline_Done( glyph->library, outline );
02007 
02008       error = FT_Outline_New( glyph->library,
02009                               num_points,
02010                               num_contours,
02011                               outline );
02012       if ( error )
02013         goto Fail;
02014 
02015       outline->n_points   = 0;
02016       outline->n_contours = 0;
02017 
02018       FT_Stroker_ExportBorder( stroker, border, outline );
02019     }
02020 
02021     if ( destroy )
02022       FT_Done_Glyph( *pglyph );
02023 
02024     *pglyph = glyph;
02025     goto Exit;
02026 
02027   Fail:
02028     FT_Done_Glyph( glyph );
02029     glyph = NULL;
02030 
02031     if ( !destroy )
02032       *pglyph = NULL;
02033 
02034   Exit:
02035     return error;
02036   }
02037 
02038 
02039 /* END */

Generated on Tue Jul 5 14:13:42 2011 for ROOT_528-00b_version by  doxygen 1.5.1