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