shithub: freetype+ttf2subf

ref: 1e03fff62b1e9c1577858551924f531adeaa76e0
dir: /src/autofit/afhints.c/

View raw version
#include "afhints.h"


  FT_LOCAL_DEF( FT_Error )
  af_axis_hints_new_segment( AF_AxisHints  axis,
                             FT_Memory     memory,
                             AF_Segment   *asegment )
  {
    FT_Error    error   = 0;
    AF_Segment  segment = NULL;

    if ( axis->num_segments >= axis->max_segments )
    {
      FT_Int  old_max = axis->max_segments;
      FT_Int  new_max = old_max;
      FT_Int  big_max = FT_INT_MAX / sizeof(*segment);

      if ( old_max >= big_max )
      {
        error = FT_Err_Out_Of_Memory;
        goto Exit;
      }

      new_max += (new_max >> 1) + 4;
      if ( new_max < old_max || new_max > big_max )
        new_max = big_max;

      if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
        goto Exit;

      axis->max_segments = new_max;
    }

    segment = axis->segments + axis->num_segments++;
    FT_ZERO( segment );

  Exit:
    *asegment = segment;
    return error;
  }

  FT_LOCAL( FT_Error)
  af_axis_hints_new_edge( AF_AxisHints  axis,
                          FT_Int        fpos,
                          FT_Memory     memory,
                          AF_Edge      *aedge )
  {
    FT_Error    error = 0;
    AF_Edge     edge  = NULL;
    AF_Edge     edges;

    if ( axis->num_edges >= axis->max_edges )
    {
      FT_Int  old_max = axis->max_edges;
      FT_Int  new_max = old_max;
      FT_Int  big_max = FT_INT_MAX / sizeof(*edge);

      if ( old_max >= big_max )
      {
        error = FT_Err_Out_Of_Memory;
        goto Exit;
      }

      new_max += (new_max >> 1) + 4;
      if ( new_max < old_max || new_max > big_max )
        new_max = big_max;

      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 && edge[-1].fpos > fpos )
    {
      edge[0] = edge[-1];
      edge--;
    }

    axis->num_edges++;

    FT_ZERO(edge);
    edge->fpos = (FT_Short) fpos;

  Exit:
    *aedge = edge;
    return error;
  }


#ifdef AF_DEBUG

