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