Home | History | Annotate | Download | only in base
      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 */
   1110