tor-browser

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

afhints.c (47927B)


      1 /****************************************************************************
      2 *
      3 * afhints.c
      4 *
      5 *   Auto-fitter hinting routines (body).
      6 *
      7 * Copyright (C) 2003-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 "afhints.h"
     20 #include "aferrors.h"
     21 #include <freetype/internal/ftcalc.h>
     22 #include <freetype/internal/ftdebug.h>
     23 
     24 
     25  /**************************************************************************
     26   *
     27   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     28   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     29   * messages during execution.
     30   */
     31 #undef  FT_COMPONENT
     32 #define FT_COMPONENT  afhints
     33 
     34 
     35  FT_LOCAL_DEF( void )
     36  af_sort_pos( FT_UInt  count,
     37               FT_Pos*  table )
     38  {
     39    FT_UInt  i, j;
     40    FT_Pos   swap;
     41 
     42 
     43    for ( i = 1; i < count; i++ )
     44    {
     45      for ( j = i; j > 0; j-- )
     46      {
     47        if ( table[j] >= table[j - 1] )
     48          break;
     49 
     50        swap         = table[j];
     51        table[j]     = table[j - 1];
     52        table[j - 1] = swap;
     53      }
     54    }
     55  }
     56 
     57 
     58  FT_LOCAL_DEF( void )
     59  af_sort_and_quantize_widths( FT_UInt*  count,
     60                               AF_Width  table,
     61                               FT_Pos    threshold )
     62  {
     63    FT_UInt      i, j;
     64    FT_UInt      cur_idx;
     65    FT_Pos       cur_val;
     66    FT_Pos       sum;
     67    AF_WidthRec  swap;
     68 
     69 
     70    if ( *count == 1 )
     71      return;
     72 
     73    /* sort */
     74    for ( i = 1; i < *count; i++ )
     75    {
     76      for ( j = i; j > 0; j-- )
     77      {
     78        if ( table[j].org >= table[j - 1].org )
     79          break;
     80 
     81        swap         = table[j];
     82        table[j]     = table[j - 1];
     83        table[j - 1] = swap;
     84      }
     85    }
     86 
     87    cur_idx = 0;
     88    cur_val = table[cur_idx].org;
     89 
     90    /* compute and use mean values for clusters not larger than  */
     91    /* `threshold'; this is very primitive and might not yield   */
     92    /* the best result, but normally, using reference character  */
     93    /* `o', `*count' is 2, so the code below is fully sufficient */
     94    for ( i = 1; i < *count; i++ )
     95    {
     96      if ( table[i].org - cur_val > threshold ||
     97           i == *count - 1                    )
     98      {
     99        sum = 0;
    100 
    101        /* fix loop for end of array */
    102        if ( table[i].org - cur_val <= threshold &&
    103             i == *count - 1                     )
    104          i++;
    105 
    106        for ( j = cur_idx; j < i; j++ )
    107        {
    108          sum         += table[j].org;
    109          table[j].org = 0;
    110        }
    111        table[cur_idx].org = sum / (FT_Pos)j;
    112 
    113        if ( i < *count - 1 )
    114        {
    115          cur_idx = i + 1;
    116          cur_val = table[cur_idx].org;
    117        }
    118      }
    119    }
    120 
    121    cur_idx = 1;
    122 
    123    /* compress array to remove zero values */
    124    for ( i = 1; i < *count; i++ )
    125    {
    126      if ( table[i].org )
    127        table[cur_idx++] = table[i];
    128    }
    129 
    130    *count = cur_idx;
    131  }
    132 
    133  /* Get new segment for given axis. */
    134 
    135  FT_LOCAL_DEF( FT_Error )
    136  af_axis_hints_new_segment( AF_AxisHints  axis,
    137                             FT_Memory     memory,
    138                             AF_Segment   *asegment )
    139  {
    140    FT_Error    error   = FT_Err_Ok;
    141    AF_Segment  segment = NULL;
    142 
    143 
    144    if ( axis->num_segments < AF_SEGMENTS_EMBEDDED )
    145    {
    146      if ( !axis->segments )
    147      {
    148        axis->segments     = axis->embedded.segments;
    149        axis->max_segments = AF_SEGMENTS_EMBEDDED;
    150      }
    151    }
    152    else if ( axis->num_segments >= axis->max_segments )
    153    {
    154      FT_UInt  old_max = axis->max_segments;
    155      FT_UInt  new_max = old_max;
    156      FT_UInt  big_max = FT_INT_MAX / sizeof ( *segment );
    157 
    158 
    159      if ( old_max >= big_max )
    160      {
    161        error = FT_THROW( Out_Of_Memory );
    162        goto Exit;
    163      }
    164 
    165      new_max += ( new_max >> 2 ) + 4;
    166      if ( new_max < old_max || new_max > big_max )
    167        new_max = big_max;
    168 
    169      if ( axis->segments == axis->embedded.segments )
    170      {
    171        if ( FT_NEW_ARRAY( axis->segments, new_max ) )
    172          goto Exit;
    173        ft_memcpy( axis->segments, axis->embedded.segments,
    174                   sizeof ( axis->embedded.segments ) );
    175      }
    176      else
    177      {
    178        if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
    179          goto Exit;
    180      }
    181 
    182      axis->max_segments = new_max;
    183    }
    184 
    185    segment = axis->segments + axis->num_segments++;
    186 
    187  Exit:
    188    *asegment = segment;
    189    return error;
    190  }
    191 
    192 
    193  /* Get new edge for given axis, direction, and position, */
    194  /* without initializing the edge itself.                 */
    195 
    196  FT_LOCAL_DEF( FT_Error )
    197  af_axis_hints_new_edge( AF_AxisHints  axis,
    198                          FT_Int        fpos,
    199                          AF_Direction  dir,
    200                          FT_Bool       top_to_bottom_hinting,
    201                          FT_Memory     memory,
    202                          AF_Edge      *anedge )
    203  {
    204    FT_Error  error = FT_Err_Ok;
    205    AF_Edge   edge  = NULL;
    206    AF_Edge   edges;
    207 
    208 
    209    if ( axis->num_edges < AF_EDGES_EMBEDDED )
    210    {
    211      if ( !axis->edges )
    212      {
    213        axis->edges     = axis->embedded.edges;
    214        axis->max_edges = AF_EDGES_EMBEDDED;
    215      }
    216    }
    217    else if ( axis->num_edges >= axis->max_edges )
    218    {
    219      FT_UInt  old_max = axis->max_edges;
    220      FT_UInt  new_max = old_max;
    221      FT_UInt  big_max = FT_INT_MAX / sizeof ( *edge );
    222 
    223 
    224      if ( old_max >= big_max )
    225      {
    226        error = FT_THROW( Out_Of_Memory );
    227        goto Exit;
    228      }
    229 
    230      new_max += ( new_max >> 2 ) + 4;
    231      if ( new_max < old_max || new_max > big_max )
    232        new_max = big_max;
    233 
    234      if ( axis->edges == axis->embedded.edges )
    235      {
    236        if ( FT_NEW_ARRAY( axis->edges, new_max ) )
    237          goto Exit;
    238        ft_memcpy( axis->edges, axis->embedded.edges,
    239                   sizeof ( axis->embedded.edges ) );
    240      }
    241      else
    242      {
    243        if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
    244          goto Exit;
    245      }
    246 
    247      axis->max_edges = new_max;
    248    }
    249 
    250    edges = axis->edges;
    251    edge  = edges + axis->num_edges;
    252 
    253    while ( edge > edges )
    254    {
    255      if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos )
    256                                 : ( edge[-1].fpos < fpos ) )
    257        break;
    258 
    259      /* we want the edge with same position and minor direction */
    260      /* to appear before those in the major one in the list     */
    261      if ( edge[-1].fpos == fpos && dir == axis->major_dir )
    262        break;
    263 
    264      edge[0] = edge[-1];
    265      edge--;
    266    }
    267 
    268    axis->num_edges++;
    269 
    270  Exit:
    271    *anedge = edge;
    272    return error;
    273  }
    274 
    275 
    276 #ifdef FT_DEBUG_AUTOFIT
    277 
    278 #include FT_CONFIG_STANDARD_LIBRARY_H
    279 
    280  /* The dump functions are used in the `ftgrid' demo program, too. */
    281 #define AF_DUMP( varformat )          \
    282          do                          \
    283          {                           \
    284            if ( to_stdout )          \
    285              printf varformat;       \
    286            else                      \
    287              FT_TRACE7( varformat ); \
    288          } while ( 0 )
    289 
    290 
    291  static const char*
    292  af_dir_str( AF_Direction  dir )
    293  {
    294    const char*  result;
    295 
    296 
    297    switch ( dir )
    298    {
    299    case AF_DIR_UP:
    300      result = "up";
    301      break;
    302    case AF_DIR_DOWN:
    303      result = "down";
    304      break;
    305    case AF_DIR_LEFT:
    306      result = "left";
    307      break;
    308    case AF_DIR_RIGHT:
    309      result = "right";
    310      break;
    311    default:
    312      result = "none";
    313    }
    314 
    315    return result;
    316  }
    317 
    318 
    319 #define AF_INDEX_NUM( ptr, base )  (int)( (ptr) ? ( (ptr) - (base) ) : -1 )
    320 
    321 
    322  static char*
    323  af_print_idx( char*   p,
    324                size_t  n,
    325                int     idx )
    326  {
    327    if ( idx == -1 )
    328    {
    329      p[0] = '-';
    330      p[1] = '-';
    331      p[2] = '\0';
    332    }
    333    else
    334      ft_snprintf( p, n, "%d", idx );
    335 
    336    return p;
    337  }
    338 
    339 
    340  static int
    341  af_get_segment_index( AF_GlyphHints  hints,
    342                        int            point_idx,
    343                        int            dimension )
    344  {
    345    AF_AxisHints  axis     = &hints->axis[dimension];
    346    AF_Point      point    = hints->points + point_idx;
    347    AF_Segment    segments = axis->segments;
    348    AF_Segment    limit    = segments + axis->num_segments;
    349    AF_Segment    segment;
    350 
    351 
    352    for ( segment = segments; segment < limit; segment++ )
    353    {
    354      if ( segment->first <= segment->last )
    355      {
    356        if ( point >= segment->first && point <= segment->last )
    357          break;
    358      }
    359      else
    360      {
    361        AF_Point  p = segment->first;
    362 
    363 
    364        for (;;)
    365        {
    366          if ( point == p )
    367            goto Exit;
    368 
    369          if ( p == segment->last )
    370            break;
    371 
    372          p = p->next;
    373        }
    374      }
    375    }
    376 
    377  Exit:
    378    if ( segment == limit )
    379      return -1;
    380 
    381    return (int)( segment - segments );
    382  }
    383 
    384 
    385  static int
    386  af_get_edge_index( AF_GlyphHints  hints,
    387                     int            segment_idx,
    388                     int            dimension )
    389  {
    390    AF_AxisHints  axis    = &hints->axis[dimension];
    391    AF_Edge       edges   = axis->edges;
    392    AF_Segment    segment = axis->segments + segment_idx;
    393 
    394 
    395    return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges );
    396  }
    397 
    398 
    399  static int
    400  af_get_strong_edge_index( AF_GlyphHints  hints,
    401                            AF_Edge*       strong_edges,
    402                            int            dimension )
    403  {
    404    AF_AxisHints  axis  = &hints->axis[dimension];
    405    AF_Edge       edges = axis->edges;
    406 
    407 
    408    return AF_INDEX_NUM( strong_edges[dimension], edges );
    409  }
    410 
    411 
    412 #ifdef __cplusplus
    413  extern "C" {
    414 #endif
    415  void
    416  af_glyph_hints_dump_points( AF_GlyphHints  hints,
    417                              FT_Bool        to_stdout )
    418  {
    419    AF_Point   points  = hints->points;
    420    AF_Point   limit   = points + hints->num_points;
    421    AF_Point*  contour = hints->contours;
    422    AF_Point*  climit  = contour + hints->num_contours;
    423    AF_Point   point;
    424 
    425 
    426    AF_DUMP(( "Table of points:\n" ));
    427 
    428    if ( hints->num_points )
    429    {
    430      AF_DUMP(( "  index  hedge  hseg  vedge  vseg  flags "
    431             /* "  XXXXX  XXXXX XXXXX  XXXXX XXXXX  XXXXXX" */
    432                "  xorg  yorg  xscale  yscale   xfit    yfit "
    433             /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
    434                "  hbef  haft  vbef  vaft" ));
    435             /* " XXXXX XXXXX XXXXX XXXXX" */
    436    }
    437    else
    438      AF_DUMP(( "  (none)\n" ));
    439 
    440    for ( point = points; point < limit; point++ )
    441    {
    442      int  point_idx     = AF_INDEX_NUM( point, points );
    443      int  segment_idx_0 = af_get_segment_index( hints, point_idx, 0 );
    444      int  segment_idx_1 = af_get_segment_index( hints, point_idx, 1 );
    445 
    446      char  buf1[16], buf2[16], buf3[16], buf4[16];
    447      char  buf5[16], buf6[16], buf7[16], buf8[16];
    448 
    449 
    450      /* insert extra newline at the beginning of a contour */
    451      if ( contour < climit && *contour == point )
    452      {
    453        AF_DUMP(( "\n" ));
    454        contour++;
    455      }
    456 
    457      AF_DUMP(( "  %5d  %5s %5s  %5s %5s  %s"
    458                " %5d %5d %7.2f %7.2f %7.2f %7.2f"
    459                " %5s %5s %5s %5s\n",
    460                point_idx,
    461                af_print_idx( buf1, 16,
    462                              af_get_edge_index( hints, segment_idx_1, 1 ) ),
    463                af_print_idx( buf2, 16, segment_idx_1 ),
    464                af_print_idx( buf3, 16,
    465                              af_get_edge_index( hints, segment_idx_0, 0 ) ),
    466                af_print_idx( buf4, 16, segment_idx_0 ),
    467                ( point->flags & AF_FLAG_NEAR )
    468                  ? " near "
    469                  : ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
    470                    ? " weak "
    471                    : "strong",
    472 
    473                point->fx,
    474                point->fy,
    475                (double)point->ox / 64,
    476                (double)point->oy / 64,
    477                (double)point->x / 64,
    478                (double)point->y / 64,
    479 
    480                af_print_idx( buf5, 16,
    481                              af_get_strong_edge_index( hints,
    482                                                        point->before,
    483                                                        1 ) ),
    484                af_print_idx( buf6, 16,
    485                              af_get_strong_edge_index( hints,
    486                                                        point->after,
    487                                                        1 ) ),
    488                af_print_idx( buf7, 16,
    489                              af_get_strong_edge_index( hints,
    490                                                        point->before,
    491                                                        0 ) ),
    492                af_print_idx( buf8, 16,
    493                              af_get_strong_edge_index( hints,
    494                                                        point->after,
    495                                                        0 ) ) ));
    496    }
    497    AF_DUMP(( "\n" ));
    498  }
    499 #ifdef __cplusplus
    500  }
    501 #endif
    502 
    503 
    504  static const char*
    505  af_edge_flags_to_string( FT_UInt  flags )
    506  {
    507    static char  temp[32];
    508    int          pos = 0;
    509 
    510 
    511    if ( flags & AF_EDGE_ROUND )
    512    {
    513      ft_memcpy( temp + pos, "round", 5 );
    514      pos += 5;
    515    }
    516    if ( flags & AF_EDGE_SERIF )
    517    {
    518      if ( pos > 0 )
    519        temp[pos++] = ' ';
    520      ft_memcpy( temp + pos, "serif", 5 );
    521      pos += 5;
    522    }
    523    if ( pos == 0 )
    524      return "normal";
    525 
    526    temp[pos] = '\0';
    527 
    528    return temp;
    529  }
    530 
    531 
    532  /* Dump the array of linked segments. */
    533 
    534 #ifdef __cplusplus
    535  extern "C" {
    536 #endif
    537  void
    538  af_glyph_hints_dump_segments( AF_GlyphHints  hints,
    539                                FT_Bool        to_stdout )
    540  {
    541    FT_Int  dimension;
    542 
    543 
    544    for ( dimension = 1; dimension >= 0; dimension-- )
    545    {
    546      AF_AxisHints  axis     = &hints->axis[dimension];
    547      AF_Point      points   = hints->points;
    548      AF_Edge       edges    = axis->edges;
    549      AF_Segment    segments = axis->segments;
    550      AF_Segment    limit    = segments + axis->num_segments;
    551      AF_Segment    seg;
    552 
    553      char  buf1[16], buf2[16], buf3[16];
    554 
    555 
    556      AF_DUMP(( "Table of %s segments:\n",
    557                dimension == AF_DIMENSION_HORZ ? "vertical"
    558                                               : "horizontal" ));
    559      if ( axis->num_segments )
    560      {
    561        AF_DUMP(( "  index   pos   delta   dir   from   to "
    562               /* "  XXXXX  XXXXX  XXXXX  XXXXX  XXXX  XXXX" */
    563                  "  link  serif  edge"
    564               /* "  XXXX  XXXXX  XXXX" */
    565                  "  height  extra     flags\n" ));
    566               /* "  XXXXXX  XXXXX  XXXXXXXXXXX" */
    567      }
    568      else
    569        AF_DUMP(( "  (none)\n" ));
    570 
    571      for ( seg = segments; seg < limit; seg++ )
    572        AF_DUMP(( "  %5d  %5d  %5d  %5s  %4d  %4d"
    573                  "  %4s  %5s  %4s"
    574                  "  %6d  %5d  %11s\n",
    575                  AF_INDEX_NUM( seg, segments ),
    576                  seg->pos,
    577                  seg->delta,
    578                  af_dir_str( (AF_Direction)seg->dir ),
    579                  AF_INDEX_NUM( seg->first, points ),
    580                  AF_INDEX_NUM( seg->last, points ),
    581 
    582                  af_print_idx( buf1, 16,
    583                                AF_INDEX_NUM( seg->link, segments ) ),
    584                  af_print_idx( buf2, 16,
    585                                AF_INDEX_NUM( seg->serif, segments ) ),
    586                  af_print_idx( buf3, 16,
    587                                AF_INDEX_NUM( seg->edge, edges ) ),
    588 
    589                  seg->height,
    590                  seg->height - ( seg->max_coord - seg->min_coord ),
    591                  af_edge_flags_to_string( seg->flags ) ));
    592      AF_DUMP(( "\n" ));
    593    }
    594  }
    595 #ifdef __cplusplus
    596  }
    597 #endif
    598 
    599 
    600  /* Fetch number of segments. */
    601 
    602 #ifdef __cplusplus
    603  extern "C" {
    604 #endif
    605  FT_Error
    606  af_glyph_hints_get_num_segments( AF_GlyphHints  hints,
    607                                   FT_Int         dimension,
    608                                   FT_UInt*       num_segments )
    609  {
    610    AF_Dimension  dim;
    611    AF_AxisHints  axis;
    612 
    613 
    614    dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
    615 
    616    axis          = &hints->axis[dim];
    617    *num_segments = axis->num_segments;
    618 
    619    return FT_Err_Ok;
    620  }
    621 #ifdef __cplusplus
    622  }
    623 #endif
    624 
    625 
    626  /* Fetch offset of segments into user supplied offset array. */
    627 
    628 #ifdef __cplusplus
    629  extern "C" {
    630 #endif
    631  FT_Error
    632  af_glyph_hints_get_segment_offset( AF_GlyphHints  hints,
    633                                     FT_Int         dimension,
    634                                     FT_UInt        idx,
    635                                     FT_Pos        *offset,
    636                                     FT_Bool       *is_blue,
    637                                     FT_Pos        *blue_offset )
    638  {
    639    AF_Dimension  dim;
    640    AF_AxisHints  axis;
    641    AF_Segment    seg;
    642 
    643 
    644    if ( !offset )
    645      return FT_THROW( Invalid_Argument );
    646 
    647    dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
    648 
    649    axis = &hints->axis[dim];
    650 
    651    if ( idx >= axis->num_segments )
    652      return FT_THROW( Invalid_Argument );
    653 
    654    seg      = &axis->segments[idx];
    655    *offset  = ( dim == AF_DIMENSION_HORZ ) ? seg->first->fx
    656                                            : seg->first->fy;
    657    if ( seg->edge )
    658      *is_blue = FT_BOOL( seg->edge->blue_edge );
    659    else
    660      *is_blue = FALSE;
    661 
    662    if ( *is_blue )
    663      *blue_offset = seg->edge->blue_edge->org;
    664    else
    665      *blue_offset = 0;
    666 
    667    return FT_Err_Ok;
    668  }
    669 #ifdef __cplusplus
    670  }
    671 #endif
    672 
    673 
    674  /* Dump the array of linked edges. */
    675 
    676 #ifdef __cplusplus
    677  extern "C" {
    678 #endif
    679  void
    680  af_glyph_hints_dump_edges( AF_GlyphHints  hints,
    681                             FT_Bool        to_stdout )
    682  {
    683    FT_Int  dimension;
    684 
    685 
    686    for ( dimension = 1; dimension >= 0; dimension-- )
    687    {
    688      AF_AxisHints  axis  = &hints->axis[dimension];
    689      AF_Edge       edges = axis->edges;
    690      AF_Edge       limit = edges + axis->num_edges;
    691      AF_Edge       edge;
    692 
    693      char  buf1[16], buf2[16];
    694 
    695 
    696      /*
    697       * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
    698       *       since they have a constant X coordinate.
    699       */
    700      if ( dimension == AF_DIMENSION_HORZ )
    701        AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
    702                  "vertical",
    703                  65536 * 64 / (double)hints->x_scale,
    704                  10 * (double)hints->x_scale / 65536 / 64 ));
    705      else
    706        AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
    707                  "horizontal",
    708                  65536 * 64 / (double)hints->y_scale,
    709                  10 * (double)hints->y_scale / 65536 / 64 ));
    710 
    711      if ( axis->num_edges )
    712      {
    713        AF_DUMP(( "  index    pos     dir   link  serif"
    714               /* "  XXXXX  XXXX.XX  XXXXX  XXXX  XXXXX" */
    715                  "  blue    opos     pos       flags\n" ));
    716               /* "    X   XXXX.XX  XXXX.XX  XXXXXXXXXXX" */
    717      }
    718      else
    719        AF_DUMP(( "  (none)\n" ));
    720 
    721      for ( edge = edges; edge < limit; edge++ )
    722        AF_DUMP(( "  %5d  %7.2f  %5s  %4s  %5s"
    723                  "    %c   %7.2f  %7.2f  %11s\n",
    724                  AF_INDEX_NUM( edge, edges ),
    725                  (double)(int)edge->opos / 64,
    726                  af_dir_str( (AF_Direction)edge->dir ),
    727                  af_print_idx( buf1, 16,
    728                                AF_INDEX_NUM( edge->link, edges ) ),
    729                  af_print_idx( buf2, 16,
    730                                AF_INDEX_NUM( edge->serif, edges ) ),
    731 
    732                  edge->blue_edge ? 'y' : 'n',
    733                  (double)edge->opos / 64,
    734                  (double)edge->pos / 64,
    735                  af_edge_flags_to_string( edge->flags ) ));
    736      AF_DUMP(( "\n" ));
    737    }
    738  }
    739 #ifdef __cplusplus
    740  }
    741 #endif
    742 
    743 #undef AF_DUMP
    744 
    745 #endif /* !FT_DEBUG_AUTOFIT */
    746 
    747 
    748  /* Compute the direction value of a given vector. */
    749 
    750  FT_LOCAL_DEF( AF_Direction )
    751  af_direction_compute( FT_Pos  dx,
    752                        FT_Pos  dy )
    753  {
    754    FT_Pos        ll, ss;  /* long and short arm lengths */
    755    AF_Direction  dir;     /* candidate direction        */
    756 
    757 
    758    if ( dy >= dx )
    759    {
    760      if ( dy >= -dx )
    761      {
    762        dir = AF_DIR_UP;
    763        ll  = dy;
    764        ss  = dx;
    765      }
    766      else
    767      {
    768        dir = AF_DIR_LEFT;
    769        ll  = -dx;
    770        ss  = dy;
    771      }
    772    }
    773    else /* dy < dx */
    774    {
    775      if ( dy >= -dx )
    776      {
    777        dir = AF_DIR_RIGHT;
    778        ll  = dx;
    779        ss  = dy;
    780      }
    781      else
    782      {
    783        dir = AF_DIR_DOWN;
    784        ll  = -dy;
    785        ss  = dx;
    786      }
    787    }
    788 
    789    /* return no direction if arm lengths do not differ enough       */
    790    /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
    791    /* the long arm is never negative                                */
    792    if ( ll <= 14 * FT_ABS( ss ) )
    793      dir = AF_DIR_NONE;
    794 
    795    return dir;
    796  }
    797 
    798 
    799  FT_LOCAL_DEF( void )
    800  af_glyph_hints_init( AF_GlyphHints  hints,
    801                       FT_Memory      memory )
    802  {
    803    /* no need to initialize the embedded items */
    804    FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) );
    805    hints->memory = memory;
    806  }
    807 
    808 
    809  FT_LOCAL_DEF( void )
    810  af_glyph_hints_done( AF_GlyphHints  hints )
    811  {
    812    FT_Memory  memory;
    813    int        dim;
    814 
    815 
    816    if ( !( hints && hints->memory ) )
    817      return;
    818 
    819    memory = hints->memory;
    820 
    821    /*
    822     * note that we don't need to free the segment and edge
    823     * buffers since they are really within the hints->points array
    824     */
    825    for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    826    {
    827      AF_AxisHints  axis = &hints->axis[dim];
    828 
    829 
    830      axis->num_segments = 0;
    831      axis->max_segments = 0;
    832      if ( axis->segments != axis->embedded.segments )
    833        FT_FREE( axis->segments );
    834 
    835      axis->num_edges = 0;
    836      axis->max_edges = 0;
    837      if ( axis->edges != axis->embedded.edges )
    838        FT_FREE( axis->edges );
    839    }
    840 
    841    if ( hints->contours != hints->embedded.contours )
    842      FT_FREE( hints->contours );
    843    if ( hints->contour_y_minima != hints->embedded.contour_y_minima )
    844      FT_FREE( hints->contour_y_minima );
    845    if ( hints->contour_y_maxima != hints->embedded.contour_y_maxima )
    846      FT_FREE( hints->contour_y_maxima );
    847    hints->max_contours = 0;
    848    hints->num_contours = 0;
    849 
    850    if ( hints->points != hints->embedded.points )
    851      FT_FREE( hints->points );
    852    hints->max_points = 0;
    853    hints->num_points = 0;
    854 
    855    hints->memory = NULL;
    856  }
    857 
    858 
    859  /* Reset metrics. */
    860 
    861  FT_LOCAL_DEF( void )
    862  af_glyph_hints_rescale( AF_GlyphHints    hints,
    863                          AF_StyleMetrics  metrics )
    864  {
    865    hints->metrics      = metrics;
    866    hints->scaler_flags = metrics->scaler.flags;
    867  }
    868 
    869 
    870  /* Recompute all AF_Point in AF_GlyphHints from the definitions */
    871  /* in a source outline.                                         */
    872 
    873  FT_LOCAL_DEF( FT_Error )
    874  af_glyph_hints_reload( AF_GlyphHints  hints,
    875                         FT_Outline*    outline )
    876  {
    877    FT_Error   error   = FT_Err_Ok;
    878    AF_Point   points;
    879    FT_Int     old_max, new_max;
    880    FT_Fixed   x_scale = hints->x_scale;
    881    FT_Fixed   y_scale = hints->y_scale;
    882    FT_Pos     x_delta = hints->x_delta;
    883    FT_Pos     y_delta = hints->y_delta;
    884    FT_Memory  memory  = hints->memory;
    885 
    886 
    887    hints->num_points   = 0;
    888    hints->num_contours = 0;
    889 
    890    hints->axis[0].num_segments = 0;
    891    hints->axis[0].num_edges    = 0;
    892    hints->axis[1].num_segments = 0;
    893    hints->axis[1].num_edges    = 0;
    894 
    895    /* first of all, reallocate the contours array if necessary */
    896    new_max = outline->n_contours;
    897    old_max = hints->max_contours;
    898 
    899    if ( new_max <= AF_CONTOURS_EMBEDDED )
    900    {
    901      if ( !hints->contours )
    902      {
    903        hints->contours         = hints->embedded.contours;
    904        hints->contour_y_minima = hints->embedded.contour_y_minima;
    905        hints->contour_y_maxima = hints->embedded.contour_y_maxima;
    906 
    907        hints->max_contours = AF_CONTOURS_EMBEDDED;
    908      }
    909    }
    910    else if ( new_max > old_max )
    911    {
    912      if ( hints->contours == hints->embedded.contours )
    913      {
    914        hints->contours         = NULL;
    915        hints->contour_y_minima = NULL;
    916        hints->contour_y_maxima = NULL;
    917      }
    918 
    919      new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */
    920 
    921      if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
    922        goto Exit;
    923      if ( FT_RENEW_ARRAY( hints->contour_y_minima, old_max, new_max ) )
    924        goto Exit;
    925      if ( FT_RENEW_ARRAY( hints->contour_y_maxima, old_max, new_max ) )
    926        goto Exit;
    927 
    928      hints->max_contours = new_max;
    929    }
    930 
    931    /*
    932     * then reallocate the points arrays if necessary --
    933     * note that we reserve two additional point positions, used to
    934     * hint metrics appropriately
    935     */
    936    new_max = outline->n_points + 2;
    937    old_max = hints->max_points;
    938 
    939    if ( new_max <= AF_POINTS_EMBEDDED )
    940    {
    941      if ( !hints->points )
    942      {
    943        hints->points     = hints->embedded.points;
    944        hints->max_points = AF_POINTS_EMBEDDED;
    945      }
    946    }
    947    else if ( new_max > old_max )
    948    {
    949      if ( hints->points == hints->embedded.points )
    950        hints->points = NULL;
    951 
    952      new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */
    953 
    954      if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
    955        goto Exit;
    956 
    957      hints->max_points = new_max;
    958    }
    959 
    960    hints->num_points   = outline->n_points;
    961    hints->num_contours = outline->n_contours;
    962 
    963    /* We can't rely on the value of `FT_Outline.flags' to know the fill   */
    964    /* direction used for a glyph, given that some fonts are broken (e.g., */
    965    /* the Arphic ones).  We thus recompute it each time we need to.       */
    966    /*                                                                     */
    967    hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
    968    hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
    969 
    970    if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
    971    {
    972      hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
    973      hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
    974    }
    975 
    976    hints->x_scale = x_scale;
    977    hints->y_scale = y_scale;
    978    hints->x_delta = x_delta;
    979    hints->y_delta = y_delta;
    980 
    981    points = hints->points;
    982    if ( hints->num_points == 0 )
    983      goto Exit;
    984 
    985    {
    986      AF_Point  point;
    987      AF_Point  point_limit = points + hints->num_points;
    988 
    989      /* value 20 in `near_limit' is heuristic */
    990      FT_UInt  units_per_em = hints->metrics->scaler.face->units_per_EM;
    991      FT_Int   near_limit   = 20 * units_per_em / 2048;
    992 
    993 
    994      /* compute coordinates & Bezier flags, next and prev */
    995      {
    996        FT_Vector*  vec           = outline->points;
    997        FT_Byte*    tag           = outline->tags;
    998        FT_UShort   endpoint      = outline->contours[0];
    999        AF_Point    end           = points + endpoint;
   1000        AF_Point    prev          = end;
   1001        FT_Int      contour_index = 0;
   1002 
   1003 
   1004        for ( point = points; point < point_limit; point++, vec++, tag++ )
   1005        {
   1006          FT_Pos  out_x, out_y;
   1007 
   1008 
   1009          point->in_dir  = (FT_Char)AF_DIR_NONE;
   1010          point->out_dir = (FT_Char)AF_DIR_NONE;
   1011 
   1012          point->fx = (FT_Short)vec->x;
   1013          point->fy = (FT_Short)vec->y;
   1014          point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
   1015          point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
   1016 
   1017          end->fx = (FT_Short)outline->points[endpoint].x;
   1018          end->fy = (FT_Short)outline->points[endpoint].y;
   1019 
   1020          switch ( FT_CURVE_TAG( *tag ) )
   1021          {
   1022          case FT_CURVE_TAG_CONIC:
   1023            point->flags = AF_FLAG_CONIC;
   1024            break;
   1025          case FT_CURVE_TAG_CUBIC:
   1026            point->flags = AF_FLAG_CUBIC;
   1027            break;
   1028          default:
   1029            point->flags = AF_FLAG_NONE;
   1030          }
   1031 
   1032          out_x = point->fx - prev->fx;
   1033          out_y = point->fy - prev->fy;
   1034 
   1035          if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
   1036            prev->flags |= AF_FLAG_NEAR;
   1037 
   1038          point->prev = prev;
   1039          prev->next  = point;
   1040          prev        = point;
   1041 
   1042          if ( point == end )
   1043          {
   1044            if ( ++contour_index < outline->n_contours )
   1045            {
   1046              endpoint = outline->contours[contour_index];
   1047              end      = points + endpoint;
   1048              prev     = end;
   1049            }
   1050          }
   1051 
   1052 #ifdef FT_DEBUG_AUTOFIT
   1053          point->before[0] = NULL;
   1054          point->before[1] = NULL;
   1055          point->after[0]  = NULL;
   1056          point->after[1]  = NULL;
   1057 #endif
   1058 
   1059        }
   1060      }
   1061 
   1062      /* set up the contours array */
   1063      {
   1064        AF_Point*   contour       = hints->contours;
   1065        AF_Point*   contour_limit = contour + hints->num_contours;
   1066        FT_UShort*  end           = outline->contours;
   1067        FT_Int      idx           = 0;
   1068 
   1069 
   1070        for ( ; contour < contour_limit; contour++, end++ )
   1071        {
   1072          contour[0] = points + idx;
   1073          idx        = *end + 1;
   1074        }
   1075      }
   1076 
   1077      {
   1078        /*
   1079         * Compute directions of `in' and `out' vectors.
   1080         *
   1081         * Note that distances between points that are very near to each
   1082         * other are accumulated.  In other words, the auto-hinter either
   1083         * prepends the small vectors between near points to the first
   1084         * non-near vector, or the sum of small vector lengths exceeds a
   1085         * threshold, thus `grouping' the small vectors.  All intermediate
   1086         * points are tagged as weak; the directions are adjusted also to
   1087         * be equal to the accumulated one.
   1088         */
   1089 
   1090        FT_Int  near_limit2 = 2 * near_limit - 1;
   1091 
   1092        AF_Point*  contour;
   1093        AF_Point*  contour_limit = hints->contours + hints->num_contours;
   1094 
   1095 
   1096        for ( contour = hints->contours; contour < contour_limit; contour++ )
   1097        {
   1098          AF_Point  first = *contour;
   1099          AF_Point  next, prev, curr;
   1100 
   1101          FT_Pos  out_x, out_y;
   1102 
   1103 
   1104          /* since the first point of a contour could be part of a */
   1105          /* series of near points, go backwards to find the first */
   1106          /* non-near point and adjust `first'                     */
   1107 
   1108          point = first;
   1109          prev  = first->prev;
   1110 
   1111          while ( prev != first )
   1112          {
   1113            out_x = point->fx - prev->fx;
   1114            out_y = point->fy - prev->fy;
   1115 
   1116            /*
   1117             * We use Taxicab metrics to measure the vector length.
   1118             *
   1119             * Note that the accumulated distances so far could have the
   1120             * opposite direction of the distance measured here.  For this
   1121             * reason we use `near_limit2' for the comparison to get a
   1122             * non-near point even in the worst case.
   1123             */
   1124            if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
   1125              break;
   1126 
   1127            point = prev;
   1128            prev  = prev->prev;
   1129          }
   1130 
   1131          /* adjust first point */
   1132          first = point;
   1133 
   1134          /* now loop over all points of the contour to get */
   1135          /* `in' and `out' vector directions               */
   1136 
   1137          curr = first;
   1138 
   1139          /*
   1140           * We abuse the `u' and `v' fields to store index deltas to the
   1141           * next and previous non-near point, respectively.
   1142           *
   1143           * To avoid problems with not having non-near points, we point to
   1144           * `first' by default as the next non-near point.
   1145           *
   1146           */
   1147          curr->u  = (FT_Pos)( first - curr );
   1148          first->v = -curr->u;
   1149 
   1150          out_x = 0;
   1151          out_y = 0;
   1152 
   1153          next = first;
   1154          do
   1155          {
   1156            AF_Direction  out_dir;
   1157 
   1158 
   1159            point = next;
   1160            next  = point->next;
   1161 
   1162            out_x += next->fx - point->fx;
   1163            out_y += next->fy - point->fy;
   1164 
   1165            if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
   1166            {
   1167              next->flags |= AF_FLAG_WEAK_INTERPOLATION;
   1168              continue;
   1169            }
   1170 
   1171            curr->u = (FT_Pos)( next - curr );
   1172            next->v = -curr->u;
   1173 
   1174            out_dir = af_direction_compute( out_x, out_y );
   1175 
   1176            /* adjust directions for all points inbetween; */
   1177            /* the loop also updates position of `curr'    */
   1178            curr->out_dir = (FT_Char)out_dir;
   1179            for ( curr = curr->next; curr != next; curr = curr->next )
   1180            {
   1181              curr->in_dir  = (FT_Char)out_dir;
   1182              curr->out_dir = (FT_Char)out_dir;
   1183            }
   1184            next->in_dir = (FT_Char)out_dir;
   1185 
   1186            curr->u  = (FT_Pos)( first - curr );
   1187            first->v = -curr->u;
   1188 
   1189            out_x = 0;
   1190            out_y = 0;
   1191 
   1192          } while ( next != first );
   1193        }
   1194 
   1195        /*
   1196         * The next step is to `simplify' an outline's topology so that we
   1197         * can identify local extrema more reliably: A series of
   1198         * non-horizontal or non-vertical vectors pointing into the same
   1199         * quadrant are handled as a single, long vector.  From a
   1200         * topological point of the view, the intermediate points are of no
   1201         * interest and thus tagged as weak.
   1202         */
   1203 
   1204        for ( point = points; point < point_limit; point++ )
   1205        {
   1206          if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
   1207            continue;
   1208 
   1209          if ( point->in_dir  == AF_DIR_NONE &&
   1210               point->out_dir == AF_DIR_NONE )
   1211          {
   1212            /* check whether both vectors point into the same quadrant */
   1213 
   1214            FT_Pos  in_x, in_y;
   1215            FT_Pos  out_x, out_y;
   1216 
   1217            AF_Point  next_u = point + point->u;
   1218            AF_Point  prev_v = point + point->v;
   1219 
   1220 
   1221            in_x = point->fx - prev_v->fx;
   1222            in_y = point->fy - prev_v->fy;
   1223 
   1224            out_x = next_u->fx - point->fx;
   1225            out_y = next_u->fy - point->fy;
   1226 
   1227            if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
   1228            {
   1229              /* yes, so tag current point as weak */
   1230              /* and update index deltas           */
   1231 
   1232              point->flags |= AF_FLAG_WEAK_INTERPOLATION;
   1233 
   1234              prev_v->u = (FT_Pos)( next_u - prev_v );
   1235              next_u->v = -prev_v->u;
   1236            }
   1237          }
   1238        }
   1239 
   1240        /*
   1241         * Finally, check for remaining weak points.  Everything else not
   1242         * collected in edges so far is then implicitly classified as strong
   1243         * points.
   1244         */
   1245 
   1246        for ( point = points; point < point_limit; point++ )
   1247        {
   1248          if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
   1249            continue;
   1250 
   1251          if ( point->flags & AF_FLAG_CONTROL )
   1252          {
   1253            /* control points are always weak */
   1254          Is_Weak_Point:
   1255            point->flags |= AF_FLAG_WEAK_INTERPOLATION;
   1256          }
   1257          else if ( point->out_dir == point->in_dir )
   1258          {
   1259            if ( point->out_dir != AF_DIR_NONE )
   1260            {
   1261              /* current point lies on a horizontal or          */
   1262              /* vertical segment (but doesn't start or end it) */
   1263              goto Is_Weak_Point;
   1264            }
   1265 
   1266            {
   1267              AF_Point  next_u = point + point->u;
   1268              AF_Point  prev_v = point + point->v;
   1269 
   1270 
   1271              if ( ft_corner_is_flat( point->fx  - prev_v->fx,
   1272                                      point->fy  - prev_v->fy,
   1273                                      next_u->fx - point->fx,
   1274                                      next_u->fy - point->fy ) )
   1275              {
   1276                /* either the `in' or the `out' vector is much more  */
   1277                /* dominant than the other one, so tag current point */
   1278                /* as weak and update index deltas                   */
   1279 
   1280                prev_v->u = (FT_Pos)( next_u - prev_v );
   1281                next_u->v = -prev_v->u;
   1282 
   1283                goto Is_Weak_Point;
   1284              }
   1285            }
   1286          }
   1287          else if ( point->in_dir == -point->out_dir )
   1288          {
   1289            /* current point forms a spike */
   1290            goto Is_Weak_Point;
   1291          }
   1292        }
   1293      }
   1294    }
   1295 
   1296  Exit:
   1297    return error;
   1298  }
   1299 
   1300 
   1301  /* Store the hinted outline in an FT_Outline structure. */
   1302 
   1303  FT_LOCAL_DEF( void )
   1304  af_glyph_hints_save( AF_GlyphHints  hints,
   1305                       FT_Outline*    outline )
   1306  {
   1307    AF_Point    point = hints->points;
   1308    AF_Point    limit = point + hints->num_points;
   1309    FT_Vector*  vec   = outline->points;
   1310    FT_Byte*    tag   = outline->tags;
   1311 
   1312 
   1313    for ( ; point < limit; point++, vec++, tag++ )
   1314    {
   1315      vec->x = point->x;
   1316      vec->y = point->y;
   1317 
   1318      if ( point->flags & AF_FLAG_CONIC )
   1319        tag[0] = FT_CURVE_TAG_CONIC;
   1320      else if ( point->flags & AF_FLAG_CUBIC )
   1321        tag[0] = FT_CURVE_TAG_CUBIC;
   1322      else
   1323        tag[0] = FT_CURVE_TAG_ON;
   1324    }
   1325  }
   1326 
   1327 
   1328  /****************************************************************
   1329   *
   1330   *                     EDGE POINT GRID-FITTING
   1331   *
   1332   ****************************************************************/
   1333 
   1334 
   1335  /* Align all points of an edge to the same coordinate value, */
   1336  /* either horizontally or vertically.                        */
   1337 
   1338  FT_LOCAL_DEF( void )
   1339  af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
   1340                                    AF_Dimension   dim )
   1341  {
   1342    AF_AxisHints  axis          = &hints->axis[dim];
   1343    AF_Segment    segments      = axis->segments;
   1344    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
   1345    AF_Segment    seg;
   1346 
   1347 
   1348    if ( dim == AF_DIMENSION_HORZ )
   1349    {
   1350      for ( seg = segments; seg < segment_limit; seg++ )
   1351      {
   1352        AF_Edge   edge = seg->edge;
   1353        AF_Point  point, first, last;
   1354 
   1355 
   1356        if ( !edge )
   1357          continue;
   1358 
   1359        first = seg->first;
   1360        last  = seg->last;
   1361        point = first;
   1362        for (;;)
   1363        {
   1364          point->x      = edge->pos;
   1365          point->flags |= AF_FLAG_TOUCH_X;
   1366 
   1367          if ( point == last )
   1368            break;
   1369 
   1370          point = point->next;
   1371        }
   1372      }
   1373    }
   1374    else
   1375    {
   1376      for ( seg = segments; seg < segment_limit; seg++ )
   1377      {
   1378        AF_Edge   edge = seg->edge;
   1379        AF_Point  point, first, last;
   1380 
   1381 
   1382        if ( !edge )
   1383          continue;
   1384 
   1385        first = seg->first;
   1386        last  = seg->last;
   1387        point = first;
   1388        for (;;)
   1389        {
   1390          point->y      = edge->pos;
   1391          point->flags |= AF_FLAG_TOUCH_Y;
   1392 
   1393          if ( point == last )
   1394            break;
   1395 
   1396          point = point->next;
   1397        }
   1398      }
   1399    }
   1400  }
   1401 
   1402 
   1403  /****************************************************************
   1404   *
   1405   *                    STRONG POINT INTERPOLATION
   1406   *
   1407   ****************************************************************/
   1408 
   1409 
   1410  /* Hint the strong points -- this is equivalent to the TrueType `IP' */
   1411  /* hinting instruction.                                              */
   1412 
   1413  FT_LOCAL_DEF( void )
   1414  af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
   1415                                      AF_Dimension   dim )
   1416  {
   1417    AF_Point      points      = hints->points;
   1418    AF_Point      point_limit = points + hints->num_points;
   1419    AF_AxisHints  axis        = &hints->axis[dim];
   1420    AF_Edge       edges       = axis->edges;
   1421    AF_Edge       edge_limit  = FT_OFFSET( edges, axis->num_edges );
   1422    FT_UInt       touch_flag;
   1423 
   1424 
   1425    if ( dim == AF_DIMENSION_HORZ )
   1426      touch_flag = AF_FLAG_TOUCH_X;
   1427    else
   1428      touch_flag  = AF_FLAG_TOUCH_Y;
   1429 
   1430    if ( edges < edge_limit )
   1431    {
   1432      AF_Point  point;
   1433      AF_Edge   edge;
   1434 
   1435 
   1436      for ( point = points; point < point_limit; point++ )
   1437      {
   1438        FT_Pos  u, ou, fu;  /* point position */
   1439        FT_Pos  delta;
   1440 
   1441 
   1442        if ( point->flags & touch_flag )
   1443          continue;
   1444 
   1445        /* if this point is candidate to weak interpolation, we       */
   1446        /* interpolate it after all strong points have been processed */
   1447 
   1448        if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) )
   1449          continue;
   1450 
   1451        if ( dim == AF_DIMENSION_VERT )
   1452        {
   1453          u  = point->fy;
   1454          ou = point->oy;
   1455        }
   1456        else
   1457        {
   1458          u  = point->fx;
   1459          ou = point->ox;
   1460        }
   1461 
   1462        fu = u;
   1463 
   1464        /* is the point before the first edge? */
   1465        edge  = edges;
   1466        delta = edge->fpos - u;
   1467        if ( delta >= 0 )
   1468        {
   1469          u = edge->pos - ( edge->opos - ou );
   1470 
   1471 #ifdef FT_DEBUG_AUTOFIT
   1472          point->before[dim] = edge;
   1473          point->after[dim]  = NULL;
   1474 #endif
   1475 
   1476          goto Store_Point;
   1477        }
   1478 
   1479        /* is the point after the last edge? */
   1480        edge  = edge_limit - 1;
   1481        delta = u - edge->fpos;
   1482        if ( delta >= 0 )
   1483        {
   1484          u = edge->pos + ( ou - edge->opos );
   1485 
   1486 #ifdef FT_DEBUG_AUTOFIT
   1487          point->before[dim] = NULL;
   1488          point->after[dim]  = edge;
   1489 #endif
   1490 
   1491          goto Store_Point;
   1492        }
   1493 
   1494        {
   1495          FT_PtrDist  min, max, mid;
   1496          FT_Pos      fpos;
   1497 
   1498 
   1499          /* find enclosing edges */
   1500          min = 0;
   1501          max = edge_limit - edges;
   1502 
   1503 #if 1
   1504          /* for a small number of edges, a linear search is better */
   1505          if ( max <= 8 )
   1506          {
   1507            FT_PtrDist  nn;
   1508 
   1509 
   1510            for ( nn = 0; nn < max; nn++ )
   1511              if ( edges[nn].fpos >= u )
   1512                break;
   1513 
   1514            if ( edges[nn].fpos == u )
   1515            {
   1516              u = edges[nn].pos;
   1517              goto Store_Point;
   1518            }
   1519            min = nn;
   1520          }
   1521          else
   1522 #endif
   1523          while ( min < max )
   1524          {
   1525            mid  = ( max + min ) >> 1;
   1526            edge = edges + mid;
   1527            fpos = edge->fpos;
   1528 
   1529            if ( u < fpos )
   1530              max = mid;
   1531            else if ( u > fpos )
   1532              min = mid + 1;
   1533            else
   1534            {
   1535              /* we are on the edge */
   1536              u = edge->pos;
   1537 
   1538 #ifdef FT_DEBUG_AUTOFIT
   1539              point->before[dim] = NULL;
   1540              point->after[dim]  = NULL;
   1541 #endif
   1542 
   1543              goto Store_Point;
   1544            }
   1545          }
   1546 
   1547          /* point is not on an edge */
   1548          {
   1549            AF_Edge  before = edges + min - 1;
   1550            AF_Edge  after  = edges + min + 0;
   1551 
   1552 
   1553 #ifdef FT_DEBUG_AUTOFIT
   1554            point->before[dim] = before;
   1555            point->after[dim]  = after;
   1556 #endif
   1557 
   1558            /* assert( before && after && before != after ) */
   1559            if ( before->scale == 0 )
   1560              before->scale = FT_DivFix( after->pos - before->pos,
   1561                                         after->fpos - before->fpos );
   1562 
   1563            u = before->pos + FT_MulFix( fu - before->fpos,
   1564                                         before->scale );
   1565          }
   1566        }
   1567 
   1568      Store_Point:
   1569        /* save the point position */
   1570        if ( dim == AF_DIMENSION_HORZ )
   1571          point->x = u;
   1572        else
   1573          point->y = u;
   1574 
   1575        point->flags |= touch_flag;
   1576      }
   1577    }
   1578  }
   1579 
   1580 
   1581  /****************************************************************
   1582   *
   1583   *                    WEAK POINT INTERPOLATION
   1584   *
   1585   ****************************************************************/
   1586 
   1587 
   1588  /* Shift the original coordinates of all points between `p1' and */
   1589  /* `p2' to get hinted coordinates, using the same difference as  */
   1590  /* given by `ref'.                                               */
   1591 
   1592  static void
   1593  af_iup_shift( AF_Point  p1,
   1594                AF_Point  p2,
   1595                AF_Point  ref )
   1596  {
   1597    AF_Point  p;
   1598    FT_Pos    delta = ref->u - ref->v;
   1599 
   1600 
   1601    if ( delta == 0 )
   1602      return;
   1603 
   1604    for ( p = p1; p < ref; p++ )
   1605      p->u = p->v + delta;
   1606 
   1607    for ( p = ref + 1; p <= p2; p++ )
   1608      p->u = p->v + delta;
   1609  }
   1610 
   1611 
   1612  /* Interpolate the original coordinates of all points between `p1' and  */
   1613  /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the       */
   1614  /* reference points.  The `u' and `v' members are the current and       */
   1615  /* original coordinate values, respectively.                            */
   1616  /*                                                                      */
   1617  /* Details can be found in the TrueType bytecode specification.         */
   1618 
   1619  static void
   1620  af_iup_interp( AF_Point  p1,
   1621                 AF_Point  p2,
   1622                 AF_Point  ref1,
   1623                 AF_Point  ref2 )
   1624  {
   1625    AF_Point  p;
   1626    FT_Pos    u, v1, v2, u1, u2, d1, d2;
   1627 
   1628 
   1629    if ( p1 > p2 )
   1630      return;
   1631 
   1632    if ( ref1->v > ref2->v )
   1633    {
   1634      p    = ref1;
   1635      ref1 = ref2;
   1636      ref2 = p;
   1637    }
   1638 
   1639    v1 = ref1->v;
   1640    v2 = ref2->v;
   1641    u1 = ref1->u;
   1642    u2 = ref2->u;
   1643    d1 = u1 - v1;
   1644    d2 = u2 - v2;
   1645 
   1646    if ( u1 == u2 || v1 == v2 )
   1647    {
   1648      for ( p = p1; p <= p2; p++ )
   1649      {
   1650        u = p->v;
   1651 
   1652        if ( u <= v1 )
   1653          u += d1;
   1654        else if ( u >= v2 )
   1655          u += d2;
   1656        else
   1657          u = u1;
   1658 
   1659        p->u = u;
   1660      }
   1661    }
   1662    else
   1663    {
   1664      FT_Fixed  scale = FT_DivFix( u2 - u1, v2 - v1 );
   1665 
   1666 
   1667      for ( p = p1; p <= p2; p++ )
   1668      {
   1669        u = p->v;
   1670 
   1671        if ( u <= v1 )
   1672          u += d1;
   1673        else if ( u >= v2 )
   1674          u += d2;
   1675        else
   1676          u = u1 + FT_MulFix( u - v1, scale );
   1677 
   1678        p->u = u;
   1679      }
   1680    }
   1681  }
   1682 
   1683 
   1684  /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
   1685  /* hinting instruction.                                             */
   1686 
   1687  FT_LOCAL_DEF( void )
   1688  af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
   1689                                    AF_Dimension   dim )
   1690  {
   1691    AF_Point   points        = hints->points;
   1692    AF_Point   point_limit   = points + hints->num_points;
   1693    AF_Point*  contour       = hints->contours;
   1694    AF_Point*  contour_limit = contour + hints->num_contours;
   1695    FT_UInt    touch_flag;
   1696    AF_Point   point;
   1697    AF_Point   end_point;
   1698    AF_Point   first_point;
   1699 
   1700 
   1701    /* PASS 1: Move segment points to edge positions */
   1702 
   1703    if ( dim == AF_DIMENSION_HORZ )
   1704    {
   1705      touch_flag = AF_FLAG_TOUCH_X;
   1706 
   1707      for ( point = points; point < point_limit; point++ )
   1708      {
   1709        point->u = point->x;
   1710        point->v = point->ox;
   1711      }
   1712    }
   1713    else
   1714    {
   1715      touch_flag = AF_FLAG_TOUCH_Y;
   1716 
   1717      for ( point = points; point < point_limit; point++ )
   1718      {
   1719        point->u = point->y;
   1720        point->v = point->oy;
   1721      }
   1722    }
   1723 
   1724    for ( ; contour < contour_limit; contour++ )
   1725    {
   1726      AF_Point  first_touched, last_touched;
   1727 
   1728 
   1729      point       = *contour;
   1730      end_point   = point->prev;
   1731      first_point = point;
   1732 
   1733      /* find first touched point */
   1734      for (;;)
   1735      {
   1736        if ( point > end_point )  /* no touched point in contour */
   1737          goto NextContour;
   1738 
   1739        if ( point->flags & touch_flag )
   1740          break;
   1741 
   1742        point++;
   1743      }
   1744 
   1745      first_touched = point;
   1746 
   1747      for (;;)
   1748      {
   1749        FT_ASSERT( point <= end_point                 &&
   1750                   ( point->flags & touch_flag ) != 0 );
   1751 
   1752        /* skip any touched neighbours */
   1753        while ( point < end_point                    &&
   1754                ( point[1].flags & touch_flag ) != 0 )
   1755          point++;
   1756 
   1757        last_touched = point;
   1758 
   1759        /* find the next touched point, if any */
   1760        point++;
   1761        for (;;)
   1762        {
   1763          if ( point > end_point )
   1764            goto EndContour;
   1765 
   1766          if ( ( point->flags & touch_flag ) != 0 )
   1767            break;
   1768 
   1769          point++;
   1770        }
   1771 
   1772        /* interpolate between last_touched and point */
   1773        af_iup_interp( last_touched + 1, point - 1,
   1774                       last_touched, point );
   1775      }
   1776 
   1777    EndContour:
   1778      /* special case: only one point was touched */
   1779      if ( last_touched == first_touched )
   1780        af_iup_shift( first_point, end_point, first_touched );
   1781 
   1782      else /* interpolate the last part */
   1783      {
   1784        if ( last_touched < end_point )
   1785          af_iup_interp( last_touched + 1, end_point,
   1786                         last_touched, first_touched );
   1787 
   1788        if ( first_touched > points )
   1789          af_iup_interp( first_point, first_touched - 1,
   1790                         last_touched, first_touched );
   1791      }
   1792 
   1793    NextContour:
   1794      ;
   1795    }
   1796 
   1797    /* now save the interpolated values back to x/y */
   1798    if ( dim == AF_DIMENSION_HORZ )
   1799    {
   1800      for ( point = points; point < point_limit; point++ )
   1801        point->x = point->u;
   1802    }
   1803    else
   1804    {
   1805      for ( point = points; point < point_limit; point++ )
   1806        point->y = point->u;
   1807    }
   1808  }
   1809 
   1810 
   1811 /* END */