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