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