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