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 /***************************************************************************/
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 )
50 {
51 int n;
52 FT_Pos best = 64 + 32 + 2;
53 FT_Pos reference = width;
56 for ( n = 0; n < count; n++ )
57 {
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 )
67 {
68 best = dist;
69 reference = w;
70 }
71 }
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 }
86 return width;
87 }
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;
102 if ( dist < 0 )
103 {
104 dist = -dist;
105 sign = -1;
106 }
108 if ( vertical )
109 {
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;
118 }
119 else
120 {
121 dist = ah_snap_width( globals->widths, globals->num_widths, dist );
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;
140 else if ( dist < 128 )
141 dist = ( dist + 42 ) & -64;
142 }
143 }
145 stem_edge->pos = base_edge->pos + sign * dist;
146 }
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;
157 UNUSED( hinter );
160 dist = serif->opos - base->opos;
161 if ( dist < 0 )
162 {
163 dist = -dist;
164 sign = -1;
165 }
167 /* do not strengthen serifs */
168 if ( base->flags & ah_edge_done )
169 {
170 if ( dist > 64 )
171 dist = ( dist + 16 ) & -64;
173 else if ( dist <= 32 )
174 dist = ( dist + 33 ) >> 1;
175 }
177 serif->pos = base->pos + sign * dist;
178 }
181 /*************************************************************************/
182 /*************************************************************************/
183 /*************************************************************************/
184 /**** ****/
185 /**** E D G E H I N T I N G ****/
186 /**** ****/
187 /*************************************************************************/
188 /*************************************************************************/
189 /*************************************************************************/
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;
202 edges = outline->horz_edges;
203 edge_limit = edges + outline->num_hedges;
205 for ( dimension = 1; dimension >= 0; dimension-- )
206 {
207 AH_Edge* edge;
208 AH_Edge* anchor = 0;
209 int has_serifs = 0;
212 if ( hinter->disable_vert_edges && !dimension )
213 goto Next_Dimension;
215 if ( hinter->disable_horz_edges && dimension )
216 goto Next_Dimension;
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;
228 if ( edge->flags & ah_edge_done )
229 continue;
231 blue = edge->blue_edge;
232 edge1 = 0;
233 edge2 = edge->link;
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 }
246 if ( !edge1 )
247 continue;
249 edge1->pos = blue[0];
250 edge1->flags |= ah_edge_done;
252 if ( edge2 && !edge2->blue_edge )
253 {
254 ah_align_linked_edge( hinter, edge1, edge2, dimension );
255 edge2->flags |= ah_edge_done;
256 }
258 if ( !anchor )
259 anchor = edge;
260 }
261 }
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;
270 if ( edge->flags & ah_edge_done )
271 continue;
273 /* skip all non-stem edges */
274 edge2 = edge->link;
275 if ( !edge2 )
276 {
277 has_serifs++;
278 continue;
279 }
281 /* now, align the stem */
283 /* this should not happen, but it's better to be safe.. */
284 if ( edge2->blue_edge || edge2 < edge )
285 {
287 #if 0
288 printf( "strange blue alignement, edge %d to %d\n",
289 edge - edges, edge2 - edges );
290 #endif
292 ah_align_linked_edge( hinter, edge2, edge, dimension );
293 edge->flags |= ah_edge_done;
294 continue;
295 }
297 {
298 FT_Bool min = 0;
299 FT_Pos delta;
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 );
310 edge->flags |= ah_edge_done;
312 if ( edge > edges && edge->pos < edge[-1].pos )
313 {
314 edge->pos = edge[-1].pos;
315 min = 1;
316 }
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;
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 }
334 if ( !has_serifs )
335 goto Next_Dimension;
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;
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 );
357 edge->flags |= ah_edge_done;
359 if ( edge > edges && edge->pos < edge[-1].pos )
360 edge->pos = edge[-1].pos;
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 }
368 Next_Dimension:
369 edges = outline->vert_edges;
370 edge_limit = edges + outline->num_vedges;
371 }
372 }
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;
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 );
389 #if 0
390 /* outline optimizer removed temporarily */
391 if ( hinter->flags & ah_hinter_optimize )
392 {
393 AH_Optimizer opt;
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
404 }
405 }
408 /*************************************************************************/
409 /*************************************************************************/
410 /*************************************************************************/
411 /**** ****/
412 /**** P O I N T H I N T I N G ****/
413 /**** ****/
414 /*************************************************************************/
415 /*************************************************************************/
416 /*************************************************************************/
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;
427 edges = outline->horz_edges;
428 edge_limit = edges + outline->num_hedges;
430 for ( dimension = 1; dimension >= 0; dimension-- )
431 {
432 AH_Edge* edge;
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;
443 do
444 {
445 AH_Point* point = seg->first;
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 }
461 if ( point == seg->last )
462 break;
464 point = point->next;
465 }
467 seg = seg->edge_next;
469 } while ( seg != edge->first );
470 }
472 edges = outline->vert_edges;
473 edge_limit = edges + outline->num_vedges;
474 }
475 }
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;
491 points = outline->points;
492 point_limit = points + outline->num_points;
494 edges = outline->horz_edges;
495 edge_limit = edges + outline->num_hedges;
496 touch_flag = ah_flah_touch_y;
498 for ( dimension = 1; dimension >= 0; dimension-- )
499 {
500 AH_Point* point;
501 AH_Edge* edge;
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;
511 if ( point->flags & touch_flag )
512 continue;
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
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 }
532 fu = u;
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 }
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 }
552 /* otherwise, interpolate the point in between */
553 {
554 AH_Edge* before = 0;
555 AH_Edge* after = 0;
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 }
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 }
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 }
588 Store_Point:
590 /* save the point position */
591 if ( dimension )
592 point->y = u;
593 else
594 point->x = u;
596 point->flags |= touch_flag;
597 }
599 edges = outline->vert_edges;
600 edge_limit = edges + outline->num_vedges;
601 touch_flag = ah_flah_touch_x;
602 }
603 }
606 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
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;
617 for ( p = p1; p < ref; p++ )
618 p->u = p->v + delta;
620 for ( p = ref + 1; p <= p2; p++ )
621 p->u = p->v + delta;
622 }
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;
639 if ( p1 > p2 )
640 return;
642 if ( v1 == v2 )
643 {
644 for ( p = p1; p <= p2; p++ )
645 {
646 u = p->v;
648 if ( u <= v1 )
649 u += d1;
650 else
651 u += d2;
653 p->u = u;
654 }
655 return;
656 }
658 if ( v1 < v2 )
659 {
660 for ( p = p1; p <= p2; p++ )
661 {
662 u = p->v;
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 );
671 p->u = u;
672 }
673 }
674 else
675 {
676 for ( p = p1; p <= p2; p++ )
677 {
678 u = p->v;
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 );
687 p->u = u;
688 }
689 }
690 }
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;
705 points = outline->points;
706 point_limit = points + outline->num_points;
708 /* PASS 1: Move segment points to edge positions */
710 touch_flag = ah_flah_touch_y;
712 contour_limit = outline->contours + outline->num_contours;
714 ah_setup_uv( outline, ah_uv_oy );
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;
724 point = points;
725 contour = outline->contours;
727 for ( ; contour < contour_limit; contour++ )
728 {
729 point = *contour;
730 end_point = point->prev;
731 first_point = point;
733 while ( point <= end_point && !( point->flags & touch_flag ) )
734 point++;
736 if ( point <= end_point )
737 {
738 AH_Point* first_touched = point;
739 AH_Point* cur_touched = point;
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 }
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 );
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 }
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;
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;
792 break; /* exit loop */
793 }
794 }
795 }
797 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
800 FT_LOCAL_DEF void
801 ah_hinter_align_points( AH_Hinter* hinter )
802 {
803 ah_hinter_align_edge_points( hinter );
805 #ifndef AH_OPTION_NO_STRONG_INTERPOLATION
806 ah_hinter_align_strong_points( hinter );
807 #endif
809 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
810 ah_hinter_align_weak_points( hinter );
811 #endif
812 }
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 /*************************************************************************/
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;
838 /* copy content */
839 *scaled = *design;
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 );
845 for ( n = 0; n < design->num_heights; n++ )
846 scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
848 /* scale the blue zones */
849 for ( n = 0; n < ah_blue_max; n++ )
850 {
851 FT_Pos delta, delta2;
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 );
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;
867 if ( delta < 0 )
868 delta2 = -delta2;
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 }
875 globals->x_scale = x_scale;
876 globals->y_scale = y_scale;
877 }
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 }
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;
897 ah_loader_done( hinter->loader );
898 ah_outline_done( hinter->glyph );
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;
906 FREE( hinter );
907 }
908 }
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;
921 *ahinter = 0;
923 /* allocate object */
924 if ( ALLOC( hinter, sizeof ( *hinter ) ) )
925 goto Exit;
927 hinter->memory = memory;
928 hinter->flags = 0;
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;
937 *ahinter = hinter;
939 Exit:
940 if ( error )
941 ah_hinter_done( hinter );
943 return error;
944 }
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;
958 if ( ALLOC( face_globals, sizeof ( *face_globals ) ) )
959 goto Exit;
961 hinter->face = face;
962 hinter->globals = face_globals;
964 if ( globals )
965 face_globals->design = *globals;
966 else
967 ah_hinter_compute_globals( hinter );
969 face->autohint.data = face_globals;
970 face->autohint.finalizer = (FT_Generic_Finalizer)
971 ah_hinter_done_face_globals;
972 face_globals->face = face;
974 Exit:
975 return error;
976 }
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;
987 FREE( globals );
988 }
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 );
1011 /* load the glyph */
1012 error = FT_Load_Glyph( face, glyph_index, load_flags );
1013 if ( error )
1014 goto Exit;
1016 /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
1017 hinter->transformed = internal->glyph_transformed;
1019 if ( hinter->transformed )
1020 {
1021 FT_Matrix imatrix;
1023 imatrix = internal->glyph_matrix;
1024 hinter->trans_delta = internal->glyph_delta;
1025 hinter->trans_matrix = imatrix;
1027 FT_Matrix_Invert( &imatrix );
1028 FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1029 }
1031 /* set linear horizontal metrics */
1032 slot->linearHoriAdvance = slot->metrics.horiAdvance;
1033 slot->linearVertAdvance = slot->metrics.vertAdvance;
1035 switch ( slot->format )
1036 {
1037 case ft_glyph_format_outline:
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;
1046 for ( ; n > 0; point++, n-- )
1047 {
1048 point->x += hinter->trans_delta.x;
1049 point->y += hinter->trans_delta.y;
1050 }
1051 }
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;
1060 MEM_Copy( gloader->current.extra_points, slot->outline.points,
1061 slot->outline.n_points * sizeof ( FT_Vector ) );
1063 MEM_Copy( gloader->current.outline.contours, slot->outline.contours,
1064 slot->outline.n_contours * sizeof ( short ) );
1066 MEM_Copy( gloader->current.outline.tags, slot->outline.tags,
1067 slot->outline.n_points * sizeof ( char ) );
1069 gloader->current.outline.n_points = slot->outline.n_points;
1070 gloader->current.outline.n_contours = slot->outline.n_contours;
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;
1078 /* be sure to check for spacing glyphs */
1079 if ( slot->outline.n_points == 0 )
1080 goto Hint_Metrics;
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;
1088 /* perform feature detection */
1089 ah_outline_detect_features( outline );
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 }
1097 /* perform alignment control */
1098 ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
1099 ah_hinter_align( hinter );
1101 /* now save the current outline into the loader's current table */
1102 ah_outline_save( outline, gloader );
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 */
1114 old_advance = hinter->pp2.x;
1115 old_lsb = edge1->opos;
1116 new_lsb = edge1->pos;
1118 hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64;
1119 hinter->pp2.x = ( ( edge2->pos +
1120 ( old_advance - edge2->opos ) ) + 32 ) & -64;
1121 }
1123 /* good, we simply add the glyph to our loader's base */
1124 ah_loader_add( gloader );
1125 break;
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;
1134 start_point = gloader->base.outline.n_points;
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;
1141 MEM_Copy( gloader->current.subglyphs, slot->subglyphs,
1142 num_subglyphs * sizeof ( FT_SubGlyph ) );
1144 gloader->current.num_subglyphs = num_subglyphs;
1145 num_base_subgs = gloader->base.num_subglyphs;
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;
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;
1160 pp1 = hinter->pp1;
1161 pp2 = hinter->pp2;
1163 num_base_points = gloader->base.outline.n_points;
1165 error = ah_hinter_load( hinter, subglyph->index,
1166 load_flags, depth + 1 );
1167 if ( error )
1168 goto Exit;
1170 /* recompute subglyph pointer */
1171 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
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 }
1184 num_points = gloader->base.outline.n_points;
1185 num_new_points = num_points - num_base_points;
1187 /* now perform the transform required for this subglyph */
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;
1200 for ( ; cur < limit; cur++, org++ )
1201 {
1202 FT_Vector_Transform( cur, &subglyph->transform );
1203 FT_Vector_Transform( org, &subglyph->transform );
1204 }
1205 }
1207 /* apply offset */
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;
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 }
1224 l += num_base_points;
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;
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 );
1239 x = ( x + 32 ) & -64;
1240 y = ( y + 32 ) & -64;
1241 }
1243 {
1244 FT_Outline dummy = gloader->base.outline;
1247 dummy.points += num_base_points;
1248 dummy.n_points = (short)num_new_points;
1250 FT_Outline_Translate( &dummy, x, y );
1251 }
1252 }
1253 }
1254 break;
1256 default:
1257 /* we don't support other formats (yet?) */
1258 error = AH_Err_Unimplemented_Feature;
1259 }
1261 Hint_Metrics:
1262 if ( depth == 0 )
1263 {
1264 FT_BBox bbox;
1267 /* transform the hinted outline if needed */
1268 if ( hinter->transformed )
1269 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
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 );
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;
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 */
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;
1295 slot->outline = slot->internal->loader->base.outline;
1296 slot->format = ft_glyph_format_outline;
1297 }
1299 Exit:
1300 return error;
1301 }
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 );
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;
1330 }
1331 hinter->globals = FACE_GLOBALS( face );
1332 face_globals = FACE_GLOBALS( face );
1334 }
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 );
1342 ah_loader_rewind( hinter->loader );
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
1351 error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1353 Exit:
1354 return error;
1355 }
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;
1370 /* allocate new master globals */
1371 if ( ALLOC( globals, sizeof ( *globals ) ) )
1372 goto Fail;
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 }
1382 *globals = FACE_GLOBALS( face )->design;
1383 *global_hints = globals;
1384 *global_len = sizeof( *globals );
1386 return;
1388 Fail:
1389 FREE( globals );
1391 *global_hints = 0;
1392 *global_len = 0;
1393 }
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;
1403 FREE( global_hints );
1404 }
1407 /* END */