1 /***************************************************************************/
5 /* Type 1 hinter (body). */
7 /* Copyright 1996-2000 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
16 /***************************************************************************/
19 /*************************************************************************/
21 /* The Hinter is in charge of fitting th scaled outline to the pixel */
22 /* grid in order to considerably improve the quality of the Type 1 font */
23 /* driver's output. */
25 /*************************************************************************/
28 #include <freetype/internal/ftdebug.h>
31 #ifdef FT_FLAT_COMPILE
38 #include <freetype/src/type1/t1objs.h>
39 #include <freetype/src/type1/t1hinter.h>
44 /*************************************************************************/
46 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
47 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
48 /* messages during execution. */
51 #define FT_COMPONENT trace_t1hint
58 #define ROUND( x ) ( ( x + ONE_PIXEL / 2 ) & -ONE_PIXEL )
61 #define SCALE( val ) FT_MulFix( val, scale )
63 /* various constants used to describe the alignment of a horizontal */
64 /* stem with regards to the blue zones */
66 #define T1_ALIGN_NONE 0
67 #define T1_ALIGN_BOTTOM 1
68 #define T1_ALIGN_TOP 2
69 #define T1_ALIGN_BOTH 3
72 /* very simple bubble sort (not a lot of elements, mostly */
73 /* pre-sorted, no need for quicksort) */
76 void t1_sort_blues( FT_Int* blues,
83 for ( i = 2; i < count; i += 2 )
88 if ( cur[-1] < cur[0] )
91 swap = cur[-2]; cur[-2] = cur[0]; cur[0] = swap;
92 swap = cur[-1]; cur[-1] = cur[1]; cur[1] = swap;
95 } while ( cur > blues );
100 /*************************************************************************/
103 /* t1_set_blue_zones */
106 /* Sets a size object's blue zones during reset. This will compute */
107 /* the `snap' zone corresponding to each blue zone. */
110 /* size :: A handle to target size object. */
113 /* FreeType error code. 0 means success. */
116 /* This functions does the following: */
118 /* 1. It extracts the bottom and top blue zones from the face object. */
120 /* 2. Each zone is then grown by `BlueFuzz', overlapping is */
121 /* eliminated by adjusting the zone edges appropriately. */
123 /* 3. For each zone, we keep its original font units position, its */
124 /* original scaled position, as well as its grown/adjusted edges. */
127 FT_Error t1_set_blue_zones( T1_Size size )
129 T1_Face face = (T1_Face)size->root.face;
130 T1_Private* priv = &face->type1.private_dict;
136 T1_Size_Hints* hints = size->hints;
139 FT_Pos min, max, threshold;
144 /***********************************************************************/
146 /* copy bottom and top blue zones in local arrays */
149 /* First of all, check the sizes of the /BlueValues and /OtherBlues */
150 /* tables. They all must contain an even number of arguments. */
151 if ( priv->num_other_blues & 1 ||
152 priv->num_blue_values & 1 )
154 FT_ERROR(( "t1_set_blue_zones: odd number of blue values\n" ));
155 return T1_Err_Syntax_Error;
158 /* copy the bottom blue zones from /OtherBlues */
160 num_bottom = priv->num_other_blues;
162 for ( n = 0; n < num_bottom; n++ )
163 blues[n] = priv->other_blues[n];
165 /* add the first blue zone in /BlueValues to the table */
166 num_top = priv->num_blue_values - 2;
169 blues[num_bottom ] = priv->blue_values[0];
170 blues[num_bottom + 1] = priv->blue_values[1];
175 /* sort the bottom blue zones */
176 t1_sort_blues( blues, num_bottom );
178 hints->num_bottom_zones = num_bottom >> 1;
180 /* now copy the /BlueValues to the top of the blues array */
183 for ( n = 0; n < num_top; n++ )
184 blues[num_bottom + n] = priv->blue_values[n + 2];
186 /* sort the top blue zones */
187 t1_sort_blues( blues + num_bottom, num_top );
192 num_blues = num_top + num_bottom;
193 hints->num_blue_zones = ( num_blues ) >> 1;
195 /***********************************************************************/
197 /* build blue snap zones from the local blues arrays */
200 scale = size->root.metrics.y_scale;
201 zone = hints->blue_zones;
202 threshold = ONE_PIXEL / 4; /* 0.25 pixels */
204 for ( n = 0; n < num_blues; n += 2, zone++ )
206 is_bottom = n < num_bottom ? 1 : 0;
208 orus = blues[n + is_bottom]; /* get alignement coordinate */
209 pix = SCALE( orus ); /* scale it */
211 min = SCALE( blues[n ] - priv->blue_fuzz );
212 max = SCALE( blues[n + 1] + priv->blue_fuzz );
214 if ( min > pix - threshold )
215 min = pix - threshold;
216 if ( max < pix + threshold )
217 max = pix + threshold;
225 /* adjust edges in case of overlap */
226 zone = hints->blue_zones;
227 for ( n = 0; n < num_blues - 2; n += 2, zone++ )
229 if ( n != num_bottom - 2 &&
230 zone[0].max > zone[1].min )
231 zone[0].max = zone[1].min = ( zone[0].pix + zone[1].pix ) / 2;
234 /* compare the current pixel size with the BlueScale value */
235 /* to know whether to supress overshoots */
237 hints->supress_overshoots =
238 size->root.metrics.y_ppem < FT_MulFix( 1000, priv->blue_scale );
240 #ifdef FT_DEBUG_LEVEL_TRACE
242 /* now print the new blue values in tracing mode */
244 FT_TRACE2(( "Blue Zones for size object at $%08lx:\n", (long)size ));
245 FT_TRACE2(( " orus pix min max\n" ));
246 FT_TRACE2(( "-------------------------------\n" ));
248 zone = hints->blue_zones;
249 for ( n = 0; n < hints->num_blue_zones; n++ )
251 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
258 FT_TRACE2(( "\nOvershoots are %s\n\n",
259 hints->supress_overshoots ? "supressed" : "active" ));
261 #endif /* DEBUG_LEVEL_TRACE */
267 /*************************************************************************/
270 /* t1_set_snap_zones */
273 /* This function set a size object's stem snap zones. */
276 /* size :: A handle to the target size object. */
279 /* FreeType error code. 0 means success. */
282 /* This function performs the following: */
284 /* 1. It reads and scales the stem snap widths from the parent face. */
286 /* 2. A `snap zone' is computed for each snap width, by `growing' it */
287 /* with a threshold of 1/2 pixel. Overlapping is avoided through */
288 /* proper edge adjustment. */
290 /* 3. Each width whose zone contain the scaled standard set width is */
291 /* removed from the table. */
293 /* 4. Finally, the standard set width is scaled, and its correponding */
294 /* `snap zone' is inserted into the sorted snap zones table. */
297 FT_Error t1_set_snap_zones( T1_Size size )
299 FT_Int n, direction, n_zones, num_zones;
301 T1_Snap_Zone* base_zone;
303 FT_Pos standard_width;
306 T1_Face face = (T1_Face)size->root.face;
307 T1_Private* priv = &face->type1.private_dict;
308 T1_Size_Hints* hints = size->hints;
311 /* start with horizontal snap zones */
313 standard_width = priv->standard_width[0];
314 n_zones = priv->num_snap_widths;
315 base_zone = hints->snap_widths;
316 orgs = priv->snap_widths;
317 scale = size->root.metrics.x_scale;
319 while ( direction < 2 )
321 /*********************************************************************/
323 /* Read and scale stem snap widths table from the physical font */
327 FT_Pos prev, orus, pix, min, max, threshold;
330 threshold = ONE_PIXEL / 4;
337 min = pix - threshold;
338 max = pix + threshold;
345 for ( n = 1; n < n_zones; n++ )
350 if ( pix - prev < 2 * threshold )
352 min = max = ( pix + prev ) / 2;
355 min = pix - threshold;
363 max = pix + threshold;
369 #ifdef FT_DEBUG_LEVEL_TRACE
371 /* print the scaled stem snap values in tracing mode */
373 FT_TRACE2(( "Set_Snap_Zones: first %s pass\n",
374 direction ? "vertical" : "horizontal" ));
376 FT_TRACE2(( "Scaled original stem snap zones:\n" ));
377 FT_TRACE2(( " orus pix min max\n" ));
378 FT_TRACE2(( "-----------------------------\n" ));
381 for ( n = 0; n < n_zones; n++, zone++ )
382 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
389 FT_TRACE2(( "Standard width = %d\n", standard_width ));
391 #endif /* FT_DEBUG_LEVEL_TRACE */
393 /*********************************************************************/
395 /* Now, each snap width which is in the range of the standard set */
396 /* width will be removed from the list. */
399 if ( standard_width > 0 )
401 T1_Snap_Zone* parent;
402 FT_Pos std_pix, std_min, std_max;
405 std_pix = SCALE( standard_width );
407 std_min = std_pix - threshold;
408 std_max = std_pix + threshold;
414 for ( n = 0; n < n_zones; n++ )
416 if ( zone->pix >= std_min && zone->pix <= std_max )
418 /* this zone must be removed from the list */
419 if ( std_min > zone->min )
421 if ( std_max < zone->max )
432 /*******************************************************************/
434 /* Now, insert the standard width zone */
437 zone = base_zone + num_zones;
438 while ( zone > base_zone && zone[-1].pix > std_max )
444 /* check border zones */
445 if ( zone > base_zone && zone[-1].max > std_min )
446 zone[-1].max = std_min;
448 if ( zone < base_zone + num_zones && zone[1].min < std_max )
449 zone[1].min = std_max;
451 zone->orus = standard_width;
461 /* save total number of stem snaps now */
463 hints->num_snap_heights = num_zones;
465 hints->num_snap_widths = num_zones;
467 #ifdef FT_DEBUG_LEVEL_TRACE
469 /* print the scaled stem snap values in tracing mode */
471 FT_TRACE2(( "Set_Snap_Zones: second %s pass\n",
472 direction ? "vertical" : "horizontal" ));
474 FT_TRACE2(( "Scaled clipped stem snap zones:\n" ));
475 FT_TRACE2(( " orus pix min max\n" ));
476 FT_TRACE2(( "-----------------------------\n" ));
479 for ( n = 0; n < num_zones; n++, zone++ )
480 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
487 FT_TRACE2(( "Standard width = %d\n", standard_width ));
489 #endif /* FT_DEBUG_LEVEL_TRACE */
491 /* continue with vertical snap zone */
493 standard_width = priv->standard_height[0];
494 n_zones = priv->num_snap_heights;
495 base_zone = hints->snap_heights;
496 orgs = priv->snap_heights;
497 scale = size->root.metrics.y_scale;
504 /*************************************************************************/
507 /* T1_New_Size_Hinter */
510 /* Allocates a new hinter structure for a given size object. */
513 /* size :: A handle to the target size object. */
516 /* FreeType Error code. 0 means success. */
519 FT_Error T1_New_Size_Hinter( T1_Size size )
521 FT_Memory memory = size->root.face->memory;
524 return MEM_Alloc( size->hints, sizeof ( *size->hints ) );
528 /*************************************************************************/
531 /* T1_Done_Size_Hinter */
534 /* Releases a given size object's hinter structure. */
537 /* size :: A handle to the target size object. */
540 void T1_Done_Size_Hinter( T1_Size size )
542 FT_Memory memory = size->root.face->memory;
549 /*************************************************************************/
552 /* T1_Reset_Size_Hinter */
555 /* Recomputes hinting information when a given size object has */
556 /* changed its resolutions/char sizes/pixel sizes. */
559 /* size :: A handle to the size object. */
562 /* FreeType error code. 0 means success. */
565 FT_Error T1_Reset_Size_Hinter( T1_Size size )
567 return t1_set_blue_zones( size ) || t1_set_snap_zones( size );
571 /*************************************************************************/
574 /* T1_New_Glyph_Hinter */
577 /* Allocates a new hinter structure for a given glyph slot. */
580 /* glyph :: A handle to the target glyph slot. */
583 /* FreeType error code. 0 means success. */
586 FT_Error T1_New_Glyph_Hinter( T1_GlyphSlot glyph )
588 FT_Memory memory = glyph->root.face->memory;
591 return MEM_Alloc( glyph->hints, sizeof ( *glyph->hints ) );
595 /*************************************************************************/
598 /* T1_Done_Glyph_Hinter */
601 /* Releases a given glyph slot's hinter structure. */
604 /* glyph :: A handle to the glyph slot. */
607 void T1_Done_Glyph_Hinter( T1_GlyphSlot glyph )
609 FT_Memory memory = glyph->root.face->memory;
612 FREE( glyph->hints );
616 /*************************************************************************/
617 /*************************************************************************/
618 /*************************************************************************/
619 /********** **********/
620 /********** HINTED GLYPH LOADER **********/
621 /********** **********/
622 /********** The following code is in charge of the first **********/
623 /********** and second pass when loading a single outline **********/
624 /********** **********/
625 /*************************************************************************/
626 /*************************************************************************/
627 /*************************************************************************/
631 FT_Error t1_hinter_ignore( void )
633 /* do nothing, used for `dotsection' which is unsupported for now */
639 FT_Error t1_hinter_stem( T1_Builder* builder,
644 T1_Stem_Table* stem_table;
646 T1_Stem_Hint* cur_stem;
647 FT_Int min, max, n, num_stems;
649 T1_Glyph_Hints* hinter = builder->glyph->hints;
652 /* select the appropriate stem array */
653 stem_table = vertical ? &hinter->vert_stems : &hinter->hori_stems;
654 stems = stem_table->stems;
655 num_stems = stem_table->num_stems;
657 /* Compute minimum and maximum coord for the stem */
658 min = pos + ( vertical
659 ? builder->left_bearing.x
660 : builder->left_bearing.y );
666 /* a negative width indicates a `ghost' stem */
673 /* Now scan the array. If we find a stem with the same borders */
674 /* simply activate it. */
678 for ( n = 0; n < num_stems; n++, cur_stem++ )
680 if ( cur_stem->min_edge.orus == min &&
681 cur_stem->max_edge.orus == max )
683 /* This stem is already in the table, simply activate it */
684 if ( ( cur_stem->hint_flags & T1_HINT_FLAG_ACTIVE ) == 0 )
686 cur_stem->hint_flags |= T1_HINT_FLAG_ACTIVE;
687 stem_table->num_active++;
694 /* add a new stem to the array if necessary */
697 if ( cur_stem >= stems + T1_HINTER_MAX_EDGES )
699 FT_ERROR(( "t1_hinter_stem: too many stems in glyph charstring\n" ));
700 return T1_Err_Syntax_Error;
703 /* on the first pass, we record the stem, otherwise, this is */
704 /* a bug in the glyph loader! */
705 if ( builder->pass == 0 )
707 cur_stem->min_edge.orus = min;
708 cur_stem->max_edge.orus = max;
709 cur_stem->hint_flags = T1_HINT_FLAG_ACTIVE;
711 stem_table->num_stems++;
712 stem_table->num_active++;
716 FT_ERROR(( "t1_hinter_stem:" ));
717 FT_ERROR(( " fatal glyph loader bug -- pass2-stem\n" ));
718 return T1_Err_Syntax_Error;
727 FT_Error t1_hinter_stem3( T1_Builder* builder,
736 /* For now, simply call `stem' 3 times */
737 return t1_hinter_stem( builder, pos0, width0, vertical ) ||
738 t1_hinter_stem( builder, pos1, width1, vertical ) ||
739 t1_hinter_stem( builder, pos2, width2, vertical );
744 FT_Error t1_hinter_changehints( T1_Builder* builder )
747 T1_Stem_Table* stem_table;
748 T1_Glyph_Hints* hinter = builder->glyph->hints;
751 /* If we are in the second pass of glyph hinting, we must */
752 /* call the function T1_Hint_Points() on the builder in order */
753 /* to force the fit the latest points to the pixel grid. */
754 if ( builder->pass == 1 )
755 T1_Hint_Points( builder );
757 /* Simply de-activate all hints in all arrays */
758 stem_table = &hinter->hori_stems;
760 for ( dimension = 2; dimension > 0; dimension-- )
762 T1_Stem_Hint* cur = stem_table->stems;
763 T1_Stem_Hint* limit = cur + stem_table->num_stems;
766 for ( ; cur < limit; cur++ )
767 cur->hint_flags &= ~T1_HINT_FLAG_ACTIVE;
769 stem_table->num_active = 0;
770 stem_table = &hinter->vert_stems;
777 const T1_Hinter_Funcs t1_hinter_funcs =
779 (T1_Hinter_ChangeHints)t1_hinter_changehints,
780 (T1_Hinter_DotSection) t1_hinter_ignore,
781 (T1_Hinter_Stem) t1_hinter_stem,
782 (T1_Hinter_Stem3) t1_hinter_stem3
786 /*************************************************************************/
787 /*************************************************************************/
788 /*************************************************************************/
789 /********** *********/
790 /********** *********/
791 /********** STEM HINTS MANAGEMENT *********/
792 /********** *********/
793 /********** The following code is in charge of computing *********/
794 /********** the placement of each scaled stem hint. *********/
795 /********** *********/
796 /*************************************************************************/
797 /*************************************************************************/
798 /*************************************************************************/
801 /*************************************************************************/
807 /* Sorta the list of active stems in increasing order, through the */
808 /* `sort' indexing table. */
811 /* table :: A stem hints table. */
814 void t1_sort_hints( T1_Stem_Table* table )
816 FT_Int num_stems = table->num_stems;
817 FT_Int num_active = 0;
818 FT_Int* sort = table->sort;
819 T1_Stem_Hint* stems = table->stems;
823 /* record active stems in sort table */
824 for ( n = 0; n < num_stems; n++ )
826 if ( stems[n].hint_flags & T1_HINT_FLAG_ACTIVE )
827 sort[num_active++] = n;
830 /* Now sort the indices. There are usually very few stems, */
831 /* and they are pre-sorted in 90% cases, so we choose a */
832 /* simple bubble sort (quicksort would be slower). */
833 for ( n = 1; n < num_active; n++ )
836 T1_Stem_Hint* cur = stems + sort[n];
842 T1_Stem_Hint* prev = stems + sort[p];
845 /* note that by definition, the active stems cannot overlap */
846 /* so we simply compare their `min' to sort them (we could compare */
847 /* their max values also; this wouldn't change anything). */
848 if ( prev->min_edge.orus <= cur->min_edge.orus )
853 sort[p ] = sort[p + 1];
860 table->num_active = num_active;
864 /*************************************************************************/
867 /* t1_hint_horizontal_stems */
870 /* Computes the location of each scaled horizontal stem hint. This */
871 /* takes care of the blue zones and the horizontal stem snap table. */
874 /* table :: The horizontal stem hints table. */
876 /* hints :: The current size's hint structure. */
878 /* blueShift :: The value of the /BlueShift as taken from the face */
881 /* scale :: The 16.16 scale used to convert outline units to */
885 /* For now, all stems are hinted independently from each other. It */
886 /* might be necessary, for better performance, to introduce the */
887 /* notion of `controlled' hints describing things like counter-stems, */
888 /* stem3, as well as overlapping stems control. */
891 void t1_hint_horizontal_stems( T1_Stem_Table* table,
892 T1_Size_Hints* hints,
896 T1_Stem_Hint* stem = table->stems;
897 T1_Stem_Hint* limit = stem + table->num_stems;
900 /* first of all, scale the blueShift */
901 blueShift = SCALE( blueShift );
903 /* then scan the horizontal stem table */
904 for ( ; stem < limit; stem++ )
906 FT_Pos bottom_orus = stem->min_edge.orus;
907 FT_Pos top_orus = stem->max_edge.orus;
909 FT_Pos top_pix = SCALE( top_orus );
910 FT_Pos bottom_pix = SCALE( bottom_orus );
911 FT_Pos width_pix = top_pix - bottom_pix;
913 FT_Pos bottom = bottom_pix;
914 FT_Pos top = top_pix;
915 FT_Int align = T1_ALIGN_NONE;
918 /*********************************************************************/
920 /* Snap pixel width if in stem snap range */
924 T1_Snap_Zone* zone = hints->snap_heights;
925 T1_Snap_Zone* zone_limit = zone + hints->num_snap_heights;
926 FT_Pos best_dist = 32000;
927 T1_Snap_Zone* best_zone = 0;
930 for ( ; zone < zone_limit; zone++ )
935 dist = width_pix - zone->min;
938 if ( dist < best_dist )
947 if ( width_pix > best_zone->pix )
950 if ( width_pix < best_zone->pix )
951 width_pix = best_zone->pix;
956 if ( width_pix > best_zone->pix )
957 width_pix = best_zone->pix;
962 /*********************************************************************/
964 /* round width - minimum 1 pixel if this isn't a ghost stem */
968 width_pix = width_pix < ONE_PIXEL ? ONE_PIXEL : ROUND( width_pix );
971 /*********************************************************************/
973 /* Now check for bottom blue zones alignement */
977 FT_Int num_blues = hints->num_bottom_zones;
978 T1_Snap_Zone* blue = hints->blue_zones;
979 T1_Snap_Zone* blue_limit = blue + num_blues;
982 for ( ; blue < blue_limit; blue++ )
984 if ( bottom_pix < blue->min )
987 if ( bottom_pix <= blue->max )
989 align = T1_ALIGN_BOTTOM;
990 bottom = ROUND( blue->pix );
992 /* implement blue shift */
993 if ( !hints->supress_overshoots )
995 FT_Pos delta = blue->pix - bottom_pix;
998 delta = delta < blueShift ? 0 : ROUND( delta );
1005 /*********************************************************************/
1007 /* check for top blue zones alignement */
1011 FT_Int num_blues = hints->num_blue_zones -
1012 hints->num_bottom_zones;
1014 T1_Snap_Zone* blue = hints->blue_zones +
1015 hints->num_bottom_zones;
1017 T1_Snap_Zone* blue_limit = blue + num_blues;
1020 for ( ; blue < blue_limit; blue++ )
1022 if ( top_pix < blue->min )
1025 if ( top_pix <= blue->max )
1027 align |= T1_ALIGN_TOP;
1028 top = ROUND( blue->pix );
1030 /* implement blue shift */
1031 if ( !hints->supress_overshoots )
1033 FT_Pos delta = top - blue->pix;
1036 delta = delta < blueShift ? 0 : ROUND( delta );
1043 /*********************************************************************/
1045 /* compute the hinted stem position, according to its alignment */
1050 case T1_ALIGN_BOTTOM: /* bottom zone alignment */
1051 bottom_pix = bottom;
1052 top_pix = bottom + width_pix;
1055 case T1_ALIGN_TOP: /* top zone alignment */
1057 bottom_pix = top - width_pix;
1060 case T1_ALIGN_BOTH: /* bottom+top zone alignment */
1061 bottom_pix = bottom;
1065 default: /* no alignment */
1066 /* XXX TODO: Add management of controlled stems */
1067 bottom = ( SCALE( bottom_orus + top_orus ) - width_pix ) / 2;
1069 bottom_pix = ROUND( bottom );
1070 top_pix = bottom_pix + width_pix;
1073 stem->min_edge.pix = bottom_pix;
1074 stem->max_edge.pix = top_pix;
1079 /*************************************************************************/
1082 /* t1_hint_vertical_stems */
1085 /* Computes the location of each scaled vertical stem hint. This */
1086 /* takes care of the vertical stem snap table. */
1089 /* table :: The vertical stem hints table. */
1090 /* hints :: The current size's hint structure. */
1091 /* scale :: The 16.16 scale used to convert outline units to */
1095 /* For now, all stems are hinted independently from each other. It */
1096 /* might be necessary, for better performance, to introduce the */
1097 /* notion of `controlled' hints describing things like counter-stems, */
1098 /* stem3 as well as overlapping stems control. */
1101 void t1_hint_vertical_stems( T1_Stem_Table* table,
1102 T1_Size_Hints* hints,
1105 T1_Stem_Hint* stem = table->stems;
1106 T1_Stem_Hint* limit = stem + table->num_stems;
1109 for ( ; stem < limit; stem++ )
1111 FT_Pos stem_left = stem->min_edge.orus;
1112 FT_Pos stem_right = stem->max_edge.orus;
1113 FT_Pos width_pix, left;
1116 width_pix = SCALE( stem_right - stem_left );
1118 /* Snap pixel width if in stem snap range */
1120 T1_Snap_Zone* zone = hints->snap_heights;
1121 T1_Snap_Zone* zone_limit = zone + hints->num_snap_heights;
1122 FT_Pos best_dist = 32000;
1123 T1_Snap_Zone* best_zone = 0;
1126 for ( ; zone < zone_limit; zone++ )
1131 dist = width_pix - zone->min;
1134 if ( dist < best_dist )
1143 if ( width_pix > best_zone->pix )
1146 if ( width_pix < best_zone->pix )
1147 width_pix = best_zone->pix;
1152 if ( width_pix > best_zone->pix )
1153 width_pix = best_zone->pix;
1158 /* round width - minimum 1 pixel if this isn't a ghost stem */
1159 if ( width_pix > 0 )
1160 width_pix = width_pix < ONE_PIXEL ? ONE_PIXEL
1161 : ROUND( width_pix );
1163 /* now place the snapped and rounded stem */
1165 /* XXX TODO: implement controlled stems for the overlapping */
1168 left = ( SCALE( stem_left + stem_right ) - width_pix ) / 2;
1170 stem->min_edge.pix = ROUND( left );
1171 stem->max_edge.pix = stem->min_edge.pix + width_pix;
1176 /*************************************************************************/
1182 /* Grid-fit a coordinate with regards to a given stem hints table. */
1185 /* table :: The source stem hints table. */
1186 /* coord :: The original coordinate, expressed in font units. */
1187 /* scale :: The 16.16 scale used to convert font units into */
1191 /* The hinted/scaled value in 26.6 pixels. */
1194 /* For now, all stems are hinted independently from each other. It */
1195 /* might be necessary, for better performance, to introduce the */
1196 /* notion of `controlled' hints describing things like counter-stems, */
1197 /* stem3 as well as overlapping stems control. */
1200 FT_Pos t1_hint_point( T1_Stem_Table* table,
1204 FT_Int num_active = table->num_active;
1206 T1_Stem_Hint* prev = 0;
1207 T1_Stem_Hint* cur = 0;
1213 /* only hint when there is at least one stem defined */
1214 if ( num_active <= 0 )
1215 return SCALE( coord );
1217 /* scan the stem table to determine placement of coordinate */
1218 /* relative to the list of sorted and stems */
1219 for ( n = 0; n < num_active; n++, prev = cur )
1221 cur = table->stems + table->sort[n];
1223 /* is it on the left of the current edge? */
1224 delta = cur->min_edge.orus - coord;
1226 return cur->min_edge.pix;
1230 /* if this is the left of the first edge, simply shift */
1232 return cur->min_edge.pix - SCALE( delta );
1234 /* otherwise, interpolate between the maximum of the */
1235 /* previous stem, and the minimum of the current one */
1236 min = &prev->max_edge;
1237 max = &cur->min_edge;
1242 /* is it within the current edge? */
1243 delta = cur->max_edge.orus - coord;
1245 return cur->max_edge.pix;
1249 /* interpolate within the stem */
1250 min = &cur->min_edge;
1251 max = &cur->max_edge;
1257 /* apparently, this coordinate is on the right of the last stem */
1258 delta = coord - cur->max_edge.orus;
1259 return cur->max_edge.pix + SCALE( delta );
1262 return min->pix + FT_MulDiv( coord - min->orus,
1263 max->pix - min->pix,
1264 max->orus - min->orus );
1268 /*************************************************************************/
1271 /* T1_Hint_Points */
1274 /* This function grid-fits several points in a given Type 1 builder */
1278 /* builder :: A handle to target Type 1 builder. */
1281 void T1_Hint_Points( T1_Builder* builder )
1283 FT_Int first = builder->hint_point;
1284 FT_Int last = builder->current->n_points - 1;
1286 T1_Size size = builder->size;
1287 FT_Fixed scale_x = size->root.metrics.x_scale;
1288 FT_Fixed scale_y = size->root.metrics.y_scale;
1290 T1_Glyph_Hints* hints = builder->glyph->hints;
1291 T1_Stem_Table* hori_stems = &hints->hori_stems;
1292 T1_Stem_Table* vert_stems = &hints->vert_stems;
1294 FT_Vector* cur = builder->current->points + first;
1295 FT_Vector* limit = cur + last - first + 1;
1298 /* first of all, sort the active stem hints */
1299 t1_sort_hints( hori_stems );
1300 t1_sort_hints( vert_stems );
1302 for ( ; cur < limit; cur++ )
1304 cur->x = t1_hint_point( vert_stems, cur->x, scale_x );
1305 cur->y = t1_hint_point( hori_stems, cur->y, scale_y );
1308 builder->hint_point = builder->current->n_points;
1312 /*************************************************************************/
1318 /* This function is used to compute the location of each stem hint */
1319 /* between the first and second passes of the glyph loader on the */
1323 /* builder :: A handle to the target builder. */
1326 void T1_Hint_Stems( T1_Builder* builder )
1328 T1_Glyph_Hints* hints = builder->glyph->hints;
1329 T1_Private* priv = &builder->face->type1.private_dict;
1331 T1_Size size = builder->size;
1332 FT_Fixed scale_x = size->root.metrics.x_scale;
1333 FT_Fixed scale_y = size->root.metrics.y_scale;
1336 t1_hint_horizontal_stems( &hints->hori_stems,
1337 builder->size->hints,
1341 t1_hint_vertical_stems( &hints->vert_stems,
1342 builder->size->hints,