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