]> git.saurik.com Git - wxWidgets.git/blob - src/freetype/autohint/ahhint.c
forgot to commit - no changes
[wxWidgets.git] / src / freetype / 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 <autohint/ahhint.h>
31 #include <autohint/ahglyph.h>
32 #include <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 */