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 <type1/t1objs.h>
39 #include <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
,