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