PageRenderTime 10ms CodeModel.GetById 2ms app.highlight 85ms RepoModel.GetById 1ms app.codeStats 0ms

/ExternalLibs/freetype2/src/base/ftstroke.c

https://bitbucket.org/King_DuckZ/aquaria
C | 2010 lines | 1380 code | 485 blank | 145 comment | 189 complexity | d15fe173f167d7c84a632d5961ee71cd MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/***************************************************************************/
   2/*                                                                         */
   3/*  ftstroke.c                                                             */
   4/*                                                                         */
   5/*    FreeType path stroker (body).                                        */
   6/*                                                                         */
   7/*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009 by                  */
   8/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
   9/*                                                                         */
  10/*  This file is part of the FreeType project, and may only be used,       */
  11/*  modified, and distributed under the terms of the FreeType project      */
  12/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
  13/*  this file you indicate that you have read the license and              */
  14/*  understand and accept it fully.                                        */
  15/*                                                                         */
  16/***************************************************************************/
  17
  18
  19#include <ft2build.h>
  20#include FT_STROKER_H
  21#include FT_TRIGONOMETRY_H
  22#include FT_OUTLINE_H
  23#include FT_INTERNAL_MEMORY_H
  24#include FT_INTERNAL_DEBUG_H
  25#include FT_INTERNAL_OBJECTS_H
  26
  27
  28  /* documentation is in ftstroke.h */
  29
  30  FT_EXPORT_DEF( FT_StrokerBorder )
  31  FT_Outline_GetInsideBorder( FT_Outline*  outline )
  32  {
  33    FT_Orientation  o = FT_Outline_Get_Orientation( outline );
  34
  35
  36    return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
  37                                        : FT_STROKER_BORDER_LEFT ;
  38  }
  39
  40
  41  /* documentation is in ftstroke.h */
  42
  43  FT_EXPORT_DEF( FT_StrokerBorder )
  44  FT_Outline_GetOutsideBorder( FT_Outline*  outline )
  45  {
  46    FT_Orientation  o = FT_Outline_Get_Orientation( outline );
  47
  48
  49    return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
  50                                        : FT_STROKER_BORDER_RIGHT ;
  51  }
  52
  53
  54 /***************************************************************************/
  55 /***************************************************************************/
  56 /*****                                                                 *****/
  57 /*****                       BEZIER COMPUTATIONS                       *****/
  58 /*****                                                                 *****/
  59 /***************************************************************************/
  60 /***************************************************************************/
  61
  62#define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
  63#define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
  64#define FT_EPSILON  2
  65
  66#define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
  67
  68
  69  static FT_Pos
  70  ft_pos_abs( FT_Pos  x )
  71  {
  72    return x >= 0 ? x : -x ;
  73  }
  74
  75
  76  static void
  77  ft_conic_split( FT_Vector*  base )
  78  {
  79    FT_Pos  a, b;
  80
  81
  82    base[4].x = base[2].x;
  83    b = base[1].x;
  84    a = base[3].x = ( base[2].x + b ) / 2;
  85    b = base[1].x = ( base[0].x + b ) / 2;
  86    base[2].x = ( a + b ) / 2;
  87
  88    base[4].y = base[2].y;
  89    b = base[1].y;
  90    a = base[3].y = ( base[2].y + b ) / 2;
  91    b = base[1].y = ( base[0].y + b ) / 2;
  92    base[2].y = ( a + b ) / 2;
  93  }
  94
  95
  96  static FT_Bool
  97  ft_conic_is_small_enough( FT_Vector*  base,
  98                            FT_Angle   *angle_in,
  99                            FT_Angle   *angle_out )
 100  {
 101    FT_Vector  d1, d2;
 102    FT_Angle   theta;
 103    FT_Int     close1, close2;
 104
 105
 106    d1.x = base[1].x - base[2].x;
 107    d1.y = base[1].y - base[2].y;
 108    d2.x = base[0].x - base[1].x;
 109    d2.y = base[0].y - base[1].y;
 110
 111    close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
 112    close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
 113
 114    if ( close1 )
 115    {
 116      if ( close2 )
 117        *angle_in = *angle_out = 0;
 118      else
 119        *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
 120    }
 121    else if ( close2 )
 122    {
 123      *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
 124    }
 125    else
 126    {
 127      *angle_in  = FT_Atan2( d1.x, d1.y );
 128      *angle_out = FT_Atan2( d2.x, d2.y );
 129    }
 130
 131    theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
 132
 133    return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
 134  }
 135
 136
 137  static void
 138  ft_cubic_split( FT_Vector*  base )
 139  {
 140    FT_Pos  a, b, c, d;
 141
 142
 143    base[6].x = base[3].x;
 144    c = base[1].x;
 145    d = base[2].x;
 146    base[1].x = a = ( base[0].x + c ) / 2;
 147    base[5].x = b = ( base[3].x + d ) / 2;
 148    c = ( c + d ) / 2;
 149    base[2].x = a = ( a + c ) / 2;
 150    base[4].x = b = ( b + c ) / 2;
 151    base[3].x = ( a + b ) / 2;
 152
 153    base[6].y = base[3].y;
 154    c = base[1].y;
 155    d = base[2].y;
 156    base[1].y = a = ( base[0].y + c ) / 2;
 157    base[5].y = b = ( base[3].y + d ) / 2;
 158    c = ( c + d ) / 2;
 159    base[2].y = a = ( a + c ) / 2;
 160    base[4].y = b = ( b + c ) / 2;
 161    base[3].y = ( a + b ) / 2;
 162  }
 163
 164
 165  static FT_Bool
 166  ft_cubic_is_small_enough( FT_Vector*  base,
 167                            FT_Angle   *angle_in,
 168                            FT_Angle   *angle_mid,
 169                            FT_Angle   *angle_out )
 170  {
 171    FT_Vector  d1, d2, d3;
 172    FT_Angle   theta1, theta2;
 173    FT_Int     close1, close2, close3;
 174
 175
 176    d1.x = base[2].x - base[3].x;
 177    d1.y = base[2].y - base[3].y;
 178    d2.x = base[1].x - base[2].x;
 179    d2.y = base[1].y - base[2].y;
 180    d3.x = base[0].x - base[1].x;
 181    d3.y = base[0].y - base[1].y;
 182
 183    close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
 184    close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
 185    close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
 186
 187    if ( close1 || close3 )
 188    {
 189      if ( close2 )
 190      {
 191        /* basically a point */
 192        *angle_in = *angle_out = *angle_mid = 0;
 193      }
 194      else if ( close1 )
 195      {
 196        *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
 197        *angle_out = FT_Atan2( d3.x, d3.y );
 198      }
 199      else  /* close2 */
 200      {
 201        *angle_in  = FT_Atan2( d1.x, d1.y );
 202        *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
 203      }
 204    }
 205    else if ( close2 )
 206    {
 207      *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
 208      *angle_out = FT_Atan2( d3.x, d3.y );
 209    }
 210    else
 211    {
 212      *angle_in  = FT_Atan2( d1.x, d1.y );
 213      *angle_mid = FT_Atan2( d2.x, d2.y );
 214      *angle_out = FT_Atan2( d3.x, d3.y );
 215    }
 216
 217    theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
 218    theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
 219
 220    return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
 221                    theta2 < FT_SMALL_CUBIC_THRESHOLD );
 222  }
 223
 224
 225 /***************************************************************************/
 226 /***************************************************************************/
 227 /*****                                                                 *****/
 228 /*****                       STROKE BORDERS                            *****/
 229 /*****                                                                 *****/
 230 /***************************************************************************/
 231 /***************************************************************************/
 232
 233  typedef enum  FT_StrokeTags_
 234  {
 235    FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
 236    FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
 237    FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
 238    FT_STROKE_TAG_END   = 8    /* sub-path end    */
 239
 240  } FT_StrokeTags;
 241
 242#define  FT_STROKE_TAG_BEGIN_END  (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
 243
 244  typedef struct  FT_StrokeBorderRec_
 245  {
 246    FT_UInt     num_points;
 247    FT_UInt     max_points;
 248    FT_Vector*  points;
 249    FT_Byte*    tags;
 250    FT_Bool     movable;
 251    FT_Int      start;    /* index of current sub-path start point */
 252    FT_Memory   memory;
 253    FT_Bool     valid;
 254
 255  } FT_StrokeBorderRec, *FT_StrokeBorder;
 256
 257
 258  static FT_Error
 259  ft_stroke_border_grow( FT_StrokeBorder  border,
 260                         FT_UInt          new_points )
 261  {
 262    FT_UInt   old_max = border->max_points;
 263    FT_UInt   new_max = border->num_points + new_points;
 264    FT_Error  error   = FT_Err_Ok;
 265
 266
 267    if ( new_max > old_max )
 268    {
 269      FT_UInt    cur_max = old_max;
 270      FT_Memory  memory  = border->memory;
 271
 272
 273      while ( cur_max < new_max )
 274        cur_max += ( cur_max >> 1 ) + 16;
 275
 276      if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
 277           FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
 278        goto Exit;
 279
 280      border->max_points = cur_max;
 281    }
 282
 283  Exit:
 284    return error;
 285  }
 286
 287
 288  static void
 289  ft_stroke_border_close( FT_StrokeBorder  border,
 290                          FT_Bool          reverse )
 291  {
 292    FT_UInt  start = border->start;
 293    FT_UInt  count = border->num_points;
 294
 295
 296    FT_ASSERT( border->start >= 0 );
 297
 298    /* don't record empty paths! */
 299    if ( count <= start + 1U )
 300      border->num_points = start;
 301    else
 302    {
 303      /* copy the last point to the start of this sub-path, since */
 304      /* it contains the `adjusted' starting coordinates          */
 305      border->num_points    = --count;
 306      border->points[start] = border->points[count];
 307
 308      if ( reverse )
 309      {
 310        /* reverse the points */
 311        {
 312          FT_Vector*  vec1 = border->points + start + 1;
 313          FT_Vector*  vec2 = border->points + count - 1;
 314
 315
 316          for ( ; vec1 < vec2; vec1++, vec2-- )
 317          {
 318            FT_Vector  tmp;
 319
 320
 321            tmp   = *vec1;
 322            *vec1 = *vec2;
 323            *vec2 = tmp;
 324          }
 325        }
 326
 327        /* then the tags */
 328        {
 329          FT_Byte*  tag1 = border->tags + start + 1;
 330          FT_Byte*  tag2 = border->tags + count - 1;
 331
 332
 333          for ( ; tag1 < tag2; tag1++, tag2-- )
 334          {
 335            FT_Byte  tmp;
 336
 337
 338            tmp   = *tag1;
 339            *tag1 = *tag2;
 340            *tag2 = tmp;
 341          }
 342        }
 343      }
 344
 345      border->tags[start    ] |= FT_STROKE_TAG_BEGIN;
 346      border->tags[count - 1] |= FT_STROKE_TAG_END;
 347    }
 348
 349    border->start   = -1;
 350    border->movable = FALSE;
 351  }
 352
 353
 354  static FT_Error
 355  ft_stroke_border_lineto( FT_StrokeBorder  border,
 356                           FT_Vector*       to,
 357                           FT_Bool          movable )
 358  {
 359    FT_Error  error = FT_Err_Ok;
 360
 361
 362    FT_ASSERT( border->start >= 0 );
 363
 364    if ( border->movable )
 365    {
 366      /* move last point */
 367      border->points[border->num_points - 1] = *to;
 368    }
 369    else
 370    {
 371      /* add one point */
 372      error = ft_stroke_border_grow( border, 1 );
 373      if ( !error )
 374      {
 375        FT_Vector*  vec = border->points + border->num_points;
 376        FT_Byte*    tag = border->tags   + border->num_points;
 377
 378
 379        vec[0] = *to;
 380        tag[0] = FT_STROKE_TAG_ON;
 381
 382        border->num_points += 1;
 383      }
 384    }
 385    border->movable = movable;
 386    return error;
 387  }
 388
 389
 390  static FT_Error
 391  ft_stroke_border_conicto( FT_StrokeBorder  border,
 392                            FT_Vector*       control,
 393                            FT_Vector*       to )
 394  {
 395    FT_Error  error;
 396
 397
 398    FT_ASSERT( border->start >= 0 );
 399
 400    error = ft_stroke_border_grow( border, 2 );
 401    if ( !error )
 402    {
 403      FT_Vector*  vec = border->points + border->num_points;
 404      FT_Byte*    tag = border->tags   + border->num_points;
 405
 406      vec[0] = *control;
 407      vec[1] = *to;
 408
 409      tag[0] = 0;
 410      tag[1] = FT_STROKE_TAG_ON;
 411
 412      border->num_points += 2;
 413    }
 414    border->movable = FALSE;
 415    return error;
 416  }
 417
 418
 419  static FT_Error
 420  ft_stroke_border_cubicto( FT_StrokeBorder  border,
 421                            FT_Vector*       control1,
 422                            FT_Vector*       control2,
 423                            FT_Vector*       to )
 424  {
 425    FT_Error  error;
 426
 427
 428    FT_ASSERT( border->start >= 0 );
 429
 430    error = ft_stroke_border_grow( border, 3 );
 431    if ( !error )
 432    {
 433      FT_Vector*  vec = border->points + border->num_points;
 434      FT_Byte*    tag = border->tags   + border->num_points;
 435
 436
 437      vec[0] = *control1;
 438      vec[1] = *control2;
 439      vec[2] = *to;
 440
 441      tag[0] = FT_STROKE_TAG_CUBIC;
 442      tag[1] = FT_STROKE_TAG_CUBIC;
 443      tag[2] = FT_STROKE_TAG_ON;
 444
 445      border->num_points += 3;
 446    }
 447    border->movable = FALSE;
 448    return error;
 449  }
 450
 451
 452#define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
 453
 454
 455  static FT_Error
 456  ft_stroke_border_arcto( FT_StrokeBorder  border,
 457                          FT_Vector*       center,
 458                          FT_Fixed         radius,
 459                          FT_Angle         angle_start,
 460                          FT_Angle         angle_diff )
 461  {
 462    FT_Angle   total, angle, step, rotate, next, theta;
 463    FT_Vector  a, b, a2, b2;
 464    FT_Fixed   length;
 465    FT_Error   error = FT_Err_Ok;
 466
 467
 468    /* compute start point */
 469    FT_Vector_From_Polar( &a, radius, angle_start );
 470    a.x += center->x;
 471    a.y += center->y;
 472
 473    total  = angle_diff;
 474    angle  = angle_start;
 475    rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
 476
 477    while ( total != 0 )
 478    {
 479      step = total;
 480      if ( step > FT_ARC_CUBIC_ANGLE )
 481        step = FT_ARC_CUBIC_ANGLE;
 482
 483      else if ( step < -FT_ARC_CUBIC_ANGLE )
 484        step = -FT_ARC_CUBIC_ANGLE;
 485
 486      next  = angle + step;
 487      theta = step;
 488      if ( theta < 0 )
 489        theta = -theta;
 490
 491      theta >>= 1;
 492
 493      /* compute end point */
 494      FT_Vector_From_Polar( &b, radius, next );
 495      b.x += center->x;
 496      b.y += center->y;
 497
 498      /* compute first and second control points */
 499      length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
 500                          ( 0x10000L + FT_Cos( theta ) ) * 3 );
 501
 502      FT_Vector_From_Polar( &a2, length, angle + rotate );
 503      a2.x += a.x;
 504      a2.y += a.y;
 505
 506      FT_Vector_From_Polar( &b2, length, next - rotate );
 507      b2.x += b.x;
 508      b2.y += b.y;
 509
 510      /* add cubic arc */
 511      error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
 512      if ( error )
 513        break;
 514
 515      /* process the rest of the arc ?? */
 516      a      = b;
 517      total -= step;
 518      angle  = next;
 519    }
 520
 521    return error;
 522  }
 523
 524
 525  static FT_Error
 526  ft_stroke_border_moveto( FT_StrokeBorder  border,
 527                           FT_Vector*       to )
 528  {
 529    /* close current open path if any ? */
 530    if ( border->start >= 0 )
 531      ft_stroke_border_close( border, FALSE );
 532
 533    border->start   = border->num_points;
 534    border->movable = FALSE;
 535
 536    return ft_stroke_border_lineto( border, to, FALSE );
 537  }
 538
 539
 540  static void
 541  ft_stroke_border_init( FT_StrokeBorder  border,
 542                         FT_Memory        memory )
 543  {
 544    border->memory = memory;
 545    border->points = NULL;
 546    border->tags   = NULL;
 547
 548    border->num_points = 0;
 549    border->max_points = 0;
 550    border->start      = -1;
 551    border->valid      = FALSE;
 552  }
 553
 554
 555  static void
 556  ft_stroke_border_reset( FT_StrokeBorder  border )
 557  {
 558    border->num_points = 0;
 559    border->start      = -1;
 560    border->valid      = FALSE;
 561  }
 562
 563
 564  static void
 565  ft_stroke_border_done( FT_StrokeBorder  border )
 566  {
 567    FT_Memory  memory = border->memory;
 568
 569
 570    FT_FREE( border->points );
 571    FT_FREE( border->tags );
 572
 573    border->num_points = 0;
 574    border->max_points = 0;
 575    border->start      = -1;
 576    border->valid      = FALSE;
 577  }
 578
 579
 580  static FT_Error
 581  ft_stroke_border_get_counts( FT_StrokeBorder  border,
 582                               FT_UInt         *anum_points,
 583                               FT_UInt         *anum_contours )
 584  {
 585    FT_Error  error        = FT_Err_Ok;
 586    FT_UInt   num_points   = 0;
 587    FT_UInt   num_contours = 0;
 588
 589    FT_UInt     count      = border->num_points;
 590    FT_Vector*  point      = border->points;
 591    FT_Byte*    tags       = border->tags;
 592    FT_Int      in_contour = 0;
 593
 594
 595    for ( ; count > 0; count--, num_points++, point++, tags++ )
 596    {
 597      if ( tags[0] & FT_STROKE_TAG_BEGIN )
 598      {
 599        if ( in_contour != 0 )
 600          goto Fail;
 601
 602        in_contour = 1;
 603      }
 604      else if ( in_contour == 0 )
 605        goto Fail;
 606
 607      if ( tags[0] & FT_STROKE_TAG_END )
 608      {
 609        in_contour = 0;
 610        num_contours++;
 611      }
 612    }
 613
 614    if ( in_contour != 0 )
 615      goto Fail;
 616
 617    border->valid = TRUE;
 618
 619  Exit:
 620    *anum_points   = num_points;
 621    *anum_contours = num_contours;
 622    return error;
 623
 624  Fail:
 625    num_points   = 0;
 626    num_contours = 0;
 627    goto Exit;
 628  }
 629
 630
 631  static void
 632  ft_stroke_border_export( FT_StrokeBorder  border,
 633                           FT_Outline*      outline )
 634  {
 635    /* copy point locations */
 636    FT_ARRAY_COPY( outline->points + outline->n_points,
 637                   border->points,
 638                   border->num_points );
 639
 640    /* copy tags */
 641    {
 642      FT_UInt   count = border->num_points;
 643      FT_Byte*  read  = border->tags;
 644      FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
 645
 646
 647      for ( ; count > 0; count--, read++, write++ )
 648      {
 649        if ( *read & FT_STROKE_TAG_ON )
 650          *write = FT_CURVE_TAG_ON;
 651        else if ( *read & FT_STROKE_TAG_CUBIC )
 652          *write = FT_CURVE_TAG_CUBIC;
 653        else
 654          *write = FT_CURVE_TAG_CONIC;
 655      }
 656    }
 657
 658    /* copy contours */
 659    {
 660      FT_UInt    count = border->num_points;
 661      FT_Byte*   tags  = border->tags;
 662      FT_Short*  write = outline->contours + outline->n_contours;
 663      FT_Short   idx   = (FT_Short)outline->n_points;
 664
 665
 666      for ( ; count > 0; count--, tags++, idx++ )
 667      {
 668        if ( *tags & FT_STROKE_TAG_END )
 669        {
 670          *write++ = idx;
 671          outline->n_contours++;
 672        }
 673      }
 674    }
 675
 676    outline->n_points  = (short)( outline->n_points + border->num_points );
 677
 678    FT_ASSERT( FT_Outline_Check( outline ) == 0 );
 679  }
 680
 681
 682 /***************************************************************************/
 683 /***************************************************************************/
 684 /*****                                                                 *****/
 685 /*****                           STROKER                               *****/
 686 /*****                                                                 *****/
 687 /***************************************************************************/
 688 /***************************************************************************/
 689
 690#define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
 691
 692  typedef struct  FT_StrokerRec_
 693  {
 694    FT_Angle             angle_in;
 695    FT_Angle             angle_out;
 696    FT_Vector            center;
 697    FT_Bool              first_point;
 698    FT_Bool              subpath_open;
 699    FT_Angle             subpath_angle;
 700    FT_Vector            subpath_start;
 701
 702    FT_Stroker_LineCap   line_cap;
 703    FT_Stroker_LineJoin  line_join;
 704    FT_Fixed             miter_limit;
 705    FT_Fixed             radius;
 706
 707    FT_Bool              valid;
 708    FT_StrokeBorderRec   borders[2];
 709    FT_Memory            memory;
 710
 711  } FT_StrokerRec;
 712
 713
 714  /* documentation is in ftstroke.h */
 715
 716  FT_EXPORT_DEF( FT_Error )
 717  FT_Stroker_New( FT_Library   library,
 718                  FT_Stroker  *astroker )
 719  {
 720    FT_Error    error;
 721    FT_Memory   memory;
 722    FT_Stroker  stroker;
 723
 724
 725    if ( !library )
 726      return FT_Err_Invalid_Argument;
 727
 728    memory = library->memory;
 729
 730    if ( !FT_NEW( stroker ) )
 731    {
 732      stroker->memory = memory;
 733
 734      ft_stroke_border_init( &stroker->borders[0], memory );
 735      ft_stroke_border_init( &stroker->borders[1], memory );
 736    }
 737    *astroker = stroker;
 738    return error;
 739  }
 740
 741
 742  /* documentation is in ftstroke.h */
 743
 744  FT_EXPORT_DEF( void )
 745  FT_Stroker_Set( FT_Stroker           stroker,
 746                  FT_Fixed             radius,
 747                  FT_Stroker_LineCap   line_cap,
 748                  FT_Stroker_LineJoin  line_join,
 749                  FT_Fixed             miter_limit )
 750  {
 751    stroker->radius      = radius;
 752    stroker->line_cap    = line_cap;
 753    stroker->line_join   = line_join;
 754    stroker->miter_limit = miter_limit;
 755
 756    FT_Stroker_Rewind( stroker );
 757  }
 758
 759
 760  /* documentation is in ftstroke.h */
 761
 762  FT_EXPORT_DEF( void )
 763  FT_Stroker_Rewind( FT_Stroker  stroker )
 764  {
 765    if ( stroker )
 766    {
 767      ft_stroke_border_reset( &stroker->borders[0] );
 768      ft_stroke_border_reset( &stroker->borders[1] );
 769    }
 770  }
 771
 772
 773  /* documentation is in ftstroke.h */
 774
 775  FT_EXPORT_DEF( void )
 776  FT_Stroker_Done( FT_Stroker  stroker )
 777  {
 778    if ( stroker )
 779    {
 780      FT_Memory  memory = stroker->memory;
 781
 782
 783      ft_stroke_border_done( &stroker->borders[0] );
 784      ft_stroke_border_done( &stroker->borders[1] );
 785
 786      stroker->memory = NULL;
 787      FT_FREE( stroker );
 788    }
 789  }
 790
 791
 792  /* creates a circular arc at a corner or cap */
 793  static FT_Error
 794  ft_stroker_arcto( FT_Stroker  stroker,
 795                    FT_Int      side )
 796  {
 797    FT_Angle         total, rotate;
 798    FT_Fixed         radius = stroker->radius;
 799    FT_Error         error  = FT_Err_Ok;
 800    FT_StrokeBorder  border = stroker->borders + side;
 801
 802
 803    rotate = FT_SIDE_TO_ROTATE( side );
 804
 805    total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 806    if ( total == FT_ANGLE_PI )
 807      total = -rotate * 2;
 808
 809    error = ft_stroke_border_arcto( border,
 810                                    &stroker->center,
 811                                    radius,
 812                                    stroker->angle_in + rotate,
 813                                    total );
 814    border->movable = FALSE;
 815    return error;
 816  }
 817
 818
 819  /* adds a cap at the end of an opened path */
 820  static FT_Error
 821  ft_stroker_cap( FT_Stroker  stroker,
 822                  FT_Angle    angle,
 823                  FT_Int      side )
 824  {
 825    FT_Error  error = FT_Err_Ok;
 826
 827
 828    if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
 829    {
 830      /* add a round cap */
 831      stroker->angle_in  = angle;
 832      stroker->angle_out = angle + FT_ANGLE_PI;
 833      error = ft_stroker_arcto( stroker, side );
 834    }
 835    else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
 836    {
 837      /* add a square cap */
 838      FT_Vector        delta, delta2;
 839      FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
 840      FT_Fixed         radius = stroker->radius;
 841      FT_StrokeBorder  border = stroker->borders + side;
 842
 843
 844      FT_Vector_From_Polar( &delta2, radius, angle + rotate );
 845      FT_Vector_From_Polar( &delta,  radius, angle );
 846
 847      delta.x += stroker->center.x + delta2.x;
 848      delta.y += stroker->center.y + delta2.y;
 849
 850      error = ft_stroke_border_lineto( border, &delta, FALSE );
 851      if ( error )
 852        goto Exit;
 853
 854      FT_Vector_From_Polar( &delta2, radius, angle - rotate );
 855      FT_Vector_From_Polar( &delta,  radius, angle );
 856
 857      delta.x += delta2.x + stroker->center.x;
 858      delta.y += delta2.y + stroker->center.y;
 859
 860      error = ft_stroke_border_lineto( border, &delta, FALSE );
 861    }
 862
 863  Exit:
 864    return error;
 865  }
 866
 867
 868  /* process an inside corner, i.e. compute intersection */
 869  static FT_Error
 870  ft_stroker_inside( FT_Stroker  stroker,
 871                     FT_Int      side)
 872  {
 873    FT_StrokeBorder  border = stroker->borders + side;
 874    FT_Angle         phi, theta, rotate;
 875    FT_Fixed         length, thcos, sigma;
 876    FT_Vector        delta;
 877    FT_Error         error = FT_Err_Ok;
 878
 879
 880    rotate = FT_SIDE_TO_ROTATE( side );
 881
 882    /* compute median angle */
 883    theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 884    if ( theta == FT_ANGLE_PI )
 885      theta = rotate;
 886    else
 887      theta = theta / 2;
 888
 889    phi = stroker->angle_in + theta;
 890
 891    thcos = FT_Cos( theta );
 892    sigma = FT_MulFix( stroker->miter_limit, thcos );
 893
 894    /* TODO: find better criterion to switch off the optimization */
 895    if ( sigma < 0x10000L )
 896    {
 897      FT_Vector_From_Polar( &delta, stroker->radius,
 898                            stroker->angle_out + rotate );
 899      delta.x += stroker->center.x;
 900      delta.y += stroker->center.y;
 901      border->movable = FALSE;
 902    }
 903    else
 904    {
 905      length = FT_DivFix( stroker->radius, thcos );
 906
 907      FT_Vector_From_Polar( &delta, length, phi + rotate );
 908      delta.x += stroker->center.x;
 909      delta.y += stroker->center.y;
 910    }
 911
 912    error = ft_stroke_border_lineto( border, &delta, FALSE );
 913
 914    return error;
 915  }
 916
 917
 918  /* process an outside corner, i.e. compute bevel/miter/round */
 919  static FT_Error
 920  ft_stroker_outside( FT_Stroker  stroker,
 921                      FT_Int      side )
 922  {
 923    FT_StrokeBorder  border = stroker->borders + side;
 924    FT_Error         error;
 925    FT_Angle         rotate;
 926
 927
 928    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
 929      error = ft_stroker_arcto( stroker, side );
 930    else
 931    {
 932      /* this is a mitered or beveled corner */
 933      FT_Fixed  sigma, radius = stroker->radius;
 934      FT_Angle  theta, phi;
 935      FT_Fixed  thcos;
 936      FT_Bool   miter;
 937
 938
 939      rotate = FT_SIDE_TO_ROTATE( side );
 940      miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
 941
 942      theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
 943      if ( theta == FT_ANGLE_PI )
 944      {
 945        theta = rotate;
 946        phi   = stroker->angle_in;
 947      }
 948      else
 949      {
 950        theta = theta / 2;
 951        phi   = stroker->angle_in + theta + rotate;
 952      }
 953
 954      thcos = FT_Cos( theta );
 955      sigma = FT_MulFix( stroker->miter_limit, thcos );
 956
 957      if ( sigma >= 0x10000L )
 958        miter = FALSE;
 959
 960      if ( miter )  /* this is a miter (broken angle) */
 961      {
 962        FT_Vector  middle, delta;
 963        FT_Fixed   length;
 964
 965
 966        /* compute middle point */
 967        FT_Vector_From_Polar( &middle,
 968                              FT_MulFix( radius, stroker->miter_limit ),
 969                              phi );
 970        middle.x += stroker->center.x;
 971        middle.y += stroker->center.y;
 972
 973        /* compute first angle point */
 974        length = FT_MulFix( radius,
 975                            FT_DivFix( 0x10000L - sigma,
 976                                       ft_pos_abs( FT_Sin( theta ) ) ) );
 977
 978        FT_Vector_From_Polar( &delta, length, phi + rotate );
 979        delta.x += middle.x;
 980        delta.y += middle.y;
 981
 982        error = ft_stroke_border_lineto( border, &delta, FALSE );
 983        if ( error )
 984          goto Exit;
 985
 986        /* compute second angle point */
 987        FT_Vector_From_Polar( &delta, length, phi - rotate );
 988        delta.x += middle.x;
 989        delta.y += middle.y;
 990
 991        error = ft_stroke_border_lineto( border, &delta, FALSE );
 992        if ( error )
 993          goto Exit;
 994
 995        /* finally, add a movable end point */
 996        FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
 997        delta.x += stroker->center.x;
 998        delta.y += stroker->center.y;
 999
1000        error = ft_stroke_border_lineto( border, &delta, TRUE );
1001      }
1002
1003      else /* this is a bevel (intersection) */
1004      {
1005        FT_Fixed   length;
1006        FT_Vector  delta;
1007
1008
1009        length = FT_DivFix( stroker->radius, thcos );
1010
1011        FT_Vector_From_Polar( &delta, length, phi );
1012        delta.x += stroker->center.x;
1013        delta.y += stroker->center.y;
1014
1015        error = ft_stroke_border_lineto( border, &delta, FALSE );
1016        if ( error )
1017          goto Exit;
1018
1019        /* now add end point */
1020        FT_Vector_From_Polar( &delta, stroker->radius,
1021                              stroker->angle_out + rotate );
1022        delta.x += stroker->center.x;
1023        delta.y += stroker->center.y;
1024
1025        error = ft_stroke_border_lineto( border, &delta, TRUE );
1026      }
1027    }
1028
1029  Exit:
1030    return error;
1031  }
1032
1033
1034  static FT_Error
1035  ft_stroker_process_corner( FT_Stroker  stroker )
1036  {
1037    FT_Error  error = FT_Err_Ok;
1038    FT_Angle  turn;
1039    FT_Int    inside_side;
1040
1041
1042    turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1043
1044    /* no specific corner processing is required if the turn is 0 */
1045    if ( turn == 0 )
1046      goto Exit;
1047
1048    /* when we turn to the right, the inside side is 0 */
1049    inside_side = 0;
1050
1051    /* otherwise, the inside side is 1 */
1052    if ( turn < 0 )
1053      inside_side = 1;
1054
1055    /* process the inside side */
1056    error = ft_stroker_inside( stroker, inside_side );
1057    if ( error )
1058      goto Exit;
1059
1060    /* process the outside side */
1061    error = ft_stroker_outside( stroker, 1 - inside_side );
1062
1063  Exit:
1064    return error;
1065  }
1066
1067
1068  /* add two points to the left and right borders corresponding to the */
1069  /* start of the subpath                                              */
1070  static FT_Error
1071  ft_stroker_subpath_start( FT_Stroker  stroker,
1072                            FT_Angle    start_angle )
1073  {
1074    FT_Vector        delta;
1075    FT_Vector        point;
1076    FT_Error         error;
1077    FT_StrokeBorder  border;
1078
1079
1080    FT_Vector_From_Polar( &delta, stroker->radius,
1081                          start_angle + FT_ANGLE_PI2 );
1082
1083    point.x = stroker->center.x + delta.x;
1084    point.y = stroker->center.y + delta.y;
1085
1086    border = stroker->borders;
1087    error = ft_stroke_border_moveto( border, &point );
1088    if ( error )
1089      goto Exit;
1090
1091    point.x = stroker->center.x - delta.x;
1092    point.y = stroker->center.y - delta.y;
1093
1094    border++;
1095    error = ft_stroke_border_moveto( border, &point );
1096
1097    /* save angle for last cap */
1098    stroker->subpath_angle = start_angle;
1099    stroker->first_point   = FALSE;
1100
1101  Exit:
1102    return error;
1103  }
1104
1105
1106  /* documentation is in ftstroke.h */
1107
1108  FT_EXPORT_DEF( FT_Error )
1109  FT_Stroker_LineTo( FT_Stroker  stroker,
1110                     FT_Vector*  to )
1111  {
1112    FT_Error         error = FT_Err_Ok;
1113    FT_StrokeBorder  border;
1114    FT_Vector        delta;
1115    FT_Angle         angle;
1116    FT_Int           side;
1117
1118    delta.x = to->x - stroker->center.x;
1119    delta.y = to->y - stroker->center.y;
1120
1121    angle = FT_Atan2( delta.x, delta.y );
1122    FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1123
1124    /* process corner if necessary */
1125    if ( stroker->first_point )
1126    {
1127      /* This is the first segment of a subpath.  We need to     */
1128      /* add a point to each border at their respective starting */
1129      /* point locations.                                        */
1130      error = ft_stroker_subpath_start( stroker, angle );
1131      if ( error )
1132        goto Exit;
1133    }
1134    else
1135    {
1136      /* process the current corner */
1137      stroker->angle_out = angle;
1138      error = ft_stroker_process_corner( stroker );
1139      if ( error )
1140        goto Exit;
1141    }
1142
1143    /* now add a line segment to both the `inside' and `outside' paths */
1144
1145    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1146    {
1147      FT_Vector  point;
1148
1149
1150      point.x = to->x + delta.x;
1151      point.y = to->y + delta.y;
1152
1153      error = ft_stroke_border_lineto( border, &point, TRUE );
1154      if ( error )
1155        goto Exit;
1156
1157      delta.x = -delta.x;
1158      delta.y = -delta.y;
1159    }
1160
1161    stroker->angle_in = angle;
1162    stroker->center   = *to;
1163
1164  Exit:
1165    return error;
1166  }
1167
1168
1169  /* documentation is in ftstroke.h */
1170
1171  FT_EXPORT_DEF( FT_Error )
1172  FT_Stroker_ConicTo( FT_Stroker  stroker,
1173                      FT_Vector*  control,
1174                      FT_Vector*  to )
1175  {
1176    FT_Error    error = FT_Err_Ok;
1177    FT_Vector   bez_stack[34];
1178    FT_Vector*  arc;
1179    FT_Vector*  limit = bez_stack + 30;
1180    FT_Angle    start_angle;
1181    FT_Bool     first_arc = TRUE;
1182
1183
1184    arc    = bez_stack;
1185    arc[0] = *to;
1186    arc[1] = *control;
1187    arc[2] = stroker->center;
1188
1189    while ( arc >= bez_stack )
1190    {
1191      FT_Angle  angle_in, angle_out;
1192
1193
1194      angle_in = angle_out = 0;  /* remove compiler warnings */
1195
1196      if ( arc < limit                                             &&
1197           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1198      {
1199        ft_conic_split( arc );
1200        arc += 2;
1201        continue;
1202      }
1203
1204      if ( first_arc )
1205      {
1206        first_arc = FALSE;
1207
1208        start_angle = angle_in;
1209
1210        /* process corner if necessary */
1211        if ( stroker->first_point )
1212          error = ft_stroker_subpath_start( stroker, start_angle );
1213        else
1214        {
1215          stroker->angle_out = start_angle;
1216          error = ft_stroker_process_corner( stroker );
1217        }
1218      }
1219
1220      /* the arc's angle is small enough; we can add it directly to each */
1221      /* border                                                          */
1222      {
1223        FT_Vector  ctrl, end;
1224        FT_Angle   theta, phi, rotate;
1225        FT_Fixed   length;
1226        FT_Int     side;
1227
1228
1229        theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
1230        phi    = angle_in + theta;
1231        length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1232
1233        for ( side = 0; side <= 1; side++ )
1234        {
1235          rotate = FT_SIDE_TO_ROTATE( side );
1236
1237          /* compute control point */
1238          FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1239          ctrl.x += arc[1].x;
1240          ctrl.y += arc[1].y;
1241
1242          /* compute end point */
1243          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1244          end.x += arc[0].x;
1245          end.y += arc[0].y;
1246
1247          error = ft_stroke_border_conicto( stroker->borders + side,
1248                                            &ctrl, &end );
1249          if ( error )
1250            goto Exit;
1251        }
1252      }
1253
1254      arc -= 2;
1255
1256      if ( arc < bez_stack )
1257        stroker->angle_in = angle_out;
1258    }
1259
1260    stroker->center = *to;
1261
1262  Exit:
1263    return error;
1264  }
1265
1266
1267  /* documentation is in ftstroke.h */
1268
1269  FT_EXPORT_DEF( FT_Error )
1270  FT_Stroker_CubicTo( FT_Stroker  stroker,
1271                      FT_Vector*  control1,
1272                      FT_Vector*  control2,
1273                      FT_Vector*  to )
1274  {
1275    FT_Error    error = FT_Err_Ok;
1276    FT_Vector   bez_stack[37];
1277    FT_Vector*  arc;
1278    FT_Vector*  limit = bez_stack + 32;
1279    FT_Angle    start_angle;
1280    FT_Bool     first_arc = TRUE;
1281
1282
1283    arc    = bez_stack;
1284    arc[0] = *to;
1285    arc[1] = *control2;
1286    arc[2] = *control1;
1287    arc[3] = stroker->center;
1288
1289    while ( arc >= bez_stack )
1290    {
1291      FT_Angle  angle_in, angle_mid, angle_out;
1292
1293
1294      /* remove compiler warnings */
1295      angle_in = angle_out = angle_mid = 0;
1296
1297      if ( arc < limit                                         &&
1298           !ft_cubic_is_small_enough( arc, &angle_in,
1299                                      &angle_mid, &angle_out ) )
1300      {
1301        ft_cubic_split( arc );
1302        arc += 3;
1303        continue;
1304      }
1305
1306      if ( first_arc )
1307      {
1308        first_arc = FALSE;
1309
1310        /* process corner if necessary */
1311        start_angle = angle_in;
1312
1313        if ( stroker->first_point )
1314          error = ft_stroker_subpath_start( stroker, start_angle );
1315        else
1316        {
1317          stroker->angle_out = start_angle;
1318          error = ft_stroker_process_corner( stroker );
1319        }
1320        if ( error )
1321          goto Exit;
1322      }
1323
1324      /* the arc's angle is small enough; we can add it directly to each */
1325      /* border                                                          */
1326      {
1327        FT_Vector  ctrl1, ctrl2, end;
1328        FT_Angle   theta1, phi1, theta2, phi2, rotate;
1329        FT_Fixed   length1, length2;
1330        FT_Int     side;
1331
1332
1333        theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
1334        theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
1335        phi1    = (angle_mid + angle_in ) / 2;
1336        phi2    = (angle_mid + angle_out ) / 2;
1337        length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1338        length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
1339
1340        for ( side = 0; side <= 1; side++ )
1341        {
1342          rotate = FT_SIDE_TO_ROTATE( side );
1343
1344          /* compute control points */
1345          FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1346          ctrl1.x += arc[2].x;
1347          ctrl1.y += arc[2].y;
1348
1349          FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1350          ctrl2.x += arc[1].x;
1351          ctrl2.y += arc[1].y;
1352
1353          /* compute end point */
1354          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1355          end.x += arc[0].x;
1356          end.y += arc[0].y;
1357
1358          error = ft_stroke_border_cubicto( stroker->borders + side,
1359                                            &ctrl1, &ctrl2, &end );
1360          if ( error )
1361            goto Exit;
1362        }
1363      }
1364
1365      arc -= 3;
1366      if ( arc < bez_stack )
1367        stroker->angle_in = angle_out;
1368    }
1369
1370    stroker->center = *to;
1371
1372  Exit:
1373    return error;
1374  }
1375
1376
1377  /* documentation is in ftstroke.h */
1378
1379  FT_EXPORT_DEF( FT_Error )
1380  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1381                           FT_Vector*  to,
1382                           FT_Bool     open )
1383  {
1384    /* We cannot process the first point, because there is not enough      */
1385    /* information regarding its corner/cap.  The latter will be processed */
1386    /* in the `FT_Stroker_EndSubPath' routine.                             */
1387    /*                                                                     */
1388    stroker->first_point  = TRUE;
1389    stroker->center       = *to;
1390    stroker->subpath_open = open;
1391
1392    /* record the subpath start point for each border */
1393    stroker->subpath_start = *to;
1394
1395    return FT_Err_Ok;
1396  }
1397
1398
1399  static FT_Error
1400  ft_stroker_add_reverse_left( FT_Stroker  stroker,
1401                               FT_Bool     open )
1402  {
1403    FT_StrokeBorder  right = stroker->borders + 0;
1404    FT_StrokeBorder  left  = stroker->borders + 1;
1405    FT_Int           new_points;
1406    FT_Error         error = FT_Err_Ok;
1407
1408
1409    FT_ASSERT( left->start >= 0 );
1410
1411    new_points = left->num_points - left->start;
1412    if ( new_points > 0 )
1413    {
1414      error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1415      if ( error )
1416        goto Exit;
1417
1418      {
1419        FT_Vector*  dst_point = right->points + right->num_points;
1420        FT_Byte*    dst_tag   = right->tags   + right->num_points;
1421        FT_Vector*  src_point = left->points  + left->num_points - 1;
1422        FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1423
1424        while ( src_point >= left->points + left->start )
1425        {
1426          *dst_point = *src_point;
1427          *dst_tag   = *src_tag;
1428
1429          if ( open )
1430            dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1431          else
1432          {
1433            FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1434
1435
1436            /* switch begin/end tags if necessary */
1437            if ( ttag == FT_STROKE_TAG_BEGIN ||
1438                 ttag == FT_STROKE_TAG_END   )
1439              dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1440
1441          }
1442
1443          src_point--;
1444          src_tag--;
1445          dst_point++;
1446          dst_tag++;
1447        }
1448      }
1449
1450      left->num_points   = left->start;
1451      right->num_points += new_points;
1452
1453      right->movable = FALSE;
1454      left->movable  = FALSE;
1455    }
1456
1457  Exit:
1458    return error;
1459  }
1460
1461
1462  /* documentation is in ftstroke.h */
1463
1464  /* there's a lot of magic in this function! */
1465  FT_EXPORT_DEF( FT_Error )
1466  FT_Stroker_EndSubPath( FT_Stroker  stroker )
1467  {
1468    FT_Error  error = FT_Err_Ok;
1469
1470
1471    if ( stroker->subpath_open )
1472    {
1473      FT_StrokeBorder  right = stroker->borders;
1474
1475      /* All right, this is an opened path, we need to add a cap between */
1476      /* right & left, add the reverse of left, then add a final cap     */
1477      /* between left & right.                                           */
1478      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1479      if ( error )
1480        goto Exit;
1481
1482      /* add reversed points from `left' to `right' */
1483      error = ft_stroker_add_reverse_left( stroker, TRUE );
1484      if ( error )
1485        goto Exit;
1486
1487      /* now add the final cap */
1488      stroker->center = stroker->subpath_start;
1489      error = ft_stroker_cap( stroker,
1490                              stroker->subpath_angle + FT_ANGLE_PI, 0 );
1491      if ( error )
1492        goto Exit;
1493
1494      /* Now end the right subpath accordingly.  The left one is */
1495      /* rewind and doesn't need further processing.             */
1496      ft_stroke_border_close( right, FALSE );
1497    }
1498    else
1499    {
1500      FT_Angle  turn;
1501      FT_Int    inside_side;
1502
1503      /* close the path if needed */
1504      if ( stroker->center.x != stroker->subpath_start.x ||
1505           stroker->center.y != stroker->subpath_start.y )
1506      {
1507        error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1508        if ( error )
1509          goto Exit;
1510      }
1511
1512      /* process the corner */
1513      stroker->angle_out = stroker->subpath_angle;
1514      turn               = FT_Angle_Diff( stroker->angle_in,
1515                                          stroker->angle_out );
1516
1517      /* no specific corner processing is required if the turn is 0 */
1518      if ( turn != 0 )
1519      {
1520        /* when we turn to the right, the inside side is 0 */
1521        inside_side = 0;
1522
1523        /* otherwise, the inside side is 1 */
1524        if ( turn < 0 )
1525          inside_side = 1;
1526
1527        error = ft_stroker_inside( stroker, inside_side );
1528        if ( error )
1529          goto Exit;
1530
1531        /* process the outside side */
1532        error = ft_stroker_outside( stroker, 1 - inside_side );
1533        if ( error )
1534          goto Exit;
1535      }
1536
1537      /* then end our two subpaths */
1538      ft_stroke_border_close( stroker->borders + 0, TRUE );
1539      ft_stroke_border_close( stroker->borders + 1, FALSE );
1540    }
1541
1542  Exit:
1543    return error;
1544  }
1545
1546
1547  /* documentation is in ftstroke.h */
1548
1549  FT_EXPORT_DEF( FT_Error )
1550  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1551                              FT_StrokerBorder  border,
1552                              FT_UInt          *anum_points,
1553                              FT_UInt          *anum_contours )
1554  {
1555    FT_UInt   num_points = 0, num_contours = 0;
1556    FT_Error  error;
1557
1558
1559    if ( !stroker || border > 1 )
1560    {
1561      error = FT_Err_Invalid_Argument;
1562      goto Exit;
1563    }
1564
1565    error = ft_stroke_border_get_counts( stroker->borders + border,
1566                                         &num_points, &num_contours );
1567  Exit:
1568    if ( anum_points )
1569      *anum_points = num_points;
1570
1571    if ( anum_contours )
1572      *anum_contours = num_contours;
1573
1574    return error;
1575  }
1576
1577
1578  /* documentation is in ftstroke.h */
1579
1580  FT_EXPORT_DEF( FT_Error )
1581  FT_Stroker_GetCounts( FT_Stroker  stroker,
1582                        FT_UInt    *anum_points,
1583                        FT_UInt    *anum_contours )
1584  {
1585    FT_UInt   count1, count2, num_points   = 0;
1586    FT_UInt   count3, count4, num_contours = 0;
1587    FT_Error  error;
1588
1589
1590    error = ft_stroke_border_get_counts( stroker->borders + 0,
1591                                         &count1, &count2 );
1592    if ( error )
1593      goto Exit;
1594
1595    error = ft_stroke_border_get_counts( stroker->borders + 1,
1596                                         &count3, &count4 );
1597    if ( error )
1598      goto Exit;
1599
1600    num_points   = count1 + count3;
1601    num_contours = count2 + count4;
1602
1603  Exit:
1604    *anum_points   = num_points;
1605    *anum_contours = num_contours;
1606    return error;
1607  }
1608
1609
1610  /* documentation is in ftstroke.h */
1611
1612  FT_EXPORT_DEF( void )
1613  FT_Stroker_ExportBorder( FT_Stroker        stroker,
1614                           FT_StrokerBorder  border,
1615                           FT_Outline*       outline )
1616  {
1617    if ( border == FT_STROKER_BORDER_LEFT  ||
1618         border == FT_STROKER_BORDER_RIGHT )
1619    {
1620      FT_StrokeBorder  sborder = & stroker->borders[border];
1621
1622
1623      if ( sborder->valid )
1624        ft_stroke_border_export( sborder, outline );
1625    }
1626  }
1627
1628
1629  /* documentation is in ftstroke.h */
1630
1631  FT_EXPORT_DEF( void )
1632  FT_Stroker_Export( FT_Stroker   stroker,
1633                     FT_Outline*  outline )
1634  {
1635    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1636    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1637  }
1638
1639
1640  /* documentation is in ftstroke.h */
1641
1642  /*
1643   *  The following is very similar to FT_Outline_Decompose, except
1644   *  that we do support opened paths, and do not scale the outline.
1645   */
1646  FT_EXPORT_DEF( FT_Error )
1647  FT_Stroker_ParseOutline( FT_Stroker   stroker,
1648                           FT_Outline*  outline,
1649                           FT_Bool      opened )
1650  {
1651    FT_Vector   v_last;
1652    FT_Vector   v_control;
1653    FT_Vector   v_start;
1654
1655    FT_Vector*  point;
1656    FT_Vector*  limit;
1657    char*       tags;
1658
1659    FT_Error    error;
1660
1661    FT_Int   n;         /* index of contour in outline     */
1662    FT_UInt  first;     /* index of first point in contour */
1663    FT_Int   tag;       /* current point's state           */
1664
1665
1666    if ( !outline || !stroker )
1667      return FT_Err_Invalid_Argument;
1668
1669    FT_Stroker_Rewind( stroker );
1670
1671    first = 0;
1672
1673    for ( n = 0; n < outline->n_contours; n++ )
1674    {
1675      FT_UInt  last;  /* index of last point in contour */
1676
1677
1678      last  = outline->contours[n];
1679      limit = outline->points + last;
1680
1681      /* skip empty points; we don't stroke these */
1682      if ( last <= first )
1683      {
1684        first = last + 1;
1685        continue;
1686      }
1687
1688      v_start = outline->points[first];
1689      v_last  = outline->points[last];
1690
1691      v_control = v_start;
1692
1693      point = outline->points + first;
1694      tags  = outline->tags   + first;
1695      tag   = FT_CURVE_TAG( tags[0] );
1696
1697      /* A contour cannot start with a cubic control point! */
1698      if ( tag == FT_CURVE_TAG_CUBIC )
1699        goto Invalid_Outline;
1700
1701      /* check first point to determine origin */
1702      if ( tag == FT_CURVE_TAG_CONIC )
1703      {
1704        /* First point is conic control.  Yes, this happens. */
1705        if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1706        {
1707          /* start at last point if it is on the curve */
1708          v_start = v_last;
1709          limit--;
1710        }
1711        else
1712        {
1713          /* if both first and last points are conic,         */
1714          /* start at their middle and record its position    */
1715          /* for closure                                      */
1716          v_start.x = ( v_start.x + v_last.x ) / 2;
1717          v_start.y = ( v_start.y + v_last.y ) / 2;
1718
1719          v_last = v_start;
1720        }
1721        point--;
1722        tags--;
1723      }
1724
1725      error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1726      if ( error )
1727        goto Exit;
1728
1729      while ( point < limit )
1730      {
1731        point++;
1732        tags++;
1733
1734        tag = FT_CURVE_TAG( tags[0] );
1735        switch ( tag )
1736        {
1737        case FT_CURVE_TAG_ON:  /* emit a single line_to */
1738          {
1739            FT_Vector  vec;
1740
1741
1742            vec.x = point->x;
1743            vec.y = point->y;
1744
1745            error = FT_Stroker_LineTo( stroker, &vec );
1746            if ( error )
1747              goto Exit;
1748            continue;
1749          }
1750
1751        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
1752          v_control.x = point->x;
1753          v_control.y = point->y;
1754
1755        Do_Conic:
1756          if ( point < limit )
1757          {
1758            FT_Vector  vec;
1759            FT_Vector  v_middle;
1760
1761
1762            point++;
1763            tags++;
1764            tag = FT_CURVE_TAG( tags[0] );
1765
1766            vec = point[0];
1767
1768            if ( tag == FT_CURVE_TAG_ON )
1769            {
1770              error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1771              if ( error )
1772                goto Exit;
1773              continue;
1774            }
1775
1776            if ( tag != FT_CURVE_TAG_CONIC )
1777              goto Invalid_Outline;
1778
1779            v_middle.x = ( v_control.x + vec.x ) / 2;
1780            v_middle.y = ( v_control.y + vec.y ) / 2;
1781
1782            error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1783            if ( error )
1784              goto Exit;
1785
1786            v_control = vec;
1787            goto Do_Conic;
1788          }
1789
1790          error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1791          goto Close;
1792
1793        default:  /* FT_CURVE_TAG_CUBIC */
1794          {
1795            FT_Vector  vec1, vec2;
1796
1797
1798            if ( point + 1 > limit                             ||
1799                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1800              goto Invalid_Outline;
1801
1802            point += 2;
1803            tags  += 2;
1804
1805            vec1 = point[-2];
1806            vec2 = point[-1];
1807
1808            if ( point <= limit )
1809            {
1810              FT_Vector  vec;
1811
1812
1813              vec = point[0];
1814
1815              error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1816              if ( error )
1817                goto Exit;
1818              continue;
1819            }
1820
1821            error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1822            goto Close;
1823          }
1824        }
1825      }
1826
1827    Close:
1828      if ( error )
1829        goto Exit;
1830
1831      error = FT_Stroker_EndSubPath( stroker );
1832      if ( error )
1833        goto Exit;
1834
1835      first = last + 1;
1836    }
1837
1838    return FT_Err_Ok;
1839
1840  Exit:
1841    return error;
1842
1843  Invalid_Outline:
1844    return FT_Err_Invalid_Outline;
1845  }
1846
1847
1848  extern const FT_Glyph_Class  ft_outline_glyph_class;
1849
1850
1851  /* documentation is in ftstroke.h */
1852
1853  FT_EXPORT_DEF( FT_Error )
1854  FT_Glyph_Stroke( FT_Glyph    *pglyph,
1855                   FT_Stroker   stroker,
1856                   FT_Bool      destroy )
1857  {
1858    FT_Error  error = FT_Err_Invalid_Argument;
1859    FT_Glyph  glyph = NULL;
1860
1861
1862    if ( pglyph == NULL )
1863      goto Exit;
1864
1865    glyph = *pglyph;
1866    if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1867      goto Exit;
1868
1869    {
1870      FT_Glyph  copy;
1871
1872
1873      error = FT_Glyph_Copy( glyph, &copy );
1874      if ( error )
1875        goto Exit;
1876
1877      glyph = copy;
1878    }
1879
1880    {
1881      FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
1882      FT_Outline*      outline = &oglyph->outline;
1883      FT_UInt          num_points, num_contours;
1884
1885
1886      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1887      if ( error )
1888        goto Fail;
1889
1890      (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1891
1892      FT_Outline_Done( glyph->library, outline );
1893
1894      error = FT_Outline_New( glyph->library,
1895                              num_points, num_contours, outline );
1896      if ( error )
1897        goto Fail;
1898
1899      outline->n_points   = 0;
1900      outline->n_contours = 0;
1901
1902      FT_Stroker_Export( stroker, outline );
1903    }
1904
1905    if ( destroy )
1906      FT_Done_Glyph( *pglyph );
1907
1908    *pglyph = glyph;
1909    goto Exit;
1910
1911  Fail:
1912    FT_Done_Glyph( glyph );
1913    glyph = NULL;
1914
1915    if ( !destroy )
1916      *pglyph = NULL;
1917
1918  Exit:
1919    return error;
1920  }
1921
1922
1923  /* documentation is in ftstroke.h */
1924
1925  FT_EXPORT_DEF( FT_Error )
1926  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
1927                         FT_Stroker   stroker,
1928                         FT_Bool      inside,
1929                         FT_Bool      destroy 

Large files files are truncated, but you can click here to view the full file