Bringing apdf from vendor into main branch.
[AROS-Contrib.git] / apdf / freetype2 / autohint / ahhint.c
blobbe5c0a2a9dfca962730ccff6ba204f4e147f3078
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 /***************************************************************************/
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
30 #define FACE_GLOBALS( face ) ((AH_Face_Globals*)(face)->autohint.data)
32 #define AH_USE_IUP
35 /*************************************************************************/
36 /*************************************************************************/
37 /**** ****/
38 /**** Hinting routines ****/
39 /**** ****/
40 /*************************************************************************/
41 /*************************************************************************/
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 )
51 int n;
52 FT_Pos best = 64 + 32 + 2;
53 FT_Pos reference = width;
56 for ( n = 0; n < count; n++ )
58 FT_Pos w;
59 FT_Pos dist;
62 w = widths[n];
63 dist = width - w;
64 if ( dist < 0 )
65 dist = -dist;
66 if ( dist < best )
68 best = dist;
69 reference = w;
73 if ( width >= reference )
75 width -= 0x21;
76 if ( width < reference )
77 width = reference;
79 else
81 width += 0x21;
82 if ( width > reference )
83 width = reference;
86 return width;
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 )
97 FT_Pos dist = stem_edge->opos - base_edge->opos;
98 AH_Globals* globals = &hinter->globals->scaled;
99 FT_Pos sign = 1;
102 if ( dist < 0 )
104 dist = -dist;
105 sign = -1;
108 if ( vertical )
110 dist = ah_snap_width( globals->heights, globals->num_heights, dist );
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;
119 else
121 dist = ah_snap_width( globals->widths, globals->num_widths, dist );
123 if ( hinter->flags & ah_hinter_monochrome )
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;
132 else
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;
140 else if ( dist < 128 )
141 dist = ( dist + 42 ) & -64;
142 else
143 /* XXX: round otherwise, prevent color fringes in LCD mode */
144 dist = ( dist + 32 ) & -64;
148 stem_edge->pos = base_edge->pos + sign * dist;
152 static void
153 ah_align_serif_edge( AH_Hinter* hinter,
154 AH_Edge* base,
155 AH_Edge* serif,
156 int vertical )
158 FT_Pos dist;
159 FT_Pos sign = 1;
161 FT_UNUSED( hinter );
164 dist = serif->opos - base->opos;
165 if ( dist < 0 )
167 dist = -dist;
168 sign = -1;
171 /* do not strengthen serifs */
172 if ( base->flags & ah_edge_done )
174 if ( dist >= 64 )
175 dist = (dist+8) & -64;
177 else if ( dist <= 32 && !vertical )
178 dist = ( dist + 33 ) >> 1;
179 else
180 dist = 0;
183 serif->pos = base->pos + sign * dist;
187 /*************************************************************************/
188 /*************************************************************************/
189 /*************************************************************************/
190 /**** ****/
191 /**** E D G E H I N T I N G ****/
192 /**** ****/
193 /*************************************************************************/
194 /*************************************************************************/
195 /*************************************************************************/
198 /* Another alternative edge hinting algorithm */
199 static void
200 ah_hint_edges_3( AH_Hinter* hinter )
202 AH_Edge* edges;
203 AH_Edge* edge_limit;
204 AH_Outline* outline = hinter->glyph;
205 FT_Int dimension;
208 edges = outline->horz_edges;
209 edge_limit = edges + outline->num_hedges;
211 for ( dimension = 1; dimension >= 0; dimension-- )
213 AH_Edge* edge;
214 AH_Edge* anchor = 0;
215 int has_serifs = 0;
218 if ( ah_debug_disable_vert && !dimension )
219 goto Next_Dimension;
221 if ( ah_debug_disable_horz && dimension )
222 goto Next_Dimension;
224 /* we begin by aligning all stems relative to the blue zone */
225 /* if needed -- that's only for horizontal edges */
226 if ( dimension )
228 for ( edge = edges; edge < edge_limit; edge++ )
230 FT_Pos* blue;
231 AH_Edge *edge1, *edge2;
234 if ( edge->flags & ah_edge_done )
235 continue;
237 blue = edge->blue_edge;
238 edge1 = 0;
239 edge2 = edge->link;
241 if ( blue )
243 edge1 = edge;
245 else if (edge2 && edge2->blue_edge)
247 blue = edge2->blue_edge;
248 edge1 = edge2;
249 edge2 = edge;
252 if ( !edge1 )
253 continue;
255 edge1->pos = blue[0];
256 edge1->flags |= ah_edge_done;
258 if ( edge2 && !edge2->blue_edge )
260 ah_align_linked_edge( hinter, edge1, edge2, dimension );
261 edge2->flags |= ah_edge_done;
264 if ( !anchor )
265 anchor = edge;
269 /* now, we will align all stem edges, trying to maintain the */
270 /* relative order of stems in the glyph.. */
271 for ( edge = edges; edge < edge_limit; edge++ )
273 AH_Edge *edge2;
276 if ( edge->flags & ah_edge_done )
277 continue;
279 /* skip all non-stem edges */
280 edge2 = edge->link;
281 if ( !edge2 )
283 has_serifs++;
284 continue;
287 /* now, align the stem */
289 /* this should not happen, but it's better to be safe.. */
290 if ( edge2->blue_edge || edge2 < edge )
293 #if 0
294 printf( "strange blue alignement, edge %d to %d\n",
295 edge - edges, edge2 - edges );
296 #endif
298 ah_align_linked_edge( hinter, edge2, edge, dimension );
299 edge->flags |= ah_edge_done;
300 continue;
304 FT_Bool min = 0;
305 FT_Pos delta;
307 if ( !anchor )
309 edge->pos = ( edge->opos + 32 ) & -64;
310 anchor = edge;
312 else
313 edge->pos = anchor->pos +
314 ( ( edge->opos - anchor->opos + 32 ) & -64 );
316 edge->flags |= ah_edge_done;
318 if ( edge > edges && edge->pos < edge[-1].pos )
320 edge->pos = edge[-1].pos;
321 min = 1;
324 ah_align_linked_edge( hinter, edge, edge2, dimension );
325 delta = 0;
326 if ( edge2 + 1 < edge_limit &&
327 edge2[1].flags & ah_edge_done )
328 delta = edge2[1].pos - edge2->pos;
330 if ( delta < 0 )
332 edge2->pos += delta;
333 if ( !min )
334 edge->pos += delta;
336 edge2->flags |= ah_edge_done;
340 if ( !has_serifs )
341 goto Next_Dimension;
343 /* now, hint the remaining edges (serifs and single) in order */
344 /* to complete our processing */
345 for ( edge = edges; edge < edge_limit; edge++ )
347 if ( edge->flags & ah_edge_done )
348 continue;
350 if ( edge->serif )
352 ah_align_serif_edge( hinter, edge->serif, edge, dimension );
354 else if ( !anchor )
356 edge->pos = ( edge->opos + 32 ) & -64;
357 anchor = edge;
359 else
360 edge->pos = anchor->pos +
361 ( ( edge->opos-anchor->opos + 32 ) & -64 );
363 edge->flags |= ah_edge_done;
365 if ( edge > edges && edge->pos < edge[-1].pos )
366 edge->pos = edge[-1].pos;
368 if ( edge + 1 < edge_limit &&
369 edge[1].flags & ah_edge_done &&
370 edge->pos > edge[1].pos )
371 edge->pos = edge[1].pos;
374 Next_Dimension:
375 edges = outline->vert_edges;
376 edge_limit = edges + outline->num_vedges;
381 FT_LOCAL_DEF( void )
382 ah_hinter_hint_edges( AH_Hinter* hinter,
383 FT_Bool no_horz_edges,
384 FT_Bool no_vert_edges )
386 #if 0
387 ah_debug_disable_horz = no_horz_edges;
388 ah_debug_disable_vert = no_vert_edges;
389 #else
390 FT_UNUSED( no_horz_edges );
391 FT_UNUSED( no_vert_edges );
392 #endif
393 /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */
394 /* reduce the problem of the disappearing eye in the `e' of Times... */
395 /* also, creates some artifacts near the blue zones? */
397 ah_hint_edges_3( hinter );
399 #if 0
400 /* outline optimizer removed temporarily */
401 if ( hinter->flags & ah_hinter_optimize )
403 AH_Optimizer opt;
406 if ( !AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ) )
408 AH_Optimizer_Compute( &opt );
409 AH_Optimizer_Done( &opt );
412 #endif
418 /*************************************************************************/
419 /*************************************************************************/
420 /*************************************************************************/
421 /**** ****/
422 /**** P O I N T H I N T I N G ****/
423 /**** ****/
424 /*************************************************************************/
425 /*************************************************************************/
426 /*************************************************************************/
428 static void
429 ah_hinter_align_edge_points( AH_Hinter* hinter )
431 AH_Outline* outline = hinter->glyph;
432 AH_Edge* edges;
433 AH_Edge* edge_limit;
434 FT_Int dimension;
437 edges = outline->horz_edges;
438 edge_limit = edges + outline->num_hedges;
440 for ( dimension = 1; dimension >= 0; dimension-- )
442 AH_Edge* edge;
445 edge = edges;
446 for ( ; edge < edge_limit; edge++ )
448 /* move the points of each segment */
449 /* in each edge to the edge's position */
450 AH_Segment* seg = edge->first;
455 AH_Point* point = seg->first;
458 for (;;)
460 if ( dimension )
462 point->y = edge->pos;
463 point->flags |= ah_flag_touch_y;
465 else
467 point->x = edge->pos;
468 point->flags |= ah_flag_touch_x;
471 if ( point == seg->last )
472 break;
474 point = point->next;
477 seg = seg->edge_next;
479 } while ( seg != edge->first );
482 edges = outline->vert_edges;
483 edge_limit = edges + outline->num_vedges;
488 /* hint the strong points -- this is equivalent to the TrueType `IP' */
489 static void
490 ah_hinter_align_strong_points( AH_Hinter* hinter )
492 AH_Outline* outline = hinter->glyph;
493 FT_Int dimension;
494 AH_Edge* edges;
495 AH_Edge* edge_limit;
496 AH_Point* points;
497 AH_Point* point_limit;
498 AH_Flags touch_flag;
501 points = outline->points;
502 point_limit = points + outline->num_points;
504 edges = outline->horz_edges;
505 edge_limit = edges + outline->num_hedges;
506 touch_flag = ah_flag_touch_y;
508 for ( dimension = 1; dimension >= 0; dimension-- )
510 AH_Point* point;
511 AH_Edge* edge;
514 if ( edges < edge_limit )
515 for ( point = points; point < point_limit; point++ )
517 FT_Pos u, ou, fu; /* point position */
518 FT_Pos delta;
521 if ( point->flags & touch_flag )
522 continue;
524 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
525 /* if this point is candidate to weak interpolation, we will */
526 /* interpolate it after all strong points have been processed */
527 if ( point->flags & ah_flag_weak_interpolation )
528 continue;
529 #endif
531 if ( dimension )
533 u = point->fy;
534 ou = point->oy;
536 else
538 u = point->fx;
539 ou = point->ox;
542 fu = u;
544 /* is the point before the first edge? */
545 edge = edges;
546 delta = edge->fpos - u;
547 if ( delta >= 0 )
549 u = edge->pos - ( edge->opos - ou );
550 goto Store_Point;
553 /* is the point after the last edge ? */
554 edge = edge_limit - 1;
555 delta = u - edge->fpos;
556 if ( delta >= 0 )
558 u = edge->pos + ( ou - edge->opos );
559 goto Store_Point;
562 /* otherwise, interpolate the point in between */
564 AH_Edge* before = 0;
565 AH_Edge* after = 0;
568 for ( edge = edges; edge < edge_limit; edge++ )
570 if ( u == edge->fpos )
572 u = edge->pos;
573 goto Store_Point;
575 if ( u < edge->fpos )
576 break;
577 before = edge;
580 for ( edge = edge_limit - 1; edge >= edges; edge-- )
582 if ( u == edge->fpos )
584 u = edge->pos;
585 goto Store_Point;
587 if ( u > edge->fpos )
588 break;
589 after = edge;
592 /* assert( before && after && before != after ) */
593 u = before->pos + FT_MulDiv( fu - before->fpos,
594 after->pos - before->pos,
595 after->fpos - before->fpos );
598 Store_Point:
600 /* save the point position */
601 if ( dimension )
602 point->y = u;
603 else
604 point->x = u;
606 point->flags |= touch_flag;
609 edges = outline->vert_edges;
610 edge_limit = edges + outline->num_vedges;
611 touch_flag = ah_flag_touch_x;
616 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
618 static void
619 ah_iup_shift( AH_Point* p1,
620 AH_Point* p2,
621 AH_Point* ref )
623 AH_Point* p;
624 FT_Pos delta = ref->u - ref->v;
627 for ( p = p1; p < ref; p++ )
628 p->u = p->v + delta;
630 for ( p = ref + 1; p <= p2; p++ )
631 p->u = p->v + delta;
635 static void
636 ah_iup_interp( AH_Point* p1,
637 AH_Point* p2,
638 AH_Point* ref1,
639 AH_Point* ref2 )
641 AH_Point* p;
642 FT_Pos u;
643 FT_Pos v1 = ref1->v;
644 FT_Pos v2 = ref2->v;
645 FT_Pos d1 = ref1->u - v1;
646 FT_Pos d2 = ref2->u - v2;
649 if ( p1 > p2 )
650 return;
652 if ( v1 == v2 )
654 for ( p = p1; p <= p2; p++ )
656 u = p->v;
658 if ( u <= v1 )
659 u += d1;
660 else
661 u += d2;
663 p->u = u;
665 return;
668 if ( v1 < v2 )
670 for ( p = p1; p <= p2; p++ )
672 u = p->v;
674 if ( u <= v1 )
675 u += d1;
676 else if ( u >= v2 )
677 u += d2;
678 else
679 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
681 p->u = u;
684 else
686 for ( p = p1; p <= p2; p++ )
688 u = p->v;
690 if ( u <= v2 )
691 u += d2;
692 else if ( u >= v1 )
693 u += d1;
694 else
695 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
697 p->u = u;
703 /* interpolate weak points -- this is equivalent to the TrueType `IUP' */
704 static void
705 ah_hinter_align_weak_points( AH_Hinter* hinter )
707 AH_Outline* outline = hinter->glyph;
708 FT_Int dimension;
709 AH_Point* points;
710 AH_Point* point_limit;
711 AH_Point** contour_limit;
712 AH_Flags touch_flag;
715 points = outline->points;
716 point_limit = points + outline->num_points;
718 /* PASS 1: Move segment points to edge positions */
720 touch_flag = ah_flag_touch_y;
722 contour_limit = outline->contours + outline->num_contours;
724 ah_setup_uv( outline, ah_uv_oy );
726 for ( dimension = 1; dimension >= 0; dimension-- )
728 AH_Point* point;
729 AH_Point* end_point;
730 AH_Point* first_point;
731 AH_Point** contour;
734 point = points;
735 contour = outline->contours;
737 for ( ; contour < contour_limit; contour++ )
739 point = *contour;
740 end_point = point->prev;
741 first_point = point;
743 while ( point <= end_point && !( point->flags & touch_flag ) )
744 point++;
746 if ( point <= end_point )
748 AH_Point* first_touched = point;
749 AH_Point* cur_touched = point;
752 point++;
753 while ( point <= end_point )
755 if ( point->flags & touch_flag )
757 /* we found two successive touched points; we interpolate */
758 /* all contour points between them */
759 ah_iup_interp( cur_touched + 1, point - 1,
760 cur_touched, point );
761 cur_touched = point;
763 point++;
766 if ( cur_touched == first_touched )
768 /* this is a special case: only one point was touched in the */
769 /* contour; we thus simply shift the whole contour */
770 ah_iup_shift( first_point, end_point, cur_touched );
772 else
774 /* now interpolate after the last touched point to the end */
775 /* of the contour */
776 ah_iup_interp( cur_touched + 1, end_point,
777 cur_touched, first_touched );
779 /* if the first contour point isn't touched, interpolate */
780 /* from the contour start to the first touched point */
781 if ( first_touched > points )
782 ah_iup_interp( first_point, first_touched - 1,
783 cur_touched, first_touched );
788 /* now save the interpolated values back to x/y */
789 if ( dimension )
791 for ( point = points; point < point_limit; point++ )
792 point->y = point->u;
794 touch_flag = ah_flag_touch_x;
795 ah_setup_uv( outline, ah_uv_ox );
797 else
799 for ( point = points; point < point_limit; point++ )
800 point->x = point->u;
802 break; /* exit loop */
807 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
810 FT_LOCAL_DEF( void )
811 ah_hinter_align_points( AH_Hinter* hinter )
813 ah_hinter_align_edge_points( hinter );
815 #ifndef AH_OPTION_NO_STRONG_INTERPOLATION
816 ah_hinter_align_strong_points( hinter );
817 #endif
819 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
820 ah_hinter_align_weak_points( hinter );
821 #endif
825 /*************************************************************************/
826 /*************************************************************************/
827 /*************************************************************************/
828 /**** ****/
829 /**** H I N T E R O B J E C T M E T H O D S ****/
830 /**** ****/
831 /*************************************************************************/
832 /*************************************************************************/
833 /*************************************************************************/
836 /* scale and fit the global metrics */
837 static void
838 ah_hinter_scale_globals( AH_Hinter* hinter,
839 FT_Fixed x_scale,
840 FT_Fixed y_scale )
842 FT_Int n;
843 AH_Face_Globals* globals = hinter->globals;
844 AH_Globals* design = &globals->design;
845 AH_Globals* scaled = &globals->scaled;
848 /* copy content */
849 *scaled = *design;
851 /* scale the standard widths & heights */
852 for ( n = 0; n < design->num_widths; n++ )
853 scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
855 for ( n = 0; n < design->num_heights; n++ )
856 scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
858 /* scale the blue zones */
859 for ( n = 0; n < ah_blue_max; n++ )
861 FT_Pos delta, delta2;
864 delta = design->blue_shoots[n] - design->blue_refs[n];
865 delta2 = delta;
866 if ( delta < 0 )
867 delta2 = -delta2;
868 delta2 = FT_MulFix( delta2, y_scale );
870 if ( delta2 < 32 )
871 delta2 = 0;
872 else if ( delta2 < 64 )
873 delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 );
874 else
875 delta2 = ( delta2 + 32 ) & -64;
877 if ( delta < 0 )
878 delta2 = -delta2;
880 scaled->blue_refs[n] =
881 ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64;
882 scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
885 globals->x_scale = x_scale;
886 globals->y_scale = y_scale;
890 static void
891 ah_hinter_align( AH_Hinter* hinter )
893 ah_hinter_align_edge_points( hinter );
894 ah_hinter_align_points( hinter );
898 /* finalize a hinter object */
899 FT_LOCAL_DEF( void )
900 ah_hinter_done( AH_Hinter* hinter )
902 if ( hinter )
904 FT_Memory memory = hinter->memory;
907 ah_loader_done( hinter->loader );
908 ah_outline_done( hinter->glyph );
910 /* note: the `globals' pointer is _not_ owned by the hinter */
911 /* but by the current face object, we don't need to */
912 /* release it */
913 hinter->globals = 0;
914 hinter->face = 0;
916 FT_FREE( hinter );
921 /* create a new empty hinter object */
922 FT_LOCAL_DEF( FT_Error )
923 ah_hinter_new( FT_Library library,
924 AH_Hinter** ahinter )
926 AH_Hinter* hinter = 0;
927 FT_Memory memory = library->memory;
928 FT_Error error;
931 *ahinter = 0;
933 /* allocate object */
934 if ( FT_NEW( hinter ) )
935 goto Exit;
937 hinter->memory = memory;
938 hinter->flags = 0;
940 /* allocate outline and loader */
941 error = ah_outline_new( memory, &hinter->glyph ) ||
942 ah_loader_new ( memory, &hinter->loader ) ||
943 ah_loader_create_extra( hinter->loader );
944 if ( error )
945 goto Exit;
947 *ahinter = hinter;
949 Exit:
950 if ( error )
951 ah_hinter_done( hinter );
953 return error;
957 /* create a face's autohint globals */
958 FT_LOCAL_DEF( FT_Error )
959 ah_hinter_new_face_globals( AH_Hinter* hinter,
960 FT_Face face,
961 AH_Globals* globals )
963 FT_Error error;
964 FT_Memory memory = hinter->memory;
965 AH_Face_Globals* face_globals;
968 if ( FT_NEW( face_globals ) )
969 goto Exit;
971 hinter->face = face;
972 hinter->globals = face_globals;
974 if ( globals )
975 face_globals->design = *globals;
976 else
977 ah_hinter_compute_globals( hinter );
979 face->autohint.data = face_globals;
980 face->autohint.finalizer = (FT_Generic_Finalizer)
981 ah_hinter_done_face_globals;
982 face_globals->face = face;
984 Exit:
985 return error;
989 /* discard a face's autohint globals */
990 FT_LOCAL_DEF( void )
991 ah_hinter_done_face_globals( AH_Face_Globals* globals )
993 FT_Face face = globals->face;
994 FT_Memory memory = face->memory;
997 FT_FREE( globals );
1001 static FT_Error
1002 ah_hinter_load( AH_Hinter* hinter,
1003 FT_UInt glyph_index,
1004 FT_UInt load_flags,
1005 FT_UInt depth )
1007 FT_Face face = hinter->face;
1008 FT_GlyphSlot slot = face->glyph;
1009 FT_Slot_Internal internal = slot->internal;
1010 FT_Fixed x_scale = face->size->metrics.x_scale;
1011 FT_Fixed y_scale = face->size->metrics.y_scale;
1012 FT_Error error;
1013 AH_Outline* outline = hinter->glyph;
1014 AH_Loader gloader = hinter->loader;
1015 FT_Bool no_horz_hints = FT_BOOL(
1016 ( load_flags & AH_HINT_NO_HORZ_EDGES ) != 0 );
1017 FT_Bool no_vert_hints = FT_BOOL(
1018 ( load_flags & AH_HINT_NO_VERT_EDGES ) != 0 );
1021 /* load the glyph */
1022 error = FT_Load_Glyph( face, glyph_index, load_flags );
1023 if ( error )
1024 goto Exit;
1026 /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
1027 hinter->transformed = internal->glyph_transformed;
1029 if ( hinter->transformed )
1031 FT_Matrix imatrix;
1033 imatrix = internal->glyph_matrix;
1034 hinter->trans_delta = internal->glyph_delta;
1035 hinter->trans_matrix = imatrix;
1037 FT_Matrix_Invert( &imatrix );
1038 FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1041 /* set linear horizontal metrics */
1042 slot->linearHoriAdvance = slot->metrics.horiAdvance;
1043 slot->linearVertAdvance = slot->metrics.vertAdvance;
1045 switch ( slot->format )
1047 case ft_glyph_format_outline:
1049 /* translate glyph outline if we need to */
1050 if ( hinter->transformed )
1052 FT_UInt n = slot->outline.n_points;
1053 FT_Vector* point = slot->outline.points;
1056 for ( ; n > 0; point++, n-- )
1058 point->x += hinter->trans_delta.x;
1059 point->y += hinter->trans_delta.y;
1063 /* copy the outline points in the loader's current */
1064 /* extra points, which is used to keep original glyph coordinates */
1065 error = ah_loader_check_points( gloader, slot->outline.n_points + 2,
1066 slot->outline.n_contours );
1067 if ( error )
1068 goto Exit;
1070 FT_MEM_COPY( gloader->current.extra_points, slot->outline.points,
1071 slot->outline.n_points * sizeof ( FT_Vector ) );
1073 FT_MEM_COPY( gloader->current.outline.contours, slot->outline.contours,
1074 slot->outline.n_contours * sizeof ( short ) );
1076 FT_MEM_COPY( gloader->current.outline.tags, slot->outline.tags,
1077 slot->outline.n_points * sizeof ( char ) );
1079 gloader->current.outline.n_points = slot->outline.n_points;
1080 gloader->current.outline.n_contours = slot->outline.n_contours;
1082 /* compute original phantom points */
1083 hinter->pp1.x = 0;
1084 hinter->pp1.y = 0;
1085 hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
1086 hinter->pp2.y = 0;
1088 /* be sure to check for spacing glyphs */
1089 if ( slot->outline.n_points == 0 )
1090 goto Hint_Metrics;
1092 /* now, load the slot image into the auto-outline, and run the */
1093 /* automatic hinting process */
1094 error = ah_outline_load( outline, face ); /* XXX: change to slot */
1095 if ( error )
1096 goto Exit;
1098 /* perform feature detection */
1099 ah_outline_detect_features( outline );
1101 if ( !no_horz_hints )
1103 ah_outline_compute_blue_edges( outline, hinter->globals );
1104 ah_outline_scale_blue_edges( outline, hinter->globals );
1107 /* perform alignment control */
1108 ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
1109 ah_hinter_align( hinter );
1111 /* now save the current outline into the loader's current table */
1112 ah_outline_save( outline, gloader );
1114 /* we now need to hint the metrics according to the change in */
1115 /* width/positioning that occured during the hinting process */
1117 FT_Pos old_advance, old_rsb, old_lsb, new_lsb;
1118 AH_Edge* edge1 = outline->vert_edges; /* leftmost edge */
1119 AH_Edge* edge2 = edge1 +
1120 outline->num_vedges - 1; /* rightmost edge */
1123 old_advance = hinter->pp2.x;
1124 old_rsb = old_advance - edge2->opos;
1125 old_lsb = edge1->opos;
1126 new_lsb = edge1->pos;
1128 hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64;
1129 hinter->pp2.x = ( ( edge2->pos + old_rsb ) + 32 ) & -64;
1131 /* try to fix certain bad advance computations */
1132 if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 )
1133 hinter->pp2.x += 64;
1136 /* good, we simply add the glyph to our loader's base */
1137 ah_loader_add( gloader );
1138 break;
1140 case ft_glyph_format_composite:
1142 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
1143 FT_UInt num_base_subgs, start_point;
1144 FT_SubGlyph subglyph;
1147 start_point = gloader->base.outline.n_points;
1149 /* first of all, copy the subglyph descriptors in the glyph loader */
1150 error = ah_loader_check_subglyphs( gloader, num_subglyphs );
1151 if ( error )
1152 goto Exit;
1154 FT_MEM_COPY( gloader->current.subglyphs, slot->subglyphs,
1155 num_subglyphs * sizeof ( FT_SubGlyph ) );
1157 gloader->current.num_subglyphs = num_subglyphs;
1158 num_base_subgs = gloader->base.num_subglyphs;
1160 /* now, read each subglyph independently */
1161 for ( nn = 0; nn < num_subglyphs; nn++ )
1163 FT_Vector pp1, pp2;
1164 FT_Pos x, y;
1165 FT_UInt num_points, num_new_points, num_base_points;
1168 /* gloader.current.subglyphs can change during glyph loading due */
1169 /* to re-allocation -- we must recompute the current subglyph on */
1170 /* each iteration */
1171 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1173 pp1 = hinter->pp1;
1174 pp2 = hinter->pp2;
1176 num_base_points = gloader->base.outline.n_points;
1178 error = ah_hinter_load( hinter, subglyph->index,
1179 load_flags, depth + 1 );
1180 if ( error )
1181 goto Exit;
1183 /* recompute subglyph pointer */
1184 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1186 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
1188 pp1 = hinter->pp1;
1189 pp2 = hinter->pp2;
1191 else
1193 hinter->pp1 = pp1;
1194 hinter->pp2 = pp2;
1197 num_points = gloader->base.outline.n_points;
1198 num_new_points = num_points - num_base_points;
1200 /* now perform the transform required for this subglyph */
1202 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
1203 FT_SUBGLYPH_FLAG_XY_SCALE |
1204 FT_SUBGLYPH_FLAG_2X2 ) )
1206 FT_Vector* cur = gloader->base.outline.points +
1207 num_base_points;
1208 FT_Vector* org = gloader->base.extra_points +
1209 num_base_points;
1210 FT_Vector* limit = cur + num_new_points;
1213 for ( ; cur < limit; cur++, org++ )
1215 FT_Vector_Transform( cur, &subglyph->transform );
1216 FT_Vector_Transform( org, &subglyph->transform );
1220 /* apply offset */
1222 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
1224 FT_Int k = subglyph->arg1;
1225 FT_UInt l = subglyph->arg2;
1226 FT_Vector* p1;
1227 FT_Vector* p2;
1230 if ( start_point + k >= num_base_points ||
1231 l >= (FT_UInt)num_new_points )
1233 error = AH_Err_Invalid_Composite;
1234 goto Exit;
1237 l += num_base_points;
1239 /* for now, only use the current point coordinates */
1240 /* we may consider another approach in the near future */
1241 p1 = gloader->base.outline.points + start_point + k;
1242 p2 = gloader->base.outline.points + start_point + l;
1244 x = p1->x - p2->x;
1245 y = p1->y - p2->y;
1247 else
1249 x = FT_MulFix( subglyph->arg1, x_scale );
1250 y = FT_MulFix( subglyph->arg2, y_scale );
1252 x = ( x + 32 ) & -64;
1253 y = ( y + 32 ) & -64;
1257 FT_Outline dummy = gloader->base.outline;
1260 dummy.points += num_base_points;
1261 dummy.n_points = (short)num_new_points;
1263 FT_Outline_Translate( &dummy, x, y );
1267 break;
1269 default:
1270 /* we don't support other formats (yet?) */
1271 error = AH_Err_Unimplemented_Feature;
1274 Hint_Metrics:
1275 if ( depth == 0 )
1277 FT_BBox bbox;
1280 /* transform the hinted outline if needed */
1281 if ( hinter->transformed )
1282 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
1284 /* we must translate our final outline by -pp1.x, and compute */
1285 /* the new metrics */
1286 if ( hinter->pp1.x )
1287 FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
1289 FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
1290 bbox.xMin &= -64;
1291 bbox.yMin &= -64;
1292 bbox.xMax = ( bbox.xMax + 63 ) & -64;
1293 bbox.yMax = ( bbox.yMax + 63 ) & -64;
1295 slot->metrics.width = bbox.xMax - bbox.xMin;
1296 slot->metrics.height = bbox.yMax - bbox.yMin;
1297 slot->metrics.horiBearingX = bbox.xMin;
1298 slot->metrics.horiBearingY = bbox.yMax;
1300 /* for mono-width fonts (like Andale, Courier, etc.), we need */
1301 /* to keep the original rounded advance width */
1302 if ( !FT_IS_FIXED_WIDTH( slot->face ) )
1303 slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
1304 else
1305 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
1306 x_scale );
1308 slot->metrics.horiAdvance = ( slot->metrics.horiAdvance + 32 ) & -64;
1310 /* now copy outline into glyph slot */
1311 ah_loader_rewind( slot->internal->loader );
1312 error = ah_loader_copy_points( slot->internal->loader, gloader );
1313 if ( error )
1314 goto Exit;
1316 slot->outline = slot->internal->loader->base.outline;
1317 slot->format = ft_glyph_format_outline;
1320 #ifdef DEBUG_HINTER
1321 ah_debug_hinter = hinter;
1322 #endif
1324 Exit:
1325 return error;
1329 /* load and hint a given glyph */
1330 FT_LOCAL_DEF( FT_Error )
1331 ah_hinter_load_glyph( AH_Hinter* hinter,
1332 FT_GlyphSlot slot,
1333 FT_Size size,
1334 FT_UInt glyph_index,
1335 FT_Int load_flags )
1337 FT_Face face = slot->face;
1338 FT_Error error;
1339 FT_Fixed x_scale = size->metrics.x_scale;
1340 FT_Fixed y_scale = size->metrics.y_scale;
1341 AH_Face_Globals* face_globals = FACE_GLOBALS( face );
1344 /* first of all, we need to check that we're using the correct face and */
1345 /* global hints to load the glyph */
1346 if ( hinter->face != face || hinter->globals != face_globals )
1348 hinter->face = face;
1349 if ( !face_globals )
1351 error = ah_hinter_new_face_globals( hinter, face, 0 );
1352 if ( error )
1353 goto Exit;
1356 hinter->globals = FACE_GLOBALS( face );
1357 face_globals = FACE_GLOBALS( face );
1361 /* now, we must check the current character pixel size to see if we */
1362 /* need to rescale the global metrics */
1363 if ( face_globals->x_scale != x_scale ||
1364 face_globals->y_scale != y_scale )
1365 ah_hinter_scale_globals( hinter, x_scale, y_scale );
1367 ah_loader_rewind( hinter->loader );
1369 #if 1
1370 load_flags = FT_LOAD_NO_SCALE
1371 | FT_LOAD_IGNORE_TRANSFORM ;
1372 #else
1373 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
1374 #endif
1376 error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1378 Exit:
1379 return error;
1383 /* retrieve a face's autohint globals for client applications */
1384 FT_LOCAL_DEF( void )
1385 ah_hinter_get_global_hints( AH_Hinter* hinter,
1386 FT_Face face,
1387 void** global_hints,
1388 long* global_len )
1390 AH_Globals* globals = 0;
1391 FT_Memory memory = hinter->memory;
1392 FT_Error error;
1395 /* allocate new master globals */
1396 if ( FT_NEW( globals ) )
1397 goto Fail;
1399 /* compute face globals if needed */
1400 if ( !FACE_GLOBALS( face ) )
1402 error = ah_hinter_new_face_globals( hinter, face, 0 );
1403 if ( error )
1404 goto Fail;
1407 *globals = FACE_GLOBALS( face )->design;
1408 *global_hints = globals;
1409 *global_len = sizeof( *globals );
1411 return;
1413 Fail:
1414 FT_FREE( globals );
1416 *global_hints = 0;
1417 *global_len = 0;
1421 FT_LOCAL_DEF( void )
1422 ah_hinter_done_global_hints( AH_Hinter* hinter,
1423 void* global_hints )
1425 FT_Memory memory = hinter->memory;
1428 FT_FREE( global_hints );
1432 /* END */