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