1 /***************************************************************************/
5 /* Glyph hinter (body). */
7 /* Copyright 2000-2001, 2002 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
35 /*************************************************************************/
36 /*************************************************************************/
38 /**** Hinting routines ****/
40 /*************************************************************************/
41 /*************************************************************************/
43 /* snap a given width in scaled coordinates to one of the */
44 /* current standard widths */
46 ah_snap_width( FT_Pos* widths,
51 FT_Pos best = 64 + 32 + 2;
52 FT_Pos reference = width;
56 for ( n = 0; n < count; n++ )
73 scaled = (reference+32) & -64;
75 if ( width >= reference )
77 if ( width < scaled + 48 )
82 if ( width > scaled - 48 )
90 /* compute the snapped width of a given stem */
91 #ifdef FT_CONFIG_CHESTER_SERIF
93 ah_compute_stem_width( AH_Hinter hinter,
96 AH_Edge_Flags base_flags,
97 AH_Edge_Flags stem_flags )
99 AH_Globals globals = &hinter->globals->scaled;
110 if ( !hinter->do_stem_adjust )
112 /* leave stem widths unchanged */
114 else if ( ( vertical && !hinter->do_vert_snapping ) ||
115 ( !vertical && !hinter->do_horz_snapping ) )
117 /* smooth hinting process, very lightly quantize the stem width */
120 /* leave the widths of serifs alone */
122 if ( ( stem_flags & AH_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
125 else if ( ( base_flags & AH_EDGE_ROUND ) )
130 else if ( dist < 56 )
134 FT_Pos delta = dist - globals->stds[vertical];
142 dist = globals->stds[vertical];
151 delta = ( dist & 63 );
157 else if ( delta < 32 )
160 else if ( delta < 54 )
167 dist = ( dist + 32 ) & -64;
172 /* strong hinting process, snap the stem width to integer pixels */
176 dist = ah_snap_width( globals->heights, globals->num_heights, dist );
178 /* in the case of vertical hinting, always round */
179 /* the stem heights to integer pixels */
181 dist = ( dist + 16 ) & -64;
187 dist = ah_snap_width( globals->widths, globals->num_widths, dist );
189 if ( hinter->flags & AH_HINTER_MONOCHROME )
191 /* monochrome horizontal hinting: snap widths to integer pixels */
192 /* with a different threshold */
196 dist = ( dist + 32 ) & -64;
200 /* for horizontal anti-aliased hinting, we adopt a more subtle */
201 /* approach: we strengthen small stems, round stems whose size */
202 /* is between 1 and 2 pixels to an integer, otherwise nothing */
204 dist = ( dist + 64 ) >> 1;
206 else if ( dist < 128 )
207 dist = ( dist + 22 ) & -64;
209 /* XXX: round otherwise, prevent color fringes in LCD mode */
210 dist = ( dist + 32 ) & -64;
221 #else /* !CHESTER_SERIF */
223 ah_compute_stem_width( AH_Hinter hinter,
227 AH_Globals globals = &hinter->globals->scaled;
238 if ( !hinter->do_stem_adjust )
240 /* leave stem widths unchanged */
242 else if ( ( vertical && !hinter->do_vert_snapping ) ||
243 ( !vertical && !hinter->do_horz_snapping ) )
245 /* smooth hinting process, very lightly quantize the stem width */
251 FT_Pos delta = dist - globals->stds[vertical];
259 dist = globals->stds[vertical];
266 delta = ( dist & 63 );
272 else if ( delta < 32 )
275 else if ( delta < 54 )
282 dist = ( dist + 32 ) & -64;
287 /* strong hinting process, snap the stem width to integer pixels */
291 dist = ah_snap_width( globals->heights, globals->num_heights, dist );
293 /* in the case of vertical hinting, always round */
294 /* the stem heights to integer pixels */
296 dist = ( dist + 16 ) & -64;
302 dist = ah_snap_width( globals->widths, globals->num_widths, dist );
304 if ( hinter->flags & AH_HINTER_MONOCHROME )
306 /* monochrome horizontal hinting: snap widths to integer pixels */
307 /* with a different threshold */
311 dist = ( dist + 32 ) & -64;
315 /* for horizontal anti-aliased hinting, we adopt a more subtle */
316 /* approach: we strengthen small stems, round stems whose size */
317 /* is between 1 and 2 pixels to an integer, otherwise nothing */
319 dist = ( dist + 64 ) >> 1;
321 else if ( dist < 128 )
322 dist = ( dist + 22 ) & -64;
324 /* XXX: round otherwise, prevent color fringes in LCD mode */
325 dist = ( dist + 32 ) & -64;
335 #endif /* !CHESTER_SERIF */
338 /* align one stem edge relative to the previous stem edge */
340 ah_align_linked_edge( AH_Hinter hinter,
345 FT_Pos dist = stem_edge->opos - base_edge->opos;
347 #ifdef FT_CONFIG_CHESTER_SERIF
348 FT_Pos fitted_width = ah_compute_stem_width( hinter,
354 stem_edge->pos = base_edge->pos + fitted_width;
356 stem_edge->pos = base_edge->pos +
357 ah_compute_stem_width( hinter, vertical, dist );
363 ah_align_serif_edge( AH_Hinter hinter,
372 FT_UNUSED( vertical );
374 dist = serif->opos - base->opos;
381 /* do not touch serifs widths !! */
383 if ( base->flags & AH_EDGE_DONE )
386 dist = (dist+8) & -64;
388 else if ( dist <= 32 && !vertical )
389 dist = ( dist + 33 ) >> 1;
395 serif->pos = base->pos + sign * dist;
399 /*************************************************************************/
400 /*************************************************************************/
401 /*************************************************************************/
403 /**** E D G E H I N T I N G ****/
405 /*************************************************************************/
406 /*************************************************************************/
407 /*************************************************************************/
410 /* Another alternative edge hinting algorithm */
412 ah_hint_edges_3( AH_Hinter hinter )
416 AH_Outline outline = hinter->glyph;
420 edges = outline->horz_edges;
421 edge_limit = edges + outline->num_hedges;
423 for ( dimension = 1; dimension >= 0; dimension-- )
430 if ( !hinter->do_horz_hints && !dimension )
433 if ( !hinter->do_vert_hints && dimension )
436 /* we begin by aligning all stems relative to the blue zone */
437 /* if needed -- that's only for horizontal edges */
440 for ( edge = edges; edge < edge_limit; edge++ )
443 AH_EdgeRec *edge1, *edge2;
446 if ( edge->flags & AH_EDGE_DONE )
449 blue = edge->blue_edge;
457 else if (edge2 && edge2->blue_edge)
459 blue = edge2->blue_edge;
467 edge1->pos = blue[0];
468 edge1->flags |= AH_EDGE_DONE;
470 if ( edge2 && !edge2->blue_edge )
472 ah_align_linked_edge( hinter, edge1, edge2, dimension );
473 edge2->flags |= AH_EDGE_DONE;
481 /* now, we will align all stem edges, trying to maintain the */
482 /* relative order of stems in the glyph.. */
483 for ( edge = edges; edge < edge_limit; edge++ )
488 if ( edge->flags & AH_EDGE_DONE )
491 /* skip all non-stem edges */
499 /* now, align the stem */
501 /* this should not happen, but it's better to be safe. */
502 if ( edge2->blue_edge || edge2 < edge )
505 ah_align_linked_edge( hinter, edge2, edge, dimension );
506 edge->flags |= AH_EDGE_DONE;
512 #ifdef FT_CONFIG_CHESTER_STEM
513 FT_Pos org_len, org_center, cur_len;
514 FT_Pos cur_pos1, error1, error2, u_off, d_off;
516 org_len = edge2->opos - edge->opos;
517 cur_len = ah_compute_stem_width( hinter, dimension, org_len,
518 edge->flags, edge2->flags );
530 org_center = edge->opos + ( org_len >> 1 );
532 cur_pos1 = ( org_center + 32 ) & -64;
534 error1 = org_center - ( cur_pos1 - u_off );
538 error2 = org_center - ( cur_pos1 + d_off );
542 if ( error1 < error2 )
547 edge->pos = cur_pos1 - cur_len / 2;
548 edge2->pos = cur_pos1 + cur_len / 2;
552 edge->pos = ( edge->opos + 32 ) & -64;
556 edge->flags |= AH_EDGE_DONE;
558 ah_align_linked_edge( hinter, edge, edge2, dimension );
559 #else /* !CHESTER_STEM */
560 edge->pos = ( edge->opos + 32 ) & -64;
563 edge->flags |= AH_EDGE_DONE;
565 ah_align_linked_edge( hinter, edge, edge2, dimension );
566 #endif /* !CHESTER_STEM */
570 FT_Pos org_pos, org_len, org_center, cur_len;
571 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
574 org_pos = anchor->pos + (edge->opos - anchor->opos);
575 org_len = edge2->opos - edge->opos;
576 org_center = org_pos + ( org_len >> 1 );
578 #ifdef FT_CONFIG_CHESTER_SERIF
579 cur_len = ah_compute_stem_width( hinter, dimension, org_len,
580 edge->flags, edge2->flags );
581 #else /* !CHESTER_SERIF */
582 cur_len = ah_compute_stem_width( hinter, dimension, org_len );
583 #endif /* !CHESTER_SERIF */
585 #ifdef FT_CONFIG_CHESTER_STEM
591 cur_pos1 = ( org_center + 32 ) & -64;
601 delta1 = org_center - (cur_pos1 - u_off);
605 delta2 = org_center - (cur_pos1 + d_off);
609 if ( delta1 < delta2 )
614 edge->pos = cur_pos1 - cur_len / 2;
615 edge2->pos = cur_pos1 + cur_len / 2;
620 org_pos = anchor->pos + (edge->opos - anchor->opos);
621 org_len = edge2->opos - edge->opos;
622 org_center = org_pos + ( org_len >> 1 );
624 cur_len = ah_compute_stem_width( hinter, dimension, org_len,
625 edge->flags, edge2->flags );
627 cur_pos1 = ( org_pos + 32 ) & -64;
628 delta1 = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
632 cur_pos2 = ( ( org_pos + org_len + 32 ) & -64 ) - cur_len;
633 delta2 = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
637 edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
638 edge2->pos = edge->pos + cur_len;
641 #else /* !CHESTER_STEM */
643 cur_pos1 = ( org_pos + 32 ) & -64;
644 delta1 = ( cur_pos1 + ( cur_len >> 1 ) - org_center );
648 cur_pos2 = ( ( org_pos + org_len + 32 ) & -64 ) - cur_len;
649 delta2 = ( cur_pos2 + ( cur_len >> 1 ) - org_center );
653 edge->pos = ( delta1 <= delta2 ) ? cur_pos1 : cur_pos2;
654 edge2->pos = edge->pos + cur_len;
656 #endif /* !CHESTER_STEM */
658 edge->flags |= AH_EDGE_DONE;
659 edge2->flags |= AH_EDGE_DONE;
661 if ( edge > edges && edge->pos < edge[-1].pos )
662 edge->pos = edge[-1].pos;
669 /* now, hint the remaining edges (serifs and single) in order */
670 /* to complete our processing */
671 for ( edge = edges; edge < edge_limit; edge++ )
673 if ( edge->flags & AH_EDGE_DONE )
677 ah_align_serif_edge( hinter, edge->serif, edge, dimension );
680 edge->pos = ( edge->opos + 32 ) & -64;
684 edge->pos = anchor->pos +
685 ( ( edge->opos-anchor->opos + 32 ) & -64 );
687 edge->flags |= AH_EDGE_DONE;
689 if ( edge > edges && edge->pos < edge[-1].pos )
690 edge->pos = edge[-1].pos;
692 if ( edge + 1 < edge_limit &&
693 edge[1].flags & AH_EDGE_DONE &&
694 edge->pos > edge[1].pos )
695 edge->pos = edge[1].pos;
699 edges = outline->vert_edges;
700 edge_limit = edges + outline->num_vedges;
706 ah_hinter_hint_edges( AH_Hinter hinter )
708 /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */
709 /* reduce the problem of the disappearing eye in the `e' of Times... */
710 /* also, creates some artifacts near the blue zones? */
712 ah_hint_edges_3( hinter );
717 /*************************************************************************/
718 /*************************************************************************/
719 /*************************************************************************/
721 /**** P O I N T H I N T I N G ****/
723 /*************************************************************************/
724 /*************************************************************************/
725 /*************************************************************************/
728 ah_hinter_align_edge_points( AH_Hinter hinter )
730 AH_Outline outline = hinter->glyph;
736 edges = outline->horz_edges;
737 edge_limit = edges + outline->num_hedges;
739 for ( dimension = 1; dimension >= 0; dimension-- )
745 for ( ; edge < edge_limit; edge++ )
747 /* move the points of each segment */
748 /* in each edge to the edge's position */
749 AH_Segment seg = edge->first;
754 AH_Point point = seg->first;
761 point->y = edge->pos;
762 point->flags |= AH_FLAG_TOUCH_Y;
766 point->x = edge->pos;
767 point->flags |= AH_FLAG_TOUCH_X;
770 if ( point == seg->last )
776 seg = seg->edge_next;
778 } while ( seg != edge->first );
781 edges = outline->vert_edges;
782 edge_limit = edges + outline->num_vedges;
787 /* hint the strong points -- this is equivalent to the TrueType `IP' */
789 ah_hinter_align_strong_points( AH_Hinter hinter )
791 AH_Outline outline = hinter->glyph;
796 AH_Point point_limit;
800 points = outline->points;
801 point_limit = points + outline->num_points;
803 edges = outline->horz_edges;
804 edge_limit = edges + outline->num_hedges;
805 touch_flag = AH_FLAG_TOUCH_Y;
807 for ( dimension = 1; dimension >= 0; dimension-- )
813 if ( edges < edge_limit )
814 for ( point = points; point < point_limit; point++ )
816 FT_Pos u, ou, fu; /* point position */
820 if ( point->flags & touch_flag )
823 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
824 /* if this point is candidate to weak interpolation, we will */
825 /* interpolate it after all strong points have been processed */
826 if ( ( point->flags & AH_FLAG_WEAK_INTERPOLATION ) &&
827 !( point->flags & AH_FLAG_INFLECTION ) )
844 /* is the point before the first edge? */
846 delta = edge->fpos - u;
849 u = edge->pos - ( edge->opos - ou );
853 /* is the point after the last edge ? */
854 edge = edge_limit - 1;
855 delta = u - edge->fpos;
858 u = edge->pos + ( ou - edge->opos );
862 /* otherwise, interpolate the point in between */
868 for ( edge = edges; edge < edge_limit; edge++ )
870 if ( u == edge->fpos )
875 if ( u < edge->fpos )
880 for ( edge = edge_limit - 1; edge >= edges; edge-- )
882 if ( u == edge->fpos )
887 if ( u > edge->fpos )
892 /* assert( before && after && before != after ) */
893 u = before->pos + FT_MulDiv( fu - before->fpos,
894 after->pos - before->pos,
895 after->fpos - before->fpos );
900 /* save the point position */
906 point->flags |= touch_flag;
909 edges = outline->vert_edges;
910 edge_limit = edges + outline->num_vedges;
911 touch_flag = AH_FLAG_TOUCH_X;
916 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
919 ah_iup_shift( AH_Point p1,
924 FT_Pos delta = ref->u - ref->v;
927 for ( p = p1; p < ref; p++ )
930 for ( p = ref + 1; p <= p2; p++ )
936 ah_iup_interp( AH_Point p1,
945 FT_Pos d1 = ref1->u - v1;
946 FT_Pos d2 = ref2->u - v2;
954 for ( p = p1; p <= p2; p++ )
970 for ( p = p1; p <= p2; p++ )
979 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
986 for ( p = p1; p <= p2; p++ )
995 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
1003 /* interpolate weak points -- this is equivalent to the TrueType `IUP' */
1005 ah_hinter_align_weak_points( AH_Hinter hinter )
1007 AH_Outline outline = hinter->glyph;
1010 AH_Point point_limit;
1011 AH_Point* contour_limit;
1012 AH_Flags touch_flag;
1015 points = outline->points;
1016 point_limit = points + outline->num_points;
1018 /* PASS 1: Move segment points to edge positions */
1020 touch_flag = AH_FLAG_TOUCH_Y;
1022 contour_limit = outline->contours + outline->num_contours;
1024 ah_setup_uv( outline, AH_UV_OY );
1026 for ( dimension = 1; dimension >= 0; dimension-- )
1030 AH_Point first_point;
1035 contour = outline->contours;
1037 for ( ; contour < contour_limit; contour++ )
1040 end_point = point->prev;
1041 first_point = point;
1043 while ( point <= end_point && !( point->flags & touch_flag ) )
1046 if ( point <= end_point )
1048 AH_Point first_touched = point;
1049 AH_Point cur_touched = point;
1053 while ( point <= end_point )
1055 if ( point->flags & touch_flag )
1057 /* we found two successive touched points; we interpolate */
1058 /* all contour points between them */
1059 ah_iup_interp( cur_touched + 1, point - 1,
1060 cur_touched, point );
1061 cur_touched = point;
1066 if ( cur_touched == first_touched )
1068 /* this is a special case: only one point was touched in the */
1069 /* contour; we thus simply shift the whole contour */
1070 ah_iup_shift( first_point, end_point, cur_touched );
1074 /* now interpolate after the last touched point to the end */
1075 /* of the contour */
1076 ah_iup_interp( cur_touched + 1, end_point,
1077 cur_touched, first_touched );
1079 /* if the first contour point isn't touched, interpolate */
1080 /* from the contour start to the first touched point */
1081 if ( first_touched > points )
1082 ah_iup_interp( first_point, first_touched - 1,
1083 cur_touched, first_touched );
1088 /* now save the interpolated values back to x/y */
1091 for ( point = points; point < point_limit; point++ )
1092 point->y = point->u;
1094 touch_flag = AH_FLAG_TOUCH_X;
1095 ah_setup_uv( outline, AH_UV_OX );
1099 for ( point = points; point < point_limit; point++ )
1100 point->x = point->u;
1102 break; /* exit loop */
1107 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
1110 FT_LOCAL_DEF( void )
1111 ah_hinter_align_points( AH_Hinter hinter )
1113 ah_hinter_align_edge_points( hinter );
1115 #ifndef AH_OPTION_NO_STRONG_INTERPOLATION
1116 ah_hinter_align_strong_points( hinter );
1119 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
1120 ah_hinter_align_weak_points( hinter );
1125 /*************************************************************************/
1126 /*************************************************************************/
1127 /*************************************************************************/
1129 /**** H I N T E R O B J E C T M E T H O D S ****/
1131 /*************************************************************************/
1132 /*************************************************************************/
1133 /*************************************************************************/
1136 /* scale and fit the global metrics */
1138 ah_hinter_scale_globals( AH_Hinter hinter,
1143 AH_Face_Globals globals = hinter->globals;
1144 AH_Globals design = &globals->design;
1145 AH_Globals scaled = &globals->scaled;
1151 /* scale the standard widths & heights */
1152 for ( n = 0; n < design->num_widths; n++ )
1153 scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
1155 for ( n = 0; n < design->num_heights; n++ )
1156 scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
1158 scaled->stds[0] = ( design->num_widths > 0 ) ? scaled->widths[0] : 32000;
1159 scaled->stds[1] = ( design->num_heights > 0 ) ? scaled->heights[0] : 32000;
1161 /* scale the blue zones */
1162 for ( n = 0; n < AH_BLUE_MAX; n++ )
1164 FT_Pos delta, delta2;
1167 delta = design->blue_shoots[n] - design->blue_refs[n];
1171 delta2 = FT_MulFix( delta2, y_scale );
1175 else if ( delta2 < 64 )
1176 delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 );
1178 delta2 = ( delta2 + 32 ) & -64;
1183 scaled->blue_refs[n] =
1184 ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64;
1185 scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
1188 globals->x_scale = x_scale;
1189 globals->y_scale = y_scale;
1194 ah_hinter_align( AH_Hinter hinter )
1196 ah_hinter_align_edge_points( hinter );
1197 ah_hinter_align_points( hinter );
1201 /* finalize a hinter object */
1202 FT_LOCAL_DEF( void )
1203 ah_hinter_done( AH_Hinter hinter )
1207 FT_Memory memory = hinter->memory;
1210 ah_loader_done( hinter->loader );
1211 ah_outline_done( hinter->glyph );
1213 /* note: the `globals' pointer is _not_ owned by the hinter */
1214 /* but by the current face object, we don't need to */
1216 hinter->globals = 0;
1224 /* create a new empty hinter object */
1225 FT_LOCAL_DEF( FT_Error )
1226 ah_hinter_new( FT_Library library,
1227 AH_Hinter *ahinter )
1229 AH_Hinter hinter = 0;
1230 FT_Memory memory = library->memory;
1236 /* allocate object */
1237 if ( FT_NEW( hinter ) )
1240 hinter->memory = memory;
1243 /* allocate outline and loader */
1244 error = ah_outline_new( memory, &hinter->glyph ) ||
1245 ah_loader_new ( memory, &hinter->loader ) ||
1246 ah_loader_create_extra( hinter->loader );
1254 ah_hinter_done( hinter );
1260 /* create a face's autohint globals */
1261 FT_LOCAL_DEF( FT_Error )
1262 ah_hinter_new_face_globals( AH_Hinter hinter,
1264 AH_Globals globals )
1267 FT_Memory memory = hinter->memory;
1268 AH_Face_Globals face_globals;
1271 if ( FT_NEW( face_globals ) )
1274 hinter->face = face;
1275 hinter->globals = face_globals;
1278 face_globals->design = *globals;
1280 ah_hinter_compute_globals( hinter );
1282 face->autohint.data = face_globals;
1283 face->autohint.finalizer = (FT_Generic_Finalizer)
1284 ah_hinter_done_face_globals;
1285 face_globals->face = face;
1292 /* discard a face's autohint globals */
1293 FT_LOCAL_DEF( void )
1294 ah_hinter_done_face_globals( AH_Face_Globals globals )
1296 FT_Face face = globals->face;
1297 FT_Memory memory = face->memory;
1305 ah_hinter_load( AH_Hinter hinter,
1306 FT_UInt glyph_index,
1307 FT_Int32 load_flags,
1310 FT_Face face = hinter->face;
1311 FT_GlyphSlot slot = face->glyph;
1312 FT_Slot_Internal internal = slot->internal;
1313 FT_Fixed x_scale = hinter->globals->x_scale;
1314 FT_Fixed y_scale = hinter->globals->y_scale;
1316 AH_Outline outline = hinter->glyph;
1317 AH_Loader gloader = hinter->loader;
1320 /* load the glyph */
1321 error = FT_Load_Glyph( face, glyph_index, load_flags );
1325 /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
1326 hinter->transformed = internal->glyph_transformed;
1328 if ( hinter->transformed )
1333 imatrix = internal->glyph_matrix;
1334 hinter->trans_delta = internal->glyph_delta;
1335 hinter->trans_matrix = imatrix;
1337 FT_Matrix_Invert( &imatrix );
1338 FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1341 /* set linear horizontal metrics */
1342 slot->linearHoriAdvance = slot->metrics.horiAdvance;
1343 slot->linearVertAdvance = slot->metrics.vertAdvance;
1345 switch ( slot->format )
1347 case FT_GLYPH_FORMAT_OUTLINE:
1349 /* translate glyph outline if we need to */
1350 if ( hinter->transformed )
1352 FT_UInt n = slot->outline.n_points;
1353 FT_Vector* point = slot->outline.points;
1356 for ( ; n > 0; point++, n-- )
1358 point->x += hinter->trans_delta.x;
1359 point->y += hinter->trans_delta.y;
1363 /* copy the outline points in the loader's current */
1364 /* extra points, which is used to keep original glyph coordinates */
1365 error = ah_loader_check_points( gloader, slot->outline.n_points + 2,
1366 slot->outline.n_contours );
1370 FT_MEM_COPY( gloader->current.extra_points, slot->outline.points,
1371 slot->outline.n_points * sizeof ( FT_Vector ) );
1373 FT_MEM_COPY( gloader->current.outline.contours, slot->outline.contours,
1374 slot->outline.n_contours * sizeof ( short ) );
1376 FT_MEM_COPY( gloader->current.outline.tags, slot->outline.tags,
1377 slot->outline.n_points * sizeof ( char ) );
1379 gloader->current.outline.n_points = slot->outline.n_points;
1380 gloader->current.outline.n_contours = slot->outline.n_contours;
1382 /* compute original phantom points */
1385 hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
1388 /* be sure to check for spacing glyphs */
1389 if ( slot->outline.n_points == 0 )
1392 /* now, load the slot image into the auto-outline, and run the */
1393 /* automatic hinting process */
1394 error = ah_outline_load( outline, x_scale, y_scale, face );
1398 /* perform feature detection */
1399 ah_outline_detect_features( outline );
1401 if ( hinter->do_vert_hints )
1403 ah_outline_compute_blue_edges( outline, hinter->globals );
1404 ah_outline_scale_blue_edges( outline, hinter->globals );
1407 /* perform alignment control */
1408 ah_hinter_hint_edges( hinter );
1409 ah_hinter_align( hinter );
1411 /* now save the current outline into the loader's current table */
1412 ah_outline_save( outline, gloader );
1414 /* we now need to hint the metrics according to the change in */
1415 /* width/positioning that occured during the hinting process */
1417 FT_Pos old_advance, old_rsb, old_lsb, new_lsb;
1418 AH_Edge edge1 = outline->vert_edges; /* leftmost edge */
1419 AH_Edge edge2 = edge1 +
1420 outline->num_vedges - 1; /* rightmost edge */
1423 old_advance = hinter->pp2.x;
1424 old_rsb = old_advance - edge2->opos;
1425 old_lsb = edge1->opos;
1426 new_lsb = edge1->pos;
1428 hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64;
1429 hinter->pp2.x = ( ( edge2->pos + old_rsb ) + 32 ) & -64;
1432 /* try to fix certain bad advance computations */
1433 if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 )
1434 hinter->pp2.x += 64;
1438 /* good, we simply add the glyph to our loader's base */
1439 ah_loader_add( gloader );
1442 case FT_GLYPH_FORMAT_COMPOSITE:
1444 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
1445 FT_UInt num_base_subgs, start_point;
1446 FT_SubGlyph subglyph;
1449 start_point = gloader->base.outline.n_points;
1451 /* first of all, copy the subglyph descriptors in the glyph loader */
1452 error = ah_loader_check_subglyphs( gloader, num_subglyphs );
1456 FT_MEM_COPY( gloader->current.subglyphs, slot->subglyphs,
1457 num_subglyphs * sizeof ( FT_SubGlyph ) );
1459 gloader->current.num_subglyphs = num_subglyphs;
1460 num_base_subgs = gloader->base.num_subglyphs;
1462 /* now, read each subglyph independently */
1463 for ( nn = 0; nn < num_subglyphs; nn++ )
1467 FT_UInt num_points, num_new_points, num_base_points;
1470 /* gloader.current.subglyphs can change during glyph loading due */
1471 /* to re-allocation -- we must recompute the current subglyph on */
1472 /* each iteration */
1473 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1478 num_base_points = gloader->base.outline.n_points;
1480 error = ah_hinter_load( hinter, subglyph->index,
1481 load_flags, depth + 1 );
1485 /* recompute subglyph pointer */
1486 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1488 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
1499 num_points = gloader->base.outline.n_points;
1500 num_new_points = num_points - num_base_points;
1502 /* now perform the transform required for this subglyph */
1504 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
1505 FT_SUBGLYPH_FLAG_XY_SCALE |
1506 FT_SUBGLYPH_FLAG_2X2 ) )
1508 FT_Vector* cur = gloader->base.outline.points +
1510 FT_Vector* org = gloader->base.extra_points +
1512 FT_Vector* limit = cur + num_new_points;
1515 for ( ; cur < limit; cur++, org++ )
1517 FT_Vector_Transform( cur, &subglyph->transform );
1518 FT_Vector_Transform( org, &subglyph->transform );
1524 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
1526 FT_Int k = subglyph->arg1;
1527 FT_UInt l = subglyph->arg2;
1532 if ( start_point + k >= num_base_points ||
1533 l >= (FT_UInt)num_new_points )
1535 error = AH_Err_Invalid_Composite;
1539 l += num_base_points;
1541 /* for now, only use the current point coordinates */
1542 /* we may consider another approach in the near future */
1543 p1 = gloader->base.outline.points + start_point + k;
1544 p2 = gloader->base.outline.points + start_point + l;
1551 x = FT_MulFix( subglyph->arg1, x_scale );
1552 y = FT_MulFix( subglyph->arg2, y_scale );
1554 x = ( x + 32 ) & -64;
1555 y = ( y + 32 ) & -64;
1559 FT_Outline dummy = gloader->base.outline;
1562 dummy.points += num_base_points;
1563 dummy.n_points = (short)num_new_points;
1565 FT_Outline_Translate( &dummy, x, y );
1572 /* we don't support other formats (yet?) */
1573 error = AH_Err_Unimplemented_Feature;
1582 /* transform the hinted outline if needed */
1583 if ( hinter->transformed )
1584 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
1586 /* we must translate our final outline by -pp1.x, and compute */
1587 /* the new metrics */
1588 if ( hinter->pp1.x )
1589 FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
1591 FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
1594 bbox.xMax = ( bbox.xMax + 63 ) & -64;
1595 bbox.yMax = ( bbox.yMax + 63 ) & -64;
1597 slot->metrics.width = bbox.xMax - bbox.xMin;
1598 slot->metrics.height = bbox.yMax - bbox.yMin;
1599 slot->metrics.horiBearingX = bbox.xMin;
1600 slot->metrics.horiBearingY = bbox.yMax;
1602 /* for mono-width fonts (like Andale, Courier, etc.), we need */
1603 /* to keep the original rounded advance width */
1604 if ( !FT_IS_FIXED_WIDTH( slot->face ) )
1605 slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
1607 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
1610 slot->metrics.horiAdvance = ( slot->metrics.horiAdvance + 32 ) & -64;
1612 /* now copy outline into glyph slot */
1613 ah_loader_rewind( slot->internal->loader );
1614 error = ah_loader_copy_points( slot->internal->loader, gloader );
1618 slot->outline = slot->internal->loader->base.outline;
1619 slot->format = FT_GLYPH_FORMAT_OUTLINE;
1623 ah_debug_hinter = hinter;
1631 /* load and hint a given glyph */
1632 FT_LOCAL_DEF( FT_Error )
1633 ah_hinter_load_glyph( AH_Hinter hinter,
1636 FT_UInt glyph_index,
1637 FT_Int32 load_flags )
1639 FT_Face face = slot->face;
1641 FT_Fixed x_scale = size->metrics.x_scale;
1642 FT_Fixed y_scale = size->metrics.y_scale;
1643 AH_Face_Globals face_globals = FACE_GLOBALS( face );
1644 FT_Render_Mode hint_mode = FT_LOAD_TARGET_MODE(load_flags);
1647 /* first of all, we need to check that we're using the correct face and */
1648 /* global hints to load the glyph */
1649 if ( hinter->face != face || hinter->globals != face_globals )
1651 hinter->face = face;
1652 if ( !face_globals )
1654 error = ah_hinter_new_face_globals( hinter, face, 0 );
1659 hinter->globals = FACE_GLOBALS( face );
1660 face_globals = FACE_GLOBALS( face );
1664 #ifdef FT_CONFIG_CHESTER_BLUE_SCALE
1665 /* try to optimize the y_scale so that the top of non-capital letters
1666 * is aligned on a pixel boundary whenever possible
1669 AH_Globals design = &face_globals->design;
1670 FT_Pos shoot = design->blue_shoots[ AH_BLUE_SMALL_TOP ];
1672 /* the value of 'shoot' will be -1000 if the font doesn't have */
1673 /* small latin letters; we simply check the sign here... */
1676 FT_Pos scaled = FT_MulFix( shoot, y_scale );
1677 FT_Pos fitted = ( scaled + 32 ) & -64;
1679 if ( scaled != fitted )
1683 y_scale = FT_MulDiv( y_scale, fitted, scaled );
1687 if ( fitted < scaled )
1688 x_scale -= x_scale/50; /* x_scale*0.98 with integers */
1692 #endif /* FT_CONFIG_CHESTER_BLUE_SCALE */
1694 /* now, we must check the current character pixel size to see if we */
1695 /* need to rescale the global metrics */
1696 if ( face_globals->x_scale != x_scale ||
1697 face_globals->y_scale != y_scale )
1698 ah_hinter_scale_globals( hinter, x_scale, y_scale );
1700 ah_loader_rewind( hinter->loader );
1702 /* reset hinting flags according to load flags and current render target */
1703 hinter->do_horz_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1704 hinter->do_vert_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
1707 hinter->do_horz_hints = !ah_debug_disable_vert; /* not a bug, the meaning */
1708 hinter->do_vert_hints = !ah_debug_disable_horz; /* of h/v is inverted! */
1711 /* we snap the width of vertical stems for the monochrome and */
1712 /* horizontal LCD rendering targets only. Corresponds to X snapping. */
1713 hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1714 hint_mode == FT_RENDER_MODE_LCD );
1716 /* we snap the width of horizontal stems for the monochrome and */
1717 /* vertical LCD rendering targets only. Corresponds to Y snapping. */
1718 hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1719 hint_mode == FT_RENDER_MODE_LCD_V );
1721 hinter->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
1724 load_flags = FT_LOAD_NO_SCALE
1725 | FT_LOAD_IGNORE_TRANSFORM ;
1727 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
1730 error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1737 /* retrieve a face's autohint globals for client applications */
1738 FT_LOCAL_DEF( void )
1739 ah_hinter_get_global_hints( AH_Hinter hinter,
1741 void** global_hints,
1744 AH_Globals globals = 0;
1745 FT_Memory memory = hinter->memory;
1749 /* allocate new master globals */
1750 if ( FT_NEW( globals ) )
1753 /* compute face globals if needed */
1754 if ( !FACE_GLOBALS( face ) )
1756 error = ah_hinter_new_face_globals( hinter, face, 0 );
1761 *globals = FACE_GLOBALS( face )->design;
1762 *global_hints = globals;
1763 *global_len = sizeof( *globals );
1775 FT_LOCAL_DEF( void )
1776 ah_hinter_done_global_hints( AH_Hinter hinter,
1777 void* global_hints )
1779 FT_Memory memory = hinter->memory;
1782 FT_FREE( global_hints );