(root)/
freetype-2.13.2/
src/
autofit/
afhints.c
       1  /****************************************************************************
       2   *
       3   * afhints.c
       4   *
       5   *   Auto-fitter hinting routines (body).
       6   *
       7   * Copyright (C) 2003-2023 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      hints->max_contours = 0;
     844      hints->num_contours = 0;
     845  
     846      if ( hints->points != hints->embedded.points )
     847        FT_FREE( hints->points );
     848      hints->max_points = 0;
     849      hints->num_points = 0;
     850  
     851      hints->memory = NULL;
     852    }
     853  
     854  
     855    /* Reset metrics. */
     856  
     857    FT_LOCAL_DEF( void )
     858    af_glyph_hints_rescale( AF_GlyphHints    hints,
     859                            AF_StyleMetrics  metrics )
     860    {
     861      hints->metrics      = metrics;
     862      hints->scaler_flags = metrics->scaler.flags;
     863    }
     864  
     865  
     866    /* Recompute all AF_Point in AF_GlyphHints from the definitions */
     867    /* in a source outline.                                         */
     868  
     869    FT_LOCAL_DEF( FT_Error )
     870    af_glyph_hints_reload( AF_GlyphHints  hints,
     871                           FT_Outline*    outline )
     872    {
     873      FT_Error   error   = FT_Err_Ok;
     874      AF_Point   points;
     875      FT_Int     old_max, new_max;
     876      FT_Fixed   x_scale = hints->x_scale;
     877      FT_Fixed   y_scale = hints->y_scale;
     878      FT_Pos     x_delta = hints->x_delta;
     879      FT_Pos     y_delta = hints->y_delta;
     880      FT_Memory  memory  = hints->memory;
     881  
     882  
     883      hints->num_points   = 0;
     884      hints->num_contours = 0;
     885  
     886      hints->axis[0].num_segments = 0;
     887      hints->axis[0].num_edges    = 0;
     888      hints->axis[1].num_segments = 0;
     889      hints->axis[1].num_edges    = 0;
     890  
     891      /* first of all, reallocate the contours array if necessary */
     892      new_max = outline->n_contours;
     893      old_max = hints->max_contours;
     894  
     895      if ( new_max <= AF_CONTOURS_EMBEDDED )
     896      {
     897        if ( !hints->contours )
     898        {
     899          hints->contours     = hints->embedded.contours;
     900          hints->max_contours = AF_CONTOURS_EMBEDDED;
     901        }
     902      }
     903      else if ( new_max > old_max )
     904      {
     905        if ( hints->contours == hints->embedded.contours )
     906          hints->contours = NULL;
     907  
     908        new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */
     909  
     910        if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
     911          goto Exit;
     912  
     913        hints->max_contours = new_max;
     914      }
     915  
     916      /*
     917       * then reallocate the points arrays if necessary --
     918       * note that we reserve two additional point positions, used to
     919       * hint metrics appropriately
     920       */
     921      new_max = outline->n_points + 2;
     922      old_max = hints->max_points;
     923  
     924      if ( new_max <= AF_POINTS_EMBEDDED )
     925      {
     926        if ( !hints->points )
     927        {
     928          hints->points     = hints->embedded.points;
     929          hints->max_points = AF_POINTS_EMBEDDED;
     930        }
     931      }
     932      else if ( new_max > old_max )
     933      {
     934        if ( hints->points == hints->embedded.points )
     935          hints->points = NULL;
     936  
     937        new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */
     938  
     939        if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
     940          goto Exit;
     941  
     942        hints->max_points = new_max;
     943      }
     944  
     945      hints->num_points   = outline->n_points;
     946      hints->num_contours = outline->n_contours;
     947  
     948      /* We can't rely on the value of `FT_Outline.flags' to know the fill   */
     949      /* direction used for a glyph, given that some fonts are broken (e.g., */
     950      /* the Arphic ones).  We thus recompute it each time we need to.       */
     951      /*                                                                     */
     952      hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
     953      hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
     954  
     955      if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
     956      {
     957        hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
     958        hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
     959      }
     960  
     961      hints->x_scale = x_scale;
     962      hints->y_scale = y_scale;
     963      hints->x_delta = x_delta;
     964      hints->y_delta = y_delta;
     965  
     966      points = hints->points;
     967      if ( hints->num_points == 0 )
     968        goto Exit;
     969  
     970      {
     971        AF_Point  point;
     972        AF_Point  point_limit = points + hints->num_points;
     973  
     974        /* value 20 in `near_limit' is heuristic */
     975        FT_UInt  units_per_em = hints->metrics->scaler.face->units_per_EM;
     976        FT_Int   near_limit   = 20 * units_per_em / 2048;
     977  
     978  
     979        /* compute coordinates & Bezier flags, next and prev */
     980        {
     981          FT_Vector*  vec           = outline->points;
     982          char*       tag           = outline->tags;
     983          FT_Short    endpoint      = outline->contours[0];
     984          AF_Point    end           = points + endpoint;
     985          AF_Point    prev          = end;
     986          FT_Int      contour_index = 0;
     987  
     988  
     989          for ( point = points; point < point_limit; point++, vec++, tag++ )
     990          {
     991            FT_Pos  out_x, out_y;
     992  
     993  
     994            point->in_dir  = (FT_Char)AF_DIR_NONE;
     995            point->out_dir = (FT_Char)AF_DIR_NONE;
     996  
     997            point->fx = (FT_Short)vec->x;
     998            point->fy = (FT_Short)vec->y;
     999            point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
    1000            point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
    1001  
    1002            end->fx = (FT_Short)outline->points[endpoint].x;
    1003            end->fy = (FT_Short)outline->points[endpoint].y;
    1004  
    1005            switch ( FT_CURVE_TAG( *tag ) )
    1006            {
    1007            case FT_CURVE_TAG_CONIC:
    1008              point->flags = AF_FLAG_CONIC;
    1009              break;
    1010            case FT_CURVE_TAG_CUBIC:
    1011              point->flags = AF_FLAG_CUBIC;
    1012              break;
    1013            default:
    1014              point->flags = AF_FLAG_NONE;
    1015            }
    1016  
    1017            out_x = point->fx - prev->fx;
    1018            out_y = point->fy - prev->fy;
    1019  
    1020            if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
    1021              prev->flags |= AF_FLAG_NEAR;
    1022  
    1023            point->prev = prev;
    1024            prev->next  = point;
    1025            prev        = point;
    1026  
    1027            if ( point == end )
    1028            {
    1029              if ( ++contour_index < outline->n_contours )
    1030              {
    1031                endpoint = outline->contours[contour_index];
    1032                end      = points + endpoint;
    1033                prev     = end;
    1034              }
    1035            }
    1036  
    1037  #ifdef FT_DEBUG_AUTOFIT
    1038            point->before[0] = NULL;
    1039            point->before[1] = NULL;
    1040            point->after[0]  = NULL;
    1041            point->after[1]  = NULL;
    1042  #endif
    1043  
    1044          }
    1045        }
    1046  
    1047        /* set up the contours array */
    1048        {
    1049          AF_Point*  contour       = hints->contours;
    1050          AF_Point*  contour_limit = contour + hints->num_contours;
    1051          short*     end           = outline->contours;
    1052          short      idx           = 0;
    1053  
    1054  
    1055          for ( ; contour < contour_limit; contour++, end++ )
    1056          {
    1057            contour[0] = points + idx;
    1058            idx        = (short)( end[0] + 1 );
    1059          }
    1060        }
    1061  
    1062        {
    1063          /*
    1064           * Compute directions of `in' and `out' vectors.
    1065           *
    1066           * Note that distances between points that are very near to each
    1067           * other are accumulated.  In other words, the auto-hinter either
    1068           * prepends the small vectors between near points to the first
    1069           * non-near vector, or the sum of small vector lengths exceeds a
    1070           * threshold, thus `grouping' the small vectors.  All intermediate
    1071           * points are tagged as weak; the directions are adjusted also to
    1072           * be equal to the accumulated one.
    1073           */
    1074  
    1075          FT_Int  near_limit2 = 2 * near_limit - 1;
    1076  
    1077          AF_Point*  contour;
    1078          AF_Point*  contour_limit = hints->contours + hints->num_contours;
    1079  
    1080  
    1081          for ( contour = hints->contours; contour < contour_limit; contour++ )
    1082          {
    1083            AF_Point  first = *contour;
    1084            AF_Point  next, prev, curr;
    1085  
    1086            FT_Pos  out_x, out_y;
    1087  
    1088  
    1089            /* since the first point of a contour could be part of a */
    1090            /* series of near points, go backwards to find the first */
    1091            /* non-near point and adjust `first'                     */
    1092  
    1093            point = first;
    1094            prev  = first->prev;
    1095  
    1096            while ( prev != first )
    1097            {
    1098              out_x = point->fx - prev->fx;
    1099              out_y = point->fy - prev->fy;
    1100  
    1101              /*
    1102               * We use Taxicab metrics to measure the vector length.
    1103               *
    1104               * Note that the accumulated distances so far could have the
    1105               * opposite direction of the distance measured here.  For this
    1106               * reason we use `near_limit2' for the comparison to get a
    1107               * non-near point even in the worst case.
    1108               */
    1109              if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
    1110                break;
    1111  
    1112              point = prev;
    1113              prev  = prev->prev;
    1114            }
    1115  
    1116            /* adjust first point */
    1117            first = point;
    1118  
    1119            /* now loop over all points of the contour to get */
    1120            /* `in' and `out' vector directions               */
    1121  
    1122            curr = first;
    1123  
    1124            /*
    1125             * We abuse the `u' and `v' fields to store index deltas to the
    1126             * next and previous non-near point, respectively.
    1127             *
    1128             * To avoid problems with not having non-near points, we point to
    1129             * `first' by default as the next non-near point.
    1130             *
    1131             */
    1132            curr->u  = (FT_Pos)( first - curr );
    1133            first->v = -curr->u;
    1134  
    1135            out_x = 0;
    1136            out_y = 0;
    1137  
    1138            next = first;
    1139            do
    1140            {
    1141              AF_Direction  out_dir;
    1142  
    1143  
    1144              point = next;
    1145              next  = point->next;
    1146  
    1147              out_x += next->fx - point->fx;
    1148              out_y += next->fy - point->fy;
    1149  
    1150              if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
    1151              {
    1152                next->flags |= AF_FLAG_WEAK_INTERPOLATION;
    1153                continue;
    1154              }
    1155  
    1156              curr->u = (FT_Pos)( next - curr );
    1157              next->v = -curr->u;
    1158  
    1159              out_dir = af_direction_compute( out_x, out_y );
    1160  
    1161              /* adjust directions for all points inbetween; */
    1162              /* the loop also updates position of `curr'    */
    1163              curr->out_dir = (FT_Char)out_dir;
    1164              for ( curr = curr->next; curr != next; curr = curr->next )
    1165              {
    1166                curr->in_dir  = (FT_Char)out_dir;
    1167                curr->out_dir = (FT_Char)out_dir;
    1168              }
    1169              next->in_dir = (FT_Char)out_dir;
    1170  
    1171              curr->u  = (FT_Pos)( first - curr );
    1172              first->v = -curr->u;
    1173  
    1174              out_x = 0;
    1175              out_y = 0;
    1176  
    1177            } while ( next != first );
    1178          }
    1179  
    1180          /*
    1181           * The next step is to `simplify' an outline's topology so that we
    1182           * can identify local extrema more reliably: A series of
    1183           * non-horizontal or non-vertical vectors pointing into the same
    1184           * quadrant are handled as a single, long vector.  From a
    1185           * topological point of the view, the intermediate points are of no
    1186           * interest and thus tagged as weak.
    1187           */
    1188  
    1189          for ( point = points; point < point_limit; point++ )
    1190          {
    1191            if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
    1192              continue;
    1193  
    1194            if ( point->in_dir  == AF_DIR_NONE &&
    1195                 point->out_dir == AF_DIR_NONE )
    1196            {
    1197              /* check whether both vectors point into the same quadrant */
    1198  
    1199              FT_Pos  in_x, in_y;
    1200              FT_Pos  out_x, out_y;
    1201  
    1202              AF_Point  next_u = point + point->u;
    1203              AF_Point  prev_v = point + point->v;
    1204  
    1205  
    1206              in_x = point->fx - prev_v->fx;
    1207              in_y = point->fy - prev_v->fy;
    1208  
    1209              out_x = next_u->fx - point->fx;
    1210              out_y = next_u->fy - point->fy;
    1211  
    1212              if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
    1213              {
    1214                /* yes, so tag current point as weak */
    1215                /* and update index deltas           */
    1216  
    1217                point->flags |= AF_FLAG_WEAK_INTERPOLATION;
    1218  
    1219                prev_v->u = (FT_Pos)( next_u - prev_v );
    1220                next_u->v = -prev_v->u;
    1221              }
    1222            }
    1223          }
    1224  
    1225          /*
    1226           * Finally, check for remaining weak points.  Everything else not
    1227           * collected in edges so far is then implicitly classified as strong
    1228           * points.
    1229           */
    1230  
    1231          for ( point = points; point < point_limit; point++ )
    1232          {
    1233            if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
    1234              continue;
    1235  
    1236            if ( point->flags & AF_FLAG_CONTROL )
    1237            {
    1238              /* control points are always weak */
    1239            Is_Weak_Point:
    1240              point->flags |= AF_FLAG_WEAK_INTERPOLATION;
    1241            }
    1242            else if ( point->out_dir == point->in_dir )
    1243            {
    1244              if ( point->out_dir != AF_DIR_NONE )
    1245              {
    1246                /* current point lies on a horizontal or          */
    1247                /* vertical segment (but doesn't start or end it) */
    1248                goto Is_Weak_Point;
    1249              }
    1250  
    1251              {
    1252                AF_Point  next_u = point + point->u;
    1253                AF_Point  prev_v = point + point->v;
    1254  
    1255  
    1256                if ( ft_corner_is_flat( point->fx  - prev_v->fx,
    1257                                        point->fy  - prev_v->fy,
    1258                                        next_u->fx - point->fx,
    1259                                        next_u->fy - point->fy ) )
    1260                {
    1261                  /* either the `in' or the `out' vector is much more  */
    1262                  /* dominant than the other one, so tag current point */
    1263                  /* as weak and update index deltas                   */
    1264  
    1265                  prev_v->u = (FT_Pos)( next_u - prev_v );
    1266                  next_u->v = -prev_v->u;
    1267  
    1268                  goto Is_Weak_Point;
    1269                }
    1270              }
    1271            }
    1272            else if ( point->in_dir == -point->out_dir )
    1273            {
    1274              /* current point forms a spike */
    1275              goto Is_Weak_Point;
    1276            }
    1277          }
    1278        }
    1279      }
    1280  
    1281    Exit:
    1282      return error;
    1283    }
    1284  
    1285  
    1286    /* Store the hinted outline in an FT_Outline structure. */
    1287  
    1288    FT_LOCAL_DEF( void )
    1289    af_glyph_hints_save( AF_GlyphHints  hints,
    1290                         FT_Outline*    outline )
    1291    {
    1292      AF_Point    point = hints->points;
    1293      AF_Point    limit = point + hints->num_points;
    1294      FT_Vector*  vec   = outline->points;
    1295      char*       tag   = outline->tags;
    1296  
    1297  
    1298      for ( ; point < limit; point++, vec++, tag++ )
    1299      {
    1300        vec->x = point->x;
    1301        vec->y = point->y;
    1302  
    1303        if ( point->flags & AF_FLAG_CONIC )
    1304          tag[0] = FT_CURVE_TAG_CONIC;
    1305        else if ( point->flags & AF_FLAG_CUBIC )
    1306          tag[0] = FT_CURVE_TAG_CUBIC;
    1307        else
    1308          tag[0] = FT_CURVE_TAG_ON;
    1309      }
    1310    }
    1311  
    1312  
    1313    /****************************************************************
    1314     *
    1315     *                     EDGE POINT GRID-FITTING
    1316     *
    1317     ****************************************************************/
    1318  
    1319  
    1320    /* Align all points of an edge to the same coordinate value, */
    1321    /* either horizontally or vertically.                        */
    1322  
    1323    FT_LOCAL_DEF( void )
    1324    af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
    1325                                      AF_Dimension   dim )
    1326    {
    1327      AF_AxisHints  axis          = & hints->axis[dim];
    1328      AF_Segment    segments      = axis->segments;
    1329      AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
    1330      AF_Segment    seg;
    1331  
    1332  
    1333      if ( dim == AF_DIMENSION_HORZ )
    1334      {
    1335        for ( seg = segments; seg < segment_limit; seg++ )
    1336        {
    1337          AF_Edge   edge = seg->edge;
    1338          AF_Point  point, first, last;
    1339  
    1340  
    1341          if ( !edge )
    1342            continue;
    1343  
    1344          first = seg->first;
    1345          last  = seg->last;
    1346          point = first;
    1347          for (;;)
    1348          {
    1349            point->x      = edge->pos;
    1350            point->flags |= AF_FLAG_TOUCH_X;
    1351  
    1352            if ( point == last )
    1353              break;
    1354  
    1355            point = point->next;
    1356          }
    1357        }
    1358      }
    1359      else
    1360      {
    1361        for ( seg = segments; seg < segment_limit; seg++ )
    1362        {
    1363          AF_Edge   edge = seg->edge;
    1364          AF_Point  point, first, last;
    1365  
    1366  
    1367          if ( !edge )
    1368            continue;
    1369  
    1370          first = seg->first;
    1371          last  = seg->last;
    1372          point = first;
    1373          for (;;)
    1374          {
    1375            point->y      = edge->pos;
    1376            point->flags |= AF_FLAG_TOUCH_Y;
    1377  
    1378            if ( point == last )
    1379              break;
    1380  
    1381            point = point->next;
    1382          }
    1383        }
    1384      }
    1385    }
    1386  
    1387  
    1388    /****************************************************************
    1389     *
    1390     *                    STRONG POINT INTERPOLATION
    1391     *
    1392     ****************************************************************/
    1393  
    1394  
    1395    /* Hint the strong points -- this is equivalent to the TrueType `IP' */
    1396    /* hinting instruction.                                              */
    1397  
    1398    FT_LOCAL_DEF( void )
    1399    af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
    1400                                        AF_Dimension   dim )
    1401    {
    1402      AF_Point      points      = hints->points;
    1403      AF_Point      point_limit = points + hints->num_points;
    1404      AF_AxisHints  axis        = &hints->axis[dim];
    1405      AF_Edge       edges       = axis->edges;
    1406      AF_Edge       edge_limit  = FT_OFFSET( edges, axis->num_edges );
    1407      FT_UInt       touch_flag;
    1408  
    1409  
    1410      if ( dim == AF_DIMENSION_HORZ )
    1411        touch_flag = AF_FLAG_TOUCH_X;
    1412      else
    1413        touch_flag  = AF_FLAG_TOUCH_Y;
    1414  
    1415      if ( edges < edge_limit )
    1416      {
    1417        AF_Point  point;
    1418        AF_Edge   edge;
    1419  
    1420  
    1421        for ( point = points; point < point_limit; point++ )
    1422        {
    1423          FT_Pos  u, ou, fu;  /* point position */
    1424          FT_Pos  delta;
    1425  
    1426  
    1427          if ( point->flags & touch_flag )
    1428            continue;
    1429  
    1430          /* if this point is candidate to weak interpolation, we       */
    1431          /* interpolate it after all strong points have been processed */
    1432  
    1433          if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) )
    1434            continue;
    1435  
    1436          if ( dim == AF_DIMENSION_VERT )
    1437          {
    1438            u  = point->fy;
    1439            ou = point->oy;
    1440          }
    1441          else
    1442          {
    1443            u  = point->fx;
    1444            ou = point->ox;
    1445          }
    1446  
    1447          fu = u;
    1448  
    1449          /* is the point before the first edge? */
    1450          edge  = edges;
    1451          delta = edge->fpos - u;
    1452          if ( delta >= 0 )
    1453          {
    1454            u = edge->pos - ( edge->opos - ou );
    1455  
    1456  #ifdef FT_DEBUG_AUTOFIT
    1457            point->before[dim] = edge;
    1458            point->after[dim]  = NULL;
    1459  #endif
    1460  
    1461            goto Store_Point;
    1462          }
    1463  
    1464          /* is the point after the last edge? */
    1465          edge  = edge_limit - 1;
    1466          delta = u - edge->fpos;
    1467          if ( delta >= 0 )
    1468          {
    1469            u = edge->pos + ( ou - edge->opos );
    1470  
    1471  #ifdef FT_DEBUG_AUTOFIT
    1472            point->before[dim] = NULL;
    1473            point->after[dim]  = edge;
    1474  #endif
    1475  
    1476            goto Store_Point;
    1477          }
    1478  
    1479          {
    1480            FT_PtrDist  min, max, mid;
    1481            FT_Pos      fpos;
    1482  
    1483  
    1484            /* find enclosing edges */
    1485            min = 0;
    1486            max = edge_limit - edges;
    1487  
    1488  #if 1
    1489            /* for a small number of edges, a linear search is better */
    1490            if ( max <= 8 )
    1491            {
    1492              FT_PtrDist  nn;
    1493  
    1494  
    1495              for ( nn = 0; nn < max; nn++ )
    1496                if ( edges[nn].fpos >= u )
    1497                  break;
    1498  
    1499              if ( edges[nn].fpos == u )
    1500              {
    1501                u = edges[nn].pos;
    1502                goto Store_Point;
    1503              }
    1504              min = nn;
    1505            }
    1506            else
    1507  #endif
    1508            while ( min < max )
    1509            {
    1510              mid  = ( max + min ) >> 1;
    1511              edge = edges + mid;
    1512              fpos = edge->fpos;
    1513  
    1514              if ( u < fpos )
    1515                max = mid;
    1516              else if ( u > fpos )
    1517                min = mid + 1;
    1518              else
    1519              {
    1520                /* we are on the edge */
    1521                u = edge->pos;
    1522  
    1523  #ifdef FT_DEBUG_AUTOFIT
    1524                point->before[dim] = NULL;
    1525                point->after[dim]  = NULL;
    1526  #endif
    1527  
    1528                goto Store_Point;
    1529              }
    1530            }
    1531  
    1532            /* point is not on an edge */
    1533            {
    1534              AF_Edge  before = edges + min - 1;
    1535              AF_Edge  after  = edges + min + 0;
    1536  
    1537  
    1538  #ifdef FT_DEBUG_AUTOFIT
    1539              point->before[dim] = before;
    1540              point->after[dim]  = after;
    1541  #endif
    1542  
    1543              /* assert( before && after && before != after ) */
    1544              if ( before->scale == 0 )
    1545                before->scale = FT_DivFix( after->pos - before->pos,
    1546                                           after->fpos - before->fpos );
    1547  
    1548              u = before->pos + FT_MulFix( fu - before->fpos,
    1549                                           before->scale );
    1550            }
    1551          }
    1552  
    1553        Store_Point:
    1554          /* save the point position */
    1555          if ( dim == AF_DIMENSION_HORZ )
    1556            point->x = u;
    1557          else
    1558            point->y = u;
    1559  
    1560          point->flags |= touch_flag;
    1561        }
    1562      }
    1563    }
    1564  
    1565  
    1566    /****************************************************************
    1567     *
    1568     *                    WEAK POINT INTERPOLATION
    1569     *
    1570     ****************************************************************/
    1571  
    1572  
    1573    /* Shift the original coordinates of all points between `p1' and */
    1574    /* `p2' to get hinted coordinates, using the same difference as  */
    1575    /* given by `ref'.                                               */
    1576  
    1577    static void
    1578    af_iup_shift( AF_Point  p1,
    1579                  AF_Point  p2,
    1580                  AF_Point  ref )
    1581    {
    1582      AF_Point  p;
    1583      FT_Pos    delta = ref->u - ref->v;
    1584  
    1585  
    1586      if ( delta == 0 )
    1587        return;
    1588  
    1589      for ( p = p1; p < ref; p++ )
    1590        p->u = p->v + delta;
    1591  
    1592      for ( p = ref + 1; p <= p2; p++ )
    1593        p->u = p->v + delta;
    1594    }
    1595  
    1596  
    1597    /* Interpolate the original coordinates of all points between `p1' and  */
    1598    /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the       */
    1599    /* reference points.  The `u' and `v' members are the current and       */
    1600    /* original coordinate values, respectively.                            */
    1601    /*                                                                      */
    1602    /* Details can be found in the TrueType bytecode specification.         */
    1603  
    1604    static void
    1605    af_iup_interp( AF_Point  p1,
    1606                   AF_Point  p2,
    1607                   AF_Point  ref1,
    1608                   AF_Point  ref2 )
    1609    {
    1610      AF_Point  p;
    1611      FT_Pos    u, v1, v2, u1, u2, d1, d2;
    1612  
    1613  
    1614      if ( p1 > p2 )
    1615        return;
    1616  
    1617      if ( ref1->v > ref2->v )
    1618      {
    1619        p    = ref1;
    1620        ref1 = ref2;
    1621        ref2 = p;
    1622      }
    1623  
    1624      v1 = ref1->v;
    1625      v2 = ref2->v;
    1626      u1 = ref1->u;
    1627      u2 = ref2->u;
    1628      d1 = u1 - v1;
    1629      d2 = u2 - v2;
    1630  
    1631      if ( u1 == u2 || v1 == v2 )
    1632      {
    1633        for ( p = p1; p <= p2; p++ )
    1634        {
    1635          u = p->v;
    1636  
    1637          if ( u <= v1 )
    1638            u += d1;
    1639          else if ( u >= v2 )
    1640            u += d2;
    1641          else
    1642            u = u1;
    1643  
    1644          p->u = u;
    1645        }
    1646      }
    1647      else
    1648      {
    1649        FT_Fixed  scale = FT_DivFix( u2 - u1, v2 - v1 );
    1650  
    1651  
    1652        for ( p = p1; p <= p2; p++ )
    1653        {
    1654          u = p->v;
    1655  
    1656          if ( u <= v1 )
    1657            u += d1;
    1658          else if ( u >= v2 )
    1659            u += d2;
    1660          else
    1661            u = u1 + FT_MulFix( u - v1, scale );
    1662  
    1663          p->u = u;
    1664        }
    1665      }
    1666    }
    1667  
    1668  
    1669    /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
    1670    /* hinting instruction.                                             */
    1671  
    1672    FT_LOCAL_DEF( void )
    1673    af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
    1674                                      AF_Dimension   dim )
    1675    {
    1676      AF_Point   points        = hints->points;
    1677      AF_Point   point_limit   = points + hints->num_points;
    1678      AF_Point*  contour       = hints->contours;
    1679      AF_Point*  contour_limit = contour + hints->num_contours;
    1680      FT_UInt    touch_flag;
    1681      AF_Point   point;
    1682      AF_Point   end_point;
    1683      AF_Point   first_point;
    1684  
    1685  
    1686      /* PASS 1: Move segment points to edge positions */
    1687  
    1688      if ( dim == AF_DIMENSION_HORZ )
    1689      {
    1690        touch_flag = AF_FLAG_TOUCH_X;
    1691  
    1692        for ( point = points; point < point_limit; point++ )
    1693        {
    1694          point->u = point->x;
    1695          point->v = point->ox;
    1696        }
    1697      }
    1698      else
    1699      {
    1700        touch_flag = AF_FLAG_TOUCH_Y;
    1701  
    1702        for ( point = points; point < point_limit; point++ )
    1703        {
    1704          point->u = point->y;
    1705          point->v = point->oy;
    1706        }
    1707      }
    1708  
    1709      for ( ; contour < contour_limit; contour++ )
    1710      {
    1711        AF_Point  first_touched, last_touched;
    1712  
    1713  
    1714        point       = *contour;
    1715        end_point   = point->prev;
    1716        first_point = point;
    1717  
    1718        /* find first touched point */
    1719        for (;;)
    1720        {
    1721          if ( point > end_point )  /* no touched point in contour */
    1722            goto NextContour;
    1723  
    1724          if ( point->flags & touch_flag )
    1725            break;
    1726  
    1727          point++;
    1728        }
    1729  
    1730        first_touched = point;
    1731  
    1732        for (;;)
    1733        {
    1734          FT_ASSERT( point <= end_point                 &&
    1735                     ( point->flags & touch_flag ) != 0 );
    1736  
    1737          /* skip any touched neighbours */
    1738          while ( point < end_point                    &&
    1739                  ( point[1].flags & touch_flag ) != 0 )
    1740            point++;
    1741  
    1742          last_touched = point;
    1743  
    1744          /* find the next touched point, if any */
    1745          point++;
    1746          for (;;)
    1747          {
    1748            if ( point > end_point )
    1749              goto EndContour;
    1750  
    1751            if ( ( point->flags & touch_flag ) != 0 )
    1752              break;
    1753  
    1754            point++;
    1755          }
    1756  
    1757          /* interpolate between last_touched and point */
    1758          af_iup_interp( last_touched + 1, point - 1,
    1759                         last_touched, point );
    1760        }
    1761  
    1762      EndContour:
    1763        /* special case: only one point was touched */
    1764        if ( last_touched == first_touched )
    1765          af_iup_shift( first_point, end_point, first_touched );
    1766  
    1767        else /* interpolate the last part */
    1768        {
    1769          if ( last_touched < end_point )
    1770            af_iup_interp( last_touched + 1, end_point,
    1771                           last_touched, first_touched );
    1772  
    1773          if ( first_touched > points )
    1774            af_iup_interp( first_point, first_touched - 1,
    1775                           last_touched, first_touched );
    1776        }
    1777  
    1778      NextContour:
    1779        ;
    1780      }
    1781  
    1782      /* now save the interpolated values back to x/y */
    1783      if ( dim == AF_DIMENSION_HORZ )
    1784      {
    1785        for ( point = points; point < point_limit; point++ )
    1786          point->x = point->u;
    1787      }
    1788      else
    1789      {
    1790        for ( point = points; point < point_limit; point++ )
    1791          point->y = point->u;
    1792      }
    1793    }
    1794  
    1795  
    1796  /* END */