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