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