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