#include <stdio.h>

  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)  ( (ptr) ? ((ptr)-(base)) : -1 )

  void
  af_glyph_hints_dump_points( AF_GlyphHints  hints )
  {
    AF_Point  points = hints->points;
    AF_Point  limit  = points + hints->num_points;
    AF_Point  point;

    printf( "Table of points:\n" );
    printf(   "  [ index |  xorg |  yorg |  xscale |  yscale |  xfit  |  yfit  |  flags ]\n" );
    for ( point = points; point < limit; point++ )
    {
      printf( "  [ %5d | %5d | %5d | %-5.2f | %-5.2f | %-5.2f | %-5.2f | %c%c%c%c%c%c ]\n",
              point - points,
              point->fx,
              point->fy,
              point->ox/64.0,
              point->oy/64.0,
              point->x/64.0,
              point->y/64.0,
              (point->flags & AF_FLAG_WEAK_INTERPOLATION) ? 'w' : ' ',
              (point->flags & AF_FLAG_INFLECTION)         ? 'i' : ' ',
              (point->flags & AF_FLAG_EXTREMA_X)          ? '<' : ' ',
              (point->flags & AF_FLAG_EXTREMA_Y)          ? 'v' : ' ',
              (point->flags & AF_FLAG_ROUND_X)            ? '(' : ' ',
              (point->flags & AF_FLAG_ROUND_Y)            ? 'u' : ' '
            );
    }
    printf( "\n" );
  }


  /* A function used to dump the array of linked segments */
  void
  af_glyph_hints_dump_segments( AF_GlyphHints  hints )
  {
    AF_Point    points = hints->points;
    FT_Int      dimension;

    for ( dimension = 1; dimension >= 0; dimension-- )
    {
      AF_AxisHints  axis = &hints->axis[dimension];
      AF_Segment    segments = axis->segments;
      AF_Segment    limit    = segments + axis->num_segments;
      AF_Segment    seg;


      printf ( "Table of %s segments:\n",
               dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
      printf ( "  [ index |  pos |  dir  | link | serif |"
               " numl | first | start ]\n" );

      for ( seg = segments; seg < limit; seg++ )
      {
        printf ( "  [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
                 seg - segments,
                 (int)seg->pos,
                 af_dir_str( seg->dir ),
                 AF_INDEX_NUM( seg->link, segments ),
                 AF_INDEX_NUM( seg->serif, segments ),
                 (int)seg->num_linked,
                 seg->first - points,
                 seg->last - points );
      }
      printf( "\n" );
    }
  }


  void
  af_glyph_hints_dump_edges( AF_GlyphHints  hints )
  {
    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;

     /* note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
      * since they have constant X coordinate
      */
      printf ( "Table of %s edges:\n",
               dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
      printf ( "  [ index |  pos |  dir  | link |"
               " serif | blue | opos  |  pos  ]\n" );

      for ( edge = edges; edge < limit; edge++ )
      {
        printf ( "  [ %5d | %4d | %5s | %4d | %5d |   %c  | %5.2f | %5.2f ]\n",
                 edge - edges,
                 (int)edge->fpos,
                 af_dir_str( edge->dir ),
                 AF_INDEX_NUM( edge->link, edges ),
                 AF_INDEX_NUM( edge->serif, edges ),
                 edge->blue_edge ? 'y' : 'n',
                 edge->opos / 64.0,
                 edge->pos / 64.0 );
      }

      printf( "\n" );
    }
  }



#endif /* AF_DEBUG */


  /* compute the direction value of a given vector */
  FT_LOCAL_DEF( AF_Direction )
  af_direction_compute( FT_Pos  dx,
                        FT_Pos  dy )
  {
    AF_Direction  dir;
    FT_Pos        ax = FT_ABS( dx );
    FT_Pos        ay = FT_ABS( dy );


    dir = AF_DIR_NONE;

    /* atan(1/12) == 4.7 degrees */

    /* test for vertical direction */
    if ( ax * 12 < ay )
    {
      dir = dy > 0 ? AF_DIR_UP : AF_DIR_DOWN;
    }
    /* test for horizontal direction */
    else if ( ay * 12 < ax )
    {
      dir = dx > 0 ? AF_DIR_RIGHT : AF_DIR_LEFT;
    }

    return dir;
  }


  /* compute all inflex points in a given glyph */
  static void
  af_glyph_hints_compute_inflections( AF_GlyphHints  hints )
  {
    AF_Point*  contour       = hints->contours;
    AF_Point*  contour_limit = contour + hints->num_contours;


    /* do each contour separately */
    for ( ; contour < contour_limit; contour++ )
    {
      AF_Point   point = contour[0];
      AF_Point   first = point;
      AF_Point   start = point;
      AF_Point   end   = point;
      AF_Point   before;
      AF_Point   after;
      AF_Angle   angle_in, angle_seg, angle_out;
      AF_Angle   diff_in, diff_out;
      FT_Int     finished = 0;


      /* compute first segment in contour */
      first = point;

      start = end = first;
      do
      {
        end = end->next;
        if ( end == first )
          goto Skip;

      } while ( end->fx == first->fx && end->fy == first->fy );

      angle_seg = af_angle_atan( end->fx - start->fx,
                                 end->fy - start->fy );

      /* extend the segment start whenever possible */
      before = start;
      do
      {
        do
        {
          start  = before;
          before = before->prev;
          if ( before == first )
            goto Skip;

        } while ( before->fx == start->fx && before->fy == start->fy );

        angle_in = af_angle_atan( start->fx - before->fx,
                                  start->fy - before->fy );

      } while ( angle_in == angle_seg );

      first   = start;
      diff_in = af_angle_diff( angle_in, angle_seg );

      /* now, process all segments in the contour */
      do
      {
        /* first, extend current segment's end whenever possible */
        after = end;
        do
        {
          do
          {
            end   = after;
            after = after->next;
            if ( after == first )
              finished = 1;

          } while ( end->fx == after->fx && end->fy == after->fy );

          angle_out = af_angle_atan( after->fx - end->fx,
                                     after->fy - end->fy );

        } while ( angle_out == angle_seg );

        diff_out = af_angle_diff( angle_seg, angle_out );

        if ( ( diff_in ^ diff_out ) < 0 )
        {
          /* diff_in and diff_out have different signs, we have */
          /* inflection points here...                          */
          do
          {
            start->flags |= AF_FLAG_INFLECTION;
            start = start->next;

          } while ( start != end );

          start->flags |= AF_FLAG_INFLECTION;
        }

        start     = end;
        end       = after;
        angle_seg = angle_out;
        diff_in   = diff_out;

      } while ( !finished );

    Skip:
      ;
    }
  }



  FT_LOCAL_DEF( void )
  af_glyph_hints_init( AF_GlyphHints  hints,
                       FT_Memory      memory )
  {
    FT_ZERO( hints );
    hints->memory = memory;
  }



  FT_LOCAL_DEF( void )
  af_glyph_hints_done( AF_GlyphHints  hints )
  {
    if ( hints && hints->memory )
    {
      FT_Memory     memory = hints->memory;
      AF_Dimension  dim;

     /* note that we don't need to free the segment and edge
      * buffers, since they're really within the hints->points array
      */
      for ( dim = 0; dim < 2; dim++ )
      {
        AF_AxisHints  axis = &hints->axis[ dim ];

        axis->num_segments = 0;
        axis->max_segments = 0;
        FT_FREE( axis->segments );

        axis->num_edges    = 0;
        axis->max_edges    = 0;
        FT_FREE( axis->edges );
      }

      FT_FREE( hints->contours );
      hints->max_contours = 0;
      hints->num_contours = 0;

      FT_FREE( hints->points );
      hints->num_points = 0;
      hints->max_points = 0;

      hints->memory = NULL;
    }
  }



  FT_LOCAL_DEF( void )
  af_glyph_hints_rescale( AF_GlyphHints     hints,
                          AF_ScriptMetrics  metrics )
  {
    hints->metrics = metrics;
  }


  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_UInt      old_max, new_max;
    AF_Scaler    scaler  = &hints->metrics->scaler;
    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->scaler_flags  = scaler->flags;
    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 when necessary
    */
    new_max = (FT_UInt) outline->n_contours;
    old_max = hints->max_contours;
    if ( new_max > old_max )
    {
      new_max = (new_max + 3) & ~3;

      if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
        goto Exit;

      hints->max_contours = new_max;
    }

   /* then, reallocate the points arrays if needed --
    * note that we reserved two additional point positions, used to
    * hint metrics appropriately
    */
    new_max = (FT_UInt)( outline->n_points + 2 );
    old_max = hints->max_points;
    if ( new_max > old_max )
    {
      new_max = ( new_max + 2 + 7 ) & ~7;

      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;


      /* compute coordinates & bezier flags */
      {
        FT_Vector*  vec = outline->points;
        char*       tag = outline->tags;


        for ( point = points; point < point_limit; point++, vec++, tag++ )
        {
          point->fx = vec->x;
          point->fy = 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;

          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 = 0;
            ;
          }
        }
      }

      /* compute `next' and `prev' */
      {
        FT_Int    contour_index;
        AF_Point  prev;
        AF_Point  first;
        AF_Point  end;


        contour_index = 0;

        first = points;
        end   = points + outline->contours[0];
        prev  = end;

        for ( point = points; point < point_limit; point++ )
        {
          point->prev = prev;
          if ( point < end )
          {
            point->next = point + 1;
            prev        = point;
          }
          else
          {
            point->next = first;
            contour_index++;
            if ( point + 1 < point_limit )
            {
              end   = points + outline->contours[contour_index];
              first = point + 1;
              prev  = end;
            }
          }
        }
      }

      /* set-up the contours array */
      {
        AF_Point*  contour       = hints->contours;
        AF_Point*  contour_limit = contour + hints->num_contours;
        short*     end           = outline->contours;
        short      idx           = 0;


        for ( ; contour < contour_limit; contour++, end++ )
        {
          contour[0] = points + idx;
          idx        = (short)( end[0] + 1 );
        }
      }

      /* compute directions of in & out vectors */
      {
        for ( point = points; point < point_limit; point++ )
        {
          AF_Point   prev;
          AF_Point   next;
          FT_Pos     in_x, in_y, out_x, out_y;


          prev   = point->prev;
          in_x   = point->fx - prev->fx;
          in_y   = point->fy - prev->fy;

          point->in_dir = af_direction_compute( in_x, in_y );

          next   = point->next;
          out_x  = next->fx - point->fx;
          out_y  = next->fy - point->fy;

          point->out_dir = af_direction_compute( out_x, out_y );

          if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) )
          {
          Is_Weak_Point:
            point->flags |= AF_FLAG_WEAK_INTERPOLATION;
          }
          else if ( point->out_dir == point->in_dir )
          {
            AF_Angle  angle_in, angle_out, delta;


            if ( point->out_dir != AF_DIR_NONE )
              goto Is_Weak_Point;

            angle_in  = af_angle_atan( in_x, in_y );
            angle_out = af_angle_atan( out_x, out_y );
            delta     = af_angle_diff( angle_in, angle_out );

            if ( delta < 2 && delta > -2 )
              goto Is_Weak_Point;
          }
          else if ( point->in_dir == -point->out_dir )
            goto Is_Weak_Point;
        }
      }
    }

   /* compute inflection points
    */
    af_glyph_hints_compute_inflections( hints );

  Exit:
    return error;
  }


  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;
    char*       tag   = outline->tags;

    for ( ; point < limit; point++, vec++, tag++ )
    {
      vec->x = (FT_Pos) point->x;
      vec->y = (FT_Pos) 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;
    }
  }


 /*
  *
  *  E D G E   P O I N T   G R I D - F I T T I N G
  *
  */


  FT_LOCAL_DEF( void )
  af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
                                    AF_Dimension   dim )
  {
    AF_AxisHints  axis       = & hints->axis[ dim ];
    AF_Edge       edges      = axis->edges;
    AF_Edge       edge_limit = edges + axis->num_edges;
    AF_Edge       edge;

    for ( edge = edges; edge < edge_limit; edge++ )
    {
      /* move the points of each segment     */
      /* in each edge to the edge's position */
      AF_Segment  seg = edge->first;


      do
      {
        AF_Point  point = seg->first;


        for (;;)
        {
          if ( dim == AF_DIMENSION_HORZ )
          {
            point->x      = edge->pos;
            point->flags |= AF_FLAG_TOUCH_X;
          }
          else
          {
            point->y      = edge->pos;
            point->flags |= AF_FLAG_TOUCH_Y;
          }

          if ( point == seg->last )
            break;

          point = point->next;
        }

        seg = seg->edge_next;

      } while ( seg != edge->first );
    }
  }


 /*
  *
  *  S T R O N G   P O I N T   I N T E R P O L A T I O N
  *
  */


  /* 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  = edges + axis->num_edges;
    AF_Flags      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 will  */
        /* interpolate it after all strong points have been processed */
        if (  ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) &&
             !( point->flags & AF_FLAG_INFLECTION )         )
          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 );
          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 );
          goto Store_Point;
        }

        {
          FT_UInt  min, max, mid;
          FT_Pos   fpos;


          /* find enclosing edges */
          min = 0;
          max = edge_limit - edges;

          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;
              goto Store_Point;
            }
          }

          {
            AF_Edge  before = edges + min - 1;
            AF_Edge  after  = edges + min + 0;


            /* 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;
      }
    }
  }


 /*
  *
  *  W E A K   P O I N T   I N T E R P O L A T I O N
  *
  */

  static void
  af_iup_shift( AF_Point  p1,
                AF_Point  p2,
                AF_Point  ref )
  {
    AF_Point  p;
    FT_Pos    delta = ref->u - ref->v;


    for ( p = p1; p < ref; p++ )
      p->u = p->v + delta;

    for ( p = ref + 1; p <= p2; p++ )
      p->u = p->v + delta;
  }


  static void
  af_iup_interp( AF_Point  p1,
                 AF_Point  p2,
                 AF_Point  ref1,
                 AF_Point  ref2 )
  {
    AF_Point  p;
    FT_Pos    u;
    FT_Pos    v1 = ref1->v;
    FT_Pos    v2 = ref2->v;
    FT_Pos    d1 = ref1->u - v1;
    FT_Pos    d2 = ref2->u - v2;


    if ( p1 > p2 )
      return;

    if ( v1 == v2 )
    {
      for ( p = p1; p <= p2; p++ )
      {
        u = p->v;

        if ( u <= v1 )
          u += d1;
        else
          u += d2;

        p->u = u;
      }
      return;
    }

    if ( v1 < v2 )
    {
      for ( p = p1; p <= p2; p++ )
      {
        u = p->v;

        if ( u <= v1 )
          u += d1;
        else if ( u >= v2 )
          u += d2;
        else
          u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );

        p->u = u;
      }
    }
    else
    {
      for ( p = p1; p <= p2; p++ )
      {
        u = p->v;

        if ( u <= v2 )
          u += d2;
        else if ( u >= v1 )
          u += d1;
        else
          u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );

        p->u = u;
      }
    }
  }


  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;
    AF_Flags    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;
      }
    }

    point   = points;

    for ( ; contour < contour_limit; contour++ )
    {
      point       = *contour;
      end_point   = point->prev;
      first_point = point;

      while ( point <= end_point && !( point->flags & touch_flag ) )
        point++;

      if ( point <= end_point )
      {
        AF_Point  first_touched = point;
        AF_Point  cur_touched   = point;


        point++;
        while ( point <= end_point )
        {
          if ( point->flags & touch_flag )
          {
            /* we found two successive touched points; we interpolate */
            /* all contour points between them                        */
            af_iup_interp( cur_touched + 1, point - 1,
                           cur_touched, point );
            cur_touched = point;
          }
          point++;
        }

        if ( cur_touched == first_touched )
        {
          /* this is a special case: only one point was touched in the */
          /* contour; we thus simply shift the whole contour           */
          af_iup_shift( first_point, end_point, cur_touched );
        }
        else
        {
          /* now interpolate after the last touched point to the end */
          /* of the contour                                          */
          af_iup_interp( cur_touched + 1, end_point,
                         cur_touched, first_touched );

          /* if the first contour point isn't touched, interpolate */
          /* from the contour start to the first touched point     */
          if ( first_touched > points )
            af_iup_interp( first_point, first_touched - 1,
                           cur_touched, first_touched );
        }
      }
    }

    /* 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;
    }
  }