1 /***************************************************************************/
   2 /*                                                                         */
   3 /*  ftoutln.c                                                              */
   4 /*                                                                         */
   5 /*    FreeType outline management (body).                                  */
   6 /*                                                                         */
   7 /*  Copyright 1996-2018 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   /*************************************************************************/
  20   /*                                                                       */
  21   /* All functions are declared in freetype.h.                             */
  22   /*                                                                       */
  23   /*************************************************************************/
  24 
  25 
  26 #include <ft2build.h>
  27 #include FT_OUTLINE_H
  28 #include FT_INTERNAL_OBJECTS_H
  29 #include FT_INTERNAL_CALC_H
  30 #include FT_INTERNAL_DEBUG_H
  31 #include FT_TRIGONOMETRY_H
  32 
  33 
  34   /*************************************************************************/
  35   /*                                                                       */
  36   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
  37   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
  38   /* messages during execution.                                            */
  39   /*                                                                       */
  40 #undef  FT_COMPONENT
  41 #define FT_COMPONENT  trace_outline
  42 
  43 
  44   static
  45   const FT_Outline  null_outline = { 0, 0, NULL, NULL, NULL, 0 };
  46 
  47 
  48   /* documentation is in ftoutln.h */
  49 
  50   FT_EXPORT_DEF( FT_Error )
  51   FT_Outline_Decompose( FT_Outline*              outline,
  52                         const FT_Outline_Funcs*  func_interface,
  53                         void*                    user )
  54   {
  55 #undef  SCALED
  56 #define SCALED( x )  ( ( (x) < 0 ? -( -(x) << shift )             \
  57                                  :  (  (x) << shift ) ) - delta )
  58 
  59     FT_Vector   v_last;
  60     FT_Vector   v_control;
  61     FT_Vector   v_start;
  62 
  63     FT_Vector*  point;
  64     FT_Vector*  limit;
  65     char*       tags;
  66 
  67     FT_Error    error;
  68 
  69     FT_Int   n;         /* index of contour in outline     */
  70     FT_UInt  first;     /* index of first point in contour */
  71     FT_Int   tag;       /* current point's state           */
  72 
  73     FT_Int   shift;
  74     FT_Pos   delta;
  75 
  76 
  77     if ( !outline )
  78       return FT_THROW( Invalid_Outline );
  79 
  80     if ( !func_interface )
  81       return FT_THROW( Invalid_Argument );
  82 
  83     shift = func_interface->shift;
  84     delta = func_interface->delta;
  85     first = 0;
  86 
  87     for ( n = 0; n < outline->n_contours; n++ )
  88     {
  89       FT_Int  last;  /* index of last point in contour */
  90 
  91 
  92       FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
  93 
  94       last = outline->contours[n];
  95       if ( last < 0 )
  96         goto Invalid_Outline;
  97       limit = outline->points + last;
  98 
  99       v_start   = outline->points[first];
 100       v_start.x = SCALED( v_start.x );
 101       v_start.y = SCALED( v_start.y );
 102 
 103       v_last   = outline->points[last];
 104       v_last.x = SCALED( v_last.x );
 105       v_last.y = SCALED( v_last.y );
 106 
 107       v_control = v_start;
 108 
 109       point = outline->points + first;
 110       tags  = outline->tags   + first;
 111       tag   = FT_CURVE_TAG( tags[0] );
 112 
 113       /* A contour cannot start with a cubic control point! */
 114       if ( tag == FT_CURVE_TAG_CUBIC )
 115         goto Invalid_Outline;
 116 
 117       /* check first point to determine origin */
 118       if ( tag == FT_CURVE_TAG_CONIC )
 119       {
 120         /* first point is conic control.  Yes, this happens. */
 121         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
 122         {
 123           /* start at last point if it is on the curve */
 124           v_start = v_last;
 125           limit--;
 126         }
 127         else
 128         {
 129           /* if both first and last points are conic,         */
 130           /* start at their middle and record its position    */
 131           /* for closure                                      */
 132           v_start.x = ( v_start.x + v_last.x ) / 2;
 133           v_start.y = ( v_start.y + v_last.y ) / 2;
 134 
 135        /* v_last = v_start; */
 136         }
 137         point--;
 138         tags--;
 139       }
 140 
 141       FT_TRACE5(( "  move to (%.2f, %.2f)\n",
 142                   v_start.x / 64.0, v_start.y / 64.0 ));
 143       error = func_interface->move_to( &v_start, user );
 144       if ( error )
 145         goto Exit;
 146 
 147       while ( point < limit )
 148       {
 149         point++;
 150         tags++;
 151 
 152         tag = FT_CURVE_TAG( tags[0] );
 153         switch ( tag )
 154         {
 155         case FT_CURVE_TAG_ON:  /* emit a single line_to */
 156           {
 157             FT_Vector  vec;
 158 
 159 
 160             vec.x = SCALED( point->x );
 161             vec.y = SCALED( point->y );
 162 
 163             FT_TRACE5(( "  line to (%.2f, %.2f)\n",
 164                         vec.x / 64.0, vec.y / 64.0 ));
 165             error = func_interface->line_to( &vec, user );
 166             if ( error )
 167               goto Exit;
 168             continue;
 169           }
 170 
 171         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
 172           v_control.x = SCALED( point->x );
 173           v_control.y = SCALED( point->y );
 174 
 175         Do_Conic:
 176           if ( point < limit )
 177           {
 178             FT_Vector  vec;
 179             FT_Vector  v_middle;
 180 
 181 
 182             point++;
 183             tags++;
 184             tag = FT_CURVE_TAG( tags[0] );
 185 
 186             vec.x = SCALED( point->x );
 187             vec.y = SCALED( point->y );
 188 
 189             if ( tag == FT_CURVE_TAG_ON )
 190             {
 191               FT_TRACE5(( "  conic to (%.2f, %.2f)"
 192                           " with control (%.2f, %.2f)\n",
 193                           vec.x / 64.0, vec.y / 64.0,
 194                           v_control.x / 64.0, v_control.y / 64.0 ));
 195               error = func_interface->conic_to( &v_control, &vec, user );
 196               if ( error )
 197                 goto Exit;
 198               continue;
 199             }
 200 
 201             if ( tag != FT_CURVE_TAG_CONIC )
 202               goto Invalid_Outline;
 203 
 204             v_middle.x = ( v_control.x + vec.x ) / 2;
 205             v_middle.y = ( v_control.y + vec.y ) / 2;
 206 
 207             FT_TRACE5(( "  conic to (%.2f, %.2f)"
 208                         " with control (%.2f, %.2f)\n",
 209                         v_middle.x / 64.0, v_middle.y / 64.0,
 210                         v_control.x / 64.0, v_control.y / 64.0 ));
 211             error = func_interface->conic_to( &v_control, &v_middle, user );
 212             if ( error )
 213               goto Exit;
 214 
 215             v_control = vec;
 216             goto Do_Conic;
 217           }
 218 
 219           FT_TRACE5(( "  conic to (%.2f, %.2f)"
 220                       " with control (%.2f, %.2f)\n",
 221                       v_start.x / 64.0, v_start.y / 64.0,
 222                       v_control.x / 64.0, v_control.y / 64.0 ));
 223           error = func_interface->conic_to( &v_control, &v_start, user );
 224           goto Close;
 225 
 226         default:  /* FT_CURVE_TAG_CUBIC */
 227           {
 228             FT_Vector  vec1, vec2;
 229 
 230 
 231             if ( point + 1 > limit                             ||
 232                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
 233               goto Invalid_Outline;
 234 
 235             point += 2;
 236             tags  += 2;
 237 
 238             vec1.x = SCALED( point[-2].x );
 239             vec1.y = SCALED( point[-2].y );
 240 
 241             vec2.x = SCALED( point[-1].x );
 242             vec2.y = SCALED( point[-1].y );
 243 
 244             if ( point <= limit )
 245             {
 246               FT_Vector  vec;
 247 
 248 
 249               vec.x = SCALED( point->x );
 250               vec.y = SCALED( point->y );
 251 
 252               FT_TRACE5(( "  cubic to (%.2f, %.2f)"
 253                           " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
 254                           vec.x / 64.0, vec.y / 64.0,
 255                           vec1.x / 64.0, vec1.y / 64.0,
 256                           vec2.x / 64.0, vec2.y / 64.0 ));
 257               error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
 258               if ( error )
 259                 goto Exit;
 260               continue;
 261             }
 262 
 263             FT_TRACE5(( "  cubic to (%.2f, %.2f)"
 264                         " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
 265                         v_start.x / 64.0, v_start.y / 64.0,
 266                         vec1.x / 64.0, vec1.y / 64.0,
 267                         vec2.x / 64.0, vec2.y / 64.0 ));
 268             error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
 269             goto Close;
 270           }
 271         }
 272       }
 273 
 274       /* close the contour with a line segment */
 275       FT_TRACE5(( "  line to (%.2f, %.2f)\n",
 276                   v_start.x / 64.0, v_start.y / 64.0 ));
 277       error = func_interface->line_to( &v_start, user );
 278 
 279     Close:
 280       if ( error )
 281         goto Exit;
 282 
 283       first = (FT_UInt)last + 1;
 284     }
 285 
 286     FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
 287     return FT_Err_Ok;
 288 
 289   Exit:
 290     FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error ));
 291     return error;
 292 
 293   Invalid_Outline:
 294     return FT_THROW( Invalid_Outline );
 295   }
 296 
 297 
 298   FT_EXPORT_DEF( FT_Error )
 299   FT_Outline_New_Internal( FT_Memory    memory,
 300                            FT_UInt      numPoints,
 301                            FT_Int       numContours,
 302                            FT_Outline  *anoutline )
 303   {
 304     FT_Error  error;
 305 
 306 
 307     if ( !anoutline || !memory )
 308       return FT_THROW( Invalid_Argument );
 309 
 310     *anoutline = null_outline;
 311 
 312     if ( numContours < 0                  ||
 313          (FT_UInt)numContours > numPoints )
 314       return FT_THROW( Invalid_Argument );
 315 
 316     if ( numPoints > FT_OUTLINE_POINTS_MAX )
 317       return FT_THROW( Array_Too_Large );
 318 
 319     if ( FT_NEW_ARRAY( anoutline->points,   numPoints   ) ||
 320          FT_NEW_ARRAY( anoutline->tags,     numPoints   ) ||
 321          FT_NEW_ARRAY( anoutline->contours, numContours ) )
 322       goto Fail;
 323 
 324     anoutline->n_points    = (FT_Short)numPoints;
 325     anoutline->n_contours  = (FT_Short)numContours;
 326     anoutline->flags      |= FT_OUTLINE_OWNER;
 327 
 328     return FT_Err_Ok;
 329 
 330   Fail:
 331     anoutline->flags |= FT_OUTLINE_OWNER;
 332     FT_Outline_Done_Internal( memory, anoutline );
 333 
 334     return error;
 335   }
 336 
 337 
 338   /* documentation is in ftoutln.h */
 339 
 340   FT_EXPORT_DEF( FT_Error )
 341   FT_Outline_New( FT_Library   library,
 342                   FT_UInt      numPoints,
 343                   FT_Int       numContours,
 344                   FT_Outline  *anoutline )
 345   {
 346     if ( !library )
 347       return FT_THROW( Invalid_Library_Handle );
 348 
 349     return FT_Outline_New_Internal( library->memory, numPoints,
 350                                     numContours, anoutline );
 351   }
 352 
 353 
 354   /* documentation is in ftoutln.h */
 355 
 356   FT_EXPORT_DEF( FT_Error )
 357   FT_Outline_Check( FT_Outline*  outline )
 358   {
 359     if ( outline )
 360     {
 361       FT_Int  n_points   = outline->n_points;
 362       FT_Int  n_contours = outline->n_contours;
 363       FT_Int  end0, end;
 364       FT_Int  n;
 365 
 366 
 367       /* empty glyph? */
 368       if ( n_points == 0 && n_contours == 0 )
 369         return FT_Err_Ok;
 370 
 371       /* check point and contour counts */
 372       if ( n_points <= 0 || n_contours <= 0 )
 373         goto Bad;
 374 
 375       end0 = end = -1;
 376       for ( n = 0; n < n_contours; n++ )
 377       {
 378         end = outline->contours[n];
 379 
 380         /* note that we don't accept empty contours */
 381         if ( end <= end0 || end >= n_points )
 382           goto Bad;
 383 
 384         end0 = end;
 385       }
 386 
 387       if ( end != n_points - 1 )
 388         goto Bad;
 389 
 390       /* XXX: check the tags array */
 391       return FT_Err_Ok;
 392     }
 393 
 394   Bad:
 395     return FT_THROW( Invalid_Argument );
 396   }
 397 
 398 
 399   /* documentation is in ftoutln.h */
 400 
 401   FT_EXPORT_DEF( FT_Error )
 402   FT_Outline_Copy( const FT_Outline*  source,
 403                    FT_Outline        *target )
 404   {
 405     FT_Int  is_owner;
 406 
 407 
 408     if ( !source || !target )
 409       return FT_THROW( Invalid_Outline );
 410 
 411     if ( source->n_points   != target->n_points   ||
 412          source->n_contours != target->n_contours )
 413       return FT_THROW( Invalid_Argument );
 414 
 415     if ( source == target )
 416       return FT_Err_Ok;
 417 
 418     if ( source->n_points )
 419     {
 420       FT_ARRAY_COPY( target->points, source->points, source->n_points );
 421       FT_ARRAY_COPY( target->tags,   source->tags,   source->n_points );
 422     }
 423 
 424     if ( source->n_contours )
 425       FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
 426 
 427     /* copy all flags, except the `FT_OUTLINE_OWNER' one */
 428     is_owner      = target->flags & FT_OUTLINE_OWNER;
 429     target->flags = source->flags;
 430 
 431     target->flags &= ~FT_OUTLINE_OWNER;
 432     target->flags |= is_owner;
 433 
 434     return FT_Err_Ok;
 435   }
 436 
 437 
 438   FT_EXPORT_DEF( FT_Error )
 439   FT_Outline_Done_Internal( FT_Memory    memory,
 440                             FT_Outline*  outline )
 441   {
 442     if ( !outline )
 443       return FT_THROW( Invalid_Outline );
 444 
 445     if ( !memory )
 446       return FT_THROW( Invalid_Argument );
 447 
 448     if ( outline->flags & FT_OUTLINE_OWNER )
 449     {
 450       FT_FREE( outline->points   );
 451       FT_FREE( outline->tags     );
 452       FT_FREE( outline->contours );
 453     }
 454     *outline = null_outline;
 455 
 456     return FT_Err_Ok;
 457   }
 458 
 459 
 460   /* documentation is in ftoutln.h */
 461 
 462   FT_EXPORT_DEF( FT_Error )
 463   FT_Outline_Done( FT_Library   library,
 464                    FT_Outline*  outline )
 465   {
 466     /* check for valid `outline' in FT_Outline_Done_Internal() */
 467 
 468     if ( !library )
 469       return FT_THROW( Invalid_Library_Handle );
 470 
 471     return FT_Outline_Done_Internal( library->memory, outline );
 472   }
 473 
 474 
 475   /* documentation is in ftoutln.h */
 476 
 477   FT_EXPORT_DEF( void )
 478   FT_Outline_Get_CBox( const FT_Outline*  outline,
 479                        FT_BBox           *acbox )
 480   {
 481     FT_Pos  xMin, yMin, xMax, yMax;
 482 
 483 
 484     if ( outline && acbox )
 485     {
 486       if ( outline->n_points == 0 )
 487       {
 488         xMin = 0;
 489         yMin = 0;
 490         xMax = 0;
 491         yMax = 0;
 492       }
 493       else
 494       {
 495         FT_Vector*  vec   = outline->points;
 496         FT_Vector*  limit = vec + outline->n_points;
 497 
 498 
 499         xMin = xMax = vec->x;
 500         yMin = yMax = vec->y;
 501         vec++;
 502 
 503         for ( ; vec < limit; vec++ )
 504         {
 505           FT_Pos  x, y;
 506 
 507 
 508           x = vec->x;
 509           if ( x < xMin ) xMin = x;
 510           if ( x > xMax ) xMax = x;
 511 
 512           y = vec->y;
 513           if ( y < yMin ) yMin = y;
 514           if ( y > yMax ) yMax = y;
 515         }
 516       }
 517       acbox->xMin = xMin;
 518       acbox->xMax = xMax;
 519       acbox->yMin = yMin;
 520       acbox->yMax = yMax;
 521     }
 522   }
 523 
 524 
 525   /* documentation is in ftoutln.h */
 526 
 527   FT_EXPORT_DEF( void )
 528   FT_Outline_Translate( const FT_Outline*  outline,
 529                         FT_Pos             xOffset,
 530                         FT_Pos             yOffset )
 531   {
 532     FT_UShort   n;
 533     FT_Vector*  vec;
 534 
 535 
 536     if ( !outline )
 537       return;
 538 
 539     vec = outline->points;
 540 
 541     for ( n = 0; n < outline->n_points; n++ )
 542     {
 543       vec->x = ADD_LONG( vec->x, xOffset );
 544       vec->y = ADD_LONG( vec->y, yOffset );
 545       vec++;
 546     }
 547   }
 548 
 549 
 550   /* documentation is in ftoutln.h */
 551 
 552   FT_EXPORT_DEF( void )
 553   FT_Outline_Reverse( FT_Outline*  outline )
 554   {
 555     FT_UShort  n;
 556     FT_Int     first, last;
 557 
 558 
 559     if ( !outline )
 560       return;
 561 
 562     first = 0;
 563 
 564     for ( n = 0; n < outline->n_contours; n++ )
 565     {
 566       last  = outline->contours[n];
 567 
 568       /* reverse point table */
 569       {
 570         FT_Vector*  p = outline->points + first;
 571         FT_Vector*  q = outline->points + last;
 572         FT_Vector   swap;
 573 
 574 
 575         while ( p < q )
 576         {
 577           swap = *p;
 578           *p   = *q;
 579           *q   = swap;
 580           p++;
 581           q--;
 582         }
 583       }
 584 
 585       /* reverse tags table */
 586       {
 587         char*  p = outline->tags + first;
 588         char*  q = outline->tags + last;
 589 
 590 
 591         while ( p < q )
 592         {
 593           char  swap;
 594 
 595 
 596           swap = *p;
 597           *p   = *q;
 598           *q   = swap;
 599           p++;
 600           q--;
 601         }
 602       }
 603 
 604       first = last + 1;
 605     }
 606 
 607     outline->flags ^= FT_OUTLINE_REVERSE_FILL;
 608   }
 609 
 610 
 611   /* documentation is in ftoutln.h */
 612 
 613   FT_EXPORT_DEF( FT_Error )
 614   FT_Outline_Render( FT_Library         library,
 615                      FT_Outline*        outline,
 616                      FT_Raster_Params*  params )
 617   {
 618     FT_Error     error;
 619     FT_Renderer  renderer;
 620     FT_ListNode  node;
 621 
 622 
 623     if ( !library )
 624       return FT_THROW( Invalid_Library_Handle );
 625 
 626     if ( !outline )
 627       return FT_THROW( Invalid_Outline );
 628 
 629     if ( !params )
 630       return FT_THROW( Invalid_Argument );
 631 
 632     renderer = library->cur_renderer;
 633     node     = library->renderers.head;
 634 
 635     params->source = (void*)outline;
 636 
 637     error = FT_ERR( Cannot_Render_Glyph );
 638     while ( renderer )
 639     {
 640       error = renderer->raster_render( renderer->raster, params );
 641       if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
 642         break;
 643 
 644       /* FT_Err_Cannot_Render_Glyph is returned if the render mode   */
 645       /* is unsupported by the current renderer for this glyph image */
 646       /* format                                                      */
 647 
 648       /* now, look for another renderer that supports the same */
 649       /* format                                                */
 650       renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
 651                                      &node );
 652     }
 653 
 654     return error;
 655   }
 656 
 657 
 658   /* documentation is in ftoutln.h */
 659 
 660   FT_EXPORT_DEF( FT_Error )
 661   FT_Outline_Get_Bitmap( FT_Library        library,
 662                          FT_Outline*       outline,
 663                          const FT_Bitmap  *abitmap )
 664   {
 665     FT_Raster_Params  params;
 666 
 667 
 668     if ( !abitmap )
 669       return FT_THROW( Invalid_Argument );
 670 
 671     /* other checks are delayed to `FT_Outline_Render' */
 672 
 673     params.target = abitmap;
 674     params.flags  = 0;
 675 
 676     if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY  ||
 677          abitmap->pixel_mode == FT_PIXEL_MODE_LCD   ||
 678          abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
 679       params.flags |= FT_RASTER_FLAG_AA;
 680 
 681     return FT_Outline_Render( library, outline, &params );
 682   }
 683 
 684 
 685   /* documentation is in freetype.h */
 686 
 687   FT_EXPORT_DEF( void )
 688   FT_Vector_Transform( FT_Vector*        vector,
 689                        const FT_Matrix*  matrix )
 690   {
 691     FT_Pos  xz, yz;
 692 
 693 
 694     if ( !vector || !matrix )
 695       return;
 696 
 697     xz = FT_MulFix( vector->x, matrix->xx ) +
 698          FT_MulFix( vector->y, matrix->xy );
 699 
 700     yz = FT_MulFix( vector->x, matrix->yx ) +
 701          FT_MulFix( vector->y, matrix->yy );
 702 
 703     vector->x = xz;
 704     vector->y = yz;
 705   }
 706 
 707 
 708   /* documentation is in ftoutln.h */
 709 
 710   FT_EXPORT_DEF( void )
 711   FT_Outline_Transform( const FT_Outline*  outline,
 712                         const FT_Matrix*   matrix )
 713   {
 714     FT_Vector*  vec;
 715     FT_Vector*  limit;
 716 
 717 
 718     if ( !outline || !matrix )
 719       return;
 720 
 721     vec   = outline->points;
 722     limit = vec + outline->n_points;
 723 
 724     for ( ; vec < limit; vec++ )
 725       FT_Vector_Transform( vec, matrix );
 726   }
 727 
 728 
 729 #if 0
 730 
 731 #define FT_OUTLINE_GET_CONTOUR( outline, c, first, last )  \
 732   do                                                       \
 733   {                                                        \
 734     (first) = ( c > 0 ) ? (outline)->points +              \
 735                             (outline)->contours[c - 1] + 1 \
 736                         : (outline)->points;               \
 737     (last) = (outline)->points + (outline)->contours[c];   \
 738   } while ( 0 )
 739 
 740 
 741   /* Is a point in some contour?                     */
 742   /*                                                 */
 743   /* We treat every point of the contour as if it    */
 744   /* it were ON.  That is, we allow false positives, */
 745   /* but disallow false negatives.  (XXX really?)    */
 746   static FT_Bool
 747   ft_contour_has( FT_Outline*  outline,
 748                   FT_Short     c,
 749                   FT_Vector*   point )
 750   {
 751     FT_Vector*  first;
 752     FT_Vector*  last;
 753     FT_Vector*  a;
 754     FT_Vector*  b;
 755     FT_UInt     n = 0;
 756 
 757 
 758     FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
 759 
 760     for ( a = first; a <= last; a++ )
 761     {
 762       FT_Pos  x;
 763       FT_Int  intersect;
 764 
 765 
 766       b = ( a == last ) ? first : a + 1;
 767 
 768       intersect = ( a->y - point->y ) ^ ( b->y - point->y );
 769 
 770       /* a and b are on the same side */
 771       if ( intersect >= 0 )
 772       {
 773         if ( intersect == 0 && a->y == point->y )
 774         {
 775           if ( ( a->x <= point->x && b->x >= point->x ) ||
 776                ( a->x >= point->x && b->x <= point->x ) )
 777             return 1;
 778         }
 779 
 780         continue;
 781       }
 782 
 783       x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
 784 
 785       if ( x < point->x )
 786         n++;
 787       else if ( x == point->x )
 788         return 1;
 789     }
 790 
 791     return n & 1;
 792   }
 793 
 794 
 795   static FT_Bool
 796   ft_contour_enclosed( FT_Outline*  outline,
 797                        FT_UShort    c )
 798   {
 799     FT_Vector*  first;
 800     FT_Vector*  last;
 801     FT_Short    i;
 802 
 803 
 804     FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
 805 
 806     for ( i = 0; i < outline->n_contours; i++ )
 807     {
 808       if ( i != c && ft_contour_has( outline, i, first ) )
 809       {
 810         FT_Vector*  pt;
 811 
 812 
 813         for ( pt = first + 1; pt <= last; pt++ )
 814           if ( !ft_contour_has( outline, i, pt ) )
 815             return 0;
 816 
 817         return 1;
 818       }
 819     }
 820 
 821     return 0;
 822   }
 823 
 824 
 825   /* This version differs from the public one in that each */
 826   /* part (contour not enclosed in another contour) of the */
 827   /* outline is checked for orientation.  This is          */
 828   /* necessary for some buggy CJK fonts.                   */
 829   static FT_Orientation
 830   ft_outline_get_orientation( FT_Outline*  outline )
 831   {
 832     FT_Short        i;
 833     FT_Vector*      first;
 834     FT_Vector*      last;
 835     FT_Orientation  orient = FT_ORIENTATION_NONE;
 836 
 837 
 838     first = outline->points;
 839     for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
 840     {
 841       FT_Vector*  point;
 842       FT_Vector*  xmin_point;
 843       FT_Pos      xmin;
 844 
 845 
 846       last = outline->points + outline->contours[i];
 847 
 848       /* skip degenerate contours */
 849       if ( last < first + 2 )
 850         continue;
 851 
 852       if ( ft_contour_enclosed( outline, i ) )
 853         continue;
 854 
 855       xmin       = first->x;
 856       xmin_point = first;
 857 
 858       for ( point = first + 1; point <= last; point++ )
 859       {
 860         if ( point->x < xmin )
 861         {
 862           xmin       = point->x;
 863           xmin_point = point;
 864         }
 865       }
 866 
 867       /* check the orientation of the contour */
 868       {
 869         FT_Vector*      prev;
 870         FT_Vector*      next;
 871         FT_Orientation  o;
 872 
 873 
 874         prev = ( xmin_point == first ) ? last : xmin_point - 1;
 875         next = ( xmin_point == last ) ? first : xmin_point + 1;
 876 
 877         if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
 878              FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
 879           o = FT_ORIENTATION_POSTSCRIPT;
 880         else
 881           o = FT_ORIENTATION_TRUETYPE;
 882 
 883         if ( orient == FT_ORIENTATION_NONE )
 884           orient = o;
 885         else if ( orient != o )
 886           return FT_ORIENTATION_NONE;
 887       }
 888     }
 889 
 890     return orient;
 891   }
 892 
 893 #endif /* 0 */
 894 
 895 
 896   /* documentation is in ftoutln.h */
 897 
 898   FT_EXPORT_DEF( FT_Error )
 899   FT_Outline_Embolden( FT_Outline*  outline,
 900                        FT_Pos       strength )
 901   {
 902     return FT_Outline_EmboldenXY( outline, strength, strength );
 903   }
 904 
 905 
 906   /* documentation is in ftoutln.h */
 907 
 908   FT_EXPORT_DEF( FT_Error )
 909   FT_Outline_EmboldenXY( FT_Outline*  outline,
 910                          FT_Pos       xstrength,
 911                          FT_Pos       ystrength )
 912   {
 913     FT_Vector*  points;
 914     FT_Int      c, first, last;
 915     FT_Int      orientation;
 916 
 917 
 918     if ( !outline )
 919       return FT_THROW( Invalid_Outline );
 920 
 921     xstrength /= 2;
 922     ystrength /= 2;
 923     if ( xstrength == 0 && ystrength == 0 )
 924       return FT_Err_Ok;
 925 
 926     orientation = FT_Outline_Get_Orientation( outline );
 927     if ( orientation == FT_ORIENTATION_NONE )
 928     {
 929       if ( outline->n_contours )
 930         return FT_THROW( Invalid_Argument );
 931       else
 932         return FT_Err_Ok;
 933     }
 934 
 935     points = outline->points;
 936 
 937     first = 0;
 938     for ( c = 0; c < outline->n_contours; c++ )
 939     {
 940       FT_Vector  in, out, anchor, shift;
 941       FT_Fixed   l_in, l_out, l_anchor = 0, l, q, d;
 942       FT_Int     i, j, k;
 943 
 944 
 945       l_in = 0;
 946       last = outline->contours[c];
 947 
 948       /* pacify compiler */
 949       in.x = in.y = anchor.x = anchor.y = 0;
 950 
 951       /* Counter j cycles though the points; counter i advances only  */
 952       /* when points are moved; anchor k marks the first moved point. */
 953       for ( i = last, j = first, k = -1;
 954             j != i && i != k;
 955             j = j < last ? j + 1 : first )
 956       {
 957         if ( j != k )
 958         {
 959           out.x = points[j].x - points[i].x;
 960           out.y = points[j].y - points[i].y;
 961           l_out = (FT_Fixed)FT_Vector_NormLen( &out );
 962 
 963           if ( l_out == 0 )
 964             continue;
 965         }
 966         else
 967         {
 968           out   = anchor;
 969           l_out = l_anchor;
 970         }
 971 
 972         if ( l_in != 0 )
 973         {
 974           if ( k < 0 )
 975           {
 976             k        = i;
 977             anchor   = in;
 978             l_anchor = l_in;
 979           }
 980 
 981           d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y );
 982 
 983           /* shift only if turn is less than ~160 degrees */
 984           if ( d > -0xF000L )
 985           {
 986             d = d + 0x10000L;
 987 
 988             /* shift components along lateral bisector in proper orientation */
 989             shift.x = in.y + out.y;
 990             shift.y = in.x + out.x;
 991 
 992             if ( orientation == FT_ORIENTATION_TRUETYPE )
 993               shift.x = -shift.x;
 994             else
 995               shift.y = -shift.y;
 996 
 997             /* restrict shift magnitude to better handle collapsing segments */
 998             q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x );
 999             if ( orientation == FT_ORIENTATION_TRUETYPE )
1000               q = -q;
1001 
1002             l = FT_MIN( l_in, l_out );
1003 
1004             /* non-strict inequalities avoid divide-by-zero when q == l == 0 */
1005             if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) )
1006               shift.x = FT_MulDiv( shift.x, xstrength, d );
1007             else
1008               shift.x = FT_MulDiv( shift.x, l, q );
1009 
1010 
1011             if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) )
1012               shift.y = FT_MulDiv( shift.y, ystrength, d );
1013             else
1014               shift.y = FT_MulDiv( shift.y, l, q );
1015           }
1016           else
1017             shift.x = shift.y = 0;
1018 
1019           for ( ;
1020                 i != j;
1021                 i = i < last ? i + 1 : first )
1022           {
1023             points[i].x += xstrength + shift.x;
1024             points[i].y += ystrength + shift.y;
1025           }
1026         }
1027         else
1028           i = j;
1029 
1030         in   = out;
1031         l_in = l_out;
1032       }
1033 
1034       first = last + 1;
1035     }
1036 
1037     return FT_Err_Ok;
1038   }
1039 
1040 
1041   /* documentation is in ftoutln.h */
1042 
1043   FT_EXPORT_DEF( FT_Orientation )
1044   FT_Outline_Get_Orientation( FT_Outline*  outline )
1045   {
1046     FT_BBox     cbox;
1047     FT_Int      xshift, yshift;
1048     FT_Vector*  points;
1049     FT_Vector   v_prev, v_cur;
1050     FT_Int      c, n, first;
1051     FT_Pos      area = 0;
1052 
1053 
1054     if ( !outline || outline->n_points <= 0 )
1055       return FT_ORIENTATION_TRUETYPE;
1056 
1057     /* We use the nonzero winding rule to find the orientation.       */
1058     /* Since glyph outlines behave much more `regular' than arbitrary */
1059     /* cubic or quadratic curves, this test deals with the polygon    */
1060     /* only that is spanned up by the control points.                 */
1061 
1062     FT_Outline_Get_CBox( outline, &cbox );
1063 
1064     /* Handle collapsed outlines to avoid undefined FT_MSB. */
1065     if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax )
1066       return FT_ORIENTATION_NONE;
1067 
1068     xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) |
1069                                   FT_ABS( cbox.xMin ) ) ) - 14;
1070     xshift = FT_MAX( xshift, 0 );
1071 
1072     yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14;
1073     yshift = FT_MAX( yshift, 0 );
1074 
1075     points = outline->points;
1076 
1077     first = 0;
1078     for ( c = 0; c < outline->n_contours; c++ )
1079     {
1080       FT_Int  last = outline->contours[c];
1081 
1082 
1083       v_prev.x = points[last].x >> xshift;
1084       v_prev.y = points[last].y >> yshift;
1085 
1086       for ( n = first; n <= last; n++ )
1087       {
1088         v_cur.x = points[n].x >> xshift;
1089         v_cur.y = points[n].y >> yshift;
1090 
1091         area = ADD_LONG( area,
1092                          ( v_cur.y - v_prev.y ) * ( v_cur.x + v_prev.x ) );
1093 
1094         v_prev = v_cur;
1095       }
1096 
1097       first = last + 1;
1098     }
1099 
1100     if ( area > 0 )
1101       return FT_ORIENTATION_POSTSCRIPT;
1102     else if ( area < 0 )
1103       return FT_ORIENTATION_TRUETYPE;
1104     else
1105       return FT_ORIENTATION_NONE;
1106   }
1107 
1108 
1109 /* END */