tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ftoutln.c (28721B)


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