Edit

kc3-lang/freetype/src/autofit/afhints.c

Branch :

  • Show log

    Commit

  • Author : Alexei Podtelezhnikov
    Date : 2024-06-20 22:16:51
    Hash : 044d142b
    Message : Use unsigned tags `FT_Outline`. This change comes along with 2a7bb4596f56 ans is only meant to reduce pointer casting in the code. * include/freetype/ftimage.h (FT_Outline): Do it. * src/*: Update `FT_Outline` users.

  • src/autofit/afhints.c
  • /****************************************************************************
     *
     * afhints.c
     *
     *   Auto-fitter hinting routines (body).
     *
     * Copyright (C) 2003-2024 by
     * David Turner, Robert Wilhelm, and Werner Lemberg.
     *
     * This file is part of the FreeType project, and may only be used,
     * modified, and distributed under the terms of the FreeType project
     * license, LICENSE.TXT.  By continuing to use, modify, or distribute
     * this file you indicate that you have read the license and
     * understand and accept it fully.
     *
     */
    
    
    #include "afhints.h"
    #include "aferrors.h"
    #include <freetype/internal/ftcalc.h>
    #include <freetype/internal/ftdebug.h>
    
    
      /**************************************************************************
       *
       * The macro FT_COMPONENT is used in trace mode.  It is an implicit
       * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
       * messages during execution.
       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT  afhints
    
    
      FT_LOCAL_DEF( void )
      af_sort_pos( FT_UInt  count,
                   FT_Pos*  table )
      {
        FT_UInt  i, j;
        FT_Pos   swap;
    
    
        for ( i = 1; i < count; i++ )
        {
          for ( j = i; j > 0; j-- )
          {
            if ( table[j] >= table[j - 1] )
              break;
    
            swap         = table[j];
            table[j]     = table[j - 1];
            table[j - 1] = swap;
          }
        }
      }
    
    
      FT_LOCAL_DEF( void )
      af_sort_and_quantize_widths( FT_UInt*  count,
                                   AF_Width  table,
                                   FT_Pos    threshold )
      {
        FT_UInt      i, j;
        FT_UInt      cur_idx;
        FT_Pos       cur_val;
        FT_Pos       sum;
        AF_WidthRec  swap;
    
    
        if ( *count == 1 )
          return;
    
        /* sort */
        for ( i = 1; i < *count; i++ )
        {
          for ( j = i; j > 0; j-- )
          {
            if ( table[j].org >= table[j - 1].org )
              break;
    
            swap         = table[j];
            table[j]     = table[j - 1];
            table[j - 1] = swap;
          }
        }
    
        cur_idx = 0;
        cur_val = table[cur_idx].org;
    
        /* compute and use mean values for clusters not larger than  */
        /* `threshold'; this is very primitive and might not yield   */
        /* the best result, but normally, using reference character  */
        /* `o', `*count' is 2, so the code below is fully sufficient */
        for ( i = 1; i < *count; i++ )
        {
          if ( table[i].org - cur_val > threshold ||
               i == *count - 1                    )
          {
            sum = 0;
    
            /* fix loop for end of array */
            if ( table[i].org - cur_val <= threshold &&
                 i == *count - 1                     )
              i++;
    
            for ( j = cur_idx; j < i; j++ )
            {
              sum         += table[j].org;
              table[j].org = 0;
            }
            table[cur_idx].org = sum / (FT_Pos)j;
    
            if ( i < *count - 1 )
            {
              cur_idx = i + 1;
              cur_val = table[cur_idx].org;
            }
          }
        }
    
        cur_idx = 1;
    
        /* compress array to remove zero values */
        for ( i = 1; i < *count; i++ )
        {
          if ( table[i].org )
            table[cur_idx++] = table[i];
        }
    
        *count = cur_idx;
      }
    
      /* Get new segment for given axis. */
    
      FT_LOCAL_DEF( FT_Error )
      af_axis_hints_new_segment( AF_AxisHints  axis,
                                 FT_Memory     memory,
                                 AF_Segment   *asegment )
      {
        FT_Error    error   = FT_Err_Ok;
        AF_Segment  segment = NULL;
    
    
        if ( axis->num_segments < AF_SEGMENTS_EMBEDDED )
        {
          if ( !axis->segments )
          {
            axis->segments     = axis->embedded.segments;
            axis->max_segments = AF_SEGMENTS_EMBEDDED;
          }
        }
        else if ( axis->num_segments >= axis->max_segments )
        {
          FT_UInt  old_max = axis->max_segments;
          FT_UInt  new_max = old_max;
          FT_UInt  big_max = FT_INT_MAX / sizeof ( *segment );
    
    
          if ( old_max >= big_max )
          {
            error = FT_THROW( Out_Of_Memory );
            goto Exit;
          }
    
          new_max += ( new_max >> 2 ) + 4;
          if ( new_max < old_max || new_max > big_max )
            new_max = big_max;
    
          if ( axis->segments == axis->embedded.segments )
          {
            if ( FT_NEW_ARRAY( axis->segments, new_max ) )
              goto Exit;
            ft_memcpy( axis->segments, axis->embedded.segments,
                       sizeof ( axis->embedded.segments ) );
          }
          else
          {
            if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
              goto Exit;
          }
    
          axis->max_segments = new_max;
        }
    
        segment = axis->segments + axis->num_segments++;
    
      Exit:
        *asegment = segment;
        return error;
      }
    
    
      /* Get new edge for given axis, direction, and position, */
      /* without initializing the edge itself.                 */
    
      FT_LOCAL_DEF( FT_Error )
      af_axis_hints_new_edge( AF_AxisHints  axis,
                              FT_Int        fpos,
                              AF_Direction  dir,
                              FT_Bool       top_to_bottom_hinting,
                              FT_Memory     memory,
                              AF_Edge      *anedge )
      {
        FT_Error  error = FT_Err_Ok;
        AF_Edge   edge  = NULL;
        AF_Edge   edges;
    
    
        if ( axis->num_edges < AF_EDGES_EMBEDDED )
        {
          if ( !axis->edges )
          {
            axis->edges     = axis->embedded.edges;
            axis->max_edges = AF_EDGES_EMBEDDED;
          }
        }
        else if ( axis->num_edges >= axis->max_edges )
        {
          FT_UInt  old_max = axis->max_edges;
          FT_UInt  new_max = old_max;
          FT_UInt  big_max = FT_INT_MAX / sizeof ( *edge );
    
    
          if ( old_max >= big_max )
          {
            error = FT_THROW( Out_Of_Memory );
            goto Exit;
          }
    
          new_max += ( new_max >> 2 ) + 4;
          if ( new_max < old_max || new_max > big_max )
            new_max = big_max;
    
          if ( axis->edges == axis->embedded.edges )
          {
            if ( FT_NEW_ARRAY( axis->edges, new_max ) )
              goto Exit;
            ft_memcpy( axis->edges, axis->embedded.edges,
                       sizeof ( axis->embedded.edges ) );
          }
          else
          {
            if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
              goto Exit;
          }
    
          axis->max_edges = new_max;
        }
    
        edges = axis->edges;
        edge  = edges + axis->num_edges;
    
        while ( edge > edges )
        {
          if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos )
                                     : ( edge[-1].fpos < fpos ) )
            break;
    
          /* we want the edge with same position and minor direction */
          /* to appear before those in the major one in the list     */
          if ( edge[-1].fpos == fpos && dir == axis->major_dir )
            break;
    
          edge[0] = edge[-1];
          edge--;
        }
    
        axis->num_edges++;
    
      Exit:
        *anedge = edge;
        return error;
      }
    
    
    #ifdef FT_DEBUG_AUTOFIT
    
    #include FT_CONFIG_STANDARD_LIBRARY_H
    
      /* The dump functions are used in the `ftgrid' demo program, too. */
    #define AF_DUMP( varformat )          \
              do                          \
              {                           \
                if ( to_stdout )          \
                  printf varformat;       \
                else                      \
                  FT_TRACE7( varformat ); \
              } while ( 0 )
    
    
      static const char*
      af_dir_str( AF_Direction  dir )
      {
        const char*  result;
    
    
        switch ( dir )
        {
        case AF_DIR_UP:
          result = "up";
          break;
        case AF_DIR_DOWN:
          result = "down";
          break;
        case AF_DIR_LEFT:
          result = "left";
          break;
        case AF_DIR_RIGHT:
          result = "right";
          break;
        default:
          result = "none";
        }
    
        return result;
      }
    
    
    #define AF_INDEX_NUM( ptr, base )  (int)( (ptr) ? ( (ptr) - (base) ) : -1 )
    
    
      static char*
      af_print_idx( char*   p,
                    size_t  n,
                    int     idx )
      {
        if ( idx == -1 )
        {
          p[0] = '-';
          p[1] = '-';
          p[2] = '\0';
        }
        else
          ft_snprintf( p, n, "%d", idx );
    
        return p;
      }
    
    
      static int
      af_get_segment_index( AF_GlyphHints  hints,
                            int            point_idx,
                            int            dimension )
      {
        AF_AxisHints  axis     = &hints->axis[dimension];
        AF_Point      point    = hints->points + point_idx;
        AF_Segment    segments = axis->segments;
        AF_Segment    limit    = segments + axis->num_segments;
        AF_Segment    segment;
    
    
        for ( segment = segments; segment < limit; segment++ )
        {
          if ( segment->first <= segment->last )
          {
            if ( point >= segment->first && point <= segment->last )
              break;
          }
          else
          {
            AF_Point  p = segment->first;
    
    
            for (;;)
            {
              if ( point == p )
                goto Exit;
    
              if ( p == segment->last )
                break;
    
              p = p->next;
            }
          }
        }
    
      Exit:
        if ( segment == limit )
          return -1;
    
        return (int)( segment - segments );
      }
    
    
      static int
      af_get_edge_index( AF_GlyphHints  hints,
                         int            segment_idx,
                         int            dimension )
      {
        AF_AxisHints  axis    = &hints->axis[dimension];
        AF_Edge       edges   = axis->edges;
        AF_Segment    segment = axis->segments + segment_idx;
    
    
        return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges );
      }
    
    
      static int
      af_get_strong_edge_index( AF_GlyphHints  hints,
                                AF_Edge*       strong_edges,
                                int            dimension )
      {
        AF_AxisHints  axis  = &hints->axis[dimension];
        AF_Edge       edges = axis->edges;
    
    
        return AF_INDEX_NUM( strong_edges[dimension], edges );
      }
    
    
    #ifdef __cplusplus
      extern "C" {
    #endif
      void
      af_glyph_hints_dump_points( AF_GlyphHints  hints,
                                  FT_Bool        to_stdout )
      {
        AF_Point   points  = hints->points;
        AF_Point   limit   = points + hints->num_points;
        AF_Point*  contour = hints->contours;
        AF_Point*  climit  = contour + hints->num_contours;
        AF_Point   point;
    
    
        AF_DUMP(( "Table of points:\n" ));
    
        if ( hints->num_points )
        {
          AF_DUMP(( "  index  hedge  hseg  vedge  vseg  flags "
                 /* "  XXXXX  XXXXX XXXXX  XXXXX XXXXX  XXXXXX" */
                    "  xorg  yorg  xscale  yscale   xfit    yfit "
                 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
                    "  hbef  haft  vbef  vaft" ));
                 /* " XXXXX XXXXX XXXXX XXXXX" */
        }
        else
          AF_DUMP(( "  (none)\n" ));
    
        for ( point = points; point < limit; point++ )
        {
          int  point_idx     = AF_INDEX_NUM( point, points );
          int  segment_idx_0 = af_get_segment_index( hints, point_idx, 0 );
          int  segment_idx_1 = af_get_segment_index( hints, point_idx, 1 );
    
          char  buf1[16], buf2[16], buf3[16], buf4[16];
          char  buf5[16], buf6[16], buf7[16], buf8[16];
    
    
          /* insert extra newline at the beginning of a contour */
          if ( contour < climit && *contour == point )
          {
            AF_DUMP(( "\n" ));
            contour++;
          }
    
          AF_DUMP(( "  %5d  %5s %5s  %5s %5s  %s"
                    " %5d %5d %7.2f %7.2f %7.2f %7.2f"
                    " %5s %5s %5s %5s\n",
                    point_idx,
                    af_print_idx( buf1, 16,
                                  af_get_edge_index( hints, segment_idx_1, 1 ) ),
                    af_print_idx( buf2, 16, segment_idx_1 ),
                    af_print_idx( buf3, 16,
                                  af_get_edge_index( hints, segment_idx_0, 0 ) ),
                    af_print_idx( buf4, 16, segment_idx_0 ),
                    ( point->flags & AF_FLAG_NEAR )
                      ? " near "
                      : ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
                        ? " weak "
                        : "strong",
    
                    point->fx,
                    point->fy,
                    (double)point->ox / 64,
                    (double)point->oy / 64,
                    (double)point->x / 64,
                    (double)point->y / 64,
    
                    af_print_idx( buf5, 16,
                                  af_get_strong_edge_index( hints,
                                                            point->before,
                                                            1 ) ),
                    af_print_idx( buf6, 16,
                                  af_get_strong_edge_index( hints,
                                                            point->after,
                                                            1 ) ),
                    af_print_idx( buf7, 16,
                                  af_get_strong_edge_index( hints,
                                                            point->before,
                                                            0 ) ),
                    af_print_idx( buf8, 16,
                                  af_get_strong_edge_index( hints,
                                                            point->after,
                                                            0 ) ) ));
        }
        AF_DUMP(( "\n" ));
      }
    #ifdef __cplusplus
      }
    #endif
    
    
      static const char*
      af_edge_flags_to_string( FT_UInt  flags )
      {
        static char  temp[32];
        int          pos = 0;
    
    
        if ( flags & AF_EDGE_ROUND )
        {
          ft_memcpy( temp + pos, "round", 5 );
          pos += 5;
        }
        if ( flags & AF_EDGE_SERIF )
        {
          if ( pos > 0 )
            temp[pos++] = ' ';
          ft_memcpy( temp + pos, "serif", 5 );
          pos += 5;
        }
        if ( pos == 0 )
          return "normal";
    
        temp[pos] = '\0';
    
        return temp;
      }
    
    
      /* Dump the array of linked segments. */
    
    #ifdef __cplusplus
      extern "C" {
    #endif
      void
      af_glyph_hints_dump_segments( AF_GlyphHints  hints,
                                    FT_Bool        to_stdout )
      {
        FT_Int  dimension;
    
    
        for ( dimension = 1; dimension >= 0; dimension-- )
        {
          AF_AxisHints  axis     = &hints->axis[dimension];
          AF_Point      points   = hints->points;
          AF_Edge       edges    = axis->edges;
          AF_Segment    segments = axis->segments;
          AF_Segment    limit    = segments + axis->num_segments;
          AF_Segment    seg;
    
          char  buf1[16], buf2[16], buf3[16];
    
    
          AF_DUMP(( "Table of %s segments:\n",
                    dimension == AF_DIMENSION_HORZ ? "vertical"
                                                   : "horizontal" ));
          if ( axis->num_segments )
          {
            AF_DUMP(( "  index   pos   delta   dir   from   to "
                   /* "  XXXXX  XXXXX  XXXXX  XXXXX  XXXX  XXXX" */
                      "  link  serif  edge"
                   /* "  XXXX  XXXXX  XXXX" */
                      "  height  extra     flags\n" ));
                   /* "  XXXXXX  XXXXX  XXXXXXXXXXX" */
          }
          else
            AF_DUMP(( "  (none)\n" ));
    
          for ( seg = segments; seg < limit; seg++ )
            AF_DUMP(( "  %5d  %5d  %5d  %5s  %4d  %4d"
                      "  %4s  %5s  %4s"
                      "  %6d  %5d  %11s\n",
                      AF_INDEX_NUM( seg, segments ),
                      seg->pos,
                      seg->delta,
                      af_dir_str( (AF_Direction)seg->dir ),
                      AF_INDEX_NUM( seg->first, points ),
                      AF_INDEX_NUM( seg->last, points ),
    
                      af_print_idx( buf1, 16,
                                    AF_INDEX_NUM( seg->link, segments ) ),
                      af_print_idx( buf2, 16,
                                    AF_INDEX_NUM( seg->serif, segments ) ),
                      af_print_idx( buf3, 16,
                                    AF_INDEX_NUM( seg->edge, edges ) ),
    
                      seg->height,
                      seg->height - ( seg->max_coord - seg->min_coord ),
                      af_edge_flags_to_string( seg->flags ) ));
          AF_DUMP(( "\n" ));
        }
      }
    #ifdef __cplusplus
      }
    #endif
    
    
      /* Fetch number of segments. */
    
    #ifdef __cplusplus
      extern "C" {
    #endif
      FT_Error
      af_glyph_hints_get_num_segments( AF_GlyphHints  hints,
                                       FT_Int         dimension,
                                       FT_UInt*       num_segments )
      {
        AF_Dimension  dim;
        AF_AxisHints  axis;
    
    
        dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
    
        axis          = &hints->axis[dim];
        *num_segments = axis->num_segments;
    
        return FT_Err_Ok;
      }
    #ifdef __cplusplus
      }
    #endif
    
    
      /* Fetch offset of segments into user supplied offset array. */
    
    #ifdef __cplusplus
      extern "C" {
    #endif
      FT_Error
      af_glyph_hints_get_segment_offset( AF_GlyphHints  hints,
                                         FT_Int         dimension,
                                         FT_UInt        idx,
                                         FT_Pos        *offset,
                                         FT_Bool       *is_blue,
                                         FT_Pos        *blue_offset )
      {
        AF_Dimension  dim;
        AF_AxisHints  axis;
        AF_Segment    seg;
    
    
        if ( !offset )
          return FT_THROW( Invalid_Argument );
    
        dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
    
        axis = &hints->axis[dim];
    
        if ( idx >= axis->num_segments )
          return FT_THROW( Invalid_Argument );
    
        seg      = &axis->segments[idx];
        *offset  = ( dim == AF_DIMENSION_HORZ ) ? seg->first->fx
                                                : seg->first->fy;
        if ( seg->edge )
          *is_blue = FT_BOOL( seg->edge->blue_edge );
        else
          *is_blue = FALSE;
    
        if ( *is_blue )
          *blue_offset = seg->edge->blue_edge->org;
        else
          *blue_offset = 0;
    
        return FT_Err_Ok;
      }
    #ifdef __cplusplus
      }
    #endif
    
    
      /* Dump the array of linked edges. */
    
    #ifdef __cplusplus
      extern "C" {
    #endif
      void
      af_glyph_hints_dump_edges( AF_GlyphHints  hints,
                                 FT_Bool        to_stdout )
      {
        FT_Int  dimension;
    
    
        for ( dimension = 1; dimension >= 0; dimension-- )
        {
          AF_AxisHints  axis  = &hints->axis[dimension];
          AF_Edge       edges = axis->edges;
          AF_Edge       limit = edges + axis->num_edges;
          AF_Edge       edge;
    
          char  buf1[16], buf2[16];
    
    
          /*
           * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
           *       since they have a constant X coordinate.
           */
          if ( dimension == AF_DIMENSION_HORZ )
            AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
                      "vertical",
                      65536 * 64 / (double)hints->x_scale,
                      10 * (double)hints->x_scale / 65536 / 64 ));
          else
            AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
                      "horizontal",
                      65536 * 64 / (double)hints->y_scale,
                      10 * (double)hints->y_scale / 65536 / 64 ));
    
          if ( axis->num_edges )
          {
            AF_DUMP(( "  index    pos     dir   link  serif"
                   /* "  XXXXX  XXXX.XX  XXXXX  XXXX  XXXXX" */
                      "  blue    opos     pos       flags\n" ));
                   /* "    X   XXXX.XX  XXXX.XX  XXXXXXXXXXX" */
          }
          else
            AF_DUMP(( "  (none)\n" ));
    
          for ( edge = edges; edge < limit; edge++ )
            AF_DUMP(( "  %5d  %7.2f  %5s  %4s  %5s"
                      "    %c   %7.2f  %7.2f  %11s\n",
                      AF_INDEX_NUM( edge, edges ),
                      (double)(int)edge->opos / 64,
                      af_dir_str( (AF_Direction)edge->dir ),
                      af_print_idx( buf1, 16,
                                    AF_INDEX_NUM( edge->link, edges ) ),
                      af_print_idx( buf2, 16,
                                    AF_INDEX_NUM( edge->serif, edges ) ),
    
                      edge->blue_edge ? 'y' : 'n',
                      (double)edge->opos / 64,
                      (double)edge->pos / 64,
                      af_edge_flags_to_string( edge->flags ) ));
          AF_DUMP(( "\n" ));
        }
      }
    #ifdef __cplusplus
      }
    #endif
    
    #undef AF_DUMP
    
    #endif /* !FT_DEBUG_AUTOFIT */
    
    
      /* Compute the direction value of a given vector. */
    
      FT_LOCAL_DEF( AF_Direction )
      af_direction_compute( FT_Pos  dx,
                            FT_Pos  dy )
      {
        FT_Pos        ll, ss;  /* long and short arm lengths */
        AF_Direction  dir;     /* candidate direction        */
    
    
        if ( dy >= dx )
        {
          if ( dy >= -dx )
          {
            dir = AF_DIR_UP;
            ll  = dy;
            ss  = dx;
          }
          else
          {
            dir = AF_DIR_LEFT;
            ll  = -dx;
            ss  = dy;
          }
        }
        else /* dy < dx */
        {
          if ( dy >= -dx )
          {
            dir = AF_DIR_RIGHT;
            ll  = dx;
            ss  = dy;
          }
          else
          {
            dir = AF_DIR_DOWN;
            ll  = -dy;
            ss  = dx;
          }
        }
    
        /* return no direction if arm lengths do not differ enough       */
        /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
        /* the long arm is never negative                                */
        if ( ll <= 14 * FT_ABS( ss ) )
          dir = AF_DIR_NONE;
    
        return dir;
      }
    
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_init( AF_GlyphHints  hints,
                           FT_Memory      memory )
      {
        /* no need to initialize the embedded items */
        FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) );
        hints->memory = memory;
      }
    
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_done( AF_GlyphHints  hints )
      {
        FT_Memory  memory;
        int        dim;
    
    
        if ( !( hints && hints->memory ) )
          return;
    
        memory = hints->memory;
    
        /*
         * note that we don't need to free the segment and edge
         * buffers since they are really within the hints->points array
         */
        for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
        {
          AF_AxisHints  axis = &hints->axis[dim];
    
    
          axis->num_segments = 0;
          axis->max_segments = 0;
          if ( axis->segments != axis->embedded.segments )
            FT_FREE( axis->segments );
    
          axis->num_edges = 0;
          axis->max_edges = 0;
          if ( axis->edges != axis->embedded.edges )
            FT_FREE( axis->edges );
        }
    
        if ( hints->contours != hints->embedded.contours )
          FT_FREE( hints->contours );
        hints->max_contours = 0;
        hints->num_contours = 0;
    
        if ( hints->points != hints->embedded.points )
          FT_FREE( hints->points );
        hints->max_points = 0;
        hints->num_points = 0;
    
        hints->memory = NULL;
      }
    
    
      /* Reset metrics. */
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_rescale( AF_GlyphHints    hints,
                              AF_StyleMetrics  metrics )
      {
        hints->metrics      = metrics;
        hints->scaler_flags = metrics->scaler.flags;
      }
    
    
      /* Recompute all AF_Point in AF_GlyphHints from the definitions */
      /* in a source outline.                                         */
    
      FT_LOCAL_DEF( FT_Error )
      af_glyph_hints_reload( AF_GlyphHints  hints,
                             FT_Outline*    outline )
      {
        FT_Error   error   = FT_Err_Ok;
        AF_Point   points;
        FT_Int     old_max, new_max;
        FT_Fixed   x_scale = hints->x_scale;
        FT_Fixed   y_scale = hints->y_scale;
        FT_Pos     x_delta = hints->x_delta;
        FT_Pos     y_delta = hints->y_delta;
        FT_Memory  memory  = hints->memory;
    
    
        hints->num_points   = 0;
        hints->num_contours = 0;
    
        hints->axis[0].num_segments = 0;
        hints->axis[0].num_edges    = 0;
        hints->axis[1].num_segments = 0;
        hints->axis[1].num_edges    = 0;
    
        /* first of all, reallocate the contours array if necessary */
        new_max = outline->n_contours;
        old_max = hints->max_contours;
    
        if ( new_max <= AF_CONTOURS_EMBEDDED )
        {
          if ( !hints->contours )
          {
            hints->contours     = hints->embedded.contours;
            hints->max_contours = AF_CONTOURS_EMBEDDED;
          }
        }
        else if ( new_max > old_max )
        {
          if ( hints->contours == hints->embedded.contours )
            hints->contours = NULL;
    
          new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */
    
          if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
            goto Exit;
    
          hints->max_contours = new_max;
        }
    
        /*
         * then reallocate the points arrays if necessary --
         * note that we reserve two additional point positions, used to
         * hint metrics appropriately
         */
        new_max = outline->n_points + 2;
        old_max = hints->max_points;
    
        if ( new_max <= AF_POINTS_EMBEDDED )
        {
          if ( !hints->points )
          {
            hints->points     = hints->embedded.points;
            hints->max_points = AF_POINTS_EMBEDDED;
          }
        }
        else if ( new_max > old_max )
        {
          if ( hints->points == hints->embedded.points )
            hints->points = NULL;
    
          new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */
    
          if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
            goto Exit;
    
          hints->max_points = new_max;
        }
    
        hints->num_points   = outline->n_points;
        hints->num_contours = outline->n_contours;
    
        /* We can't rely on the value of `FT_Outline.flags' to know the fill   */
        /* direction used for a glyph, given that some fonts are broken (e.g., */
        /* the Arphic ones).  We thus recompute it each time we need to.       */
        /*                                                                     */
        hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
        hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
    
        if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
        {
          hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
          hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
        }
    
        hints->x_scale = x_scale;
        hints->y_scale = y_scale;
        hints->x_delta = x_delta;
        hints->y_delta = y_delta;
    
        points = hints->points;
        if ( hints->num_points == 0 )
          goto Exit;
    
        {
          AF_Point  point;
          AF_Point  point_limit = points + hints->num_points;
    
          /* value 20 in `near_limit' is heuristic */
          FT_UInt  units_per_em = hints->metrics->scaler.face->units_per_EM;
          FT_Int   near_limit   = 20 * units_per_em / 2048;
    
    
          /* compute coordinates & Bezier flags, next and prev */
          {
            FT_Vector*  vec           = outline->points;
            FT_Byte*    tag           = outline->tags;
            FT_UShort   endpoint      = outline->contours[0];
            AF_Point    end           = points + endpoint;
            AF_Point    prev          = end;
            FT_Int      contour_index = 0;
    
    
            for ( point = points; point < point_limit; point++, vec++, tag++ )
            {
              FT_Pos  out_x, out_y;
    
    
              point->in_dir  = (FT_Char)AF_DIR_NONE;
              point->out_dir = (FT_Char)AF_DIR_NONE;
    
              point->fx = (FT_Short)vec->x;
              point->fy = (FT_Short)vec->y;
              point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
              point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
    
              end->fx = (FT_Short)outline->points[endpoint].x;
              end->fy = (FT_Short)outline->points[endpoint].y;
    
              switch ( FT_CURVE_TAG( *tag ) )
              {
              case FT_CURVE_TAG_CONIC:
                point->flags = AF_FLAG_CONIC;
                break;
              case FT_CURVE_TAG_CUBIC:
                point->flags = AF_FLAG_CUBIC;
                break;
              default:
                point->flags = AF_FLAG_NONE;
              }
    
              out_x = point->fx - prev->fx;
              out_y = point->fy - prev->fy;
    
              if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
                prev->flags |= AF_FLAG_NEAR;
    
              point->prev = prev;
              prev->next  = point;
              prev        = point;
    
              if ( point == end )
              {
                if ( ++contour_index < outline->n_contours )
                {
                  endpoint = outline->contours[contour_index];
                  end      = points + endpoint;
                  prev     = end;
                }
              }
    
    #ifdef FT_DEBUG_AUTOFIT
              point->before[0] = NULL;
              point->before[1] = NULL;
              point->after[0]  = NULL;
              point->after[1]  = NULL;
    #endif
    
            }
          }
    
          /* set up the contours array */
          {
            AF_Point*   contour       = hints->contours;
            AF_Point*   contour_limit = contour + hints->num_contours;
            FT_UShort*  end           = outline->contours;
            FT_Int      idx           = 0;
    
    
            for ( ; contour < contour_limit; contour++, end++ )
            {
              contour[0] = points + idx;
              idx        = *end + 1;
            }
          }
    
          {
            /*
             * Compute directions of `in' and `out' vectors.
             *
             * Note that distances between points that are very near to each
             * other are accumulated.  In other words, the auto-hinter either
             * prepends the small vectors between near points to the first
             * non-near vector, or the sum of small vector lengths exceeds a
             * threshold, thus `grouping' the small vectors.  All intermediate
             * points are tagged as weak; the directions are adjusted also to
             * be equal to the accumulated one.
             */
    
            FT_Int  near_limit2 = 2 * near_limit - 1;
    
            AF_Point*  contour;
            AF_Point*  contour_limit = hints->contours + hints->num_contours;
    
    
            for ( contour = hints->contours; contour < contour_limit; contour++ )
            {
              AF_Point  first = *contour;
              AF_Point  next, prev, curr;
    
              FT_Pos  out_x, out_y;
    
    
              /* since the first point of a contour could be part of a */
              /* series of near points, go backwards to find the first */
              /* non-near point and adjust `first'                     */
    
              point = first;
              prev  = first->prev;
    
              while ( prev != first )
              {
                out_x = point->fx - prev->fx;
                out_y = point->fy - prev->fy;
    
                /*
                 * We use Taxicab metrics to measure the vector length.
                 *
                 * Note that the accumulated distances so far could have the
                 * opposite direction of the distance measured here.  For this
                 * reason we use `near_limit2' for the comparison to get a
                 * non-near point even in the worst case.
                 */
                if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
                  break;
    
                point = prev;
                prev  = prev->prev;
              }
    
              /* adjust first point */
              first = point;
    
              /* now loop over all points of the contour to get */
              /* `in' and `out' vector directions               */
    
              curr = first;
    
              /*
               * We abuse the `u' and `v' fields to store index deltas to the
               * next and previous non-near point, respectively.
               *
               * To avoid problems with not having non-near points, we point to
               * `first' by default as the next non-near point.
               *
               */
              curr->u  = (FT_Pos)( first - curr );
              first->v = -curr->u;
    
              out_x = 0;
              out_y = 0;
    
              next = first;
              do
              {
                AF_Direction  out_dir;
    
    
                point = next;
                next  = point->next;
    
                out_x += next->fx - point->fx;
                out_y += next->fy - point->fy;
    
                if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
                {
                  next->flags |= AF_FLAG_WEAK_INTERPOLATION;
                  continue;
                }
    
                curr->u = (FT_Pos)( next - curr );
                next->v = -curr->u;
    
                out_dir = af_direction_compute( out_x, out_y );
    
                /* adjust directions for all points inbetween; */
                /* the loop also updates position of `curr'    */
                curr->out_dir = (FT_Char)out_dir;
                for ( curr = curr->next; curr != next; curr = curr->next )
                {
                  curr->in_dir  = (FT_Char)out_dir;
                  curr->out_dir = (FT_Char)out_dir;
                }
                next->in_dir = (FT_Char)out_dir;
    
                curr->u  = (FT_Pos)( first - curr );
                first->v = -curr->u;
    
                out_x = 0;
                out_y = 0;
    
              } while ( next != first );
            }
    
            /*
             * The next step is to `simplify' an outline's topology so that we
             * can identify local extrema more reliably: A series of
             * non-horizontal or non-vertical vectors pointing into the same
             * quadrant are handled as a single, long vector.  From a
             * topological point of the view, the intermediate points are of no
             * interest and thus tagged as weak.
             */
    
            for ( point = points; point < point_limit; point++ )
            {
              if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
                continue;
    
              if ( point->in_dir  == AF_DIR_NONE &&
                   point->out_dir == AF_DIR_NONE )
              {
                /* check whether both vectors point into the same quadrant */
    
                FT_Pos  in_x, in_y;
                FT_Pos  out_x, out_y;
    
                AF_Point  next_u = point + point->u;
                AF_Point  prev_v = point + point->v;
    
    
                in_x = point->fx - prev_v->fx;
                in_y = point->fy - prev_v->fy;
    
                out_x = next_u->fx - point->fx;
                out_y = next_u->fy - point->fy;
    
                if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
                {
                  /* yes, so tag current point as weak */
                  /* and update index deltas           */
    
                  point->flags |= AF_FLAG_WEAK_INTERPOLATION;
    
                  prev_v->u = (FT_Pos)( next_u - prev_v );
                  next_u->v = -prev_v->u;
                }
              }
            }
    
            /*
             * Finally, check for remaining weak points.  Everything else not
             * collected in edges so far is then implicitly classified as strong
             * points.
             */
    
            for ( point = points; point < point_limit; point++ )
            {
              if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
                continue;
    
              if ( point->flags & AF_FLAG_CONTROL )
              {
                /* control points are always weak */
              Is_Weak_Point:
                point->flags |= AF_FLAG_WEAK_INTERPOLATION;
              }
              else if ( point->out_dir == point->in_dir )
              {
                if ( point->out_dir != AF_DIR_NONE )
                {
                  /* current point lies on a horizontal or          */
                  /* vertical segment (but doesn't start or end it) */
                  goto Is_Weak_Point;
                }
    
                {
                  AF_Point  next_u = point + point->u;
                  AF_Point  prev_v = point + point->v;
    
    
                  if ( ft_corner_is_flat( point->fx  - prev_v->fx,
                                          point->fy  - prev_v->fy,
                                          next_u->fx - point->fx,
                                          next_u->fy - point->fy ) )
                  {
                    /* either the `in' or the `out' vector is much more  */
                    /* dominant than the other one, so tag current point */
                    /* as weak and update index deltas                   */
    
                    prev_v->u = (FT_Pos)( next_u - prev_v );
                    next_u->v = -prev_v->u;
    
                    goto Is_Weak_Point;
                  }
                }
              }
              else if ( point->in_dir == -point->out_dir )
              {
                /* current point forms a spike */
                goto Is_Weak_Point;
              }
            }
          }
        }
    
      Exit:
        return error;
      }
    
    
      /* Store the hinted outline in an FT_Outline structure. */
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_save( AF_GlyphHints  hints,
                           FT_Outline*    outline )
      {
        AF_Point    point = hints->points;
        AF_Point    limit = point + hints->num_points;
        FT_Vector*  vec   = outline->points;
        FT_Byte*    tag   = outline->tags;
    
    
        for ( ; point < limit; point++, vec++, tag++ )
        {
          vec->x = point->x;
          vec->y = point->y;
    
          if ( point->flags & AF_FLAG_CONIC )
            tag[0] = FT_CURVE_TAG_CONIC;
          else if ( point->flags & AF_FLAG_CUBIC )
            tag[0] = FT_CURVE_TAG_CUBIC;
          else
            tag[0] = FT_CURVE_TAG_ON;
        }
      }
    
    
      /****************************************************************
       *
       *                     EDGE POINT GRID-FITTING
       *
       ****************************************************************/
    
    
      /* Align all points of an edge to the same coordinate value, */
      /* either horizontally or vertically.                        */
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
                                        AF_Dimension   dim )
      {
        AF_AxisHints  axis          = & hints->axis[dim];
        AF_Segment    segments      = axis->segments;
        AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
        AF_Segment    seg;
    
    
        if ( dim == AF_DIMENSION_HORZ )
        {
          for ( seg = segments; seg < segment_limit; seg++ )
          {
            AF_Edge   edge = seg->edge;
            AF_Point  point, first, last;
    
    
            if ( !edge )
              continue;
    
            first = seg->first;
            last  = seg->last;
            point = first;
            for (;;)
            {
              point->x      = edge->pos;
              point->flags |= AF_FLAG_TOUCH_X;
    
              if ( point == last )
                break;
    
              point = point->next;
            }
          }
        }
        else
        {
          for ( seg = segments; seg < segment_limit; seg++ )
          {
            AF_Edge   edge = seg->edge;
            AF_Point  point, first, last;
    
    
            if ( !edge )
              continue;
    
            first = seg->first;
            last  = seg->last;
            point = first;
            for (;;)
            {
              point->y      = edge->pos;
              point->flags |= AF_FLAG_TOUCH_Y;
    
              if ( point == last )
                break;
    
              point = point->next;
            }
          }
        }
      }
    
    
      /****************************************************************
       *
       *                    STRONG POINT INTERPOLATION
       *
       ****************************************************************/
    
    
      /* Hint the strong points -- this is equivalent to the TrueType `IP' */
      /* hinting instruction.                                              */
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
                                          AF_Dimension   dim )
      {
        AF_Point      points      = hints->points;
        AF_Point      point_limit = points + hints->num_points;
        AF_AxisHints  axis        = &hints->axis[dim];
        AF_Edge       edges       = axis->edges;
        AF_Edge       edge_limit  = FT_OFFSET( edges, axis->num_edges );
        FT_UInt       touch_flag;
    
    
        if ( dim == AF_DIMENSION_HORZ )
          touch_flag = AF_FLAG_TOUCH_X;
        else
          touch_flag  = AF_FLAG_TOUCH_Y;
    
        if ( edges < edge_limit )
        {
          AF_Point  point;
          AF_Edge   edge;
    
    
          for ( point = points; point < point_limit; point++ )
          {
            FT_Pos  u, ou, fu;  /* point position */
            FT_Pos  delta;
    
    
            if ( point->flags & touch_flag )
              continue;
    
            /* if this point is candidate to weak interpolation, we       */
            /* interpolate it after all strong points have been processed */
    
            if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) )
              continue;
    
            if ( dim == AF_DIMENSION_VERT )
            {
              u  = point->fy;
              ou = point->oy;
            }
            else
            {
              u  = point->fx;
              ou = point->ox;
            }
    
            fu = u;
    
            /* is the point before the first edge? */
            edge  = edges;
            delta = edge->fpos - u;
            if ( delta >= 0 )
            {
              u = edge->pos - ( edge->opos - ou );
    
    #ifdef FT_DEBUG_AUTOFIT
              point->before[dim] = edge;
              point->after[dim]  = NULL;
    #endif
    
              goto Store_Point;
            }
    
            /* is the point after the last edge? */
            edge  = edge_limit - 1;
            delta = u - edge->fpos;
            if ( delta >= 0 )
            {
              u = edge->pos + ( ou - edge->opos );
    
    #ifdef FT_DEBUG_AUTOFIT
              point->before[dim] = NULL;
              point->after[dim]  = edge;
    #endif
    
              goto Store_Point;
            }
    
            {
              FT_PtrDist  min, max, mid;
              FT_Pos      fpos;
    
    
              /* find enclosing edges */
              min = 0;
              max = edge_limit - edges;
    
    #if 1
              /* for a small number of edges, a linear search is better */
              if ( max <= 8 )
              {
                FT_PtrDist  nn;
    
    
                for ( nn = 0; nn < max; nn++ )
                  if ( edges[nn].fpos >= u )
                    break;
    
                if ( edges[nn].fpos == u )
                {
                  u = edges[nn].pos;
                  goto Store_Point;
                }
                min = nn;
              }
              else
    #endif
              while ( min < max )
              {
                mid  = ( max + min ) >> 1;
                edge = edges + mid;
                fpos = edge->fpos;
    
                if ( u < fpos )
                  max = mid;
                else if ( u > fpos )
                  min = mid + 1;
                else
                {
                  /* we are on the edge */
                  u = edge->pos;
    
    #ifdef FT_DEBUG_AUTOFIT
                  point->before[dim] = NULL;
                  point->after[dim]  = NULL;
    #endif
    
                  goto Store_Point;
                }
              }
    
              /* point is not on an edge */
              {
                AF_Edge  before = edges + min - 1;
                AF_Edge  after  = edges + min + 0;
    
    
    #ifdef FT_DEBUG_AUTOFIT
                point->before[dim] = before;
                point->after[dim]  = after;
    #endif
    
                /* assert( before && after && before != after ) */
                if ( before->scale == 0 )
                  before->scale = FT_DivFix( after->pos - before->pos,
                                             after->fpos - before->fpos );
    
                u = before->pos + FT_MulFix( fu - before->fpos,
                                             before->scale );
              }
            }
    
          Store_Point:
            /* save the point position */
            if ( dim == AF_DIMENSION_HORZ )
              point->x = u;
            else
              point->y = u;
    
            point->flags |= touch_flag;
          }
        }
      }
    
    
      /****************************************************************
       *
       *                    WEAK POINT INTERPOLATION
       *
       ****************************************************************/
    
    
      /* Shift the original coordinates of all points between `p1' and */
      /* `p2' to get hinted coordinates, using the same difference as  */
      /* given by `ref'.                                               */
    
      static void
      af_iup_shift( AF_Point  p1,
                    AF_Point  p2,
                    AF_Point  ref )
      {
        AF_Point  p;
        FT_Pos    delta = ref->u - ref->v;
    
    
        if ( delta == 0 )
          return;
    
        for ( p = p1; p < ref; p++ )
          p->u = p->v + delta;
    
        for ( p = ref + 1; p <= p2; p++ )
          p->u = p->v + delta;
      }
    
    
      /* Interpolate the original coordinates of all points between `p1' and  */
      /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the       */
      /* reference points.  The `u' and `v' members are the current and       */
      /* original coordinate values, respectively.                            */
      /*                                                                      */
      /* Details can be found in the TrueType bytecode specification.         */
    
      static void
      af_iup_interp( AF_Point  p1,
                     AF_Point  p2,
                     AF_Point  ref1,
                     AF_Point  ref2 )
      {
        AF_Point  p;
        FT_Pos    u, v1, v2, u1, u2, d1, d2;
    
    
        if ( p1 > p2 )
          return;
    
        if ( ref1->v > ref2->v )
        {
          p    = ref1;
          ref1 = ref2;
          ref2 = p;
        }
    
        v1 = ref1->v;
        v2 = ref2->v;
        u1 = ref1->u;
        u2 = ref2->u;
        d1 = u1 - v1;
        d2 = u2 - v2;
    
        if ( u1 == u2 || v1 == v2 )
        {
          for ( p = p1; p <= p2; p++ )
          {
            u = p->v;
    
            if ( u <= v1 )
              u += d1;
            else if ( u >= v2 )
              u += d2;
            else
              u = u1;
    
            p->u = u;
          }
        }
        else
        {
          FT_Fixed  scale = FT_DivFix( u2 - u1, v2 - v1 );
    
    
          for ( p = p1; p <= p2; p++ )
          {
            u = p->v;
    
            if ( u <= v1 )
              u += d1;
            else if ( u >= v2 )
              u += d2;
            else
              u = u1 + FT_MulFix( u - v1, scale );
    
            p->u = u;
          }
        }
      }
    
    
      /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
      /* hinting instruction.                                             */
    
      FT_LOCAL_DEF( void )
      af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
                                        AF_Dimension   dim )
      {
        AF_Point   points        = hints->points;
        AF_Point   point_limit   = points + hints->num_points;
        AF_Point*  contour       = hints->contours;
        AF_Point*  contour_limit = contour + hints->num_contours;
        FT_UInt    touch_flag;
        AF_Point   point;
        AF_Point   end_point;
        AF_Point   first_point;
    
    
        /* PASS 1: Move segment points to edge positions */
    
        if ( dim == AF_DIMENSION_HORZ )
        {
          touch_flag = AF_FLAG_TOUCH_X;
    
          for ( point = points; point < point_limit; point++ )
          {
            point->u = point->x;
            point->v = point->ox;
          }
        }
        else
        {
          touch_flag = AF_FLAG_TOUCH_Y;
    
          for ( point = points; point < point_limit; point++ )
          {
            point->u = point->y;
            point->v = point->oy;
          }
        }
    
        for ( ; contour < contour_limit; contour++ )
        {
          AF_Point  first_touched, last_touched;
    
    
          point       = *contour;
          end_point   = point->prev;
          first_point = point;
    
          /* find first touched point */
          for (;;)
          {
            if ( point > end_point )  /* no touched point in contour */
              goto NextContour;
    
            if ( point->flags & touch_flag )
              break;
    
            point++;
          }
    
          first_touched = point;
    
          for (;;)
          {
            FT_ASSERT( point <= end_point                 &&
                       ( point->flags & touch_flag ) != 0 );
    
            /* skip any touched neighbours */
            while ( point < end_point                    &&
                    ( point[1].flags & touch_flag ) != 0 )
              point++;
    
            last_touched = point;
    
            /* find the next touched point, if any */
            point++;
            for (;;)
            {
              if ( point > end_point )
                goto EndContour;
    
              if ( ( point->flags & touch_flag ) != 0 )
                break;
    
              point++;
            }
    
            /* interpolate between last_touched and point */
            af_iup_interp( last_touched + 1, point - 1,
                           last_touched, point );
          }
    
        EndContour:
          /* special case: only one point was touched */
          if ( last_touched == first_touched )
            af_iup_shift( first_point, end_point, first_touched );
    
          else /* interpolate the last part */
          {
            if ( last_touched < end_point )
              af_iup_interp( last_touched + 1, end_point,
                             last_touched, first_touched );
    
            if ( first_touched > points )
              af_iup_interp( first_point, first_touched - 1,
                             last_touched, first_touched );
          }
    
        NextContour:
          ;
        }
    
        /* now save the interpolated values back to x/y */
        if ( dim == AF_DIMENSION_HORZ )
        {
          for ( point = points; point < point_limit; point++ )
            point->x = point->u;
        }
        else
        {
          for ( point = points; point < point_limit; point++ )
            point->y = point->u;
        }
      }
    
    
    /* END */