1 /***************************************************************************/
2 /*                                                                         */
3 /*  afhints.c                                                              */
4 /*                                                                         */
5 /*    Auto-fitter hinting routines (body).                                 */
6 /*                                                                         */
7 /*  Copyright 2003-2007, 2009-2014 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 >= axis->max_segments )
47     {
48       FT_Int  old_max = axis->max_segments;
49       FT_Int  new_max = old_max;
50       FT_Int  big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) );
51 
52 
53       if ( old_max >= big_max )
54       {
55         error = FT_THROW( Out_Of_Memory );
56         goto Exit;
57       }
58 
59       new_max += ( new_max >> 2 ) + 4;
60       if ( new_max < old_max || new_max > big_max )
61         new_max = big_max;
62 
63       if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
64         goto Exit;
65 
66       axis->max_segments = new_max;
67     }
68 
69     segment = axis->segments + axis->num_segments++;
70 
71   Exit:
72     *asegment = segment;
73     return error;
74   }
75 
76 
77   /* Get new edge for given axis, direction, and position. */
78 
79   FT_LOCAL( FT_Error )
af_axis_hints_new_edge(AF_AxisHints axis,FT_Int fpos,AF_Direction dir,FT_Memory memory,AF_Edge * anedge)80   af_axis_hints_new_edge( AF_AxisHints  axis,
81                           FT_Int        fpos,
82                           AF_Direction  dir,
83                           FT_Memory     memory,
84                           AF_Edge      *anedge )
85   {
86     FT_Error  error = FT_Err_Ok;
87     AF_Edge   edge  = NULL;
88     AF_Edge   edges;
89 
90 
91     if ( axis->num_edges >= axis->max_edges )
92     {
93       FT_Int  old_max = axis->max_edges;
94       FT_Int  new_max = old_max;
95       FT_Int  big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) );
96 
97 
98       if ( old_max >= big_max )
99       {
100         error = FT_THROW( Out_Of_Memory );
101         goto Exit;
102       }
103 
104       new_max += ( new_max >> 2 ) + 4;
105       if ( new_max < old_max || new_max > big_max )
106         new_max = big_max;
107 
108       if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
109         goto Exit;
110 
111       axis->max_edges = new_max;
112     }
113 
114     edges = axis->edges;
115     edge  = edges + axis->num_edges;
116 
117     while ( edge > edges )
118     {
119       if ( edge[-1].fpos < fpos )
120         break;
121 
122       /* we want the edge with same position and minor direction */
123       /* to appear before those in the major one in the list     */
124       if ( edge[-1].fpos == fpos && dir == axis->major_dir )
125         break;
126 
127       edge[0] = edge[-1];
128       edge--;
129     }
130 
131     axis->num_edges++;
132 
133     FT_ZERO( edge );
134     edge->fpos = (FT_Short)fpos;
135     edge->dir  = (FT_Char)dir;
136 
137   Exit:
138     *anedge = edge;
139     return error;
140   }
141 
142 
143 #ifdef FT_DEBUG_AUTOFIT
144 
145 #include FT_CONFIG_STANDARD_LIBRARY_H
146 
147   /* The dump functions are used in the `ftgrid' demo program, too. */
148 #define AF_DUMP( varformat )          \
149           do                          \
150           {                           \
151             if ( to_stdout )          \
152               printf varformat;       \
153             else                      \
154               FT_TRACE7( varformat ); \
155           } while ( 0 )
156 
157 
158   static const char*
af_dir_str(AF_Direction dir)159   af_dir_str( AF_Direction  dir )
160   {
161     const char*  result;
162 
163 
164     switch ( dir )
165     {
166     case AF_DIR_UP:
167       result = "up";
168       break;
169     case AF_DIR_DOWN:
170       result = "down";
171       break;
172     case AF_DIR_LEFT:
173       result = "left";
174       break;
175     case AF_DIR_RIGHT:
176       result = "right";
177       break;
178     default:
179       result = "none";
180     }
181 
182     return result;
183   }
184 
185 
186 #define AF_INDEX_NUM( ptr, base )  ( (ptr) ? ( (ptr) - (base) ) : -1 )
187 
188 
189 #ifdef __cplusplus
190   extern "C" {
191 #endif
192   void
af_glyph_hints_dump_points(AF_GlyphHints hints,FT_Bool to_stdout)193   af_glyph_hints_dump_points( AF_GlyphHints  hints,
194                               FT_Bool        to_stdout )
195   {
196     AF_Point  points = hints->points;
197     AF_Point  limit  = points + hints->num_points;
198     AF_Point  point;
199 
200 
201     AF_DUMP(( "Table of points:\n"
202               "  [ index |  xorg |  yorg | xscale | yscale"
203               " |  xfit |  yfit |  flags ]\n" ));
204 
205     for ( point = points; point < limit; point++ )
206       AF_DUMP(( "  [ %5d | %5d | %5d | %6.2f | %6.2f"
207                 " | %5.2f | %5.2f | %c%c%c%c%c%c ]\n",
208                 point - points,
209                 point->fx,
210                 point->fy,
211                 point->ox / 64.0,
212                 point->oy / 64.0,
213                 point->x / 64.0,
214                 point->y / 64.0,
215                 ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? 'w' : ' ',
216                 ( point->flags & AF_FLAG_INFLECTION )         ? 'i' : ' ',
217                 ( point->flags & AF_FLAG_EXTREMA_X )          ? '<' : ' ',
218                 ( point->flags & AF_FLAG_EXTREMA_Y )          ? 'v' : ' ',
219                 ( point->flags & AF_FLAG_ROUND_X )            ? '(' : ' ',
220                 ( point->flags & AF_FLAG_ROUND_Y )            ? 'u' : ' '));
221     AF_DUMP(( "\n" ));
222   }
223 #ifdef __cplusplus
224   }
225 #endif
226 
227 
228   static const char*
af_edge_flags_to_string(AF_Edge_Flags flags)229   af_edge_flags_to_string( AF_Edge_Flags  flags )
230   {
231     static char  temp[32];
232     int          pos = 0;
233 
234 
235     if ( flags & AF_EDGE_ROUND )
236     {
237       ft_memcpy( temp + pos, "round", 5 );
238       pos += 5;
239     }
240     if ( flags & AF_EDGE_SERIF )
241     {
242       if ( pos > 0 )
243         temp[pos++] = ' ';
244       ft_memcpy( temp + pos, "serif", 5 );
245       pos += 5;
246     }
247     if ( pos == 0 )
248       return "normal";
249 
250     temp[pos] = '\0';
251 
252     return temp;
253   }
254 
255 
256   /* Dump the array of linked segments. */
257 
258 #ifdef __cplusplus
259   extern "C" {
260 #endif
261   void
af_glyph_hints_dump_segments(AF_GlyphHints hints,FT_Bool to_stdout)262   af_glyph_hints_dump_segments( AF_GlyphHints  hints,
263                                 FT_Bool        to_stdout )
264   {
265     FT_Int  dimension;
266 
267 
268     for ( dimension = 1; dimension >= 0; dimension-- )
269     {
270       AF_AxisHints  axis     = &hints->axis[dimension];
271       AF_Point      points   = hints->points;
272       AF_Edge       edges    = axis->edges;
273       AF_Segment    segments = axis->segments;
274       AF_Segment    limit    = segments + axis->num_segments;
275       AF_Segment    seg;
276 
277 
278       AF_DUMP(( "Table of %s segments:\n",
279                 dimension == AF_DIMENSION_HORZ ? "vertical"
280                                                : "horizontal" ));
281       if ( axis->num_segments )
282         AF_DUMP(( "  [ index |  pos  |  dir  | from"
283                   " |  to  | link | serif | edge"
284                   " | height | extra |    flags    ]\n" ));
285       else
286         AF_DUMP(( "  (none)\n" ));
287 
288       for ( seg = segments; seg < limit; seg++ )
289         AF_DUMP(( "  [ %5d | %5.2g | %5s | %4d"
290                   " | %4d | %4d | %5d | %4d"
291                   " | %6d | %5d | %11s ]\n",
292                   seg - segments,
293                   dimension == AF_DIMENSION_HORZ
294                                ? (int)seg->first->ox / 64.0
295                                : (int)seg->first->oy / 64.0,
296                   af_dir_str( (AF_Direction)seg->dir ),
297                   AF_INDEX_NUM( seg->first, points ),
298                   AF_INDEX_NUM( seg->last, points ),
299                   AF_INDEX_NUM( seg->link, segments ),
300                   AF_INDEX_NUM( seg->serif, segments ),
301                   AF_INDEX_NUM( seg->edge, edges ),
302                   seg->height,
303                   seg->height - ( seg->max_coord - seg->min_coord ),
304                   af_edge_flags_to_string( (AF_Edge_Flags)seg->flags ) ));
305       AF_DUMP(( "\n" ));
306     }
307   }
308 #ifdef __cplusplus
309   }
310 #endif
311 
312 
313   /* Fetch number of segments. */
314 
315 #ifdef __cplusplus
316   extern "C" {
317 #endif
318   FT_Error
af_glyph_hints_get_num_segments(AF_GlyphHints hints,FT_Int dimension,FT_Int * num_segments)319   af_glyph_hints_get_num_segments( AF_GlyphHints  hints,
320                                    FT_Int         dimension,
321                                    FT_Int*        num_segments )
322   {
323     AF_Dimension  dim;
324     AF_AxisHints  axis;
325 
326 
327     dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
328 
329     axis          = &hints->axis[dim];
330     *num_segments = axis->num_segments;
331 
332     return FT_Err_Ok;
333   }
334 #ifdef __cplusplus
335   }
336 #endif
337 
338 
339   /* Fetch offset of segments into user supplied offset array. */
340 
341 #ifdef __cplusplus
342   extern "C" {
343 #endif
344   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)345   af_glyph_hints_get_segment_offset( AF_GlyphHints  hints,
346                                      FT_Int         dimension,
347                                      FT_Int         idx,
348                                      FT_Pos        *offset,
349                                      FT_Bool       *is_blue,
350                                      FT_Pos        *blue_offset )
351   {
352     AF_Dimension  dim;
353     AF_AxisHints  axis;
354     AF_Segment    seg;
355 
356 
357     if ( !offset )
358       return FT_THROW( Invalid_Argument );
359 
360     dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
361 
362     axis = &hints->axis[dim];
363 
364     if ( idx < 0 || idx >= axis->num_segments )
365       return FT_THROW( Invalid_Argument );
366 
367     seg      = &axis->segments[idx];
368     *offset  = ( dim == AF_DIMENSION_HORZ ) ? seg->first->ox
369                                             : seg->first->oy;
370     if ( seg->edge )
371       *is_blue = (FT_Bool)( seg->edge->blue_edge != 0 );
372     else
373       *is_blue = FALSE;
374 
375     if ( *is_blue )
376       *blue_offset = seg->edge->blue_edge->cur;
377     else
378       *blue_offset = 0;
379 
380     return FT_Err_Ok;
381   }
382 #ifdef __cplusplus
383   }
384 #endif
385 
386 
387   /* Dump the array of linked edges. */
388 
389 #ifdef __cplusplus
390   extern "C" {
391 #endif
392   void
af_glyph_hints_dump_edges(AF_GlyphHints hints,FT_Bool to_stdout)393   af_glyph_hints_dump_edges( AF_GlyphHints  hints,
394                              FT_Bool        to_stdout )
395   {
396     FT_Int  dimension;
397 
398 
399     for ( dimension = 1; dimension >= 0; dimension-- )
400     {
401       AF_AxisHints  axis  = &hints->axis[dimension];
402       AF_Edge       edges = axis->edges;
403       AF_Edge       limit = edges + axis->num_edges;
404       AF_Edge       edge;
405 
406 
407       /*
408        *  note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
409        *        since they have a constant X coordinate.
410        */
411       AF_DUMP(( "Table of %s edges:\n",
412                 dimension == AF_DIMENSION_HORZ ? "vertical"
413                                                : "horizontal" ));
414       if ( axis->num_edges )
415         AF_DUMP(( "  [ index |  pos  |  dir  | link"
416                   " | serif | blue | opos  |  pos  |    flags    ]\n" ));
417       else
418         AF_DUMP(( "  (none)\n" ));
419 
420       for ( edge = edges; edge < limit; edge++ )
421         AF_DUMP(( "  [ %5d | %5.2g | %5s | %4d"
422                   " | %5d |   %c  | %5.2f | %5.2f | %11s ]\n",
423                   edge - edges,
424                   (int)edge->opos / 64.0,
425                   af_dir_str( (AF_Direction)edge->dir ),
426                   AF_INDEX_NUM( edge->link, edges ),
427                   AF_INDEX_NUM( edge->serif, edges ),
428                   edge->blue_edge ? 'y' : 'n',
429                   edge->opos / 64.0,
430                   edge->pos / 64.0,
431                   af_edge_flags_to_string( (AF_Edge_Flags)edge->flags ) ));
432       AF_DUMP(( "\n" ));
433     }
434   }
435 #ifdef __cplusplus
436   }
437 #endif
438 
439 #undef AF_DUMP
440 
441 #endif /* !FT_DEBUG_AUTOFIT */
442 
443 
444   /* Compute the direction value of a given vector. */
445 
446   FT_LOCAL_DEF( AF_Direction )
af_direction_compute(FT_Pos dx,FT_Pos dy)447   af_direction_compute( FT_Pos  dx,
448                         FT_Pos  dy )
449   {
450     FT_Pos        ll, ss;  /* long and short arm lengths */
451     AF_Direction  dir;     /* candidate direction        */
452 
453 
454     if ( dy >= dx )
455     {
456       if ( dy >= -dx )
457       {
458         dir = AF_DIR_UP;
459         ll  = dy;
460         ss  = dx;
461       }
462       else
463       {
464         dir = AF_DIR_LEFT;
465         ll  = -dx;
466         ss  = dy;
467       }
468     }
469     else /* dy < dx */
470     {
471       if ( dy >= -dx )
472       {
473         dir = AF_DIR_RIGHT;
474         ll  = dx;
475         ss  = dy;
476       }
477       else
478       {
479         dir = AF_DIR_DOWN;
480         ll  = dy;
481         ss  = dx;
482       }
483     }
484 
485     /* return no direction if arm lengths differ too much            */
486     /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
487     ss *= 14;
488     if ( FT_ABS( ll ) <= FT_ABS( ss ) )
489       dir = AF_DIR_NONE;
490 
491     return dir;
492   }
493 
494 
495   FT_LOCAL_DEF( void )
af_glyph_hints_init(AF_GlyphHints hints,FT_Memory memory)496   af_glyph_hints_init( AF_GlyphHints  hints,
497                        FT_Memory      memory )
498   {
499     FT_ZERO( hints );
500     hints->memory = memory;
501   }
502 
503 
504   FT_LOCAL_DEF( void )
af_glyph_hints_done(AF_GlyphHints hints)505   af_glyph_hints_done( AF_GlyphHints  hints )
506   {
507     FT_Memory  memory = hints->memory;
508     int        dim;
509 
510 
511     if ( !( hints && hints->memory ) )
512       return;
513 
514     /*
515      *  note that we don't need to free the segment and edge
516      *  buffers since they are really within the hints->points array
517      */
518     for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
519     {
520       AF_AxisHints  axis = &hints->axis[dim];
521 
522 
523       axis->num_segments = 0;
524       axis->max_segments = 0;
525       FT_FREE( axis->segments );
526 
527       axis->num_edges = 0;
528       axis->max_edges = 0;
529       FT_FREE( axis->edges );
530     }
531 
532     FT_FREE( hints->contours );
533     hints->max_contours = 0;
534     hints->num_contours = 0;
535 
536     FT_FREE( hints->points );
537     hints->num_points = 0;
538     hints->max_points = 0;
539 
540     hints->memory = NULL;
541   }
542 
543 
544   /* Reset metrics. */
545 
546   FT_LOCAL_DEF( void )
af_glyph_hints_rescale(AF_GlyphHints hints,AF_StyleMetrics metrics)547   af_glyph_hints_rescale( AF_GlyphHints    hints,
548                           AF_StyleMetrics  metrics )
549   {
550     hints->metrics      = metrics;
551     hints->scaler_flags = metrics->scaler.flags;
552   }
553 
554 
555   /* Recompute all AF_Point in AF_GlyphHints from the definitions */
556   /* in a source outline.                                         */
557 
558   FT_LOCAL_DEF( FT_Error )
af_glyph_hints_reload(AF_GlyphHints hints,FT_Outline * outline)559   af_glyph_hints_reload( AF_GlyphHints  hints,
560                          FT_Outline*    outline )
561   {
562     FT_Error   error   = FT_Err_Ok;
563     AF_Point   points;
564     FT_UInt    old_max, new_max;
565     FT_Fixed   x_scale = hints->x_scale;
566     FT_Fixed   y_scale = hints->y_scale;
567     FT_Pos     x_delta = hints->x_delta;
568     FT_Pos     y_delta = hints->y_delta;
569     FT_Memory  memory  = hints->memory;
570 
571 
572     hints->num_points   = 0;
573     hints->num_contours = 0;
574 
575     hints->axis[0].num_segments = 0;
576     hints->axis[0].num_edges    = 0;
577     hints->axis[1].num_segments = 0;
578     hints->axis[1].num_edges    = 0;
579 
580     /* first of all, reallocate the contours array if necessary */
581     new_max = (FT_UInt)outline->n_contours;
582     old_max = hints->max_contours;
583     if ( new_max > old_max )
584     {
585       new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */
586 
587       if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
588         goto Exit;
589 
590       hints->max_contours = new_max;
591     }
592 
593     /*
594      *  then reallocate the points arrays if necessary --
595      *  note that we reserve two additional point positions, used to
596      *  hint metrics appropriately
597      */
598     new_max = (FT_UInt)( outline->n_points + 2 );
599     old_max = hints->max_points;
600     if ( new_max > old_max )
601     {
602       new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */
603 
604       if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
605         goto Exit;
606 
607       hints->max_points = new_max;
608     }
609 
610     hints->num_points   = outline->n_points;
611     hints->num_contours = outline->n_contours;
612 
613     /* We can't rely on the value of `FT_Outline.flags' to know the fill   */
614     /* direction used for a glyph, given that some fonts are broken (e.g., */
615     /* the Arphic ones).  We thus recompute it each time we need to.       */
616     /*                                                                     */
617     hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
618     hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
619 
620     if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
621     {
622       hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
623       hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
624     }
625 
626     hints->x_scale = x_scale;
627     hints->y_scale = y_scale;
628     hints->x_delta = x_delta;
629     hints->y_delta = y_delta;
630 
631     hints->xmin_delta = 0;
632     hints->xmax_delta = 0;
633 
634     points = hints->points;
635     if ( hints->num_points == 0 )
636       goto Exit;
637 
638     {
639       AF_Point  point;
640       AF_Point  point_limit = points + hints->num_points;
641 
642 
643       /* compute coordinates & Bezier flags, next and prev */
644       {
645         FT_Vector*  vec           = outline->points;
646         char*       tag           = outline->tags;
647         AF_Point    end           = points + outline->contours[0];
648         AF_Point    prev          = end;
649         FT_Int      contour_index = 0;
650 
651 
652         for ( point = points; point < point_limit; point++, vec++, tag++ )
653         {
654           point->fx = (FT_Short)vec->x;
655           point->fy = (FT_Short)vec->y;
656           point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
657           point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
658 
659           switch ( FT_CURVE_TAG( *tag ) )
660           {
661           case FT_CURVE_TAG_CONIC:
662             point->flags = AF_FLAG_CONIC;
663             break;
664           case FT_CURVE_TAG_CUBIC:
665             point->flags = AF_FLAG_CUBIC;
666             break;
667           default:
668             point->flags = AF_FLAG_NONE;
669           }
670 
671           point->prev = prev;
672           prev->next  = point;
673           prev        = point;
674 
675           if ( point == end )
676           {
677             if ( ++contour_index < outline->n_contours )
678             {
679               end  = points + outline->contours[contour_index];
680               prev = end;
681             }
682           }
683         }
684       }
685 
686       /* set up the contours array */
687       {
688         AF_Point*  contour       = hints->contours;
689         AF_Point*  contour_limit = contour + hints->num_contours;
690         short*     end           = outline->contours;
691         short      idx           = 0;
692 
693 
694         for ( ; contour < contour_limit; contour++, end++ )
695         {
696           contour[0] = points + idx;
697           idx        = (short)( end[0] + 1 );
698         }
699       }
700 
701       /* compute directions of in & out vectors */
702       {
703         AF_Point      first  = points;
704         AF_Point      prev   = NULL;
705         FT_Pos        in_x   = 0;
706         FT_Pos        in_y   = 0;
707         AF_Direction  in_dir = AF_DIR_NONE;
708 
709         FT_Pos  last_good_in_x = 0;
710         FT_Pos  last_good_in_y = 0;
711 
712         FT_UInt  units_per_em = hints->metrics->scaler.face->units_per_EM;
713         FT_Int   near_limit   = 20 * units_per_em / 2048;
714 
715 
716         for ( point = points; point < point_limit; point++ )
717         {
718           AF_Point  next;
719           FT_Pos    out_x, out_y;
720 
721 
722           if ( point == first )
723           {
724             prev = first->prev;
725 
726             in_x = first->fx - prev->fx;
727             in_y = first->fy - prev->fy;
728 
729             last_good_in_x = in_x;
730             last_good_in_y = in_y;
731 
732             if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
733             {
734               /* search first non-near point to get a good `in_dir' value */
735 
736               AF_Point  point_ = prev;
737 
738 
739               while ( point_ != first )
740               {
741                 AF_Point  prev_ = point_->prev;
742 
743                 FT_Pos  in_x_ = point_->fx - prev_->fx;
744                 FT_Pos  in_y_ = point_->fy - prev_->fy;
745 
746 
747                 if ( FT_ABS( in_x_ ) + FT_ABS( in_y_ ) >= near_limit )
748                 {
749                   last_good_in_x = in_x_;
750                   last_good_in_y = in_y_;
751 
752                   break;
753                 }
754 
755                 point_ = prev_;
756               }
757             }
758 
759             in_dir = af_direction_compute( in_x, in_y );
760             first  = prev + 1;
761           }
762 
763           point->in_dir = (FT_Char)in_dir;
764 
765           /* check whether the current point is near to the previous one */
766           /* (value 20 in `near_limit' is heuristic; we use Taxicab      */
767           /* metrics for the test)                                       */
768 
769           if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
770             point->flags |= AF_FLAG_NEAR;
771           else
772           {
773             last_good_in_x = in_x;
774             last_good_in_y = in_y;
775           }
776 
777           next  = point->next;
778           out_x = next->fx - point->fx;
779           out_y = next->fy - point->fy;
780 
781           in_dir         = af_direction_compute( out_x, out_y );
782           point->out_dir = (FT_Char)in_dir;
783 
784           /* Check for weak points.  The remaining points not collected */
785           /* in edges are then implicitly classified as strong points.  */
786 
787           if ( point->flags & AF_FLAG_CONTROL )
788           {
789             /* control points are always weak */
790           Is_Weak_Point:
791             point->flags |= AF_FLAG_WEAK_INTERPOLATION;
792           }
793           else if ( point->out_dir == point->in_dir )
794           {
795             if ( point->out_dir != AF_DIR_NONE )
796             {
797               /* current point lies on a horizontal or          */
798               /* vertical segment (but doesn't start or end it) */
799               goto Is_Weak_Point;
800             }
801 
802             /* test whether `in' and `out' direction is approximately */
803             /* the same (and use the last good `in' vector in case    */
804             /* the current point is near to the previous one)         */
805             if ( ft_corner_is_flat(
806                    point->flags & AF_FLAG_NEAR ? last_good_in_x : in_x,
807                    point->flags & AF_FLAG_NEAR ? last_good_in_y : in_y,
808                    out_x,
809                    out_y ) )
810             {
811               /* current point lies on a straight, diagonal line */
812               /* (more or less)                                  */
813               goto Is_Weak_Point;
814             }
815           }
816           else if ( point->in_dir == -point->out_dir )
817           {
818             /* current point forms a spike */
819             goto Is_Weak_Point;
820           }
821 
822           in_x = out_x;
823           in_y = out_y;
824         }
825       }
826     }
827 
828   Exit:
829     return error;
830   }
831 
832 
833   /* Store the hinted outline in an FT_Outline structure. */
834 
835   FT_LOCAL_DEF( void )
af_glyph_hints_save(AF_GlyphHints hints,FT_Outline * outline)836   af_glyph_hints_save( AF_GlyphHints  hints,
837                        FT_Outline*    outline )
838   {
839     AF_Point    point = hints->points;
840     AF_Point    limit = point + hints->num_points;
841     FT_Vector*  vec   = outline->points;
842     char*       tag   = outline->tags;
843 
844 
845     for ( ; point < limit; point++, vec++, tag++ )
846     {
847       vec->x = point->x;
848       vec->y = point->y;
849 
850       if ( point->flags & AF_FLAG_CONIC )
851         tag[0] = FT_CURVE_TAG_CONIC;
852       else if ( point->flags & AF_FLAG_CUBIC )
853         tag[0] = FT_CURVE_TAG_CUBIC;
854       else
855         tag[0] = FT_CURVE_TAG_ON;
856     }
857   }
858 
859 
860   /****************************************************************
861    *
862    *                     EDGE POINT GRID-FITTING
863    *
864    ****************************************************************/
865 
866 
867   /* Align all points of an edge to the same coordinate value, */
868   /* either horizontally or vertically.                        */
869 
870   FT_LOCAL_DEF( void )
af_glyph_hints_align_edge_points(AF_GlyphHints hints,AF_Dimension dim)871   af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
872                                     AF_Dimension   dim )
873   {
874     AF_AxisHints  axis          = & hints->axis[dim];
875     AF_Segment    segments      = axis->segments;
876     AF_Segment    segment_limit = segments + axis->num_segments;
877     AF_Segment    seg;
878 
879 
880     if ( dim == AF_DIMENSION_HORZ )
881     {
882       for ( seg = segments; seg < segment_limit; seg++ )
883       {
884         AF_Edge   edge = seg->edge;
885         AF_Point  point, first, last;
886 
887 
888         if ( edge == NULL )
889           continue;
890 
891         first = seg->first;
892         last  = seg->last;
893         point = first;
894         for (;;)
895         {
896           point->x      = edge->pos;
897           point->flags |= AF_FLAG_TOUCH_X;
898 
899           if ( point == last )
900             break;
901 
902           point = point->next;
903         }
904       }
905     }
906     else
907     {
908       for ( seg = segments; seg < segment_limit; seg++ )
909       {
910         AF_Edge   edge = seg->edge;
911         AF_Point  point, first, last;
912 
913 
914         if ( edge == NULL )
915           continue;
916 
917         first = seg->first;
918         last  = seg->last;
919         point = first;
920         for (;;)
921         {
922           point->y      = edge->pos;
923           point->flags |= AF_FLAG_TOUCH_Y;
924 
925           if ( point == last )
926             break;
927 
928           point = point->next;
929         }
930       }
931     }
932   }
933 
934 
935   /****************************************************************
936    *
937    *                    STRONG POINT INTERPOLATION
938    *
939    ****************************************************************/
940 
941 
942   /* Hint the strong points -- this is equivalent to the TrueType `IP' */
943   /* hinting instruction.                                              */
944 
945   FT_LOCAL_DEF( void )
af_glyph_hints_align_strong_points(AF_GlyphHints hints,AF_Dimension dim)946   af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
947                                       AF_Dimension   dim )
948   {
949     AF_Point      points      = hints->points;
950     AF_Point      point_limit = points + hints->num_points;
951     AF_AxisHints  axis        = &hints->axis[dim];
952     AF_Edge       edges       = axis->edges;
953     AF_Edge       edge_limit  = edges + axis->num_edges;
954     AF_Flags      touch_flag;
955 
956 
957     if ( dim == AF_DIMENSION_HORZ )
958       touch_flag = AF_FLAG_TOUCH_X;
959     else
960       touch_flag  = AF_FLAG_TOUCH_Y;
961 
962     if ( edges < edge_limit )
963     {
964       AF_Point  point;
965       AF_Edge   edge;
966 
967 
968       for ( point = points; point < point_limit; point++ )
969       {
970         FT_Pos  u, ou, fu;  /* point position */
971         FT_Pos  delta;
972 
973 
974         if ( point->flags & touch_flag )
975           continue;
976 
977         /* if this point is candidate to weak interpolation, we       */
978         /* interpolate it after all strong points have been processed */
979 
980         if (  ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) &&
981              !( point->flags & AF_FLAG_INFLECTION )         )
982           continue;
983 
984         if ( dim == AF_DIMENSION_VERT )
985         {
986           u  = point->fy;
987           ou = point->oy;
988         }
989         else
990         {
991           u  = point->fx;
992           ou = point->ox;
993         }
994 
995         fu = u;
996 
997         /* is the point before the first edge? */
998         edge  = edges;
999         delta = edge->fpos - u;
1000         if ( delta >= 0 )
1001         {
1002           u = edge->pos - ( edge->opos - ou );
1003           goto Store_Point;
1004         }
1005 
1006         /* is the point after the last edge? */
1007         edge  = edge_limit - 1;
1008         delta = u - edge->fpos;
1009         if ( delta >= 0 )
1010         {
1011           u = edge->pos + ( ou - edge->opos );
1012           goto Store_Point;
1013         }
1014 
1015         {
1016           FT_PtrDist  min, max, mid;
1017           FT_Pos      fpos;
1018 
1019 
1020           /* find enclosing edges */
1021           min = 0;
1022           max = edge_limit - edges;
1023 
1024 #if 1
1025           /* for a small number of edges, a linear search is better */
1026           if ( max <= 8 )
1027           {
1028             FT_PtrDist  nn;
1029 
1030 
1031             for ( nn = 0; nn < max; nn++ )
1032               if ( edges[nn].fpos >= u )
1033                 break;
1034 
1035             if ( edges[nn].fpos == u )
1036             {
1037               u = edges[nn].pos;
1038               goto Store_Point;
1039             }
1040             min = nn;
1041           }
1042           else
1043 #endif
1044           while ( min < max )
1045           {
1046             mid  = ( max + min ) >> 1;
1047             edge = edges + mid;
1048             fpos = edge->fpos;
1049 
1050             if ( u < fpos )
1051               max = mid;
1052             else if ( u > fpos )
1053               min = mid + 1;
1054             else
1055             {
1056               /* we are on the edge */
1057               u = edge->pos;
1058               goto Store_Point;
1059             }
1060           }
1061 
1062           /* point is not on an edge */
1063           {
1064             AF_Edge  before = edges + min - 1;
1065             AF_Edge  after  = edges + min + 0;
1066 
1067 
1068             /* assert( before && after && before != after ) */
1069             if ( before->scale == 0 )
1070               before->scale = FT_DivFix( after->pos - before->pos,
1071                                          after->fpos - before->fpos );
1072 
1073             u = before->pos + FT_MulFix( fu - before->fpos,
1074                                          before->scale );
1075           }
1076         }
1077 
1078       Store_Point:
1079         /* save the point position */
1080         if ( dim == AF_DIMENSION_HORZ )
1081           point->x = u;
1082         else
1083           point->y = u;
1084 
1085         point->flags |= touch_flag;
1086       }
1087     }
1088   }
1089 
1090 
1091   /****************************************************************
1092    *
1093    *                    WEAK POINT INTERPOLATION
1094    *
1095    ****************************************************************/
1096 
1097 
1098   /* Shift the original coordinates of all points between `p1' and */
1099   /* `p2' to get hinted coordinates, using the same difference as  */
1100   /* given by `ref'.                                               */
1101 
1102   static void
af_iup_shift(AF_Point p1,AF_Point p2,AF_Point ref)1103   af_iup_shift( AF_Point  p1,
1104                 AF_Point  p2,
1105                 AF_Point  ref )
1106   {
1107     AF_Point  p;
1108     FT_Pos    delta = ref->u - ref->v;
1109 
1110 
1111     if ( delta == 0 )
1112       return;
1113 
1114     for ( p = p1; p < ref; p++ )
1115       p->u = p->v + delta;
1116 
1117     for ( p = ref + 1; p <= p2; p++ )
1118       p->u = p->v + delta;
1119   }
1120 
1121 
1122   /* Interpolate the original coordinates of all points between `p1' and  */
1123   /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the       */
1124   /* reference points.  The `u' and `v' members are the current and       */
1125   /* original coordinate values, respectively.                            */
1126   /*                                                                      */
1127   /* Details can be found in the TrueType bytecode specification.         */
1128 
1129   static void
af_iup_interp(AF_Point p1,AF_Point p2,AF_Point ref1,AF_Point ref2)1130   af_iup_interp( AF_Point  p1,
1131                  AF_Point  p2,
1132                  AF_Point  ref1,
1133                  AF_Point  ref2 )
1134   {
1135     AF_Point  p;
1136     FT_Pos    u;
1137     FT_Pos    v1 = ref1->v;
1138     FT_Pos    v2 = ref2->v;
1139     FT_Pos    d1 = ref1->u - v1;
1140     FT_Pos    d2 = ref2->u - v2;
1141 
1142 
1143     if ( p1 > p2 )
1144       return;
1145 
1146     if ( v1 == v2 )
1147     {
1148       for ( p = p1; p <= p2; p++ )
1149       {
1150         u = p->v;
1151 
1152         if ( u <= v1 )
1153           u += d1;
1154         else
1155           u += d2;
1156 
1157         p->u = u;
1158       }
1159       return;
1160     }
1161 
1162     if ( v1 < v2 )
1163     {
1164       for ( p = p1; p <= p2; p++ )
1165       {
1166         u = p->v;
1167 
1168         if ( u <= v1 )
1169           u += d1;
1170         else if ( u >= v2 )
1171           u += d2;
1172         else
1173           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
1174 
1175         p->u = u;
1176       }
1177     }
1178     else
1179     {
1180       for ( p = p1; p <= p2; p++ )
1181       {
1182         u = p->v;
1183 
1184         if ( u <= v2 )
1185           u += d2;
1186         else if ( u >= v1 )
1187           u += d1;
1188         else
1189           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
1190 
1191         p->u = u;
1192       }
1193     }
1194   }
1195 
1196 
1197   /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
1198   /* hinting instruction.                                             */
1199 
1200   FT_LOCAL_DEF( void )
af_glyph_hints_align_weak_points(AF_GlyphHints hints,AF_Dimension dim)1201   af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
1202                                     AF_Dimension   dim )
1203   {
1204     AF_Point   points        = hints->points;
1205     AF_Point   point_limit   = points + hints->num_points;
1206     AF_Point*  contour       = hints->contours;
1207     AF_Point*  contour_limit = contour + hints->num_contours;
1208     AF_Flags   touch_flag;
1209     AF_Point   point;
1210     AF_Point   end_point;
1211     AF_Point   first_point;
1212 
1213 
1214     /* PASS 1: Move segment points to edge positions */
1215 
1216     if ( dim == AF_DIMENSION_HORZ )
1217     {
1218       touch_flag = AF_FLAG_TOUCH_X;
1219 
1220       for ( point = points; point < point_limit; point++ )
1221       {
1222         point->u = point->x;
1223         point->v = point->ox;
1224       }
1225     }
1226     else
1227     {
1228       touch_flag = AF_FLAG_TOUCH_Y;
1229 
1230       for ( point = points; point < point_limit; point++ )
1231       {
1232         point->u = point->y;
1233         point->v = point->oy;
1234       }
1235     }
1236 
1237     for ( ; contour < contour_limit; contour++ )
1238     {
1239       AF_Point  first_touched, last_touched;
1240 
1241 
1242       point       = *contour;
1243       end_point   = point->prev;
1244       first_point = point;
1245 
1246       /* find first touched point */
1247       for (;;)
1248       {
1249         if ( point > end_point )  /* no touched point in contour */
1250           goto NextContour;
1251 
1252         if ( point->flags & touch_flag )
1253           break;
1254 
1255         point++;
1256       }
1257 
1258       first_touched = point;
1259 
1260       for (;;)
1261       {
1262         FT_ASSERT( point <= end_point                 &&
1263                    ( point->flags & touch_flag ) != 0 );
1264 
1265         /* skip any touched neighbours */
1266         while ( point < end_point                    &&
1267                 ( point[1].flags & touch_flag ) != 0 )
1268           point++;
1269 
1270         last_touched = point;
1271 
1272         /* find the next touched point, if any */
1273         point++;
1274         for (;;)
1275         {
1276           if ( point > end_point )
1277             goto EndContour;
1278 
1279           if ( ( point->flags & touch_flag ) != 0 )
1280             break;
1281 
1282           point++;
1283         }
1284 
1285         /* interpolate between last_touched and point */
1286         af_iup_interp( last_touched + 1, point - 1,
1287                        last_touched, point );
1288       }
1289 
1290     EndContour:
1291       /* special case: only one point was touched */
1292       if ( last_touched == first_touched )
1293         af_iup_shift( first_point, end_point, first_touched );
1294 
1295       else /* interpolate the last part */
1296       {
1297         if ( last_touched < end_point )
1298           af_iup_interp( last_touched + 1, end_point,
1299                          last_touched, first_touched );
1300 
1301         if ( first_touched > points )
1302           af_iup_interp( first_point, first_touched - 1,
1303                          last_touched, first_touched );
1304       }
1305 
1306     NextContour:
1307       ;
1308     }
1309 
1310     /* now save the interpolated values back to x/y */
1311     if ( dim == AF_DIMENSION_HORZ )
1312     {
1313       for ( point = points; point < point_limit; point++ )
1314         point->x = point->u;
1315     }
1316     else
1317     {
1318       for ( point = points; point < point_limit; point++ )
1319         point->y = point->u;
1320     }
1321   }
1322 
1323 
1324 #ifdef AF_CONFIG_OPTION_USE_WARPER
1325 
1326   /* Apply (small) warp scale and warp delta for given dimension. */
1327 
1328   FT_LOCAL_DEF( void )
af_glyph_hints_scale_dim(AF_GlyphHints hints,AF_Dimension dim,FT_Fixed scale,FT_Pos delta)1329   af_glyph_hints_scale_dim( AF_GlyphHints  hints,
1330                             AF_Dimension   dim,
1331                             FT_Fixed       scale,
1332                             FT_Pos         delta )
1333   {
1334     AF_Point  points       = hints->points;
1335     AF_Point  points_limit = points + hints->num_points;
1336     AF_Point  point;
1337 
1338 
1339     if ( dim == AF_DIMENSION_HORZ )
1340     {
1341       for ( point = points; point < points_limit; point++ )
1342         point->x = FT_MulFix( point->fx, scale ) + delta;
1343     }
1344     else
1345     {
1346       for ( point = points; point < points_limit; point++ )
1347         point->y = FT_MulFix( point->fy, scale ) + delta;
1348     }
1349   }
1350 
1351 #endif /* AF_CONFIG_OPTION_USE_WARPER */
1352 
1353 /* END */
1354