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