1 /***************************************************************************/
5 /* PostScript hinting algorithm (body). */
7 /* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 */
9 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
11 /* This file is part of the FreeType project, and may only be used */
12 /* modified and distributed under the terms of the FreeType project */
13 /* license, LICENSE.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 /***************************************************************************/
21 #include FT_INTERNAL_OBJECTS_H
22 #include FT_INTERNAL_DEBUG_H
23 #include FT_INTERNAL_CALC_H
30 #define FT_COMPONENT trace_pshalgo2
34 PSH_Hint_Table ps_debug_hint_table
= 0;
35 PSH_HintFunc ps_debug_hint_func
= 0;
36 PSH_Glyph ps_debug_glyph
= 0;
40 #define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */
41 /* and similar glyphs */
42 #define STRONGER /* slightly increase the contrast of smooth */
46 /*************************************************************************/
47 /*************************************************************************/
49 /***** BASIC HINTS RECORDINGS *****/
51 /*************************************************************************/
52 /*************************************************************************/
54 /* return true if two stem hints overlap */
56 psh_hint_overlap( PSH_Hint hint1
,
59 return hint1
->org_pos
+ hint1
->org_len
>= hint2
->org_pos
&&
60 hint2
->org_pos
+ hint2
->org_len
>= hint1
->org_pos
;
64 /* destroy hints table */
66 psh_hint_table_done( PSH_Hint_Table table
,
69 FT_FREE( table
->zones
);
73 FT_FREE( table
->sort
);
74 FT_FREE( table
->hints
);
77 table
->sort_global
= 0;
81 /* deactivate all hints in a table */
83 psh_hint_table_deactivate( PSH_Hint_Table table
)
85 FT_UInt count
= table
->max_hints
;
86 PSH_Hint hint
= table
->hints
;
89 for ( ; count
> 0; count
--, hint
++ )
91 psh_hint_deactivate( hint
);
97 /* internal function to record a new hint */
99 psh_hint_table_record( PSH_Hint_Table table
,
102 PSH_Hint hint
= table
->hints
+ idx
;
105 if ( idx
>= table
->max_hints
)
107 FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx
));
111 /* ignore active hints */
112 if ( psh_hint_is_active( hint
) )
115 psh_hint_activate( hint
);
117 /* now scan the current active hint set to check */
118 /* whether `hint' overlaps with another hint */
120 PSH_Hint
* sorted
= table
->sort_global
;
121 FT_UInt count
= table
->num_hints
;
126 for ( ; count
> 0; count
--, sorted
++ )
130 if ( psh_hint_overlap( hint
, hint2
) )
132 hint
->parent
= hint2
;
138 if ( table
->num_hints
< table
->max_hints
)
139 table
->sort_global
[table
->num_hints
++] = hint
;
141 FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" ));
146 psh_hint_table_record_mask( PSH_Hint_Table table
,
149 FT_Int mask
= 0, val
= 0;
150 FT_Byte
* cursor
= hint_mask
->bytes
;
154 limit
= hint_mask
->num_bits
;
156 for ( idx
= 0; idx
< limit
; idx
++ )
165 psh_hint_table_record( table
, idx
);
172 /* create hints table */
174 psh_hint_table_init( PSH_Hint_Table table
,
176 PS_Mask_Table hint_masks
,
177 PS_Mask_Table counter_masks
,
183 FT_UNUSED( counter_masks
);
186 count
= hints
->num_hints
;
188 /* allocate our tables */
189 if ( FT_NEW_ARRAY( table
->sort
, 2 * count
) ||
190 FT_NEW_ARRAY( table
->hints
, count
) ||
191 FT_NEW_ARRAY( table
->zones
, 2 * count
+ 1 ) )
194 table
->max_hints
= count
;
195 table
->sort_global
= table
->sort
+ count
;
196 table
->num_hints
= 0;
197 table
->num_zones
= 0;
200 /* initialize the `table->hints' array */
202 PSH_Hint write
= table
->hints
;
203 PS_Hint read
= hints
->hints
;
206 for ( ; count
> 0; count
--, write
++, read
++ )
208 write
->org_pos
= read
->pos
;
209 write
->org_len
= read
->len
;
210 write
->flags
= read
->flags
;
214 /* we now need to determine the initial `parent' stems; first */
215 /* activate the hints that are given by the initial hint masks */
218 PS_Mask mask
= hint_masks
->masks
;
221 count
= hint_masks
->num_masks
;
222 table
->hint_masks
= hint_masks
;
224 for ( ; count
> 0; count
--, mask
++ )
225 psh_hint_table_record_mask( table
, mask
);
228 /* finally, do a linear parse in case some hints were left alone */
229 if ( table
->num_hints
!= table
->max_hints
)
234 FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" ));
236 count
= table
->max_hints
;
237 for ( idx
= 0; idx
< count
; idx
++ )
238 psh_hint_table_record( table
, idx
);
247 psh_hint_table_activate_mask( PSH_Hint_Table table
,
250 FT_Int mask
= 0, val
= 0;
251 FT_Byte
* cursor
= hint_mask
->bytes
;
252 FT_UInt idx
, limit
, count
;
255 limit
= hint_mask
->num_bits
;
258 psh_hint_table_deactivate( table
);
260 for ( idx
= 0; idx
< limit
; idx
++ )
270 PSH_Hint hint
= &table
->hints
[idx
];
273 if ( !psh_hint_is_active( hint
) )
278 PSH_Hint
* sort
= table
->sort
;
282 for ( count2
= count
; count2
> 0; count2
--, sort
++ )
285 if ( psh_hint_overlap( hint
, hint2
) )
286 FT_TRACE0(( "psh_hint_table_activate_mask:"
287 " found overlapping hints\n" ))
295 psh_hint_activate( hint
);
296 if ( count
< table
->max_hints
)
297 table
->sort
[count
++] = hint
;
299 FT_TRACE0(( "psh_hint_tableactivate_mask:"
300 " too many active hints\n" ));
307 table
->num_hints
= count
;
309 /* now, sort the hints; they are guaranteed to not overlap */
310 /* so we can compare their "org_pos" field directly */
313 PSH_Hint hint1
, hint2
;
314 PSH_Hint
* sort
= table
->sort
;
317 /* a simple bubble sort will do, since in 99% of cases, the hints */
318 /* will be already sorted -- and the sort will be linear */
319 for ( i1
= 1; i1
< (FT_Int
)count
; i1
++ )
322 for ( i2
= i1
- 1; i2
>= 0; i2
-- )
326 if ( hint2
->org_pos
< hint1
->org_pos
)
329 sort
[i2
+ 1] = hint2
;
337 /*************************************************************************/
338 /*************************************************************************/
340 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/
342 /*************************************************************************/
343 /*************************************************************************/
347 psh_dimension_quantize_len( PSH_Dimension dim
,
349 FT_Bool do_snapping
)
355 FT_Pos delta
= len
- dim
->stdw
.widths
[0].cur
;
363 len
= dim
->stdw
.widths
[0].cur
;
370 delta
= ( len
& 63 );
376 else if ( delta
< 32 )
379 else if ( delta
< 54 )
386 len
= FT_PIX_ROUND( len
);
390 len
= FT_PIX_ROUND( len
);
400 ps_simple_scale( PSH_Hint_Table table
,
409 for ( count
= 0; count
< table
->max_hints
; count
++ )
411 hint
= table
->hints
+ count
;
413 hint
->cur_pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
414 hint
->cur_len
= FT_MulFix( hint
->org_len
, scale
);
416 if ( ps_debug_hint_func
)
417 ps_debug_hint_func( hint
, dimension
);
421 #endif /* DEBUG_HINTER */
425 psh_hint_snap_stem_side_delta( FT_Fixed pos
,
428 FT_Fixed delta1
= FT_PIX_ROUND( pos
) - pos
;
429 FT_Fixed delta2
= FT_PIX_ROUND( pos
+ len
) - pos
- len
;
432 if ( FT_ABS( delta1
) <= FT_ABS( delta2
) )
440 psh_hint_align( PSH_Hint hint
,
445 PSH_Dimension dim
= &globals
->dimension
[dimension
];
446 FT_Fixed scale
= dim
->scale_mult
;
447 FT_Fixed delta
= dim
->scale_delta
;
450 if ( !psh_hint_is_fitted( hint
) )
452 FT_Pos pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
453 FT_Pos len
= FT_MulFix( hint
->org_len
, scale
);
457 PSH_AlignmentRec align
;
460 /* ignore stem alignments when requested through the hint flags */
461 if ( ( dimension
== 0 && !glyph
->do_horz_hints
) ||
462 ( dimension
== 1 && !glyph
->do_vert_hints
) )
467 psh_hint_set_fitted( hint
);
471 /* perform stem snapping when requested - this is necessary
472 * for monochrome and LCD hinting modes only
474 do_snapping
= ( dimension
== 0 && glyph
->do_horz_snapping
) ||
475 ( dimension
== 1 && glyph
->do_vert_snapping
);
477 hint
->cur_len
= fit_len
= len
;
479 /* check blue zones for horizontal stems */
480 align
.align
= PSH_BLUE_ALIGN_NONE
;
481 align
.align_bot
= align
.align_top
= 0;
483 if ( dimension
== 1 )
484 psh_blues_snap_stem( &globals
->blues
,
485 hint
->org_pos
+ hint
->org_len
,
489 switch ( align
.align
)
491 case PSH_BLUE_ALIGN_TOP
:
492 /* the top of the stem is aligned against a blue zone */
493 hint
->cur_pos
= align
.align_top
- fit_len
;
496 case PSH_BLUE_ALIGN_BOT
:
497 /* the bottom of the stem is aligned against a blue zone */
498 hint
->cur_pos
= align
.align_bot
;
501 case PSH_BLUE_ALIGN_TOP
| PSH_BLUE_ALIGN_BOT
:
502 /* both edges of the stem are aligned against blue zones */
503 hint
->cur_pos
= align
.align_bot
;
504 hint
->cur_len
= align
.align_top
- align
.align_bot
;
509 PSH_Hint parent
= hint
->parent
;
514 FT_Pos par_org_center
, par_cur_center
;
515 FT_Pos cur_org_center
, cur_delta
;
518 /* ensure that parent is already fitted */
519 if ( !psh_hint_is_fitted( parent
) )
520 psh_hint_align( parent
, globals
, dimension
, glyph
);
522 /* keep original relation between hints, this is, use the */
523 /* scaled distance between the centers of the hints to */
524 /* compute the new position */
525 par_org_center
= parent
->org_pos
+ ( parent
->org_len
>> 1 );
526 par_cur_center
= parent
->cur_pos
+ ( parent
->cur_len
>> 1 );
527 cur_org_center
= hint
->org_pos
+ ( hint
->org_len
>> 1 );
529 cur_delta
= FT_MulFix( cur_org_center
- par_org_center
, scale
);
530 pos
= par_cur_center
+ cur_delta
- ( len
>> 1 );
534 hint
->cur_len
= fit_len
;
536 /* Stem adjustment tries to snap stem widths to standard
537 * ones. This is important to prevent unpleasant rounding
540 if ( glyph
->do_stem_adjust
)
544 /* the stem is less than one pixel; we will center it
545 * around the nearest pixel center
549 /* This is a special case where we also widen the stem
550 * and align it to the pixel grid.
552 * stem_center = pos + (len/2)
553 * nearest_pixel_center = FT_ROUND(stem_center-32)+32
554 * new_pos = nearest_pixel_center-32
555 * = FT_ROUND(stem_center-32)
556 * = FT_FLOOR(stem_center-32+32)
557 * = FT_FLOOR(stem_center)
560 pos
= FT_PIX_FLOOR( pos
+ ( len
>> 1 ) );
565 /* This is a very small stem; we simply align it to the
566 * pixel grid, trying to find the minimal displacement.
570 * left_nearest_edge = ROUND(pos)
571 * right_nearest_edge = ROUND(right)
573 * if ( ABS(left_nearest_edge - left) <=
574 * ABS(right_nearest_edge - right) )
579 FT_Pos left_nearest
= FT_PIX_ROUND( pos
);
580 FT_Pos right_nearest
= FT_PIX_ROUND( pos
+ len
);
581 FT_Pos left_disp
= left_nearest
- pos
;
582 FT_Pos right_disp
= right_nearest
- ( pos
+ len
);
586 left_disp
= -left_disp
;
587 if ( right_disp
< 0 )
588 right_disp
= -right_disp
;
589 if ( left_disp
<= right_disp
)
596 /* this is a ghost stem; we simply round it */
597 pos
= FT_PIX_ROUND( pos
);
602 len
= psh_dimension_quantize_len( dim
, len
, 0 );
606 /* now that we have a good hinted stem width, try to position */
607 /* the stem along a pixel grid integer coordinate */
608 hint
->cur_pos
= pos
+ psh_hint_snap_stem_side_delta( pos
, len
);
621 len
= FT_PIX_ROUND( len
);
623 switch ( align
.align
)
625 case PSH_BLUE_ALIGN_TOP
:
626 hint
->cur_pos
= align
.align_top
- len
;
630 case PSH_BLUE_ALIGN_BOT
:
634 case PSH_BLUE_ALIGN_BOT
| PSH_BLUE_ALIGN_TOP
:
642 pos
= FT_PIX_FLOOR( pos
+ ( len
>> 1 ) ) + 32;
644 pos
= FT_PIX_ROUND( pos
+ ( len
>> 1 ) );
646 hint
->cur_pos
= pos
- ( len
>> 1 );
651 psh_hint_set_fitted( hint
);
654 if ( ps_debug_hint_func
)
655 ps_debug_hint_func( hint
, dimension
);
661 #if 0 /* not used for now, experimental */
664 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
668 psh_hint_align_light( PSH_Hint hint
,
673 PSH_Dimension dim
= &globals
->dimension
[dimension
];
674 FT_Fixed scale
= dim
->scale_mult
;
675 FT_Fixed delta
= dim
->scale_delta
;
678 if ( !psh_hint_is_fitted( hint
) )
680 FT_Pos pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
681 FT_Pos len
= FT_MulFix( hint
->org_len
, scale
);
685 PSH_AlignmentRec align
;
688 /* ignore stem alignments when requested through the hint flags */
689 if ( ( dimension
== 0 && !glyph
->do_horz_hints
) ||
690 ( dimension
== 1 && !glyph
->do_vert_hints
) )
695 psh_hint_set_fitted( hint
);
701 hint
->cur_len
= fit_len
;
703 /* check blue zones for horizontal stems */
704 align
.align
= PSH_BLUE_ALIGN_NONE
;
705 align
.align_bot
= align
.align_top
= 0;
707 if ( dimension
== 1 )
708 psh_blues_snap_stem( &globals
->blues
,
709 hint
->org_pos
+ hint
->org_len
,
713 switch ( align
.align
)
715 case PSH_BLUE_ALIGN_TOP
:
716 /* the top of the stem is aligned against a blue zone */
717 hint
->cur_pos
= align
.align_top
- fit_len
;
720 case PSH_BLUE_ALIGN_BOT
:
721 /* the bottom of the stem is aligned against a blue zone */
722 hint
->cur_pos
= align
.align_bot
;
725 case PSH_BLUE_ALIGN_TOP
| PSH_BLUE_ALIGN_BOT
:
726 /* both edges of the stem are aligned against blue zones */
727 hint
->cur_pos
= align
.align_bot
;
728 hint
->cur_len
= align
.align_top
- align
.align_bot
;
733 PSH_Hint parent
= hint
->parent
;
738 FT_Pos par_org_center
, par_cur_center
;
739 FT_Pos cur_org_center
, cur_delta
;
742 /* ensure that parent is already fitted */
743 if ( !psh_hint_is_fitted( parent
) )
744 psh_hint_align_light( parent
, globals
, dimension
, glyph
);
746 par_org_center
= parent
->org_pos
+ ( parent
->org_len
/ 2 );
747 par_cur_center
= parent
->cur_pos
+ ( parent
->cur_len
/ 2 );
748 cur_org_center
= hint
->org_pos
+ ( hint
->org_len
/ 2 );
750 cur_delta
= FT_MulFix( cur_org_center
- par_org_center
, scale
);
751 pos
= par_cur_center
+ cur_delta
- ( len
>> 1 );
754 /* Stems less than one pixel wide are easy -- we want to
755 * make them as dark as possible, so they must fall within
756 * one pixel. If the stem is split between two pixels
757 * then snap the edge that is nearer to the pixel boundary
758 * to the pixel boundary.
762 if ( ( pos
+ len
+ 63 ) / 64 != pos
/ 64 + 1 )
763 pos
+= psh_hint_snap_stem_side_delta ( pos
, len
);
766 /* Position stems other to minimize the amount of mid-grays.
767 * There are, in general, two positions that do this,
768 * illustrated as A) and B) below.
772 * A) |--------------------------------|
773 * B) |--------------------------------|
774 * C) |--------------------------------|
776 * Position A) (split the excess stem equally) should be better
777 * for stems of width N + f where f < 0.5.
779 * Position B) (split the deficiency equally) should be better
780 * for stems of width N + f where f > 0.5.
782 * It turns out though that minimizing the total number of lit
783 * pixels is also important, so position C), with one edge
784 * aligned with a pixel boundary is actually preferable
785 * to A). There are also more possibile positions for C) than
786 * for A) or B), so it involves less distortion of the overall
791 FT_Fixed frac_len
= len
& 63;
792 FT_Fixed center
= pos
+ ( len
>> 1 );
793 FT_Fixed delta_a
, delta_b
;
796 if ( ( len
/ 64 ) & 1 )
798 delta_a
= FT_PIX_FLOOR( center
) + 32 - center
;
799 delta_b
= FT_PIX_ROUND( center
) - center
;
803 delta_a
= FT_PIX_ROUND( center
) - center
;
804 delta_b
= FT_PIX_FLOOR( center
) + 32 - center
;
807 /* We choose between B) and C) above based on the amount
808 * of fractinal stem width; for small amounts, choose
809 * C) always, for large amounts, B) always, and inbetween,
810 * pick whichever one involves less stem movement.
814 pos
+= psh_hint_snap_stem_side_delta ( pos
, len
);
816 else if ( frac_len
< 48 )
818 FT_Fixed side_delta
= psh_hint_snap_stem_side_delta ( pos
,
821 if ( FT_ABS( side_delta
) < FT_ABS( delta_b
) )
836 psh_hint_set_fitted( hint
);
839 if ( ps_debug_hint_func
)
840 ps_debug_hint_func( hint
, dimension
);
849 psh_hint_table_align_hints( PSH_Hint_Table table
,
859 PSH_Dimension dim
= &globals
->dimension
[dimension
];
860 FT_Fixed scale
= dim
->scale_mult
;
861 FT_Fixed delta
= dim
->scale_delta
;
864 if ( ps_debug_no_vert_hints
&& dimension
== 0 )
866 ps_simple_scale( table
, scale
, delta
, dimension
);
870 if ( ps_debug_no_horz_hints
&& dimension
== 1 )
872 ps_simple_scale( table
, scale
, delta
, dimension
);
876 #endif /* DEBUG_HINTER*/
879 count
= table
->max_hints
;
881 for ( ; count
> 0; count
--, hint
++ )
882 psh_hint_align( hint
, globals
, dimension
, glyph
);
886 /*************************************************************************/
887 /*************************************************************************/
889 /***** POINTS INTERPOLATION ROUTINES *****/
891 /*************************************************************************/
892 /*************************************************************************/
894 #define PSH_ZONE_MIN -3200000L
895 #define PSH_ZONE_MAX +3200000L
897 #define xxDEBUG_ZONES
902 #include FT_CONFIG_STANDARD_LIBRARY_H
905 psh_print_zone( PSH_Zone zone
)
907 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
908 zone
->scale
/ 65536.0,
916 #define psh_print_zone( x ) do { } while ( 0 )
918 #endif /* DEBUG_ZONES */
921 /*************************************************************************/
922 /*************************************************************************/
924 /***** HINTER GLYPH MANAGEMENT *****/
926 /*************************************************************************/
927 /*************************************************************************/
931 #define psh_corner_is_flat ft_corner_is_flat
932 #define psh_corner_orientation ft_corner_orientation
936 FT_LOCAL_DEF( FT_Int
)
937 psh_corner_is_flat( FT_Pos x_in
,
945 FT_Pos d_in
, d_out
, d_corner
;
970 return ( d_in
+ d_out
- d_corner
) < ( d_corner
>> 4 );
974 psh_corner_orientation( FT_Pos in_x
,
982 /* deal with the trivial cases quickly */
990 else if ( in_x
== 0 )
997 else if ( out_y
== 0 )
1004 else if ( out_x
== 0 )
1011 else /* general case */
1013 long long delta
= (long long)in_x
* out_y
- (long long)in_y
* out_x
;
1018 result
= 1 - 2 * ( delta
< 0 );
1027 #ifdef COMPUTE_INFLEXS
1029 /* compute all inflex points in a given glyph */
1031 psh_glyph_compute_inflections( PSH_Glyph glyph
)
1036 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1038 PSH_Point first
, start
, end
, before
, after
;
1039 FT_Pos in_x
, in_y
, out_x
, out_y
;
1040 FT_Int orient_prev
, orient_cur
;
1041 FT_Int finished
= 0;
1044 /* we need at least 4 points to create an inflection point */
1045 if ( glyph
->contours
[n
].count
< 4 )
1048 /* compute first segment in contour */
1049 first
= glyph
->contours
[n
].start
;
1051 start
= end
= first
;
1058 in_x
= end
->org_u
- start
->org_u
;
1059 in_y
= end
->org_v
- start
->org_v
;
1061 } while ( in_x
== 0 && in_y
== 0 );
1063 /* extend the segment start whenever possible */
1070 before
= before
->prev
;
1071 if ( before
== first
)
1074 out_x
= start
->org_u
- before
->org_u
;
1075 out_y
= start
->org_v
- before
->org_v
;
1077 } while ( out_x
== 0 && out_y
== 0 );
1079 orient_prev
= psh_corner_orientation( in_x
, in_y
, out_x
, out_y
);
1081 } while ( orient_prev
== 0 );
1087 /* now, process all segments in the contour */
1090 /* first, extend current segment's end whenever possible */
1097 after
= after
->next
;
1098 if ( after
== first
)
1101 out_x
= after
->org_u
- end
->org_u
;
1102 out_y
= after
->org_v
- end
->org_v
;
1104 } while ( out_x
== 0 && out_y
== 0 );
1106 orient_cur
= psh_corner_orientation( in_x
, in_y
, out_x
, out_y
);
1108 } while ( orient_cur
== 0 );
1110 if ( ( orient_cur
^ orient_prev
) < 0 )
1114 psh_point_set_inflex( start
);
1115 start
= start
->next
;
1117 while ( start
!= end
);
1119 psh_point_set_inflex( start
);
1124 orient_prev
= orient_cur
;
1128 } while ( !finished
);
1135 #endif /* COMPUTE_INFLEXS */
1139 psh_glyph_done( PSH_Glyph glyph
)
1141 FT_Memory memory
= glyph
->memory
;
1144 psh_hint_table_done( &glyph
->hint_tables
[1], memory
);
1145 psh_hint_table_done( &glyph
->hint_tables
[0], memory
);
1147 FT_FREE( glyph
->points
);
1148 FT_FREE( glyph
->contours
);
1150 glyph
->num_points
= 0;
1151 glyph
->num_contours
= 0;
1158 psh_compute_dir( FT_Pos dx
,
1162 int result
= PSH_DIR_NONE
;
1165 ax
= ( dx
>= 0 ) ? dx
: -dx
;
1166 ay
= ( dy
>= 0 ) ? dy
: -dy
;
1170 /* |dy| <<< |dx| means a near-horizontal segment */
1171 result
= ( dx
>= 0 ) ? PSH_DIR_RIGHT
: PSH_DIR_LEFT
;
1173 else if ( ax
* 12 < ay
)
1175 /* |dx| <<< |dy| means a near-vertical segment */
1176 result
= ( dy
>= 0 ) ? PSH_DIR_UP
: PSH_DIR_DOWN
;
1183 /* load outline point coordinates into hinter glyph */
1185 psh_glyph_load_points( PSH_Glyph glyph
,
1188 FT_Vector
* vec
= glyph
->outline
->points
;
1189 PSH_Point point
= glyph
->points
;
1190 FT_UInt count
= glyph
->num_points
;
1193 for ( ; count
> 0; count
--, point
++, vec
++ )
1197 if ( dimension
== 0 )
1199 point
->org_u
= vec
->x
;
1200 point
->org_v
= vec
->y
;
1204 point
->org_u
= vec
->y
;
1205 point
->org_v
= vec
->x
;
1209 point
->org_x
= vec
->x
;
1210 point
->org_y
= vec
->y
;
1217 /* save hinted point coordinates back to outline */
1219 psh_glyph_save_points( PSH_Glyph glyph
,
1223 PSH_Point point
= glyph
->points
;
1224 FT_Vector
* vec
= glyph
->outline
->points
;
1225 char* tags
= glyph
->outline
->tags
;
1228 for ( n
= 0; n
< glyph
->num_points
; n
++ )
1230 if ( dimension
== 0 )
1231 vec
[n
].x
= point
->cur_u
;
1233 vec
[n
].y
= point
->cur_u
;
1235 if ( psh_point_is_strong( point
) )
1236 tags
[n
] |= (char)( ( dimension
== 0 ) ? 32 : 64 );
1240 if ( dimension
== 0 )
1242 point
->cur_x
= point
->cur_u
;
1243 point
->flags_x
= point
->flags2
| point
->flags
;
1247 point
->cur_y
= point
->cur_u
;
1248 point
->flags_y
= point
->flags2
| point
->flags
;
1259 psh_glyph_init( PSH_Glyph glyph
,
1260 FT_Outline
* outline
,
1262 PSH_Globals globals
)
1268 /* clear all fields */
1269 FT_MEM_ZERO( glyph
, sizeof ( *glyph
) );
1271 memory
= glyph
->memory
= globals
->memory
;
1273 /* allocate and setup points + contours arrays */
1274 if ( FT_NEW_ARRAY( glyph
->points
, outline
->n_points
) ||
1275 FT_NEW_ARRAY( glyph
->contours
, outline
->n_contours
) )
1278 glyph
->num_points
= outline
->n_points
;
1279 glyph
->num_contours
= outline
->n_contours
;
1282 FT_UInt first
= 0, next
, n
;
1283 PSH_Point points
= glyph
->points
;
1284 PSH_Contour contour
= glyph
->contours
;
1287 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1293 next
= outline
->contours
[n
] + 1;
1294 count
= next
- first
;
1296 contour
->start
= points
+ first
;
1297 contour
->count
= (FT_UInt
)count
;
1301 point
= points
+ first
;
1303 point
->prev
= points
+ next
- 1;
1304 point
->contour
= contour
;
1306 for ( ; count
> 1; count
-- )
1308 point
[0].next
= point
+ 1;
1309 point
[1].prev
= point
;
1311 point
->contour
= contour
;
1313 point
->next
= points
+ first
;
1322 PSH_Point points
= glyph
->points
;
1323 PSH_Point point
= points
;
1324 FT_Vector
* vec
= outline
->points
;
1328 for ( n
= 0; n
< glyph
->num_points
; n
++, point
++ )
1330 FT_Int n_prev
= (FT_Int
)( point
->prev
- points
);
1331 FT_Int n_next
= (FT_Int
)( point
->next
- points
);
1332 FT_Pos dxi
, dyi
, dxo
, dyo
;
1335 if ( !( outline
->tags
[n
] & FT_CURVE_TAG_ON
) )
1336 point
->flags
= PSH_POINT_OFF
;
1338 dxi
= vec
[n
].x
- vec
[n_prev
].x
;
1339 dyi
= vec
[n
].y
- vec
[n_prev
].y
;
1341 point
->dir_in
= (FT_Char
)psh_compute_dir( dxi
, dyi
);
1343 dxo
= vec
[n_next
].x
- vec
[n
].x
;
1344 dyo
= vec
[n_next
].y
- vec
[n
].y
;
1346 point
->dir_out
= (FT_Char
)psh_compute_dir( dxo
, dyo
);
1348 /* detect smooth points */
1349 if ( point
->flags
& PSH_POINT_OFF
)
1350 point
->flags
|= PSH_POINT_SMOOTH
;
1352 else if ( point
->dir_in
== point
->dir_out
)
1354 if ( point
->dir_out
!= PSH_DIR_NONE
||
1355 psh_corner_is_flat( dxi
, dyi
, dxo
, dyo
) )
1356 point
->flags
|= PSH_POINT_SMOOTH
;
1361 glyph
->outline
= outline
;
1362 glyph
->globals
= globals
;
1364 #ifdef COMPUTE_INFLEXS
1365 psh_glyph_load_points( glyph
, 0 );
1366 psh_glyph_compute_inflections( glyph
);
1367 #endif /* COMPUTE_INFLEXS */
1369 /* now deal with hints tables */
1370 error
= psh_hint_table_init( &glyph
->hint_tables
[0],
1371 &ps_hints
->dimension
[0].hints
,
1372 &ps_hints
->dimension
[0].masks
,
1373 &ps_hints
->dimension
[0].counters
,
1378 error
= psh_hint_table_init( &glyph
->hint_tables
[1],
1379 &ps_hints
->dimension
[1].hints
,
1380 &ps_hints
->dimension
[1].masks
,
1381 &ps_hints
->dimension
[1].counters
,
1391 /* compute all extrema in a glyph for a given dimension */
1393 psh_glyph_compute_extrema( PSH_Glyph glyph
)
1398 /* first of all, compute all local extrema */
1399 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1401 PSH_Point first
= glyph
->contours
[n
].start
;
1402 PSH_Point point
, before
, after
;
1405 if ( glyph
->contours
[n
].count
== 0 )
1414 before
= before
->prev
;
1415 if ( before
== first
)
1418 } while ( before
->org_u
== point
->org_u
);
1420 first
= point
= before
->next
;
1427 after
= after
->next
;
1428 if ( after
== first
)
1431 } while ( after
->org_u
== point
->org_u
);
1433 if ( before
->org_u
< point
->org_u
)
1435 if ( after
->org_u
< point
->org_u
)
1441 else /* before->org_u > point->org_u */
1443 if ( after
->org_u
> point
->org_u
)
1449 psh_point_set_extremum( point
);
1450 point
= point
->next
;
1452 } while ( point
!= after
);
1456 before
= after
->prev
;
1465 /* for each extremum, determine its direction along the */
1466 /* orthogonal axis */
1467 for ( n
= 0; n
< glyph
->num_points
; n
++ )
1469 PSH_Point point
, before
, after
;
1472 point
= &glyph
->points
[n
];
1476 if ( psh_point_is_extremum( point
) )
1480 before
= before
->prev
;
1481 if ( before
== point
)
1484 } while ( before
->org_v
== point
->org_v
);
1488 after
= after
->next
;
1489 if ( after
== point
)
1492 } while ( after
->org_v
== point
->org_v
);
1495 if ( before
->org_v
< point
->org_v
&&
1496 after
->org_v
> point
->org_v
)
1498 psh_point_set_positive( point
);
1500 else if ( before
->org_v
> point
->org_v
&&
1501 after
->org_v
< point
->org_v
)
1503 psh_point_set_negative( point
);
1512 /* major_dir is the direction for points on the bottom/left of the stem; */
1513 /* Points on the top/right of the stem will have a direction of */
1517 psh_hint_table_find_strong_points( PSH_Hint_Table table
,
1523 PSH_Hint
* sort
= table
->sort
;
1524 FT_UInt num_hints
= table
->num_hints
;
1527 for ( ; count
> 0; count
--, point
++ )
1529 FT_Int point_dir
= 0;
1530 FT_Pos org_u
= point
->org_u
;
1533 if ( psh_point_is_strong( point
) )
1536 if ( PSH_DIR_COMPARE( point
->dir_in
, major_dir
) )
1537 point_dir
= point
->dir_in
;
1539 else if ( PSH_DIR_COMPARE( point
->dir_out
, major_dir
) )
1540 point_dir
= point
->dir_out
;
1544 if ( point_dir
== major_dir
)
1549 for ( nn
= 0; nn
< num_hints
; nn
++ )
1551 PSH_Hint hint
= sort
[nn
];
1552 FT_Pos d
= org_u
- hint
->org_pos
;
1555 if ( d
< threshold
&& -d
< threshold
)
1557 psh_point_set_strong( point
);
1558 point
->flags2
|= PSH_POINT_EDGE_MIN
;
1564 else if ( point_dir
== -major_dir
)
1569 for ( nn
= 0; nn
< num_hints
; nn
++ )
1571 PSH_Hint hint
= sort
[nn
];
1572 FT_Pos d
= org_u
- hint
->org_pos
- hint
->org_len
;
1575 if ( d
< threshold
&& -d
< threshold
)
1577 psh_point_set_strong( point
);
1578 point
->flags2
|= PSH_POINT_EDGE_MAX
;
1587 else if ( psh_point_is_extremum( point
) )
1589 /* treat extrema as special cases for stem edge alignment */
1590 FT_UInt nn
, min_flag
, max_flag
;
1593 if ( major_dir
== PSH_DIR_HORIZONTAL
)
1595 min_flag
= PSH_POINT_POSITIVE
;
1596 max_flag
= PSH_POINT_NEGATIVE
;
1600 min_flag
= PSH_POINT_NEGATIVE
;
1601 max_flag
= PSH_POINT_POSITIVE
;
1604 if ( point
->flags2
& min_flag
)
1606 for ( nn
= 0; nn
< num_hints
; nn
++ )
1608 PSH_Hint hint
= sort
[nn
];
1609 FT_Pos d
= org_u
- hint
->org_pos
;
1612 if ( d
< threshold
&& -d
< threshold
)
1614 point
->flags2
|= PSH_POINT_EDGE_MIN
;
1616 psh_point_set_strong( point
);
1621 else if ( point
->flags2
& max_flag
)
1623 for ( nn
= 0; nn
< num_hints
; nn
++ )
1625 PSH_Hint hint
= sort
[nn
];
1626 FT_Pos d
= org_u
- hint
->org_pos
- hint
->org_len
;
1629 if ( d
< threshold
&& -d
< threshold
)
1631 point
->flags2
|= PSH_POINT_EDGE_MAX
;
1633 psh_point_set_strong( point
);
1639 if ( point
->hint
== NULL
)
1641 for ( nn
= 0; nn
< num_hints
; nn
++ )
1643 PSH_Hint hint
= sort
[nn
];
1646 if ( org_u
>= hint
->org_pos
&&
1647 org_u
<= hint
->org_pos
+ hint
->org_len
)
1661 /* the accepted shift for strong points in fractional pixels */
1662 #define PSH_STRONG_THRESHOLD 32
1664 /* the maximum shift value in font units */
1665 #define PSH_STRONG_THRESHOLD_MAXIMUM 30
1668 /* find strong points in a glyph */
1670 psh_glyph_find_strong_points( PSH_Glyph glyph
,
1673 /* a point is `strong' if it is located on a stem edge and */
1674 /* has an `in' or `out' tangent parallel to the hint's direction */
1676 PSH_Hint_Table table
= &glyph
->hint_tables
[dimension
];
1677 PS_Mask mask
= table
->hint_masks
->masks
;
1678 FT_UInt num_masks
= table
->hint_masks
->num_masks
;
1680 FT_Int major_dir
= dimension
== 0 ? PSH_DIR_VERTICAL
1681 : PSH_DIR_HORIZONTAL
;
1682 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1683 FT_Fixed scale
= dim
->scale_mult
;
1687 threshold
= (FT_Int
)FT_DivFix( PSH_STRONG_THRESHOLD
, scale
);
1688 if ( threshold
> PSH_STRONG_THRESHOLD_MAXIMUM
)
1689 threshold
= PSH_STRONG_THRESHOLD_MAXIMUM
;
1691 /* process secondary hints to `selected' points */
1692 if ( num_masks
> 1 && glyph
->num_points
> 0 )
1694 /* the `endchar' op can reduce the number of points */
1695 first
= mask
->end_point
> glyph
->num_points
1699 for ( ; num_masks
> 1; num_masks
--, mask
++ )
1705 next
= mask
->end_point
> glyph
->num_points
1708 count
= next
- first
;
1711 PSH_Point point
= glyph
->points
+ first
;
1714 psh_hint_table_activate_mask( table
, mask
);
1716 psh_hint_table_find_strong_points( table
, point
, count
,
1717 threshold
, major_dir
);
1723 /* process primary hints for all points */
1724 if ( num_masks
== 1 )
1726 FT_UInt count
= glyph
->num_points
;
1727 PSH_Point point
= glyph
->points
;
1730 psh_hint_table_activate_mask( table
, table
->hint_masks
->masks
);
1732 psh_hint_table_find_strong_points( table
, point
, count
,
1733 threshold
, major_dir
);
1736 /* now, certain points may have been attached to a hint and */
1737 /* not marked as strong; update their flags then */
1739 FT_UInt count
= glyph
->num_points
;
1740 PSH_Point point
= glyph
->points
;
1743 for ( ; count
> 0; count
--, point
++ )
1744 if ( point
->hint
&& !psh_point_is_strong( point
) )
1745 psh_point_set_strong( point
);
1750 /* find points in a glyph which are in a blue zone and have `in' or */
1751 /* `out' tangents parallel to the horizontal axis */
1753 psh_glyph_find_blue_points( PSH_Blues blues
,
1756 PSH_Blue_Table table
;
1758 FT_UInt glyph_count
= glyph
->num_points
;
1760 PSH_Point point
= glyph
->points
;
1763 for ( ; glyph_count
> 0; glyph_count
--, point
++ )
1768 /* check tangents */
1769 if ( !PSH_DIR_COMPARE( point
->dir_in
, PSH_DIR_HORIZONTAL
) &&
1770 !PSH_DIR_COMPARE( point
->dir_out
, PSH_DIR_HORIZONTAL
) )
1773 /* skip strong points */
1774 if ( psh_point_is_strong( point
) )
1779 /* look up top zones */
1780 table
= &blues
->normal_top
;
1781 blue_count
= table
->count
;
1782 zone
= table
->zones
;
1784 for ( ; blue_count
> 0; blue_count
--, zone
++ )
1786 FT_Pos delta
= y
- zone
->org_bottom
;
1789 if ( delta
< -blues
->blue_fuzz
)
1792 if ( y
<= zone
->org_top
+ blues
->blue_fuzz
)
1793 if ( blues
->no_overshoots
|| delta
<= blues
->blue_threshold
)
1795 point
->cur_u
= zone
->cur_bottom
;
1796 psh_point_set_strong( point
);
1797 psh_point_set_fitted( point
);
1801 /* look up bottom zones */
1802 table
= &blues
->normal_bottom
;
1803 blue_count
= table
->count
;
1804 zone
= table
->zones
+ blue_count
- 1;
1806 for ( ; blue_count
> 0; blue_count
--, zone
-- )
1808 FT_Pos delta
= zone
->org_top
- y
;
1811 if ( delta
< -blues
->blue_fuzz
)
1814 if ( y
>= zone
->org_bottom
- blues
->blue_fuzz
)
1815 if ( blues
->no_overshoots
|| delta
< blues
->blue_threshold
)
1817 point
->cur_u
= zone
->cur_top
;
1818 psh_point_set_strong( point
);
1819 psh_point_set_fitted( point
);
1826 /* interpolate strong points with the help of hinted coordinates */
1828 psh_glyph_interpolate_strong_points( PSH_Glyph glyph
,
1831 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1832 FT_Fixed scale
= dim
->scale_mult
;
1834 FT_UInt count
= glyph
->num_points
;
1835 PSH_Point point
= glyph
->points
;
1838 for ( ; count
> 0; count
--, point
++ )
1840 PSH_Hint hint
= point
->hint
;
1848 if ( psh_point_is_edge_min( point
) )
1849 point
->cur_u
= hint
->cur_pos
;
1851 else if ( psh_point_is_edge_max( point
) )
1852 point
->cur_u
= hint
->cur_pos
+ hint
->cur_len
;
1856 delta
= point
->org_u
- hint
->org_pos
;
1859 point
->cur_u
= hint
->cur_pos
+ FT_MulFix( delta
, scale
);
1861 else if ( delta
>= hint
->org_len
)
1862 point
->cur_u
= hint
->cur_pos
+ hint
->cur_len
+
1863 FT_MulFix( delta
- hint
->org_len
, scale
);
1865 else /* hint->org_len > 0 */
1866 point
->cur_u
= hint
->cur_pos
+
1867 FT_MulDiv( delta
, hint
->cur_len
,
1870 psh_point_set_fitted( point
);
1876 #define PSH_MAX_STRONG_INTERNAL 16
1879 psh_glyph_interpolate_normal_points( PSH_Glyph glyph
,
1884 /* first technique: a point is strong if it is a local extremum */
1886 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1887 FT_Fixed scale
= dim
->scale_mult
;
1888 FT_Memory memory
= glyph
->memory
;
1890 PSH_Point
* strongs
= NULL
;
1891 PSH_Point strongs_0
[PSH_MAX_STRONG_INTERNAL
];
1892 FT_UInt num_strongs
= 0;
1894 PSH_Point points
= glyph
->points
;
1895 PSH_Point points_end
= points
+ glyph
->num_points
;
1899 /* first count the number of strong points */
1900 for ( point
= points
; point
< points_end
; point
++ )
1902 if ( psh_point_is_strong( point
) )
1906 if ( num_strongs
== 0 ) /* nothing to do here */
1909 /* allocate an array to store a list of points, */
1910 /* stored in increasing org_u order */
1911 if ( num_strongs
<= PSH_MAX_STRONG_INTERNAL
)
1912 strongs
= strongs_0
;
1918 if ( FT_NEW_ARRAY( strongs
, num_strongs
) )
1923 for ( point
= points
; point
< points_end
; point
++ )
1928 if ( !psh_point_is_strong( point
) )
1931 for ( insert
= strongs
+ num_strongs
; insert
> strongs
; insert
-- )
1933 if ( insert
[-1]->org_u
<= point
->org_u
)
1936 insert
[0] = insert
[-1];
1942 /* now try to interpolate all normal points */
1943 for ( point
= points
; point
< points_end
; point
++ )
1945 if ( psh_point_is_strong( point
) )
1948 /* sometimes, some local extrema are smooth points */
1949 if ( psh_point_is_smooth( point
) )
1951 if ( point
->dir_in
== PSH_DIR_NONE
||
1952 point
->dir_in
!= point
->dir_out
)
1955 if ( !psh_point_is_extremum( point
) &&
1956 !psh_point_is_inflex( point
) )
1959 point
->flags
&= ~PSH_POINT_SMOOTH
;
1962 /* find best enclosing point coordinates then interpolate */
1964 PSH_Point before
, after
;
1968 for ( nn
= 0; nn
< num_strongs
; nn
++ )
1969 if ( strongs
[nn
]->org_u
> point
->org_u
)
1972 if ( nn
== 0 ) /* point before the first strong point */
1976 point
->cur_u
= after
->cur_u
+
1977 FT_MulFix( point
->org_u
- after
->org_u
,
1982 before
= strongs
[nn
- 1];
1984 for ( nn
= num_strongs
; nn
> 0; nn
-- )
1985 if ( strongs
[nn
- 1]->org_u
< point
->org_u
)
1988 if ( nn
== num_strongs
) /* point is after last strong point */
1990 before
= strongs
[nn
- 1];
1992 point
->cur_u
= before
->cur_u
+
1993 FT_MulFix( point
->org_u
- before
->org_u
,
2001 after
= strongs
[nn
];
2003 /* now interpolate point between before and after */
2006 if ( u
== before
->org_u
)
2007 point
->cur_u
= before
->cur_u
;
2009 else if ( u
== after
->org_u
)
2010 point
->cur_u
= after
->cur_u
;
2013 point
->cur_u
= before
->cur_u
+
2014 FT_MulDiv( u
- before
->org_u
,
2015 after
->cur_u
- before
->cur_u
,
2016 after
->org_u
- before
->org_u
);
2019 psh_point_set_fitted( point
);
2023 if ( strongs
!= strongs_0
)
2031 /* interpolate other points */
2033 psh_glyph_interpolate_other_points( PSH_Glyph glyph
,
2036 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
2037 FT_Fixed scale
= dim
->scale_mult
;
2038 FT_Fixed delta
= dim
->scale_delta
;
2039 PSH_Contour contour
= glyph
->contours
;
2040 FT_UInt num_contours
= glyph
->num_contours
;
2043 for ( ; num_contours
> 0; num_contours
--, contour
++ )
2045 PSH_Point start
= contour
->start
;
2046 PSH_Point first
, next
, point
;
2050 /* count the number of strong points in this contour */
2051 next
= start
+ contour
->count
;
2055 for ( point
= start
; point
< next
; point
++ )
2056 if ( psh_point_is_fitted( point
) )
2064 /* if there are less than 2 fitted points in the contour, we */
2065 /* simply scale and eventually translate the contour points */
2066 if ( fit_count
< 2 )
2068 if ( fit_count
== 1 )
2069 delta
= first
->cur_u
- FT_MulFix( first
->org_u
, scale
);
2071 for ( point
= start
; point
< next
; point
++ )
2072 if ( point
!= first
)
2073 point
->cur_u
= FT_MulFix( point
->org_u
, scale
) + delta
;
2078 /* there are more than 2 strong points in this contour; we */
2079 /* need to interpolate weak points between them */
2085 /* skip consecutive fitted points */
2089 if ( next
== start
)
2092 if ( !psh_point_is_fitted( next
) )
2098 /* find next fitted point after unfitted one */
2102 if ( psh_point_is_fitted( next
) )
2106 /* now interpolate between them */
2108 FT_Pos org_a
, org_ab
, cur_a
, cur_ab
;
2109 FT_Pos org_c
, org_ac
, cur_c
;
2113 if ( first
->org_u
<= next
->org_u
)
2115 org_a
= first
->org_u
;
2116 cur_a
= first
->cur_u
;
2117 org_ab
= next
->org_u
- org_a
;
2118 cur_ab
= next
->cur_u
- cur_a
;
2122 org_a
= next
->org_u
;
2123 cur_a
= next
->cur_u
;
2124 org_ab
= first
->org_u
- org_a
;
2125 cur_ab
= first
->cur_u
- cur_a
;
2128 scale_ab
= 0x10000L
;
2130 scale_ab
= FT_DivFix( cur_ab
, org_ab
);
2132 point
= first
->next
;
2135 org_c
= point
->org_u
;
2136 org_ac
= org_c
- org_a
;
2140 /* on the left of the interpolation zone */
2141 cur_c
= cur_a
+ FT_MulFix( org_ac
, scale
);
2143 else if ( org_ac
>= org_ab
)
2145 /* on the right on the interpolation zone */
2146 cur_c
= cur_a
+ cur_ab
+ FT_MulFix( org_ac
- org_ab
, scale
);
2150 /* within the interpolation zone */
2151 cur_c
= cur_a
+ FT_MulFix( org_ac
, scale_ab
);
2154 point
->cur_u
= cur_c
;
2156 point
= point
->next
;
2158 } while ( point
!= next
);
2161 /* keep going until all points in the contours have been processed */
2164 } while ( first
!= start
);
2172 /*************************************************************************/
2173 /*************************************************************************/
2175 /***** HIGH-LEVEL INTERFACE *****/
2177 /*************************************************************************/
2178 /*************************************************************************/
2181 ps_hints_apply( PS_Hints ps_hints
,
2182 FT_Outline
* outline
,
2183 PSH_Globals globals
,
2184 FT_Render_Mode hint_mode
)
2186 PSH_GlyphRec glyphrec
;
2187 PSH_Glyph glyph
= &glyphrec
;
2195 /* something to do? */
2196 if ( outline
->n_points
== 0 || outline
->n_contours
== 0 )
2201 memory
= globals
->memory
;
2203 if ( ps_debug_glyph
)
2205 psh_glyph_done( ps_debug_glyph
);
2206 FT_FREE( ps_debug_glyph
);
2209 if ( FT_NEW( glyph
) )
2212 ps_debug_glyph
= glyph
;
2214 #endif /* DEBUG_HINTER */
2216 error
= psh_glyph_init( glyph
, outline
, ps_hints
, globals
);
2220 /* try to optimize the y_scale so that the top of non-capital letters
2221 * is aligned on a pixel boundary whenever possible
2224 PSH_Dimension dim_x
= &glyph
->globals
->dimension
[0];
2225 PSH_Dimension dim_y
= &glyph
->globals
->dimension
[1];
2227 FT_Fixed x_scale
= dim_x
->scale_mult
;
2228 FT_Fixed y_scale
= dim_y
->scale_mult
;
2230 FT_Fixed old_x_scale
= x_scale
;
2231 FT_Fixed old_y_scale
= y_scale
;
2236 FT_Bool rescale
= FALSE
;
2239 scaled
= FT_MulFix( globals
->blues
.normal_top
.zones
->org_ref
, y_scale
);
2240 fitted
= FT_PIX_ROUND( scaled
);
2242 if ( fitted
!= 0 && scaled
!= fitted
)
2246 y_scale
= FT_MulDiv( y_scale
, fitted
, scaled
);
2248 if ( fitted
< scaled
)
2249 x_scale
-= x_scale
/ 50;
2251 psh_globals_set_scale( glyph
->globals
, x_scale
, y_scale
, 0, 0 );
2254 glyph
->do_horz_hints
= 1;
2255 glyph
->do_vert_hints
= 1;
2257 glyph
->do_horz_snapping
= FT_BOOL( hint_mode
== FT_RENDER_MODE_MONO
||
2258 hint_mode
== FT_RENDER_MODE_LCD
);
2260 glyph
->do_vert_snapping
= FT_BOOL( hint_mode
== FT_RENDER_MODE_MONO
||
2261 hint_mode
== FT_RENDER_MODE_LCD_V
);
2263 glyph
->do_stem_adjust
= FT_BOOL( hint_mode
!= FT_RENDER_MODE_LIGHT
);
2265 for ( dimension
= 0; dimension
< 2; dimension
++ )
2267 /* load outline coordinates into glyph */
2268 psh_glyph_load_points( glyph
, dimension
);
2270 /* compute local extrema */
2271 psh_glyph_compute_extrema( glyph
);
2273 /* compute aligned stem/hints positions */
2274 psh_hint_table_align_hints( &glyph
->hint_tables
[dimension
],
2279 /* find strong points, align them, then interpolate others */
2280 psh_glyph_find_strong_points( glyph
, dimension
);
2281 if ( dimension
== 1 )
2282 psh_glyph_find_blue_points( &globals
->blues
, glyph
);
2283 psh_glyph_interpolate_strong_points( glyph
, dimension
);
2284 psh_glyph_interpolate_normal_points( glyph
, dimension
);
2285 psh_glyph_interpolate_other_points( glyph
, dimension
);
2287 /* save hinted coordinates back to outline */
2288 psh_glyph_save_points( glyph
, dimension
);
2291 psh_globals_set_scale( glyph
->globals
,
2292 old_x_scale
, old_y_scale
, 0, 0 );
2298 #ifndef DEBUG_HINTER
2299 psh_glyph_done( glyph
);