update for HEAD-2003050101
[reactos.git] / lib / freetype / src / autohint / ahhint.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ahhint.c                                                               */
4 /*                                                                         */
5 /*    Glyph hinter (body).                                                 */
6 /*                                                                         */
7 /*  Copyright 2000-2001, 2002 Catharon Productions Inc.                    */
8 /*  Author: David Turner                                                   */
9 /*                                                                         */
10 /*  This file is part of the Catharon Typography Project and shall only    */
11 /*  be used, modified, and distributed under the terms of the Catharon     */
12 /*  Open Source License that should come with this file under the name     */
13 /*  `CatharonLicense.txt'.  By continuing to use, modify, or distribute    */
14 /*  this file you indicate that you have read the license and              */
15 /*  understand and accept it fully.                                        */
16 /*                                                                         */
17 /*  Note that this license is compatible with the FreeType license.        */
18 /*                                                                         */
19 /***************************************************************************/
20
21
22 #include <ft2build.h>
23 #include "ahhint.h"
24 #include "ahglyph.h"
25 #include "ahangles.h"
26 #include "aherrors.h"
27 #include FT_OUTLINE_H
28
29
30 #define FACE_GLOBALS( face )  ((AH_Face_Globals)(face)->autohint.data)
31
32 #define AH_USE_IUP
33 #define OPTIM_STEM_SNAP
34
35   /*************************************************************************/
36   /*************************************************************************/
37   /****                                                                 ****/
38   /****   Hinting routines                                              ****/
39   /****                                                                 ****/
40   /*************************************************************************/
41   /*************************************************************************/
42
43   /* snap a given width in scaled coordinates to one of the */
44   /* current standard widths                                */
45   static FT_Pos
46   ah_snap_width( FT_Pos*  widths,
47                  FT_Int   count,
48                  FT_Pos   width )
49   {
50     int     n;
51     FT_Pos  best      = 64 + 32 + 2;
52     FT_Pos  reference = width;
53     FT_Pos  scaled;
54
55
56     for ( n = 0; n < count; n++ )
57     {
58       FT_Pos  w;
59       FT_Pos  dist;
60
61
62       w = widths[n];
63       dist = width - w;
64       if ( dist < 0 )
65         dist = -dist;
66       if ( dist < best )
67       {
68         best      = dist;
69         reference = w;
70       }
71     }
72
73     scaled = (reference+32) & -64;
74
75     if ( width >= reference )
76     {
77       if ( width < scaled + 48 )
78         width = reference;
79     }
80     else
81     {
82       if ( width > scaled - 48 )
83         width = reference;
84     }
85
86     return width;
87   }
88
89
90   /* compute the snapped width of a given stem */
91 #ifdef FT_CONFIG_CHESTER_SERIF
92   static FT_Pos
93   ah_compute_stem_width( AH_Hinter      hinter,
94                          int            vertical,
95                          FT_Pos         width,
96                          AH_Edge_Flags  base_flags,
97                          AH_Edge_Flags  stem_flags )
98   {
99     AH_Globals  globals = &hinter->globals->scaled;
100     FT_Pos      dist    = width;
101     FT_Int      sign    = 0;
102
103
104     if ( dist < 0 )
105     {
106       dist = -width;
107       sign = 1;
108     }
109
110     if ( !hinter->do_stem_adjust )
111     {
112       /* leave stem widths unchanged */
113     }
114     else if ( (  vertical && !hinter->do_vert_snapping ) ||
115               ( !vertical && !hinter->do_horz_snapping ) )
116     {
117       /* smooth hinting process, very lightly quantize the stem width */
118       /*                                                              */
119
120       /* leave the widths of serifs alone */
121
122       if ( ( stem_flags & AH_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
123         goto Done_Width;
124
125       else if ( ( base_flags & AH_EDGE_ROUND ) )
126       {
127         if ( dist < 80 )
128           dist = 64;
129       }
130       else if ( dist < 56 )
131         dist = 56;
132
133       {
134         FT_Pos  delta = dist - globals->stds[vertical];
135
136
137         if ( delta < 0 )
138           delta = -delta;
139
140         if ( delta < 40 )
141         {
142           dist = globals->stds[vertical];
143           if ( dist < 48 )
144             dist = 48;
145
146           goto Done_Width;
147         }
148
149         if ( dist < 3 * 64 )
150         {
151           delta = ( dist & 63 );
152           dist &= -64;
153
154           if ( delta < 10 )
155             dist += delta;
156
157           else if ( delta < 32 )
158             dist += 10;
159
160           else if ( delta < 54 )
161             dist += 54;
162
163           else
164             dist += delta;
165         }
166         else
167           dist = ( dist + 32 ) & -64;
168       }
169     }
170     else
171     {
172       /* strong hinting process, snap the stem width to integer pixels */
173       /*                                                               */
174       if ( vertical )
175       {
176         dist = ah_snap_width( globals->heights, globals->num_heights, dist );
177
178         /* in the case of vertical hinting, always round */
179         /* the stem heights to integer pixels            */
180         if ( dist >= 64 )
181           dist = ( dist + 16 ) & -64;
182         else
183           dist = 64;
184       }
185       else
186       {
187         dist = ah_snap_width( globals->widths,  globals->num_widths, dist );
188
189         if ( hinter->flags & AH_HINTER_MONOCHROME )
190         {
191           /* monochrome horizontal hinting: snap widths to integer pixels */
192           /* with a different threshold                                   */
193           if ( dist < 64 )
194             dist = 64;
195           else
196             dist = ( dist + 32 ) & -64;
197         }
198         else
199         {
200           /* for horizontal anti-aliased hinting, we adopt a more subtle */
201           /* approach: we strengthen small stems, round stems whose size */
202           /* is between 1 and 2 pixels to an integer, otherwise nothing  */
203           if ( dist < 48 )
204             dist = ( dist + 64 ) >> 1;
205
206           else if ( dist < 128 )
207             dist = ( dist + 22 ) & -64;
208           else
209             /* XXX: round otherwise, prevent color fringes in LCD mode */
210             dist = ( dist + 32 ) & -64;
211         }
212       }
213     }
214
215   Done_Width:
216     if ( sign )
217       dist = -dist;
218
219     return dist;
220   }
221 #else /* !CHESTER_SERIF */
222   static FT_Pos
223   ah_compute_stem_width( AH_Hinter  hinter,
224                          int        vertical,
225                          FT_Pos     width )
226   {
227     AH_Globals  globals = &hinter->globals->scaled;
228     FT_Pos      dist    = width;
229     FT_Int      sign    = 0;
230
231
232     if ( dist < 0 )
233     {
234       dist = -width;
235       sign = 1;
236     }
237
238     if ( !hinter->do_stem_adjust )
239     {
240       /* leave stem widths unchanged */
241     }
242     else if ( (  vertical && !hinter->do_vert_snapping ) ||
243               ( !vertical && !hinter->do_horz_snapping ) )
244     {
245       /* smooth hinting process, very lightly quantize the stem width */
246       /*                                                              */
247       if ( dist < 64 )
248         dist = 64;
249
250       {
251         FT_Pos  delta = dist - globals->stds[vertical];
252
253
254         if ( delta < 0 )
255           delta = -delta;
256
257         if ( delta < 40 )
258         {
259           dist = globals->stds[vertical];
260           if ( dist < 48 )
261             dist = 48;
262         }
263
264         if ( dist < 3 * 64 )
265         {
266           delta = ( dist & 63 );
267           dist &= -64;
268
269           if ( delta < 10 )
270             dist += delta;
271
272           else if ( delta < 32 )
273             dist += 10;
274
275           else if ( delta < 54 )
276             dist += 54;
277
278           else
279             dist += delta;
280         }
281         else
282           dist = ( dist + 32 ) & -64;
283       }
284     }
285     else
286     {
287       /* strong hinting process, snap the stem width to integer pixels */
288       /*                                                               */
289       if ( vertical )
290       {
291         dist = ah_snap_width( globals->heights, globals->num_heights, dist );
292
293         /* in the case of vertical hinting, always round */
294         /* the stem heights to integer pixels            */
295         if ( dist >= 64 )
296           dist = ( dist + 16 ) & -64;
297         else
298           dist = 64;
299       }
300       else
301       {
302         dist = ah_snap_width( globals->widths,  globals->num_widths, dist );
303
304         if ( hinter->flags & AH_HINTER_MONOCHROME )
305         {
306           /* monochrome horizontal hinting: snap widths to integer pixels */
307           /* with a different threshold                                   */
308           if ( dist < 64 )
309             dist = 64;
310           else
311             dist = ( dist + 32 ) & -64;
312         }
313         else
314         {
315           /* for horizontal anti-aliased hinting, we adopt a more subtle */
316           /* approach: we strengthen small stems, round stems whose size */
317           /* is between 1 and 2 pixels to an integer, otherwise nothing  */
318           if ( dist < 48 )
319             dist = ( dist + 64 ) >> 1;
320
321           else if ( dist < 128 )
322             dist = ( dist + 22 ) & -64;
323           else
324             /* XXX: round otherwise, prevent color fringes in LCD mode */
325             dist = ( dist + 32 ) & -64;
326         }
327       }
328     }
329
330     if ( sign )
331       dist = -dist;
332
333     return dist;
334   }
335 #endif /* !CHESTER_SERIF */
336
337
338   /* align one stem edge relative to the previous stem edge */
339   static void
340   ah_align_linked_edge( AH_Hinter  hinter,
341                         AH_Edge    base_edge,
342                         AH_Edge    stem_edge,
343                         int        vertical )
344   {
345     FT_Pos  dist = stem_edge->opos - base_edge->opos;
346
347 #ifdef FT_CONFIG_CHESTER_SERIF
348     FT_Pos  fitted_width = ah_compute_stem_width( hinter,
349                                                   vertical,
350                                                   dist,
351                                                   base_edge->flags,
352                                                   stem_edge->flags );
353
354     stem_edge->pos = base_edge->pos + fitted_width;
355 #else
356     stem_edge->pos = base_edge->pos +
357                      ah_compute_stem_width( hinter, vertical, dist );
358 #endif
359   }
360
361
362   static void
363   ah_align_serif_edge( AH_Hinter  hinter,
364                        AH_Edge    base,
365                        AH_Edge    serif,
366                        int        vertical )
367   {
368     FT_Pos  dist;
369     FT_Pos  sign = 1;
370
371     FT_UNUSED( hinter );
372     FT_UNUSED( vertical );
373
374     dist = serif->opos - base->opos;
375     if ( dist < 0 )
376     {
377       dist = -dist;
378       sign = -1;
379     }
380
381     /* do not touch serifs widths !! */
382 #if 0
383     if ( base->flags & AH_EDGE_DONE )
384     {
385       if ( dist >= 64 )
386         dist = (dist+8) & -64;
387
388       else if ( dist <= 32 && !vertical )
389         dist = ( dist + 33 ) >> 1;
390       else
391         dist = 0;
392     }
393 #endif
394
395     serif->pos = base->pos + sign * dist;
396   }
397
398
399   /*************************************************************************/
400   /*************************************************************************/
401   /*************************************************************************/
402   /****                                                                 ****/
403   /****       E D G E   H I N T I N G                                   ****/
404   /****                                                                 ****/
405   /*************************************************************************/
406   /*************************************************************************/
407   /*************************************************************************/
408
409
410   /* Another alternative edge hinting algorithm */
411   static void
412   ah_hint_edges_3( AH_Hinter  hinter )
413   {
414     AH_Edge     edges;
415     AH_Edge     edge_limit;
416     AH_Outline  outline = hinter->glyph;
417     FT_Int      dimension;
418
419
420     edges      = outline->horz_edges;
421     edge_limit = edges + outline->num_hedges;
422
423     for ( dimension = 1; dimension >= 0; dimension-- )
424     {
425       AH_Edge  edge;
426       AH_Edge  anchor = 0;
427       int      has_serifs = 0;
428
429
430       if ( !hinter->do_horz_hints && !dimension )
431         goto Next_Dimension;
432
433       if ( !hinter->do_vert_hints && dimension )
434         goto Next_Dimension;
435
436       /* we begin by aligning all stems relative to the blue zone */
437       /* if needed -- that's only for horizontal edges            */
438       if ( dimension )
439       {
440         for ( edge = edges; edge < edge_limit; edge++ )
441         {
442           FT_Pos*      blue;
443           AH_EdgeRec  *edge1, *edge2;
444
445
446           if ( edge->flags & AH_EDGE_DONE )
447             continue;
448
449           blue  = edge->blue_edge;
450           edge1 = 0;
451           edge2 = edge->link;
452
453           if ( blue )
454           {
455             edge1 = edge;
456           }
457           else if (edge2 && edge2->blue_edge)
458           {
459             blue  = edge2->blue_edge;
460             edge1 = edge2;
461             edge2 = edge;
462           }
463
464           if ( !edge1 )
465             continue;
466
467           edge1->pos    = blue[0];
468           edge1->flags |= AH_EDGE_DONE;
469
470           if ( edge2 && !edge2->blue_edge )
471           {
472             ah_align_linked_edge( hinter, edge1, edge2, dimension );
473             edge2->flags |= AH_EDGE_DONE;
474           }
475
476           if ( !anchor )
477             anchor = edge;
478         }
479       }
480
481       /* now, we will align all stem edges, trying to maintain the */
482       /* relative order of stems in the glyph..                    */
483       for ( edge = edges; edge < edge_limit; edge++ )
484       {
485         AH_EdgeRec*  edge2;
486
487
488         if ( edge->flags & AH_EDGE_DONE )
489           continue;
490
491         /* skip all non-stem edges */
492         edge2 = edge->link;
493         if ( !edge2 )
494         {
495           has_serifs++;
496           continue;
497         }
498
499         /* now, align the stem */
500
501         /* this should not happen, but it's better to be safe. */
502         if ( edge2->blue_edge || edge2 < edge )
503         {
504
505           ah_align_linked_edge( hinter, edge2, edge, dimension );
506           edge->flags |= AH_EDGE_DONE;
507           continue;
508         }
509
510         if ( !anchor )
511         {
512 #ifdef FT_CONFIG_CHESTER_STEM
513           FT_Pos   org_len, org_center, cur_len;
514           FT_Pos   cur_pos1, error1, error2, u_off, d_off;
515
516           org_len    = edge2->opos - edge->opos;
517           cur_len    = ah_compute_stem_width( hinter, dimension, org_len,
518                                               edge->flags, edge2->flags   );
519
520           if (cur_len <= 64 )
521             u_off = d_off = 32;
522           else
523           {
524             u_off = 38;
525             d_off = 26;
526           }
527
528           if ( cur_len < 96 )
529           {
530             org_center = edge->opos + ( org_len >> 1 );
531
532             cur_pos1   = ( org_center + 32 ) & -64;
533
534             error1 = org_center - ( cur_pos1 - u_off );
535             if ( error1 < 0 )
536               error1 = -error1;
537
538             error2 = org_center - ( cur_pos1 + d_off );
539             if ( error2 < 0 )
540               error2 = -error2;
541
542             if ( error1 < error2 )
543               cur_pos1 -= u_off;
544             else
545               cur_pos1 += d_off;
546
547             edge->pos  = cur_pos1 - cur_len / 2;
548             edge2->pos = cur_pos1 + cur_len / 2;
549
550           }
551           else
552             edge->pos = ( edge->opos + 32 ) & -64;
553
554           anchor    = edge;
555
556           edge->flags |= AH_EDGE_DONE;
557
558           ah_align_linked_edge( hinter, edge, edge2, dimension );
559 #else /* !CHESTER_STEM */
560           edge->pos = ( edge->opos + 32 ) & -64;
561           anchor    = edge;
562
563           edge->flags |= AH_EDGE_DONE;
564
565           ah_align_linked_edge( hinter, edge, edge2, dimension );
566 #endif /* !CHESTER_STEM */
567         }
568         else
569         {
570           FT_Pos   org_pos, org_len, org_center, cur_len;
571           FT_Pos   cur_pos1, cur_pos2, delta1, delta2;
572
573
574           org_pos    = anchor->pos + (edge->opos - anchor->opos);
575           org_len    = edge2->opos - edge->opos;
576           org_center = org_pos + ( org_len >> 1 );
577
578 #ifdef FT_CONFIG_CHESTER_SERIF
579           cur_len    = ah_compute_stem_width( hinter, dimension, org_len,
580                                               edge->flags, edge2->flags  );
581 #else  /* !CHESTER_SERIF */
582           cur_len    = ah_compute_stem_width( hinter, dimension, org_len );
583 #endif /* !CHESTER_SERIF */
584
585 #ifdef FT_CONFIG_CHESTER_STEM
586           if ( cur_len < 96 )
587           {
588             FT_Pos  u_off, d_off;
589
590
591             cur_pos1   = ( org_center + 32 ) & -64;
592
593             if (cur_len <= 64 )
594               u_off = d_off = 32;
595             else
596             {
597               u_off = 38;
598               d_off = 26;
599             }
600
601             delta1 = org_center - (cur_pos1 - u_off);
602             if ( delta1 < 0 )
603               delta1 = -delta1;
604
605             delta2 = org_center - (cur_pos1 + d_off);
606             if ( delta2 < 0 )
607               delta2 = -delta2;
608
609             if ( delta1 < delta2 )
610               cur_pos1 -= u_off;
611             else
612               cur_pos1 += d_off;
613
614             edge->pos  = cur_pos1 - cur_len / 2;
615             edge2->pos = cur_pos1 + cur_len / 2;
616           }
617           else
618           {
619
620             org_pos    = anchor->pos + (edge->opos - anchor->opos);
621             org_len    = edge2->opos - edge->opos;
622             org_center = org_pos + ( org_len >> 1 );
623
624             cur_len    = ah_compute_stem_width( hinter, dimension, org_len,
625                                                 edge->flags, edge2->flags );
626
627             cur_pos1   = ( org_pos + 32 ) & -64;
628             delta1     = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
629             if ( delta1 < 0 )
630               delta1 = -delta1;
631
632             cur_pos2   = ( ( org_pos + org_len + 32 ) & -64 ) - cur_len;
633             delta2     = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
634             if ( delta2 < 0 )
635               delta2 = -delta2;
636
637             edge->pos  = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
638             edge2->pos = edge->pos + cur_len;
639           }
640
641 #else /* !CHESTER_STEM */
642
643           cur_pos1   = ( org_pos + 32 ) & -64;
644           delta1     = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
645           if ( delta1 < 0 )
646             delta1 = -delta1;
647
648           cur_pos2   = ( ( org_pos + org_len + 32 ) & -64 ) - cur_len;
649           delta2     = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
650           if ( delta2 < 0 )
651             delta2 = -delta2;
652
653           edge->pos  = ( delta1 <= delta2 ) ? cur_pos1 : cur_pos2;
654           edge2->pos = edge->pos + cur_len;
655
656 #endif /* !CHESTER_STEM */
657
658           edge->flags  |= AH_EDGE_DONE;
659           edge2->flags |= AH_EDGE_DONE;
660
661           if ( edge > edges && edge->pos < edge[-1].pos )
662             edge->pos = edge[-1].pos;
663         }
664       }
665
666       if ( !has_serifs )
667         goto Next_Dimension;
668
669       /* now, hint the remaining edges (serifs and single) in order */
670       /* to complete our processing                                 */
671       for ( edge = edges; edge < edge_limit; edge++ )
672       {
673         if ( edge->flags & AH_EDGE_DONE )
674           continue;
675
676         if ( edge->serif )
677           ah_align_serif_edge( hinter, edge->serif, edge, dimension );
678         else if ( !anchor )
679         {
680           edge->pos = ( edge->opos + 32 ) & -64;
681           anchor    = edge;
682         }
683         else
684           edge->pos = anchor->pos +
685                       ( ( edge->opos-anchor->opos + 32 ) & -64 );
686
687         edge->flags |= AH_EDGE_DONE;
688
689         if ( edge > edges && edge->pos < edge[-1].pos )
690           edge->pos = edge[-1].pos;
691
692         if ( edge + 1 < edge_limit        &&
693              edge[1].flags & AH_EDGE_DONE &&
694              edge->pos > edge[1].pos      )
695           edge->pos = edge[1].pos;
696       }
697
698     Next_Dimension:
699       edges      = outline->vert_edges;
700       edge_limit = edges + outline->num_vedges;
701     }
702   }
703
704
705   FT_LOCAL_DEF( void )
706   ah_hinter_hint_edges( AH_Hinter  hinter )
707   {
708     /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help      */
709     /* reduce the problem of the disappearing eye in the `e' of Times... */
710     /* also, creates some artifacts near the blue zones?                 */
711     {
712       ah_hint_edges_3( hinter );
713     }
714   }
715
716
717   /*************************************************************************/
718   /*************************************************************************/
719   /*************************************************************************/
720   /****                                                                 ****/
721   /****       P O I N T   H I N T I N G                                 ****/
722   /****                                                                 ****/
723   /*************************************************************************/
724   /*************************************************************************/
725   /*************************************************************************/
726
727   static void
728   ah_hinter_align_edge_points( AH_Hinter  hinter )
729   {
730     AH_Outline  outline = hinter->glyph;
731     AH_Edge     edges;
732     AH_Edge     edge_limit;
733     FT_Int      dimension;
734
735
736     edges      = outline->horz_edges;
737     edge_limit = edges + outline->num_hedges;
738
739     for ( dimension = 1; dimension >= 0; dimension-- )
740     {
741       AH_Edge  edge;
742
743
744       edge = edges;
745       for ( ; edge < edge_limit; edge++ )
746       {
747         /* move the points of each segment     */
748         /* in each edge to the edge's position */
749         AH_Segment  seg = edge->first;
750
751
752         do
753         {
754           AH_Point  point = seg->first;
755
756
757           for (;;)
758           {
759             if ( dimension )
760             {
761               point->y      = edge->pos;
762               point->flags |= AH_FLAG_TOUCH_Y;
763             }
764             else
765             {
766               point->x      = edge->pos;
767               point->flags |= AH_FLAG_TOUCH_X;
768             }
769
770             if ( point == seg->last )
771               break;
772
773             point = point->next;
774           }
775
776           seg = seg->edge_next;
777
778         } while ( seg != edge->first );
779       }
780
781       edges      = outline->vert_edges;
782       edge_limit = edges + outline->num_vedges;
783     }
784   }
785
786
787   /* hint the strong points -- this is equivalent to the TrueType `IP' */
788   static void
789   ah_hinter_align_strong_points( AH_Hinter  hinter )
790   {
791     AH_Outline  outline = hinter->glyph;
792     FT_Int      dimension;
793     AH_Edge     edges;
794     AH_Edge     edge_limit;
795     AH_Point    points;
796     AH_Point    point_limit;
797     AH_Flags    touch_flag;
798
799
800     points      = outline->points;
801     point_limit = points + outline->num_points;
802
803     edges       = outline->horz_edges;
804     edge_limit  = edges + outline->num_hedges;
805     touch_flag  = AH_FLAG_TOUCH_Y;
806
807     for ( dimension = 1; dimension >= 0; dimension-- )
808     {
809       AH_Point  point;
810       AH_Edge   edge;
811
812
813       if ( edges < edge_limit )
814         for ( point = points; point < point_limit; point++ )
815         {
816           FT_Pos  u, ou, fu;  /* point position */
817           FT_Pos  delta;
818
819
820           if ( point->flags & touch_flag )
821             continue;
822
823 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
824           /* if this point is candidate to weak interpolation, we will  */
825           /* interpolate it after all strong points have been processed */
826           if (  ( point->flags & AH_FLAG_WEAK_INTERPOLATION ) &&
827                !( point->flags & AH_FLAG_INFLECTION )         )
828             continue;
829 #endif
830
831           if ( dimension )
832           {
833             u  = point->fy;
834             ou = point->oy;
835           }
836           else
837           {
838             u  = point->fx;
839             ou = point->ox;
840           }
841
842           fu = u;
843
844           /* is the point before the first edge? */
845           edge  = edges;
846           delta = edge->fpos - u;
847           if ( delta >= 0 )
848           {
849             u = edge->pos - ( edge->opos - ou );
850             goto Store_Point;
851           }
852
853           /* is the point after the last edge ? */
854           edge  = edge_limit - 1;
855           delta = u - edge->fpos;
856           if ( delta >= 0 )
857           {
858             u = edge->pos + ( ou - edge->opos );
859             goto Store_Point;
860           }
861
862           /* otherwise, interpolate the point in between */
863           {
864             AH_Edge  before = 0;
865             AH_Edge  after  = 0;
866
867
868             for ( edge = edges; edge < edge_limit; edge++ )
869             {
870               if ( u == edge->fpos )
871               {
872                 u = edge->pos;
873                 goto Store_Point;
874               }
875               if ( u < edge->fpos )
876                 break;
877               before = edge;
878             }
879
880             for ( edge = edge_limit - 1; edge >= edges; edge-- )
881             {
882               if ( u == edge->fpos )
883               {
884                 u = edge->pos;
885                 goto Store_Point;
886               }
887               if ( u > edge->fpos )
888                 break;
889               after = edge;
890             }
891
892             /* assert( before && after && before != after ) */
893             u = before->pos + FT_MulDiv( fu - before->fpos,
894                                          after->pos - before->pos,
895                                          after->fpos - before->fpos );
896           }
897
898         Store_Point:
899
900           /* save the point position */
901           if ( dimension )
902             point->y = u;
903           else
904             point->x = u;
905
906           point->flags |= touch_flag;
907         }
908
909       edges      = outline->vert_edges;
910       edge_limit = edges + outline->num_vedges;
911       touch_flag = AH_FLAG_TOUCH_X;
912     }
913   }
914
915
916 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
917
918   static void
919   ah_iup_shift( AH_Point  p1,
920                 AH_Point  p2,
921                 AH_Point  ref )
922   {
923     AH_Point  p;
924     FT_Pos    delta = ref->u - ref->v;
925
926
927     for ( p = p1; p < ref; p++ )
928       p->u = p->v + delta;
929
930     for ( p = ref + 1; p <= p2; p++ )
931       p->u = p->v + delta;
932   }
933
934
935   static void
936   ah_iup_interp( AH_Point  p1,
937                  AH_Point  p2,
938                  AH_Point  ref1,
939                  AH_Point  ref2 )
940   {
941     AH_Point  p;
942     FT_Pos    u;
943     FT_Pos    v1 = ref1->v;
944     FT_Pos    v2 = ref2->v;
945     FT_Pos    d1 = ref1->u - v1;
946     FT_Pos    d2 = ref2->u - v2;
947
948
949     if ( p1 > p2 )
950       return;
951
952     if ( v1 == v2 )
953     {
954       for ( p = p1; p <= p2; p++ )
955       {
956         u = p->v;
957
958         if ( u <= v1 )
959           u += d1;
960         else
961           u += d2;
962
963         p->u = u;
964       }
965       return;
966     }
967
968     if ( v1 < v2 )
969     {
970       for ( p = p1; p <= p2; p++ )
971       {
972         u = p->v;
973
974         if ( u <= v1 )
975           u += d1;
976         else if ( u >= v2 )
977           u += d2;
978         else
979           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
980
981         p->u = u;
982       }
983     }
984     else
985     {
986       for ( p = p1; p <= p2; p++ )
987       {
988         u = p->v;
989
990         if ( u <= v2 )
991           u += d2;
992         else if ( u >= v1 )
993           u += d1;
994         else
995           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
996
997         p->u = u;
998       }
999     }
1000   }
1001
1002
1003   /* interpolate weak points -- this is equivalent to the TrueType `IUP' */
1004   static void
1005   ah_hinter_align_weak_points( AH_Hinter  hinter )
1006   {
1007     AH_Outline  outline = hinter->glyph;
1008     FT_Int      dimension;
1009     AH_Point    points;
1010     AH_Point    point_limit;
1011     AH_Point*   contour_limit;
1012     AH_Flags    touch_flag;
1013
1014
1015     points      = outline->points;
1016     point_limit = points + outline->num_points;
1017
1018     /* PASS 1: Move segment points to edge positions */
1019
1020     touch_flag = AH_FLAG_TOUCH_Y;
1021
1022     contour_limit = outline->contours + outline->num_contours;
1023
1024     ah_setup_uv( outline, AH_UV_OY );
1025
1026     for ( dimension = 1; dimension >= 0; dimension-- )
1027     {
1028       AH_Point   point;
1029       AH_Point   end_point;
1030       AH_Point   first_point;
1031       AH_Point*  contour;
1032
1033
1034       point   = points;
1035       contour = outline->contours;
1036
1037       for ( ; contour < contour_limit; contour++ )
1038       {
1039         point       = *contour;
1040         end_point   = point->prev;
1041         first_point = point;
1042
1043         while ( point <= end_point && !( point->flags & touch_flag ) )
1044           point++;
1045
1046         if ( point <= end_point )
1047         {
1048           AH_Point  first_touched = point;
1049           AH_Point  cur_touched   = point;
1050
1051
1052           point++;
1053           while ( point <= end_point )
1054           {
1055             if ( point->flags & touch_flag )
1056             {
1057               /* we found two successive touched points; we interpolate */
1058               /* all contour points between them                        */
1059               ah_iup_interp( cur_touched + 1, point - 1,
1060                              cur_touched, point );
1061               cur_touched = point;
1062             }
1063             point++;
1064           }
1065
1066           if ( cur_touched == first_touched )
1067           {
1068             /* this is a special case: only one point was touched in the */
1069             /* contour; we thus simply shift the whole contour           */
1070             ah_iup_shift( first_point, end_point, cur_touched );
1071           }
1072           else
1073           {
1074             /* now interpolate after the last touched point to the end */
1075             /* of the contour                                          */
1076             ah_iup_interp( cur_touched + 1, end_point,
1077                            cur_touched, first_touched );
1078
1079             /* if the first contour point isn't touched, interpolate */
1080             /* from the contour start to the first touched point     */
1081             if ( first_touched > points )
1082               ah_iup_interp( first_point, first_touched - 1,
1083                              cur_touched, first_touched );
1084           }
1085         }
1086       }
1087
1088       /* now save the interpolated values back to x/y */
1089       if ( dimension )
1090       {
1091         for ( point = points; point < point_limit; point++ )
1092           point->y = point->u;
1093
1094         touch_flag = AH_FLAG_TOUCH_X;
1095         ah_setup_uv( outline, AH_UV_OX );
1096       }
1097       else
1098       {
1099         for ( point = points; point < point_limit; point++ )
1100           point->x = point->u;
1101
1102         break;  /* exit loop */
1103       }
1104     }
1105   }
1106
1107 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
1108
1109
1110   FT_LOCAL_DEF( void )
1111   ah_hinter_align_points( AH_Hinter  hinter )
1112   {
1113     ah_hinter_align_edge_points( hinter );
1114
1115 #ifndef AH_OPTION_NO_STRONG_INTERPOLATION
1116     ah_hinter_align_strong_points( hinter );
1117 #endif
1118
1119 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
1120     ah_hinter_align_weak_points( hinter );
1121 #endif
1122   }
1123
1124
1125   /*************************************************************************/
1126   /*************************************************************************/
1127   /*************************************************************************/
1128   /****                                                                 ****/
1129   /****       H I N T E R   O B J E C T   M E T H O D S                 ****/
1130   /****                                                                 ****/
1131   /*************************************************************************/
1132   /*************************************************************************/
1133   /*************************************************************************/
1134
1135
1136   /* scale and fit the global metrics */
1137   static void
1138   ah_hinter_scale_globals( AH_Hinter  hinter,
1139                            FT_Fixed   x_scale,
1140                            FT_Fixed   y_scale )
1141   {
1142     FT_Int           n;
1143     AH_Face_Globals  globals = hinter->globals;
1144     AH_Globals       design = &globals->design;
1145     AH_Globals       scaled = &globals->scaled;
1146
1147
1148     /* copy content */
1149     *scaled = *design;
1150
1151     /* scale the standard widths & heights */
1152     for ( n = 0; n < design->num_widths; n++ )
1153       scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
1154
1155     for ( n = 0; n < design->num_heights; n++ )
1156       scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
1157
1158     scaled->stds[0] = ( design->num_widths  > 0 ) ? scaled->widths[0]  : 32000;
1159     scaled->stds[1] = ( design->num_heights > 0 ) ? scaled->heights[0] : 32000;
1160
1161     /* scale the blue zones */
1162     for ( n = 0; n < AH_BLUE_MAX; n++ )
1163     {
1164       FT_Pos  delta, delta2;
1165
1166
1167       delta = design->blue_shoots[n] - design->blue_refs[n];
1168       delta2 = delta;
1169       if ( delta < 0 )
1170         delta2 = -delta2;
1171       delta2 = FT_MulFix( delta2, y_scale );
1172
1173       if ( delta2 < 32 )
1174         delta2 = 0;
1175       else if ( delta2 < 64 )
1176         delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 );
1177       else
1178         delta2 = ( delta2 + 32 ) & -64;
1179
1180       if ( delta < 0 )
1181         delta2 = -delta2;
1182
1183       scaled->blue_refs[n] =
1184         ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64;
1185       scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
1186     }
1187
1188     globals->x_scale = x_scale;
1189     globals->y_scale = y_scale;
1190   }
1191
1192
1193   static void
1194   ah_hinter_align( AH_Hinter  hinter )
1195   {
1196     ah_hinter_align_edge_points( hinter );
1197     ah_hinter_align_points( hinter );
1198   }
1199
1200
1201   /* finalize a hinter object */
1202   FT_LOCAL_DEF( void )
1203   ah_hinter_done( AH_Hinter  hinter )
1204   {
1205     if ( hinter )
1206     {
1207       FT_Memory  memory = hinter->memory;
1208
1209
1210       ah_loader_done( hinter->loader );
1211       ah_outline_done( hinter->glyph );
1212
1213       /* note: the `globals' pointer is _not_ owned by the hinter */
1214       /*       but by the current face object, we don't need to   */
1215       /*       release it                                         */
1216       hinter->globals = 0;
1217       hinter->face    = 0;
1218
1219       FT_FREE( hinter );
1220     }
1221   }
1222
1223
1224   /* create a new empty hinter object */
1225   FT_LOCAL_DEF( FT_Error )
1226   ah_hinter_new( FT_Library  library,
1227                  AH_Hinter  *ahinter )
1228   {
1229     AH_Hinter  hinter = 0;
1230     FT_Memory  memory = library->memory;
1231     FT_Error   error;
1232
1233
1234     *ahinter = 0;
1235
1236     /* allocate object */
1237     if ( FT_NEW( hinter ) )
1238       goto Exit;
1239
1240     hinter->memory = memory;
1241     hinter->flags  = 0;
1242
1243     /* allocate outline and loader */
1244     error = ah_outline_new( memory, &hinter->glyph )  ||
1245             ah_loader_new ( memory, &hinter->loader ) ||
1246             ah_loader_create_extra( hinter->loader );
1247     if ( error )
1248       goto Exit;
1249
1250     *ahinter = hinter;
1251
1252   Exit:
1253     if ( error )
1254       ah_hinter_done( hinter );
1255
1256     return error;
1257   }
1258
1259
1260   /* create a face's autohint globals */
1261   FT_LOCAL_DEF( FT_Error )
1262   ah_hinter_new_face_globals( AH_Hinter   hinter,
1263                               FT_Face     face,
1264                               AH_Globals  globals )
1265   {
1266     FT_Error         error;
1267     FT_Memory        memory = hinter->memory;
1268     AH_Face_Globals  face_globals;
1269
1270
1271     if ( FT_NEW( face_globals ) )
1272       goto Exit;
1273
1274     hinter->face    = face;
1275     hinter->globals = face_globals;
1276
1277     if ( globals )
1278       face_globals->design = *globals;
1279     else
1280       ah_hinter_compute_globals( hinter );
1281
1282     face->autohint.data      = face_globals;
1283     face->autohint.finalizer = (FT_Generic_Finalizer)
1284                                  ah_hinter_done_face_globals;
1285     face_globals->face       = face;
1286
1287   Exit:
1288     return error;
1289   }
1290
1291
1292   /* discard a face's autohint globals */
1293   FT_LOCAL_DEF( void )
1294   ah_hinter_done_face_globals( AH_Face_Globals  globals )
1295   {
1296     FT_Face    face   = globals->face;
1297     FT_Memory  memory = face->memory;
1298
1299
1300     FT_FREE( globals );
1301   }
1302
1303
1304   static FT_Error
1305   ah_hinter_load( AH_Hinter  hinter,
1306                   FT_UInt    glyph_index,
1307                   FT_Int32   load_flags,
1308                   FT_UInt    depth )
1309   {
1310     FT_Face           face     = hinter->face;
1311     FT_GlyphSlot      slot     = face->glyph;
1312     FT_Slot_Internal  internal = slot->internal;
1313     FT_Fixed          x_scale  = hinter->globals->x_scale;
1314     FT_Fixed          y_scale  = hinter->globals->y_scale;
1315     FT_Error          error;
1316     AH_Outline        outline  = hinter->glyph;
1317     AH_Loader         gloader  = hinter->loader;
1318
1319
1320     /* load the glyph */
1321     error = FT_Load_Glyph( face, glyph_index, load_flags );
1322     if ( error )
1323       goto Exit;
1324
1325     /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
1326     hinter->transformed = internal->glyph_transformed;
1327
1328     if ( hinter->transformed )
1329     {
1330       FT_Matrix  imatrix;
1331
1332
1333       imatrix              = internal->glyph_matrix;
1334       hinter->trans_delta  = internal->glyph_delta;
1335       hinter->trans_matrix = imatrix;
1336
1337       FT_Matrix_Invert( &imatrix );
1338       FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1339     }
1340
1341     /* set linear horizontal metrics */
1342     slot->linearHoriAdvance = slot->metrics.horiAdvance;
1343     slot->linearVertAdvance = slot->metrics.vertAdvance;
1344
1345     switch ( slot->format )
1346     {
1347     case FT_GLYPH_FORMAT_OUTLINE:
1348
1349       /* translate glyph outline if we need to */
1350       if ( hinter->transformed )
1351       {
1352         FT_UInt     n     = slot->outline.n_points;
1353         FT_Vector*  point = slot->outline.points;
1354
1355
1356         for ( ; n > 0; point++, n-- )
1357         {
1358           point->x += hinter->trans_delta.x;
1359           point->y += hinter->trans_delta.y;
1360         }
1361       }
1362
1363       /* copy the outline points in the loader's current                */
1364       /* extra points, which is used to keep original glyph coordinates */
1365       error = ah_loader_check_points( gloader, slot->outline.n_points + 2,
1366                                       slot->outline.n_contours );
1367       if ( error )
1368         goto Exit;
1369
1370       FT_MEM_COPY( gloader->current.extra_points, slot->outline.points,
1371                    slot->outline.n_points * sizeof ( FT_Vector ) );
1372
1373       FT_MEM_COPY( gloader->current.outline.contours, slot->outline.contours,
1374                    slot->outline.n_contours * sizeof ( short ) );
1375
1376       FT_MEM_COPY( gloader->current.outline.tags, slot->outline.tags,
1377                    slot->outline.n_points * sizeof ( char ) );
1378
1379       gloader->current.outline.n_points   = slot->outline.n_points;
1380       gloader->current.outline.n_contours = slot->outline.n_contours;
1381
1382       /* compute original phantom points */
1383       hinter->pp1.x = 0;
1384       hinter->pp1.y = 0;
1385       hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
1386       hinter->pp2.y = 0;
1387
1388       /* be sure to check for spacing glyphs */
1389       if ( slot->outline.n_points == 0 )
1390         goto Hint_Metrics;
1391
1392       /* now, load the slot image into the auto-outline, and run the */
1393       /* automatic hinting process                                   */
1394       error = ah_outline_load( outline, x_scale, y_scale, face );
1395       if ( error )
1396         goto Exit;
1397
1398       /* perform feature detection */
1399       ah_outline_detect_features( outline );
1400
1401       if ( hinter->do_vert_hints )
1402       {
1403         ah_outline_compute_blue_edges( outline, hinter->globals );
1404         ah_outline_scale_blue_edges( outline, hinter->globals );
1405       }
1406
1407       /* perform alignment control */
1408       ah_hinter_hint_edges( hinter );
1409       ah_hinter_align( hinter );
1410
1411       /* now save the current outline into the loader's current table */
1412       ah_outline_save( outline, gloader );
1413
1414       /* we now need to hint the metrics according to the change in */
1415       /* width/positioning that occured during the hinting process  */
1416       {
1417         FT_Pos   old_advance, old_rsb, old_lsb, new_lsb;
1418         AH_Edge  edge1 = outline->vert_edges;     /* leftmost edge  */
1419         AH_Edge  edge2 = edge1 +
1420                          outline->num_vedges - 1; /* rightmost edge */
1421
1422
1423         old_advance = hinter->pp2.x;
1424         old_rsb     = old_advance - edge2->opos;
1425         old_lsb     = edge1->opos;
1426         new_lsb     = edge1->pos;
1427
1428         hinter->pp1.x = ( ( new_lsb    - old_lsb ) + 32 ) & -64;
1429         hinter->pp2.x = ( ( edge2->pos + old_rsb ) + 32 ) & -64;
1430
1431 #if 0
1432         /* try to fix certain bad advance computations */
1433         if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 )
1434           hinter->pp2.x += 64;
1435 #endif
1436       }
1437
1438       /* good, we simply add the glyph to our loader's base */
1439       ah_loader_add( gloader );
1440       break;
1441
1442     case FT_GLYPH_FORMAT_COMPOSITE:
1443       {
1444         FT_UInt      nn, num_subglyphs = slot->num_subglyphs;
1445         FT_UInt      num_base_subgs, start_point;
1446         FT_SubGlyph  subglyph;
1447
1448
1449         start_point   = gloader->base.outline.n_points;
1450
1451         /* first of all, copy the subglyph descriptors in the glyph loader */
1452         error = ah_loader_check_subglyphs( gloader, num_subglyphs );
1453         if ( error )
1454           goto Exit;
1455
1456         FT_MEM_COPY( gloader->current.subglyphs, slot->subglyphs,
1457                      num_subglyphs * sizeof ( FT_SubGlyph ) );
1458
1459         gloader->current.num_subglyphs = num_subglyphs;
1460         num_base_subgs = gloader->base.num_subglyphs;
1461
1462         /* now, read each subglyph independently */
1463         for ( nn = 0; nn < num_subglyphs; nn++ )
1464         {
1465           FT_Vector  pp1, pp2;
1466           FT_Pos     x, y;
1467           FT_UInt    num_points, num_new_points, num_base_points;
1468
1469
1470           /* gloader.current.subglyphs can change during glyph loading due */
1471           /* to re-allocation -- we must recompute the current subglyph on */
1472           /* each iteration                                                */
1473           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1474
1475           pp1 = hinter->pp1;
1476           pp2 = hinter->pp2;
1477
1478           num_base_points = gloader->base.outline.n_points;
1479
1480           error = ah_hinter_load( hinter, subglyph->index,
1481                                   load_flags, depth + 1 );
1482           if ( error )
1483             goto Exit;
1484
1485           /* recompute subglyph pointer */
1486           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1487
1488           if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
1489           {
1490             pp1 = hinter->pp1;
1491             pp2 = hinter->pp2;
1492           }
1493           else
1494           {
1495             hinter->pp1 = pp1;
1496             hinter->pp2 = pp2;
1497           }
1498
1499           num_points     = gloader->base.outline.n_points;
1500           num_new_points = num_points - num_base_points;
1501
1502           /* now perform the transform required for this subglyph */
1503
1504           if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE    |
1505                                    FT_SUBGLYPH_FLAG_XY_SCALE |
1506                                    FT_SUBGLYPH_FLAG_2X2      ) )
1507           {
1508             FT_Vector*  cur   = gloader->base.outline.points +
1509                                 num_base_points;
1510             FT_Vector*  org   = gloader->base.extra_points +
1511                                 num_base_points;
1512             FT_Vector*  limit = cur + num_new_points;
1513
1514
1515             for ( ; cur < limit; cur++, org++ )
1516             {
1517               FT_Vector_Transform( cur, &subglyph->transform );
1518               FT_Vector_Transform( org, &subglyph->transform );
1519             }
1520           }
1521
1522           /* apply offset */
1523
1524           if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
1525           {
1526             FT_Int      k = subglyph->arg1;
1527             FT_UInt     l = subglyph->arg2;
1528             FT_Vector*  p1;
1529             FT_Vector*  p2;
1530
1531
1532             if ( start_point + k >= num_base_points          ||
1533                                l >= (FT_UInt)num_new_points  )
1534             {
1535               error = AH_Err_Invalid_Composite;
1536               goto Exit;
1537             }
1538
1539             l += num_base_points;
1540
1541             /* for now, only use the current point coordinates     */
1542             /* we may consider another approach in the near future */
1543             p1 = gloader->base.outline.points + start_point + k;
1544             p2 = gloader->base.outline.points + start_point + l;
1545
1546             x = p1->x - p2->x;
1547             y = p1->y - p2->y;
1548           }
1549           else
1550           {
1551             x = FT_MulFix( subglyph->arg1, x_scale );
1552             y = FT_MulFix( subglyph->arg2, y_scale );
1553
1554             x = ( x + 32 ) & -64;
1555             y = ( y + 32 ) & -64;
1556           }
1557
1558           {
1559             FT_Outline  dummy = gloader->base.outline;
1560
1561
1562             dummy.points  += num_base_points;
1563             dummy.n_points = (short)num_new_points;
1564
1565             FT_Outline_Translate( &dummy, x, y );
1566           }
1567         }
1568       }
1569       break;
1570
1571     default:
1572       /* we don't support other formats (yet?) */
1573       error = AH_Err_Unimplemented_Feature;
1574     }
1575
1576   Hint_Metrics:
1577     if ( depth == 0 )
1578     {
1579       FT_BBox  bbox;
1580
1581
1582       /* transform the hinted outline if needed */
1583       if ( hinter->transformed )
1584         FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
1585
1586       /* we must translate our final outline by -pp1.x, and compute */
1587       /* the new metrics                                            */
1588       if ( hinter->pp1.x )
1589         FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
1590
1591       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
1592       bbox.xMin &= -64;
1593       bbox.yMin &= -64;
1594       bbox.xMax  = ( bbox.xMax + 63 ) & -64;
1595       bbox.yMax  = ( bbox.yMax + 63 ) & -64;
1596
1597       slot->metrics.width        = bbox.xMax - bbox.xMin;
1598       slot->metrics.height       = bbox.yMax - bbox.yMin;
1599       slot->metrics.horiBearingX = bbox.xMin;
1600       slot->metrics.horiBearingY = bbox.yMax;
1601
1602       /* for mono-width fonts (like Andale, Courier, etc.), we need */
1603       /* to keep the original rounded advance width                 */
1604       if ( !FT_IS_FIXED_WIDTH( slot->face ) )
1605         slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
1606       else
1607         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
1608                                                x_scale );
1609
1610       slot->metrics.horiAdvance = ( slot->metrics.horiAdvance + 32 ) & -64;
1611
1612       /* now copy outline into glyph slot */
1613       ah_loader_rewind( slot->internal->loader );
1614       error = ah_loader_copy_points( slot->internal->loader, gloader );
1615       if ( error )
1616         goto Exit;
1617
1618       slot->outline = slot->internal->loader->base.outline;
1619       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
1620     }
1621
1622 #ifdef DEBUG_HINTER
1623     ah_debug_hinter = hinter;
1624 #endif
1625
1626   Exit:
1627     return error;
1628   }
1629
1630
1631   /* load and hint a given glyph */
1632   FT_LOCAL_DEF( FT_Error )
1633   ah_hinter_load_glyph( AH_Hinter     hinter,
1634                         FT_GlyphSlot  slot,
1635                         FT_Size       size,
1636                         FT_UInt       glyph_index,
1637                         FT_Int32      load_flags )
1638   {
1639     FT_Face          face         = slot->face;
1640     FT_Error         error;
1641     FT_Fixed         x_scale      = size->metrics.x_scale;
1642     FT_Fixed         y_scale      = size->metrics.y_scale;
1643     AH_Face_Globals  face_globals = FACE_GLOBALS( face );
1644     FT_Render_Mode   hint_mode    = FT_LOAD_TARGET_MODE(load_flags);
1645
1646
1647     /* first of all, we need to check that we're using the correct face and */
1648     /* global hints to load the glyph                                       */
1649     if ( hinter->face != face || hinter->globals != face_globals )
1650     {
1651       hinter->face = face;
1652       if ( !face_globals )
1653       {
1654         error = ah_hinter_new_face_globals( hinter, face, 0 );
1655         if ( error )
1656           goto Exit;
1657
1658       }
1659       hinter->globals = FACE_GLOBALS( face );
1660       face_globals    = FACE_GLOBALS( face );
1661
1662     }
1663
1664 #ifdef FT_CONFIG_CHESTER_BLUE_SCALE
1665    /* try to optimize the y_scale so that the top of non-capital letters
1666     * is aligned on a pixel boundary whenever possible
1667     */
1668     {
1669       AH_Globals  design = &face_globals->design;
1670       FT_Pos      shoot  = design->blue_shoots[ AH_BLUE_SMALL_TOP ];
1671
1672      /* the value of 'shoot' will be -1000 if the font doesn't have */
1673      /* small latin letters; we simply check the sign here...       */
1674       if ( shoot > 0 )
1675       {
1676         FT_Pos  scaled = FT_MulFix( shoot, y_scale );
1677         FT_Pos  fitted = ( scaled + 32 ) & -64;
1678
1679         if ( scaled != fitted )
1680         {
1681          /* adjust y_scale
1682           */
1683           y_scale = FT_MulDiv( y_scale, fitted, scaled );
1684
1685          /* adust x_scale
1686           */
1687           if ( fitted < scaled )
1688             x_scale -= x_scale/50;  /* x_scale*0.98 with integers */
1689         }
1690       }
1691     }
1692 #endif /* FT_CONFIG_CHESTER_BLUE_SCALE */
1693
1694     /* now, we must check the current character pixel size to see if we */
1695     /* need to rescale the global metrics                               */
1696     if ( face_globals->x_scale != x_scale ||
1697          face_globals->y_scale != y_scale )
1698       ah_hinter_scale_globals( hinter, x_scale, y_scale );
1699
1700     ah_loader_rewind( hinter->loader );
1701
1702     /* reset hinting flags according to load flags and current render target */
1703     hinter->do_horz_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1704     hinter->do_vert_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1705
1706 #ifdef DEBUG_HINTER
1707     hinter->do_horz_hints = !ah_debug_disable_vert;  /* not a bug, the meaning */
1708     hinter->do_vert_hints = !ah_debug_disable_horz;  /* of h/v is inverted!    */
1709 #endif
1710
1711     /* we snap the width of vertical stems for the monochrome and         */
1712     /* horizontal LCD rendering targets only.  Corresponds to X snapping. */
1713     hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1714                                         hint_mode == FT_RENDER_MODE_LCD  );
1715
1716     /* we snap the width of horizontal stems for the monochrome and     */
1717     /* vertical LCD rendering targets only.  Corresponds to Y snapping. */
1718     hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO   ||
1719                                         hint_mode == FT_RENDER_MODE_LCD_V  );
1720
1721     hinter->do_stem_adjust   = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
1722
1723 #if 1
1724     load_flags  = FT_LOAD_NO_SCALE
1725                 | FT_LOAD_IGNORE_TRANSFORM ;
1726 #else
1727     load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
1728 #endif
1729
1730     error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1731
1732   Exit:
1733     return error;
1734   }
1735
1736
1737   /* retrieve a face's autohint globals for client applications */
1738   FT_LOCAL_DEF( void )
1739   ah_hinter_get_global_hints( AH_Hinter  hinter,
1740                               FT_Face    face,
1741                               void**     global_hints,
1742                               long*      global_len )
1743   {
1744     AH_Globals  globals = 0;
1745     FT_Memory   memory  = hinter->memory;
1746     FT_Error    error;
1747
1748
1749     /* allocate new master globals */
1750     if ( FT_NEW( globals ) )
1751       goto Fail;
1752
1753     /* compute face globals if needed */
1754     if ( !FACE_GLOBALS( face ) )
1755     {
1756       error = ah_hinter_new_face_globals( hinter, face, 0 );
1757       if ( error )
1758         goto Fail;
1759     }
1760
1761     *globals      = FACE_GLOBALS( face )->design;
1762     *global_hints = globals;
1763     *global_len   = sizeof( *globals );
1764
1765     return;
1766
1767   Fail:
1768     FT_FREE( globals );
1769
1770     *global_hints = 0;
1771     *global_len   = 0;
1772   }
1773
1774
1775   FT_LOCAL_DEF( void )
1776   ah_hinter_done_global_hints( AH_Hinter  hinter,
1777                                void*      global_hints )
1778   {
1779     FT_Memory  memory = hinter->memory;
1780
1781
1782     FT_FREE( global_hints );
1783   }
1784
1785
1786 /* END */