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