1
/***************************************************************************/
5
/* Glyph hinter (body). */
7
/* Copyright 2000-2001, 2002, 2003, 2004 Catharon Productions Inc. */
8
/* Author: David Turner */
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. */
17
/* Note that this license is compatible with the FreeType license. */
19
/***************************************************************************/
30
#define FACE_GLOBALS( face ) ( (AH_Face_Globals)(face)->autohint.data )
33
#define OPTIM_STEM_SNAP
36
/*************************************************************************/
37
/*************************************************************************/
39
/**** Hinting routines ****/
41
/*************************************************************************/
42
/*************************************************************************/
44
/* snap a given width in scaled coordinates to one of the */
45
/* current standard widths */
47
ah_snap_width( FT_Pos* widths,
52
FT_Pos best = 64 + 32 + 2;
53
FT_Pos reference = width;
57
for ( n = 0; n < count; n++ )
74
scaled = FT_PIX_ROUND( reference );
76
if ( width >= reference )
78
if ( width < scaled + 48 )
83
if ( width > scaled - 48 )
91
/* compute the snapped width of a given stem */
93
#ifdef FT_CONFIG_CHESTER_SERIF
96
ah_compute_stem_width( AH_Hinter hinter,
99
AH_Edge_Flags base_flags,
100
AH_Edge_Flags stem_flags )
102
AH_Globals globals = &hinter->globals->scaled;
113
if ( !hinter->do_stem_adjust )
115
/* leave stem widths unchanged */
117
else if ( ( vertical && !hinter->do_vert_snapping ) ||
118
( !vertical && !hinter->do_horz_snapping ) )
120
/* smooth hinting process: very lightly quantize the stem width */
123
/* leave the widths of serifs alone */
125
if ( ( stem_flags & AH_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
128
else if ( ( base_flags & AH_EDGE_ROUND ) )
133
else if ( dist < 56 )
137
FT_Pos delta = dist - globals->stds[vertical];
145
dist = globals->stds[vertical];
160
else if ( delta < 32 )
163
else if ( delta < 54 )
170
dist = ( dist + 32 ) & ~63;
175
/* strong hinting process: snap the stem width to integer pixels */
179
dist = ah_snap_width( globals->heights, globals->num_heights, dist );
181
/* in the case of vertical hinting, always round */
182
/* the stem heights to integer pixels */
184
dist = ( dist + 16 ) & ~63;
190
dist = ah_snap_width( globals->widths, globals->num_widths, dist );
192
if ( hinter->flags & AH_HINTER_MONOCHROME )
194
/* monochrome horizontal hinting: snap widths to integer pixels */
195
/* with a different threshold */
199
dist = ( dist + 32 ) & ~63;
203
/* for horizontal anti-aliased hinting, we adopt a more subtle */
204
/* approach: we strengthen small stems, round stems whose size */
205
/* is between 1 and 2 pixels to an integer, otherwise nothing */
207
dist = ( dist + 64 ) >> 1;
209
else if ( dist < 128 )
210
dist = ( dist + 22 ) & ~63;
212
/* XXX: round otherwise to prevent color fringes in LCD mode */
213
dist = ( dist + 32 ) & ~63;
225
#else /* !FT_CONFIG_CHESTER_SERIF */
228
ah_compute_stem_width( AH_Hinter hinter,
232
AH_Globals globals = &hinter->globals->scaled;
243
if ( !hinter->do_stem_adjust )
245
/* leave stem widths unchanged */
247
else if ( ( vertical && !hinter->do_vert_snapping ) ||
248
( !vertical && !hinter->do_horz_snapping ) )
250
/* smooth hinting process: very lightly quantize the stem width */
256
FT_Pos delta = dist - globals->stds[vertical];
264
dist = globals->stds[vertical];
277
else if ( delta < 32 )
280
else if ( delta < 54 )
287
dist = ( dist + 32 ) & ~63;
292
/* strong hinting process: snap the stem width to integer pixels */
296
dist = ah_snap_width( globals->heights, globals->num_heights, dist );
298
/* in the case of vertical hinting, always round */
299
/* the stem heights to integer pixels */
301
dist = ( dist + 16 ) & ~63;
307
dist = ah_snap_width( globals->widths, globals->num_widths, dist );
309
if ( hinter->flags & AH_HINTER_MONOCHROME )
311
/* monochrome horizontal hinting: snap widths to integer pixels */
312
/* with a different threshold */
316
dist = ( dist + 32 ) & ~63;
320
/* for horizontal anti-aliased hinting, we adopt a more subtle */
321
/* approach: we strengthen small stems, round stems whose size */
322
/* is between 1 and 2 pixels to an integer, otherwise nothing */
324
dist = ( dist + 64 ) >> 1;
326
else if ( dist < 128 )
327
dist = ( dist + 22 ) & ~63;
329
/* XXX: round otherwise to prevent color fringes in LCD mode */
330
dist = ( dist + 32 ) & ~63;
341
#endif /* !FT_CONFIG_CHESTER_SERIF */
344
/* align one stem edge relative to the previous stem edge */
346
ah_align_linked_edge( AH_Hinter hinter,
351
FT_Pos dist = stem_edge->opos - base_edge->opos;
353
#ifdef FT_CONFIG_CHESTER_SERIF
355
FT_Pos fitted_width = ah_compute_stem_width( hinter,
362
stem_edge->pos = base_edge->pos + fitted_width;
366
stem_edge->pos = base_edge->pos +
367
ah_compute_stem_width( hinter, vertical, dist );
375
ah_align_serif_edge( AH_Hinter hinter,
384
FT_UNUSED( vertical );
387
dist = serif->opos - base->opos;
395
/* do not touch serifs widths! */
396
if ( base->flags & AH_EDGE_DONE )
399
dist = ( dist + 8 ) & ~63;
401
else if ( dist <= 32 && !vertical )
402
dist = ( dist + 33 ) >> 1;
409
serif->pos = base->pos + sign * dist;
413
/*************************************************************************/
414
/*************************************************************************/
415
/*************************************************************************/
417
/**** E D G E H I N T I N G ****/
419
/*************************************************************************/
420
/*************************************************************************/
421
/*************************************************************************/
425
ah_hinter_hint_edges( AH_Hinter hinter )
429
AH_Outline outline = hinter->glyph;
434
edges = outline->horz_edges;
435
edge_limit = edges + outline->num_hedges;
437
for ( dimension = 1; dimension >= 0; dimension-- )
444
if ( !hinter->do_horz_hints && !dimension )
447
if ( !hinter->do_vert_hints && dimension )
450
/* we begin by aligning all stems relative to the blue zone */
451
/* if needed -- that's only for horizontal edges */
454
for ( edge = edges; edge < edge_limit; edge++ )
457
AH_EdgeRec *edge1, *edge2;
460
if ( edge->flags & AH_EDGE_DONE )
463
blue = edge->blue_edge;
471
else if ( edge2 && edge2->blue_edge )
473
blue = edge2->blue_edge;
481
edge1->pos = blue[0];
482
edge1->flags |= AH_EDGE_DONE;
484
if ( edge2 && !edge2->blue_edge )
486
ah_align_linked_edge( hinter, edge1, edge2, dimension );
487
edge2->flags |= AH_EDGE_DONE;
495
/* now we will align all stem edges, trying to maintain the */
496
/* relative order of stems in the glyph */
497
for ( edge = edges; edge < edge_limit; edge++ )
502
if ( edge->flags & AH_EDGE_DONE )
505
/* skip all non-stem edges */
513
/* now align the stem */
515
/* this should not happen, but it's better to be safe */
516
if ( edge2->blue_edge || edge2 < edge )
518
ah_align_linked_edge( hinter, edge2, edge, dimension );
519
edge->flags |= AH_EDGE_DONE;
526
#ifdef FT_CONFIG_CHESTER_STEM
528
FT_Pos org_len, org_center, cur_len;
529
FT_Pos cur_pos1, error1, error2, u_off, d_off;
532
org_len = edge2->opos - edge->opos;
533
cur_len = ah_compute_stem_width( hinter, dimension, org_len,
534
edge->flags, edge2->flags );
546
org_center = edge->opos + ( org_len >> 1 );
548
cur_pos1 = FT_PIX_ROUND( org_center );
550
error1 = org_center - ( cur_pos1 - u_off );
554
error2 = org_center - ( cur_pos1 + d_off );
558
if ( error1 < error2 )
563
edge->pos = cur_pos1 - cur_len / 2;
564
edge2->pos = cur_pos1 + cur_len / 2;
568
edge->pos = FT_PIX_ROUND( edge->opos );
572
edge->flags |= AH_EDGE_DONE;
574
ah_align_linked_edge( hinter, edge, edge2, dimension );
576
#else /* !FT_CONFIG_CHESTER_STEM */
578
edge->pos = FT_PIX_ROUND( edge->opos );
581
edge->flags |= AH_EDGE_DONE;
583
ah_align_linked_edge( hinter, edge, edge2, dimension );
585
#endif /* !FT_CONFIG_CHESTER_STEM */
590
FT_Pos org_pos, org_len, org_center, cur_len;
591
FT_Pos cur_pos1, cur_pos2, delta1, delta2;
594
org_pos = anchor->pos + ( edge->opos - anchor->opos );
595
org_len = edge2->opos - edge->opos;
596
org_center = org_pos + ( org_len >> 1 );
598
#ifdef FT_CONFIG_CHESTER_SERIF
600
cur_len = ah_compute_stem_width( hinter, dimension, org_len,
601
edge->flags, edge2->flags );
604
#else /* !FT_CONFIG_CHESTER_SERIF */
606
cur_len = ah_compute_stem_width( hinter, dimension, org_len );
608
#endif /* !FT_CONFIG_CHESTER_SERIF */
610
#ifdef FT_CONFIG_CHESTER_STEM
617
cur_pos1 = FT_PIX_ROUND( org_center );
627
delta1 = org_center - ( cur_pos1 - u_off );
631
delta2 = org_center - ( cur_pos1 + d_off );
635
if ( delta1 < delta2 )
640
edge->pos = cur_pos1 - cur_len / 2;
641
edge2->pos = cur_pos1 + cur_len / 2;
645
org_pos = anchor->pos + ( edge->opos - anchor->opos );
646
org_len = edge2->opos - edge->opos;
647
org_center = org_pos + ( org_len >> 1 );
649
cur_len = ah_compute_stem_width( hinter, dimension, org_len,
650
edge->flags, edge2->flags );
652
cur_pos1 = FT_PIX_ROUND( org_pos );
653
delta1 = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
657
cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
658
delta2 = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
662
edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
663
edge2->pos = edge->pos + cur_len;
666
#else /* !FT_CONFIG_CHESTER_STEM */
668
cur_pos1 = FT_PIX_ROUND( org_pos );
669
delta1 = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
673
cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
674
delta2 = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
678
edge->pos = ( delta1 <= delta2 ) ? cur_pos1 : cur_pos2;
679
edge2->pos = edge->pos + cur_len;
681
#endif /* !FT_CONFIG_CHESTER_STEM */
683
edge->flags |= AH_EDGE_DONE;
684
edge2->flags |= AH_EDGE_DONE;
686
if ( edge > edges && edge->pos < edge[-1].pos )
687
edge->pos = edge[-1].pos;
691
/* make sure that lowercase m's maintain their symmetry */
693
/* In general, lowercase m's have six vertical edges if they are sans */
694
/* serif, or twelve if they are avec serif. This implementation is */
695
/* based on that assumption, and seems to work very well with most */
696
/* faces. However, if for a certain face this assumption is not */
697
/* true, the m is just rendered like before. In addition, any stem */
698
/* correction will only be applied to symmetrical glyphs (even if the */
699
/* glyph is not an m), so the potential for unwanted distortion is */
700
/* relatively low. */
702
/* We don't handle horizontal edges since we can't easily assure that */
703
/* the third (lowest) stem aligns with the base line; it might end up */
704
/* one pixel higher or lower. */
706
n_edges = (FT_Int)( edge_limit - edges );
707
if ( !dimension && ( n_edges == 6 || n_edges == 12 ) )
709
AH_EdgeRec *edge1, *edge2, *edge3;
710
FT_Pos dist1, dist2, span, delta;
726
dist1 = edge2->opos - edge1->opos;
727
dist2 = edge3->opos - edge2->opos;
729
span = dist1 - dist2;
735
delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
738
edge3->link->pos -= delta;
740
/* move the serifs along with the stem */
743
( edges + 8 )->pos -= delta;
744
( edges + 11 )->pos -= delta;
747
edge3->flags |= AH_EDGE_DONE;
749
edge3->link->flags |= AH_EDGE_DONE;
756
/* now hint the remaining edges (serifs and single) in order */
757
/* to complete our processing */
758
for ( edge = edges; edge < edge_limit; edge++ )
760
if ( edge->flags & AH_EDGE_DONE )
764
ah_align_serif_edge( hinter, edge->serif, edge, dimension );
767
edge->pos = FT_PIX_ROUND( edge->opos );
771
edge->pos = anchor->pos +
772
FT_PIX_ROUND( edge->opos - anchor->opos );
774
edge->flags |= AH_EDGE_DONE;
776
if ( edge > edges && edge->pos < edge[-1].pos )
777
edge->pos = edge[-1].pos;
779
if ( edge + 1 < edge_limit &&
780
edge[1].flags & AH_EDGE_DONE &&
781
edge->pos > edge[1].pos )
782
edge->pos = edge[1].pos;
786
edges = outline->vert_edges;
787
edge_limit = edges + outline->num_vedges;
792
/*************************************************************************/
793
/*************************************************************************/
794
/*************************************************************************/
796
/**** P O I N T H I N T I N G ****/
798
/*************************************************************************/
799
/*************************************************************************/
800
/*************************************************************************/
803
ah_hinter_align_edge_points( AH_Hinter hinter )
805
AH_Outline outline = hinter->glyph;
811
edges = outline->horz_edges;
812
edge_limit = edges + outline->num_hedges;
814
for ( dimension = 1; dimension >= 0; dimension-- )
820
for ( ; edge < edge_limit; edge++ )
822
/* move the points of each segment */
823
/* in each edge to the edge's position */
824
AH_Segment seg = edge->first;
829
AH_Point point = seg->first;
836
point->y = edge->pos;
837
point->flags |= AH_FLAG_TOUCH_Y;
841
point->x = edge->pos;
842
point->flags |= AH_FLAG_TOUCH_X;
845
if ( point == seg->last )
851
seg = seg->edge_next;
853
} while ( seg != edge->first );
856
edges = outline->vert_edges;
857
edge_limit = edges + outline->num_vedges;
862
/* hint the strong points -- this is equivalent to the TrueType `IP' */
863
/* hinting instruction */
865
ah_hinter_align_strong_points( AH_Hinter hinter )
867
AH_Outline outline = hinter->glyph;
872
AH_Point point_limit;
876
points = outline->points;
877
point_limit = points + outline->num_points;
879
edges = outline->horz_edges;
880
edge_limit = edges + outline->num_hedges;
881
touch_flag = AH_FLAG_TOUCH_Y;
883
for ( dimension = 1; dimension >= 0; dimension-- )
889
if ( edges < edge_limit )
890
for ( point = points; point < point_limit; point++ )
892
FT_Pos u, ou, fu; /* point position */
896
if ( point->flags & touch_flag )
899
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
900
/* if this point is candidate to weak interpolation, we will */
901
/* interpolate it after all strong points have been processed */
902
if ( ( point->flags & AH_FLAG_WEAK_INTERPOLATION ) &&
903
!( point->flags & AH_FLAG_INFLECTION ) )
920
/* is the point before the first edge? */
922
delta = edge->fpos - u;
925
u = edge->pos - ( edge->opos - ou );
929
/* is the point after the last edge? */
930
edge = edge_limit - 1;
931
delta = u - edge->fpos;
934
u = edge->pos + ( ou - edge->opos );
940
FT_UInt min, max, mid;
944
/* find enclosing edges */
946
max = (FT_UInt)( edge_limit - edges );
950
mid = ( max + min ) >> 1;
960
/* we are on the edge */
967
AH_Edge before = edges + min - 1;
968
AH_Edge after = edges + min + 0;
971
/* assert( before && after && before != after ) */
972
if ( before->scale == 0 )
973
before->scale = FT_DivFix( after->pos - before->pos,
974
after->fpos - before->fpos );
976
u = before->pos + FT_MulFix( fu - before->fpos,
983
/* otherwise, interpolate the point in between */
989
for ( edge = edges; edge < edge_limit; edge++ )
991
if ( u == edge->fpos )
996
if ( u < edge->fpos )
1001
for ( edge = edge_limit - 1; edge >= edges; edge-- )
1003
if ( u == edge->fpos )
1008
if ( u > edge->fpos )
1013
if ( before->scale == 0 )
1014
before->scale = FT_DivFix( after->pos - before->pos,
1015
after->fpos - before->fpos );
1017
u = before->pos + FT_MulFix( fu - before->fpos,
1025
/* save the point position */
1031
point->flags |= touch_flag;
1034
edges = outline->vert_edges;
1035
edge_limit = edges + outline->num_vedges;
1036
touch_flag = AH_FLAG_TOUCH_X;
1041
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
1044
ah_iup_shift( AH_Point p1,
1049
FT_Pos delta = ref->u - ref->v;
1052
for ( p = p1; p < ref; p++ )
1053
p->u = p->v + delta;
1055
for ( p = ref + 1; p <= p2; p++ )
1056
p->u = p->v + delta;
1061
ah_iup_interp( AH_Point p1,
1068
FT_Pos v1 = ref1->v;
1069
FT_Pos v2 = ref2->v;
1070
FT_Pos d1 = ref1->u - v1;
1071
FT_Pos d2 = ref2->u - v2;
1079
for ( p = p1; p <= p2; p++ )
1095
for ( p = p1; p <= p2; p++ )
1104
u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
1111
for ( p = p1; p <= p2; p++ )
1120
u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
1128
/* interpolate weak points -- this is equivalent to the TrueType `IUP' */
1129
/* hinting instruction */
1131
ah_hinter_align_weak_points( AH_Hinter hinter )
1133
AH_Outline outline = hinter->glyph;
1136
AH_Point point_limit;
1137
AH_Point* contour_limit;
1138
AH_Flags touch_flag;
1141
points = outline->points;
1142
point_limit = points + outline->num_points;
1144
/* PASS 1: Move segment points to edge positions */
1146
touch_flag = AH_FLAG_TOUCH_Y;
1148
contour_limit = outline->contours + outline->num_contours;
1150
ah_setup_uv( outline, AH_UV_OY );
1152
for ( dimension = 1; dimension >= 0; dimension-- )
1156
AH_Point first_point;
1161
contour = outline->contours;
1163
for ( ; contour < contour_limit; contour++ )
1166
end_point = point->prev;
1167
first_point = point;
1169
while ( point <= end_point && !( point->flags & touch_flag ) )
1172
if ( point <= end_point )
1174
AH_Point first_touched = point;
1175
AH_Point cur_touched = point;
1179
while ( point <= end_point )
1181
if ( point->flags & touch_flag )
1183
/* we found two successive touched points; we interpolate */
1184
/* all contour points between them */
1185
ah_iup_interp( cur_touched + 1, point - 1,
1186
cur_touched, point );
1187
cur_touched = point;
1192
if ( cur_touched == first_touched )
1194
/* this is a special case: only one point was touched in the */
1195
/* contour; we thus simply shift the whole contour */
1196
ah_iup_shift( first_point, end_point, cur_touched );
1200
/* now interpolate after the last touched point to the end */
1201
/* of the contour */
1202
ah_iup_interp( cur_touched + 1, end_point,
1203
cur_touched, first_touched );
1205
/* if the first contour point isn't touched, interpolate */
1206
/* from the contour start to the first touched point */
1207
if ( first_touched > points )
1208
ah_iup_interp( first_point, first_touched - 1,
1209
cur_touched, first_touched );
1214
/* now save the interpolated values back to x/y */
1217
for ( point = points; point < point_limit; point++ )
1218
point->y = point->u;
1220
touch_flag = AH_FLAG_TOUCH_X;
1221
ah_setup_uv( outline, AH_UV_OX );
1225
for ( point = points; point < point_limit; point++ )
1226
point->x = point->u;
1228
break; /* exit loop */
1233
#endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
1236
FT_LOCAL_DEF( void )
1237
ah_hinter_align_points( AH_Hinter hinter )
1239
ah_hinter_align_edge_points( hinter );
1241
#ifndef AH_OPTION_NO_STRONG_INTERPOLATION
1242
ah_hinter_align_strong_points( hinter );
1245
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
1246
ah_hinter_align_weak_points( hinter );
1251
/*************************************************************************/
1252
/*************************************************************************/
1253
/*************************************************************************/
1255
/**** H I N T E R O B J E C T M E T H O D S ****/
1257
/*************************************************************************/
1258
/*************************************************************************/
1259
/*************************************************************************/
1262
/* scale and fit the global metrics */
1264
ah_hinter_scale_globals( AH_Hinter hinter,
1269
AH_Face_Globals globals = hinter->globals;
1270
AH_Globals design = &globals->design;
1271
AH_Globals scaled = &globals->scaled;
1277
/* scale the standard widths & heights */
1278
for ( n = 0; n < design->num_widths; n++ )
1279
scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
1281
for ( n = 0; n < design->num_heights; n++ )
1282
scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
1284
scaled->stds[0] = ( design->num_widths > 0 ) ? scaled->widths[0] : 32000;
1285
scaled->stds[1] = ( design->num_heights > 0 ) ? scaled->heights[0] : 32000;
1287
/* scale the blue zones */
1288
for ( n = 0; n < AH_BLUE_MAX; n++ )
1290
FT_Pos delta, delta2;
1293
delta = design->blue_shoots[n] - design->blue_refs[n];
1297
delta2 = FT_MulFix( delta2, y_scale );
1301
else if ( delta2 < 64 )
1302
delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
1304
delta2 = FT_PIX_ROUND( delta2 );
1309
scaled->blue_refs[n] =
1310
FT_PIX_ROUND( FT_MulFix( design->blue_refs[n], y_scale ) );
1312
scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
1315
globals->x_scale = x_scale;
1316
globals->y_scale = y_scale;
1321
ah_hinter_align( AH_Hinter hinter )
1323
ah_hinter_align_edge_points( hinter );
1324
ah_hinter_align_points( hinter );
1328
/* finalize a hinter object */
1329
FT_LOCAL_DEF( void )
1330
ah_hinter_done( AH_Hinter hinter )
1334
FT_Memory memory = hinter->memory;
1337
ah_loader_done( hinter->loader );
1338
ah_outline_done( hinter->glyph );
1340
/* note: the `globals' pointer is _not_ owned by the hinter */
1341
/* but by the current face object; we don't need to */
1343
hinter->globals = 0;
1351
/* create a new empty hinter object */
1352
FT_LOCAL_DEF( FT_Error )
1353
ah_hinter_new( FT_Library library,
1354
AH_Hinter *ahinter )
1356
AH_Hinter hinter = 0;
1357
FT_Memory memory = library->memory;
1363
/* allocate object */
1364
if ( FT_NEW( hinter ) )
1367
hinter->memory = memory;
1370
/* allocate outline and loader */
1371
error = ah_outline_new( memory, &hinter->glyph ) ||
1372
ah_loader_new ( memory, &hinter->loader ) ||
1373
ah_loader_create_extra( hinter->loader );
1381
ah_hinter_done( hinter );
1387
/* create a face's autohint globals */
1388
FT_LOCAL_DEF( FT_Error )
1389
ah_hinter_new_face_globals( AH_Hinter hinter,
1391
AH_Globals globals )
1394
FT_Memory memory = hinter->memory;
1395
AH_Face_Globals face_globals;
1398
if ( FT_NEW( face_globals ) )
1401
hinter->face = face;
1402
hinter->globals = face_globals;
1405
face_globals->design = *globals;
1407
ah_hinter_compute_globals( hinter );
1409
face->autohint.data = face_globals;
1410
face->autohint.finalizer = (FT_Generic_Finalizer)
1411
ah_hinter_done_face_globals;
1412
face_globals->face = face;
1419
/* discard a face's autohint globals */
1420
FT_LOCAL_DEF( void )
1421
ah_hinter_done_face_globals( AH_Face_Globals globals )
1423
FT_Face face = globals->face;
1424
FT_Memory memory = face->memory;
1432
ah_hinter_load( AH_Hinter hinter,
1433
FT_UInt glyph_index,
1434
FT_Int32 load_flags,
1437
FT_Face face = hinter->face;
1438
FT_GlyphSlot slot = face->glyph;
1439
FT_Slot_Internal internal = slot->internal;
1440
FT_Fixed x_scale = hinter->globals->x_scale;
1441
FT_Fixed y_scale = hinter->globals->y_scale;
1443
AH_Outline outline = hinter->glyph;
1444
AH_Loader gloader = hinter->loader;
1447
/* load the glyph */
1448
error = FT_Load_Glyph( face, glyph_index, load_flags );
1452
/* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
1453
hinter->transformed = internal->glyph_transformed;
1455
if ( hinter->transformed )
1460
imatrix = internal->glyph_matrix;
1461
hinter->trans_delta = internal->glyph_delta;
1462
hinter->trans_matrix = imatrix;
1464
FT_Matrix_Invert( &imatrix );
1465
FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1468
/* set linear horizontal metrics */
1469
slot->linearHoriAdvance = slot->metrics.horiAdvance;
1470
slot->linearVertAdvance = slot->metrics.vertAdvance;
1472
switch ( slot->format )
1474
case FT_GLYPH_FORMAT_OUTLINE:
1476
/* translate glyph outline if we need to */
1477
if ( hinter->transformed )
1479
FT_UInt n = slot->outline.n_points;
1480
FT_Vector* point = slot->outline.points;
1483
for ( ; n > 0; point++, n-- )
1485
point->x += hinter->trans_delta.x;
1486
point->y += hinter->trans_delta.y;
1490
/* copy the outline points in the loader's current */
1491
/* extra points which is used to keep original glyph coordinates */
1492
error = ah_loader_check_points( gloader, slot->outline.n_points + 4,
1493
slot->outline.n_contours );
1497
FT_ARRAY_COPY( gloader->current.extra_points, slot->outline.points,
1498
slot->outline.n_points );
1500
FT_ARRAY_COPY( gloader->current.outline.contours, slot->outline.contours,
1501
slot->outline.n_contours );
1503
FT_ARRAY_COPY( gloader->current.outline.tags, slot->outline.tags,
1504
slot->outline.n_points );
1506
gloader->current.outline.n_points = slot->outline.n_points;
1507
gloader->current.outline.n_contours = slot->outline.n_contours;
1509
/* compute original horizontal phantom points, ignoring vertical ones */
1512
hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
1515
/* be sure to check for spacing glyphs */
1516
if ( slot->outline.n_points == 0 )
1519
/* now load the slot image into the auto-outline and run the */
1520
/* automatic hinting process */
1521
error = ah_outline_load( outline, x_scale, y_scale, face );
1525
/* perform feature detection */
1526
ah_outline_detect_features( outline );
1528
if ( hinter->do_vert_hints )
1530
ah_outline_compute_blue_edges( outline, hinter->globals );
1531
ah_outline_scale_blue_edges( outline, hinter->globals );
1534
/* perform alignment control */
1535
ah_hinter_hint_edges( hinter );
1536
ah_hinter_align( hinter );
1538
/* now save the current outline into the loader's current table */
1539
ah_outline_save( outline, gloader );
1541
/* we now need to hint the metrics according to the change in */
1542
/* width/positioning that occured during the hinting process */
1543
if ( outline->num_vedges > 0 )
1545
FT_Pos old_advance, old_rsb, old_lsb, new_lsb, pp1x_uh, pp2x_uh;
1546
AH_Edge edge1 = outline->vert_edges; /* leftmost edge */
1547
AH_Edge edge2 = edge1 +
1548
outline->num_vedges - 1; /* rightmost edge */
1551
old_advance = hinter->pp2.x;
1552
old_rsb = old_advance - edge2->opos;
1553
old_lsb = edge1->opos;
1554
new_lsb = edge1->pos;
1556
/* remember unhinted values to later account for rounding errors */
1558
pp1x_uh = new_lsb - old_lsb;
1559
pp2x_uh = edge2->pos + old_rsb;
1561
/* prefer too much space over too little space for very small sizes */
1569
hinter->pp1.x = FT_PIX_ROUND( pp1x_uh );
1570
hinter->pp2.x = FT_PIX_ROUND( pp2x_uh );
1572
slot->lsb_delta = hinter->pp1.x - pp1x_uh;
1573
slot->rsb_delta = hinter->pp2.x - pp2x_uh;
1576
/* try to fix certain bad advance computations */
1577
if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 )
1578
hinter->pp2.x += 64;
1584
hinter->pp1.x = ( hinter->pp1.x + 32 ) & -64;
1585
hinter->pp2.x = ( hinter->pp2.x + 32 ) & -64;
1588
/* good, we simply add the glyph to our loader's base */
1589
ah_loader_add( gloader );
1592
case FT_GLYPH_FORMAT_COMPOSITE:
1594
FT_UInt nn, num_subglyphs = slot->num_subglyphs;
1595
FT_UInt num_base_subgs, start_point;
1596
FT_SubGlyph subglyph;
1599
start_point = gloader->base.outline.n_points;
1601
/* first of all, copy the subglyph descriptors in the glyph loader */
1602
error = ah_loader_check_subglyphs( gloader, num_subglyphs );
1606
FT_ARRAY_COPY( gloader->current.subglyphs, slot->subglyphs,
1609
gloader->current.num_subglyphs = num_subglyphs;
1610
num_base_subgs = gloader->base.num_subglyphs;
1612
/* now, read each subglyph independently */
1613
for ( nn = 0; nn < num_subglyphs; nn++ )
1617
FT_UInt num_points, num_new_points, num_base_points;
1620
/* gloader.current.subglyphs can change during glyph loading due */
1621
/* to re-allocation -- we must recompute the current subglyph on */
1622
/* each iteration */
1623
subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1628
num_base_points = gloader->base.outline.n_points;
1630
error = ah_hinter_load( hinter, subglyph->index,
1631
load_flags, depth + 1 );
1635
/* recompute subglyph pointer */
1636
subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1638
if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
1649
num_points = gloader->base.outline.n_points;
1650
num_new_points = num_points - num_base_points;
1652
/* now perform the transform required for this subglyph */
1654
if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
1655
FT_SUBGLYPH_FLAG_XY_SCALE |
1656
FT_SUBGLYPH_FLAG_2X2 ) )
1658
FT_Vector* cur = gloader->base.outline.points +
1660
FT_Vector* org = gloader->base.extra_points +
1662
FT_Vector* limit = cur + num_new_points;
1665
for ( ; cur < limit; cur++, org++ )
1667
FT_Vector_Transform( cur, &subglyph->transform );
1668
FT_Vector_Transform( org, &subglyph->transform );
1674
if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
1676
FT_Int k = subglyph->arg1;
1677
FT_UInt l = subglyph->arg2;
1682
if ( start_point + k >= num_base_points ||
1683
l >= (FT_UInt)num_new_points )
1685
error = AH_Err_Invalid_Composite;
1689
l += num_base_points;
1691
/* for now, only use the current point coordinates; */
1692
/* we may consider another approach in the near future */
1693
p1 = gloader->base.outline.points + start_point + k;
1694
p2 = gloader->base.outline.points + start_point + l;
1701
x = FT_MulFix( subglyph->arg1, x_scale );
1702
y = FT_MulFix( subglyph->arg2, y_scale );
1704
x = FT_PIX_ROUND(x);
1705
y = FT_PIX_ROUND(y);
1709
FT_Outline dummy = gloader->base.outline;
1712
dummy.points += num_base_points;
1713
dummy.n_points = (short)num_new_points;
1715
FT_Outline_Translate( &dummy, x, y );
1722
/* we don't support other formats (yet?) */
1723
error = AH_Err_Unimplemented_Feature;
1732
/* transform the hinted outline if needed */
1733
if ( hinter->transformed )
1734
FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
1736
/* we must translate our final outline by -pp1.x and compute */
1737
/* the new metrics */
1738
if ( hinter->pp1.x )
1739
FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
1741
FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
1742
bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
1743
bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
1744
bbox.xMax = FT_PIX_CEIL( bbox.xMax );
1745
bbox.yMax = FT_PIX_CEIL( bbox.yMax );
1747
slot->metrics.width = bbox.xMax - bbox.xMin;
1748
slot->metrics.height = bbox.yMax - bbox.yMin;
1749
slot->metrics.horiBearingX = bbox.xMin;
1750
slot->metrics.horiBearingY = bbox.yMax;
1752
/* for mono-width fonts (like Andale, Courier, etc.) we need */
1753
/* to keep the original rounded advance width */
1754
if ( !FT_IS_FIXED_WIDTH( slot->face ) )
1755
slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
1757
slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
1760
slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
1762
/* now copy outline into glyph slot */
1763
ah_loader_rewind( slot->internal->loader );
1764
error = ah_loader_copy_points( slot->internal->loader, gloader );
1768
slot->outline = slot->internal->loader->base.outline;
1769
slot->format = FT_GLYPH_FORMAT_OUTLINE;
1773
ah_debug_hinter = hinter;
1781
/* load and hint a given glyph */
1782
FT_LOCAL_DEF( FT_Error )
1783
ah_hinter_load_glyph( AH_Hinter hinter,
1786
FT_UInt glyph_index,
1787
FT_Int32 load_flags )
1789
FT_Face face = slot->face;
1791
FT_Fixed x_scale = size->metrics.x_scale;
1792
FT_Fixed y_scale = size->metrics.y_scale;
1793
AH_Face_Globals face_globals = FACE_GLOBALS( face );
1794
FT_Render_Mode hint_mode = FT_LOAD_TARGET_MODE( load_flags );
1797
/* first of all, we need to check that we're using the correct face and */
1798
/* global hints to load the glyph */
1799
if ( hinter->face != face || hinter->globals != face_globals )
1801
hinter->face = face;
1802
if ( !face_globals )
1804
error = ah_hinter_new_face_globals( hinter, face, 0 );
1809
hinter->globals = FACE_GLOBALS( face );
1810
face_globals = FACE_GLOBALS( face );
1814
#ifdef FT_CONFIG_CHESTER_BLUE_SCALE
1816
/* try to optimize the y_scale so that the top of non-capital letters
1817
* is aligned on a pixel boundary whenever possible
1820
AH_Globals design = &face_globals->design;
1821
FT_Pos shoot = design->blue_shoots[AH_BLUE_SMALL_TOP];
1824
/* the value of 'shoot' will be -1000 if the font doesn't have */
1825
/* small latin letters; we simply check the sign here... */
1828
FT_Pos scaled = FT_MulFix( shoot, y_scale );
1829
FT_Pos fitted = FT_PIX_ROUND( scaled );
1832
if ( scaled != fitted )
1836
y_scale = FT_MulDiv( y_scale, fitted, scaled );
1840
if ( fitted < scaled )
1841
x_scale -= x_scale / 50; /* x_scale*0.98 with integers */
1846
#endif /* FT_CONFIG_CHESTER_BLUE_SCALE */
1848
/* now, we must check the current character pixel size to see if we */
1849
/* need to rescale the global metrics */
1850
if ( face_globals->x_scale != x_scale ||
1851
face_globals->y_scale != y_scale )
1852
ah_hinter_scale_globals( hinter, x_scale, y_scale );
1854
ah_loader_rewind( hinter->loader );
1856
/* reset hinting flags according to load flags and current render target */
1857
hinter->do_horz_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1858
hinter->do_vert_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1861
hinter->do_horz_hints = !ah_debug_disable_vert; /* not a bug, the meaning */
1862
hinter->do_vert_hints = !ah_debug_disable_horz; /* of h/v is inverted! */
1865
/* we snap the width of vertical stems for the monochrome and */
1866
/* horizontal LCD rendering targets only. Corresponds to X snapping. */
1867
hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1868
hint_mode == FT_RENDER_MODE_LCD );
1870
/* we snap the width of horizontal stems for the monochrome and */
1871
/* vertical LCD rendering targets only. Corresponds to Y snapping. */
1872
hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1873
hint_mode == FT_RENDER_MODE_LCD_V );
1875
hinter->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
1877
load_flags |= FT_LOAD_NO_SCALE
1878
| FT_LOAD_IGNORE_TRANSFORM;
1879
load_flags &= ~FT_LOAD_RENDER;
1881
error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1888
/* retrieve a face's autohint globals for client applications */
1889
FT_LOCAL_DEF( void )
1890
ah_hinter_get_global_hints( AH_Hinter hinter,
1892
void** global_hints,
1895
AH_Globals globals = 0;
1896
FT_Memory memory = hinter->memory;
1900
/* allocate new master globals */
1901
if ( FT_NEW( globals ) )
1904
/* compute face globals if needed */
1905
if ( !FACE_GLOBALS( face ) )
1907
error = ah_hinter_new_face_globals( hinter, face, 0 );
1912
*globals = FACE_GLOBALS( face )->design;
1913
*global_hints = globals;
1914
*global_len = sizeof( *globals );
1926
FT_LOCAL_DEF( void )
1927
ah_hinter_done_global_hints( AH_Hinter hinter,
1928
void* global_hints )
1930
FT_Memory memory = hinter->memory;
1933
FT_FREE( global_hints );