1 /***************************************************************************/
5 /* PostScript hinting algorithm (body). */
7 /* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 /***************************************************************************/
20 #include FT_INTERNAL_OBJECTS_H
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_CALC_H
29 #define FT_COMPONENT trace_pshalgo2
33 PSH_Hint_Table ps_debug_hint_table
= 0;
34 PSH_HintFunc ps_debug_hint_func
= 0;
35 PSH_Glyph ps_debug_glyph
= 0;
39 #define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */
40 /* and similar glyphs */
41 #define STRONGER /* slightly increase the contrast of smooth */
45 /*************************************************************************/
46 /*************************************************************************/
48 /***** BASIC HINTS RECORDINGS *****/
50 /*************************************************************************/
51 /*************************************************************************/
53 /* return true if two stem hints overlap */
55 psh_hint_overlap( PSH_Hint hint1
,
58 return hint1
->org_pos
+ hint1
->org_len
>= hint2
->org_pos
&&
59 hint2
->org_pos
+ hint2
->org_len
>= hint1
->org_pos
;
63 /* destroy hints table */
65 psh_hint_table_done( PSH_Hint_Table table
,
68 FT_FREE( table
->zones
);
72 FT_FREE( table
->sort
);
73 FT_FREE( table
->hints
);
76 table
->sort_global
= 0;
80 /* deactivate all hints in a table */
82 psh_hint_table_deactivate( PSH_Hint_Table table
)
84 FT_UInt count
= table
->max_hints
;
85 PSH_Hint hint
= table
->hints
;
88 for ( ; count
> 0; count
--, hint
++ )
90 psh_hint_deactivate( hint
);
96 /* internal function to record a new hint */
98 psh_hint_table_record( PSH_Hint_Table table
,
101 PSH_Hint hint
= table
->hints
+ idx
;
104 if ( idx
>= table
->max_hints
)
106 FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx
));
110 /* ignore active hints */
111 if ( psh_hint_is_active( hint
) )
114 psh_hint_activate( hint
);
116 /* now scan the current active hint set to check */
117 /* whether `hint' overlaps with another hint */
119 PSH_Hint
* sorted
= table
->sort_global
;
120 FT_UInt count
= table
->num_hints
;
125 for ( ; count
> 0; count
--, sorted
++ )
129 if ( psh_hint_overlap( hint
, hint2
) )
131 hint
->parent
= hint2
;
137 if ( table
->num_hints
< table
->max_hints
)
138 table
->sort_global
[table
->num_hints
++] = hint
;
140 FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" ));
145 psh_hint_table_record_mask( PSH_Hint_Table table
,
148 FT_Int mask
= 0, val
= 0;
149 FT_Byte
* cursor
= hint_mask
->bytes
;
153 limit
= hint_mask
->num_bits
;
155 for ( idx
= 0; idx
< limit
; idx
++ )
164 psh_hint_table_record( table
, idx
);
171 /* create hints table */
173 psh_hint_table_init( PSH_Hint_Table table
,
175 PS_Mask_Table hint_masks
,
176 PS_Mask_Table counter_masks
,
182 FT_UNUSED( counter_masks
);
185 count
= hints
->num_hints
;
187 /* allocate our tables */
188 if ( FT_NEW_ARRAY( table
->sort
, 2 * count
) ||
189 FT_NEW_ARRAY( table
->hints
, count
) ||
190 FT_NEW_ARRAY( table
->zones
, 2 * count
+ 1 ) )
193 table
->max_hints
= count
;
194 table
->sort_global
= table
->sort
+ count
;
195 table
->num_hints
= 0;
196 table
->num_zones
= 0;
199 /* initialize the `table->hints' array */
201 PSH_Hint write
= table
->hints
;
202 PS_Hint read
= hints
->hints
;
205 for ( ; count
> 0; count
--, write
++, read
++ )
207 write
->org_pos
= read
->pos
;
208 write
->org_len
= read
->len
;
209 write
->flags
= read
->flags
;
213 /* we now need to determine the initial `parent' stems; first */
214 /* activate the hints that are given by the initial hint masks */
217 PS_Mask mask
= hint_masks
->masks
;
220 count
= hint_masks
->num_masks
;
221 table
->hint_masks
= hint_masks
;
223 for ( ; count
> 0; count
--, mask
++ )
224 psh_hint_table_record_mask( table
, mask
);
227 /* finally, do a linear parse in case some hints were left alone */
228 if ( table
->num_hints
!= table
->max_hints
)
233 FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" ));
235 count
= table
->max_hints
;
236 for ( idx
= 0; idx
< count
; idx
++ )
237 psh_hint_table_record( table
, idx
);
246 psh_hint_table_activate_mask( PSH_Hint_Table table
,
249 FT_Int mask
= 0, val
= 0;
250 FT_Byte
* cursor
= hint_mask
->bytes
;
251 FT_UInt idx
, limit
, count
;
254 limit
= hint_mask
->num_bits
;
257 psh_hint_table_deactivate( table
);
259 for ( idx
= 0; idx
< limit
; idx
++ )
269 PSH_Hint hint
= &table
->hints
[idx
];
272 if ( !psh_hint_is_active( hint
) )
277 PSH_Hint
* sort
= table
->sort
;
281 for ( count2
= count
; count2
> 0; count2
--, sort
++ )
284 if ( psh_hint_overlap( hint
, hint2
) )
285 FT_TRACE0(( "psh_hint_table_activate_mask:"
286 " found overlapping hints\n" ))
294 psh_hint_activate( hint
);
295 if ( count
< table
->max_hints
)
296 table
->sort
[count
++] = hint
;
298 FT_TRACE0(( "psh_hint_tableactivate_mask:"
299 " too many active hints\n" ));
306 table
->num_hints
= count
;
308 /* now, sort the hints; they are guaranteed to not overlap */
309 /* so we can compare their "org_pos" field directly */
312 PSH_Hint hint1
, hint2
;
313 PSH_Hint
* sort
= table
->sort
;
316 /* a simple bubble sort will do, since in 99% of cases, the hints */
317 /* will be already sorted -- and the sort will be linear */
318 for ( i1
= 1; i1
< (FT_Int
)count
; i1
++ )
321 for ( i2
= i1
- 1; i2
>= 0; i2
-- )
325 if ( hint2
->org_pos
< hint1
->org_pos
)
328 sort
[i2
+ 1] = hint2
;
336 /*************************************************************************/
337 /*************************************************************************/
339 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/
341 /*************************************************************************/
342 /*************************************************************************/
346 psh_dimension_quantize_len( PSH_Dimension dim
,
348 FT_Bool do_snapping
)
354 FT_Pos delta
= len
- dim
->stdw
.widths
[0].cur
;
362 len
= dim
->stdw
.widths
[0].cur
;
369 delta
= ( len
& 63 );
375 else if ( delta
< 32 )
378 else if ( delta
< 54 )
385 len
= FT_PIX_ROUND( len
);
389 len
= FT_PIX_ROUND( len
);
399 ps_simple_scale( PSH_Hint_Table table
,
408 for ( count
= 0; count
< table
->max_hints
; count
++ )
410 hint
= table
->hints
+ count
;
412 hint
->cur_pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
413 hint
->cur_len
= FT_MulFix( hint
->org_len
, scale
);
415 if ( ps_debug_hint_func
)
416 ps_debug_hint_func( hint
, dimension
);
420 #endif /* DEBUG_HINTER */
424 psh_hint_snap_stem_side_delta( FT_Fixed pos
,
427 FT_Fixed delta1
= FT_PIX_ROUND( pos
) - pos
;
428 FT_Fixed delta2
= FT_PIX_ROUND( pos
+ len
) - pos
- len
;
431 if ( FT_ABS( delta1
) <= FT_ABS( delta2
) )
439 psh_hint_align( PSH_Hint hint
,
444 PSH_Dimension dim
= &globals
->dimension
[dimension
];
445 FT_Fixed scale
= dim
->scale_mult
;
446 FT_Fixed delta
= dim
->scale_delta
;
449 if ( !psh_hint_is_fitted( hint
) )
451 FT_Pos pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
452 FT_Pos len
= FT_MulFix( hint
->org_len
, scale
);
456 PSH_AlignmentRec align
;
459 /* ignore stem alignments when requested through the hint flags */
460 if ( ( dimension
== 0 && !glyph
->do_horz_hints
) ||
461 ( dimension
== 1 && !glyph
->do_vert_hints
) )
466 psh_hint_set_fitted( hint
);
470 /* perform stem snapping when requested - this is necessary
471 * for monochrome and LCD hinting modes only
473 do_snapping
= ( dimension
== 0 && glyph
->do_horz_snapping
) ||
474 ( dimension
== 1 && glyph
->do_vert_snapping
);
476 hint
->cur_len
= fit_len
= len
;
478 /* check blue zones for horizontal stems */
479 align
.align
= PSH_BLUE_ALIGN_NONE
;
480 align
.align_bot
= align
.align_top
= 0;
482 if ( dimension
== 1 )
483 psh_blues_snap_stem( &globals
->blues
,
484 hint
->org_pos
+ hint
->org_len
,
488 switch ( align
.align
)
490 case PSH_BLUE_ALIGN_TOP
:
491 /* the top of the stem is aligned against a blue zone */
492 hint
->cur_pos
= align
.align_top
- fit_len
;
495 case PSH_BLUE_ALIGN_BOT
:
496 /* the bottom of the stem is aligned against a blue zone */
497 hint
->cur_pos
= align
.align_bot
;
500 case PSH_BLUE_ALIGN_TOP
| PSH_BLUE_ALIGN_BOT
:
501 /* both edges of the stem are aligned against blue zones */
502 hint
->cur_pos
= align
.align_bot
;
503 hint
->cur_len
= align
.align_top
- align
.align_bot
;
508 PSH_Hint parent
= hint
->parent
;
513 FT_Pos par_org_center
, par_cur_center
;
514 FT_Pos cur_org_center
, cur_delta
;
517 /* ensure that parent is already fitted */
518 if ( !psh_hint_is_fitted( parent
) )
519 psh_hint_align( parent
, globals
, dimension
, glyph
);
521 /* keep original relation between hints, this is, use the */
522 /* scaled distance between the centers of the hints to */
523 /* compute the new position */
524 par_org_center
= parent
->org_pos
+ ( parent
->org_len
>> 1 );
525 par_cur_center
= parent
->cur_pos
+ ( parent
->cur_len
>> 1 );
526 cur_org_center
= hint
->org_pos
+ ( hint
->org_len
>> 1 );
528 cur_delta
= FT_MulFix( cur_org_center
- par_org_center
, scale
);
529 pos
= par_cur_center
+ cur_delta
- ( len
>> 1 );
533 hint
->cur_len
= fit_len
;
535 /* Stem adjustment tries to snap stem widths to standard
536 * ones. This is important to prevent unpleasant rounding
539 if ( glyph
->do_stem_adjust
)
543 /* the stem is less than one pixel; we will center it
544 * around the nearest pixel center
548 /* This is a special case where we also widen the stem
549 * and align it to the pixel grid.
551 * stem_center = pos + (len/2)
552 * nearest_pixel_center = FT_ROUND(stem_center-32)+32
553 * new_pos = nearest_pixel_center-32
554 * = FT_ROUND(stem_center-32)
555 * = FT_FLOOR(stem_center-32+32)
556 * = FT_FLOOR(stem_center)
559 pos
= FT_PIX_FLOOR( pos
+ ( len
>> 1 ) );
564 /* This is a very small stem; we simply align it to the
565 * pixel grid, trying to find the minimal displacement.
569 * left_nearest_edge = ROUND(pos)
570 * right_nearest_edge = ROUND(right)
572 * if ( ABS(left_nearest_edge - left) <=
573 * ABS(right_nearest_edge - right) )
578 FT_Pos left_nearest
= FT_PIX_ROUND( pos
);
579 FT_Pos right_nearest
= FT_PIX_ROUND( pos
+ len
);
580 FT_Pos left_disp
= left_nearest
- pos
;
581 FT_Pos right_disp
= right_nearest
- ( pos
+ len
);
585 left_disp
= -left_disp
;
586 if ( right_disp
< 0 )
587 right_disp
= -right_disp
;
588 if ( left_disp
<= right_disp
)
595 /* this is a ghost stem; we simply round it */
596 pos
= FT_PIX_ROUND( pos
);
601 len
= psh_dimension_quantize_len( dim
, len
, 0 );
605 /* now that we have a good hinted stem width, try to position */
606 /* the stem along a pixel grid integer coordinate */
607 hint
->cur_pos
= pos
+ psh_hint_snap_stem_side_delta( pos
, len
);
620 len
= FT_PIX_ROUND( len
);
622 switch ( align
.align
)
624 case PSH_BLUE_ALIGN_TOP
:
625 hint
->cur_pos
= align
.align_top
- len
;
629 case PSH_BLUE_ALIGN_BOT
:
633 case PSH_BLUE_ALIGN_BOT
| PSH_BLUE_ALIGN_TOP
:
641 pos
= FT_PIX_FLOOR( pos
+ ( len
>> 1 ) ) + 32;
643 pos
= FT_PIX_ROUND( pos
+ ( len
>> 1 ) );
645 hint
->cur_pos
= pos
- ( len
>> 1 );
650 psh_hint_set_fitted( hint
);
653 if ( ps_debug_hint_func
)
654 ps_debug_hint_func( hint
, dimension
);
660 #if 0 /* not used for now, experimental */
663 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
667 psh_hint_align_light( PSH_Hint hint
,
672 PSH_Dimension dim
= &globals
->dimension
[dimension
];
673 FT_Fixed scale
= dim
->scale_mult
;
674 FT_Fixed delta
= dim
->scale_delta
;
677 if ( !psh_hint_is_fitted( hint
) )
679 FT_Pos pos
= FT_MulFix( hint
->org_pos
, scale
) + delta
;
680 FT_Pos len
= FT_MulFix( hint
->org_len
, scale
);
684 PSH_AlignmentRec align
;
687 /* ignore stem alignments when requested through the hint flags */
688 if ( ( dimension
== 0 && !glyph
->do_horz_hints
) ||
689 ( dimension
== 1 && !glyph
->do_vert_hints
) )
694 psh_hint_set_fitted( hint
);
700 hint
->cur_len
= fit_len
;
702 /* check blue zones for horizontal stems */
703 align
.align
= PSH_BLUE_ALIGN_NONE
;
704 align
.align_bot
= align
.align_top
= 0;
706 if ( dimension
== 1 )
707 psh_blues_snap_stem( &globals
->blues
,
708 hint
->org_pos
+ hint
->org_len
,
712 switch ( align
.align
)
714 case PSH_BLUE_ALIGN_TOP
:
715 /* the top of the stem is aligned against a blue zone */
716 hint
->cur_pos
= align
.align_top
- fit_len
;
719 case PSH_BLUE_ALIGN_BOT
:
720 /* the bottom of the stem is aligned against a blue zone */
721 hint
->cur_pos
= align
.align_bot
;
724 case PSH_BLUE_ALIGN_TOP
| PSH_BLUE_ALIGN_BOT
:
725 /* both edges of the stem are aligned against blue zones */
726 hint
->cur_pos
= align
.align_bot
;
727 hint
->cur_len
= align
.align_top
- align
.align_bot
;
732 PSH_Hint parent
= hint
->parent
;
737 FT_Pos par_org_center
, par_cur_center
;
738 FT_Pos cur_org_center
, cur_delta
;
741 /* ensure that parent is already fitted */
742 if ( !psh_hint_is_fitted( parent
) )
743 psh_hint_align_light( parent
, globals
, dimension
, glyph
);
745 par_org_center
= parent
->org_pos
+ ( parent
->org_len
/ 2 );
746 par_cur_center
= parent
->cur_pos
+ ( parent
->cur_len
/ 2 );
747 cur_org_center
= hint
->org_pos
+ ( hint
->org_len
/ 2 );
749 cur_delta
= FT_MulFix( cur_org_center
- par_org_center
, scale
);
750 pos
= par_cur_center
+ cur_delta
- ( len
>> 1 );
753 /* Stems less than one pixel wide are easy -- we want to
754 * make them as dark as possible, so they must fall within
755 * one pixel. If the stem is split between two pixels
756 * then snap the edge that is nearer to the pixel boundary
757 * to the pixel boundary.
761 if ( ( pos
+ len
+ 63 ) / 64 != pos
/ 64 + 1 )
762 pos
+= psh_hint_snap_stem_side_delta ( pos
, len
);
765 /* Position stems other to minimize the amount of mid-grays.
766 * There are, in general, two positions that do this,
767 * illustrated as A) and B) below.
771 * A) |--------------------------------|
772 * B) |--------------------------------|
773 * C) |--------------------------------|
775 * Position A) (split the excess stem equally) should be better
776 * for stems of width N + f where f < 0.5.
778 * Position B) (split the deficiency equally) should be better
779 * for stems of width N + f where f > 0.5.
781 * It turns out though that minimizing the total number of lit
782 * pixels is also important, so position C), with one edge
783 * aligned with a pixel boundary is actually preferable
784 * to A). There are also more possibile positions for C) than
785 * for A) or B), so it involves less distortion of the overall
790 FT_Fixed frac_len
= len
& 63;
791 FT_Fixed center
= pos
+ ( len
>> 1 );
792 FT_Fixed delta_a
, delta_b
;
795 if ( ( len
/ 64 ) & 1 )
797 delta_a
= FT_PIX_FLOOR( center
) + 32 - center
;
798 delta_b
= FT_PIX_ROUND( center
) - center
;
802 delta_a
= FT_PIX_ROUND( center
) - center
;
803 delta_b
= FT_PIX_FLOOR( center
) + 32 - center
;
806 /* We choose between B) and C) above based on the amount
807 * of fractinal stem width; for small amounts, choose
808 * C) always, for large amounts, B) always, and inbetween,
809 * pick whichever one involves less stem movement.
813 pos
+= psh_hint_snap_stem_side_delta ( pos
, len
);
815 else if ( frac_len
< 48 )
817 FT_Fixed side_delta
= psh_hint_snap_stem_side_delta ( pos
,
820 if ( FT_ABS( side_delta
) < FT_ABS( delta_b
) )
835 psh_hint_set_fitted( hint
);
838 if ( ps_debug_hint_func
)
839 ps_debug_hint_func( hint
, dimension
);
848 psh_hint_table_align_hints( PSH_Hint_Table table
,
858 PSH_Dimension dim
= &globals
->dimension
[dimension
];
859 FT_Fixed scale
= dim
->scale_mult
;
860 FT_Fixed delta
= dim
->scale_delta
;
863 if ( ps_debug_no_vert_hints
&& dimension
== 0 )
865 ps_simple_scale( table
, scale
, delta
, dimension
);
869 if ( ps_debug_no_horz_hints
&& dimension
== 1 )
871 ps_simple_scale( table
, scale
, delta
, dimension
);
875 #endif /* DEBUG_HINTER*/
878 count
= table
->max_hints
;
880 for ( ; count
> 0; count
--, hint
++ )
881 psh_hint_align( hint
, globals
, dimension
, glyph
);
885 /*************************************************************************/
886 /*************************************************************************/
888 /***** POINTS INTERPOLATION ROUTINES *****/
890 /*************************************************************************/
891 /*************************************************************************/
893 #define PSH_ZONE_MIN -3200000L
894 #define PSH_ZONE_MAX +3200000L
896 #define xxDEBUG_ZONES
901 #include FT_CONFIG_STANDARD_LIBRARY_H
904 psh_print_zone( PSH_Zone zone
)
906 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
907 zone
->scale
/ 65536.0,
915 #define psh_print_zone( x ) do { } while ( 0 )
917 #endif /* DEBUG_ZONES */
920 /*************************************************************************/
921 /*************************************************************************/
923 /***** HINTER GLYPH MANAGEMENT *****/
925 /*************************************************************************/
926 /*************************************************************************/
930 #define psh_corner_is_flat ft_corner_is_flat
931 #define psh_corner_orientation ft_corner_orientation
935 FT_LOCAL_DEF( FT_Int
)
936 psh_corner_is_flat( FT_Pos x_in
,
944 FT_Pos d_in
, d_out
, d_corner
;
969 return ( d_in
+ d_out
- d_corner
) < ( d_corner
>> 4 );
973 psh_corner_orientation( FT_Pos in_x
,
981 /* deal with the trivial cases quickly */
989 else if ( in_x
== 0 )
996 else if ( out_y
== 0 )
1003 else if ( out_x
== 0 )
1010 else /* general case */
1012 long long delta
= (long long)in_x
* out_y
- (long long)in_y
* out_x
;
1017 result
= 1 - 2 * ( delta
< 0 );
1026 #ifdef COMPUTE_INFLEXS
1028 /* compute all inflex points in a given glyph */
1030 psh_glyph_compute_inflections( PSH_Glyph glyph
)
1035 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1037 PSH_Point first
, start
, end
, before
, after
;
1038 FT_Pos in_x
, in_y
, out_x
, out_y
;
1039 FT_Int orient_prev
, orient_cur
;
1040 FT_Int finished
= 0;
1043 /* we need at least 4 points to create an inflection point */
1044 if ( glyph
->contours
[n
].count
< 4 )
1047 /* compute first segment in contour */
1048 first
= glyph
->contours
[n
].start
;
1050 start
= end
= first
;
1057 in_x
= end
->org_u
- start
->org_u
;
1058 in_y
= end
->org_v
- start
->org_v
;
1060 } while ( in_x
== 0 && in_y
== 0 );
1062 /* extend the segment start whenever possible */
1069 before
= before
->prev
;
1070 if ( before
== first
)
1073 out_x
= start
->org_u
- before
->org_u
;
1074 out_y
= start
->org_v
- before
->org_v
;
1076 } while ( out_x
== 0 && out_y
== 0 );
1078 orient_prev
= psh_corner_orientation( in_x
, in_y
, out_x
, out_y
);
1080 } while ( orient_prev
== 0 );
1086 /* now, process all segments in the contour */
1089 /* first, extend current segment's end whenever possible */
1096 after
= after
->next
;
1097 if ( after
== first
)
1100 out_x
= after
->org_u
- end
->org_u
;
1101 out_y
= after
->org_v
- end
->org_v
;
1103 } while ( out_x
== 0 && out_y
== 0 );
1105 orient_cur
= psh_corner_orientation( in_x
, in_y
, out_x
, out_y
);
1107 } while ( orient_cur
== 0 );
1109 if ( ( orient_cur
^ orient_prev
) < 0 )
1113 psh_point_set_inflex( start
);
1114 start
= start
->next
;
1116 while ( start
!= end
);
1118 psh_point_set_inflex( start
);
1123 orient_prev
= orient_cur
;
1127 } while ( !finished
);
1134 #endif /* COMPUTE_INFLEXS */
1138 psh_glyph_done( PSH_Glyph glyph
)
1140 FT_Memory memory
= glyph
->memory
;
1143 psh_hint_table_done( &glyph
->hint_tables
[1], memory
);
1144 psh_hint_table_done( &glyph
->hint_tables
[0], memory
);
1146 FT_FREE( glyph
->points
);
1147 FT_FREE( glyph
->contours
);
1149 glyph
->num_points
= 0;
1150 glyph
->num_contours
= 0;
1157 psh_compute_dir( FT_Pos dx
,
1161 int result
= PSH_DIR_NONE
;
1164 ax
= ( dx
>= 0 ) ? dx
: -dx
;
1165 ay
= ( dy
>= 0 ) ? dy
: -dy
;
1169 /* |dy| <<< |dx| means a near-horizontal segment */
1170 result
= ( dx
>= 0 ) ? PSH_DIR_RIGHT
: PSH_DIR_LEFT
;
1172 else if ( ax
* 12 < ay
)
1174 /* |dx| <<< |dy| means a near-vertical segment */
1175 result
= ( dy
>= 0 ) ? PSH_DIR_UP
: PSH_DIR_DOWN
;
1182 /* load outline point coordinates into hinter glyph */
1184 psh_glyph_load_points( PSH_Glyph glyph
,
1187 FT_Vector
* vec
= glyph
->outline
->points
;
1188 PSH_Point point
= glyph
->points
;
1189 FT_UInt count
= glyph
->num_points
;
1192 for ( ; count
> 0; count
--, point
++, vec
++ )
1196 if ( dimension
== 0 )
1198 point
->org_u
= vec
->x
;
1199 point
->org_v
= vec
->y
;
1203 point
->org_u
= vec
->y
;
1204 point
->org_v
= vec
->x
;
1208 point
->org_x
= vec
->x
;
1209 point
->org_y
= vec
->y
;
1216 /* save hinted point coordinates back to outline */
1218 psh_glyph_save_points( PSH_Glyph glyph
,
1222 PSH_Point point
= glyph
->points
;
1223 FT_Vector
* vec
= glyph
->outline
->points
;
1224 char* tags
= glyph
->outline
->tags
;
1227 for ( n
= 0; n
< glyph
->num_points
; n
++ )
1229 if ( dimension
== 0 )
1230 vec
[n
].x
= point
->cur_u
;
1232 vec
[n
].y
= point
->cur_u
;
1234 if ( psh_point_is_strong( point
) )
1235 tags
[n
] |= (char)( ( dimension
== 0 ) ? 32 : 64 );
1239 if ( dimension
== 0 )
1241 point
->cur_x
= point
->cur_u
;
1242 point
->flags_x
= point
->flags2
| point
->flags
;
1246 point
->cur_y
= point
->cur_u
;
1247 point
->flags_y
= point
->flags2
| point
->flags
;
1258 psh_glyph_init( PSH_Glyph glyph
,
1259 FT_Outline
* outline
,
1261 PSH_Globals globals
)
1267 /* clear all fields */
1268 FT_MEM_ZERO( glyph
, sizeof ( *glyph
) );
1270 memory
= glyph
->memory
= globals
->memory
;
1272 /* allocate and setup points + contours arrays */
1273 if ( FT_NEW_ARRAY( glyph
->points
, outline
->n_points
) ||
1274 FT_NEW_ARRAY( glyph
->contours
, outline
->n_contours
) )
1277 glyph
->num_points
= outline
->n_points
;
1278 glyph
->num_contours
= outline
->n_contours
;
1281 FT_UInt first
= 0, next
, n
;
1282 PSH_Point points
= glyph
->points
;
1283 PSH_Contour contour
= glyph
->contours
;
1286 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1292 next
= outline
->contours
[n
] + 1;
1293 count
= next
- first
;
1295 contour
->start
= points
+ first
;
1296 contour
->count
= (FT_UInt
)count
;
1300 point
= points
+ first
;
1302 point
->prev
= points
+ next
- 1;
1303 point
->contour
= contour
;
1305 for ( ; count
> 1; count
-- )
1307 point
[0].next
= point
+ 1;
1308 point
[1].prev
= point
;
1310 point
->contour
= contour
;
1312 point
->next
= points
+ first
;
1321 PSH_Point points
= glyph
->points
;
1322 PSH_Point point
= points
;
1323 FT_Vector
* vec
= outline
->points
;
1327 for ( n
= 0; n
< glyph
->num_points
; n
++, point
++ )
1329 FT_Int n_prev
= (FT_Int
)( point
->prev
- points
);
1330 FT_Int n_next
= (FT_Int
)( point
->next
- points
);
1331 FT_Pos dxi
, dyi
, dxo
, dyo
;
1334 if ( !( outline
->tags
[n
] & FT_CURVE_TAG_ON
) )
1335 point
->flags
= PSH_POINT_OFF
;
1337 dxi
= vec
[n
].x
- vec
[n_prev
].x
;
1338 dyi
= vec
[n
].y
- vec
[n_prev
].y
;
1340 point
->dir_in
= (FT_Char
)psh_compute_dir( dxi
, dyi
);
1342 dxo
= vec
[n_next
].x
- vec
[n
].x
;
1343 dyo
= vec
[n_next
].y
- vec
[n
].y
;
1345 point
->dir_out
= (FT_Char
)psh_compute_dir( dxo
, dyo
);
1347 /* detect smooth points */
1348 if ( point
->flags
& PSH_POINT_OFF
)
1349 point
->flags
|= PSH_POINT_SMOOTH
;
1351 else if ( point
->dir_in
== point
->dir_out
)
1353 if ( point
->dir_out
!= PSH_DIR_NONE
||
1354 psh_corner_is_flat( dxi
, dyi
, dxo
, dyo
) )
1355 point
->flags
|= PSH_POINT_SMOOTH
;
1360 glyph
->outline
= outline
;
1361 glyph
->globals
= globals
;
1363 #ifdef COMPUTE_INFLEXS
1364 psh_glyph_load_points( glyph
, 0 );
1365 psh_glyph_compute_inflections( glyph
);
1366 #endif /* COMPUTE_INFLEXS */
1368 /* now deal with hints tables */
1369 error
= psh_hint_table_init( &glyph
->hint_tables
[0],
1370 &ps_hints
->dimension
[0].hints
,
1371 &ps_hints
->dimension
[0].masks
,
1372 &ps_hints
->dimension
[0].counters
,
1377 error
= psh_hint_table_init( &glyph
->hint_tables
[1],
1378 &ps_hints
->dimension
[1].hints
,
1379 &ps_hints
->dimension
[1].masks
,
1380 &ps_hints
->dimension
[1].counters
,
1390 /* compute all extrema in a glyph for a given dimension */
1392 psh_glyph_compute_extrema( PSH_Glyph glyph
)
1397 /* first of all, compute all local extrema */
1398 for ( n
= 0; n
< glyph
->num_contours
; n
++ )
1400 PSH_Point first
= glyph
->contours
[n
].start
;
1401 PSH_Point point
, before
, after
;
1404 if ( glyph
->contours
[n
].count
== 0 )
1413 before
= before
->prev
;
1414 if ( before
== first
)
1417 } while ( before
->org_u
== point
->org_u
);
1419 first
= point
= before
->next
;
1426 after
= after
->next
;
1427 if ( after
== first
)
1430 } while ( after
->org_u
== point
->org_u
);
1432 if ( before
->org_u
< point
->org_u
)
1434 if ( after
->org_u
< point
->org_u
)
1440 else /* before->org_u > point->org_u */
1442 if ( after
->org_u
> point
->org_u
)
1448 psh_point_set_extremum( point
);
1449 point
= point
->next
;
1451 } while ( point
!= after
);
1455 before
= after
->prev
;
1464 /* for each extremum, determine its direction along the */
1465 /* orthogonal axis */
1466 for ( n
= 0; n
< glyph
->num_points
; n
++ )
1468 PSH_Point point
, before
, after
;
1471 point
= &glyph
->points
[n
];
1475 if ( psh_point_is_extremum( point
) )
1479 before
= before
->prev
;
1480 if ( before
== point
)
1483 } while ( before
->org_v
== point
->org_v
);
1487 after
= after
->next
;
1488 if ( after
== point
)
1491 } while ( after
->org_v
== point
->org_v
);
1494 if ( before
->org_v
< point
->org_v
&&
1495 after
->org_v
> point
->org_v
)
1497 psh_point_set_positive( point
);
1499 else if ( before
->org_v
> point
->org_v
&&
1500 after
->org_v
< point
->org_v
)
1502 psh_point_set_negative( point
);
1511 /* major_dir is the direction for points on the bottom/left of the stem; */
1512 /* Points on the top/right of the stem will have a direction of */
1516 psh_hint_table_find_strong_points( PSH_Hint_Table table
,
1522 PSH_Hint
* sort
= table
->sort
;
1523 FT_UInt num_hints
= table
->num_hints
;
1526 for ( ; count
> 0; count
--, point
++ )
1528 FT_Int point_dir
= 0;
1529 FT_Pos org_u
= point
->org_u
;
1532 if ( psh_point_is_strong( point
) )
1535 if ( PSH_DIR_COMPARE( point
->dir_in
, major_dir
) )
1536 point_dir
= point
->dir_in
;
1538 else if ( PSH_DIR_COMPARE( point
->dir_out
, major_dir
) )
1539 point_dir
= point
->dir_out
;
1543 if ( point_dir
== major_dir
)
1548 for ( nn
= 0; nn
< num_hints
; nn
++ )
1550 PSH_Hint hint
= sort
[nn
];
1551 FT_Pos d
= org_u
- hint
->org_pos
;
1554 if ( d
< threshold
&& -d
< threshold
)
1556 psh_point_set_strong( point
);
1557 point
->flags2
|= PSH_POINT_EDGE_MIN
;
1563 else if ( point_dir
== -major_dir
)
1568 for ( nn
= 0; nn
< num_hints
; nn
++ )
1570 PSH_Hint hint
= sort
[nn
];
1571 FT_Pos d
= org_u
- hint
->org_pos
- hint
->org_len
;
1574 if ( d
< threshold
&& -d
< threshold
)
1576 psh_point_set_strong( point
);
1577 point
->flags2
|= PSH_POINT_EDGE_MAX
;
1586 else if ( psh_point_is_extremum( point
) )
1588 /* treat extrema as special cases for stem edge alignment */
1589 FT_UInt nn
, min_flag
, max_flag
;
1592 if ( major_dir
== PSH_DIR_HORIZONTAL
)
1594 min_flag
= PSH_POINT_POSITIVE
;
1595 max_flag
= PSH_POINT_NEGATIVE
;
1599 min_flag
= PSH_POINT_NEGATIVE
;
1600 max_flag
= PSH_POINT_POSITIVE
;
1603 if ( point
->flags2
& min_flag
)
1605 for ( nn
= 0; nn
< num_hints
; nn
++ )
1607 PSH_Hint hint
= sort
[nn
];
1608 FT_Pos d
= org_u
- hint
->org_pos
;
1611 if ( d
< threshold
&& -d
< threshold
)
1613 point
->flags2
|= PSH_POINT_EDGE_MIN
;
1615 psh_point_set_strong( point
);
1620 else if ( point
->flags2
& max_flag
)
1622 for ( nn
= 0; nn
< num_hints
; nn
++ )
1624 PSH_Hint hint
= sort
[nn
];
1625 FT_Pos d
= org_u
- hint
->org_pos
- hint
->org_len
;
1628 if ( d
< threshold
&& -d
< threshold
)
1630 point
->flags2
|= PSH_POINT_EDGE_MAX
;
1632 psh_point_set_strong( point
);
1638 if ( point
->hint
== NULL
)
1640 for ( nn
= 0; nn
< num_hints
; nn
++ )
1642 PSH_Hint hint
= sort
[nn
];
1645 if ( org_u
>= hint
->org_pos
&&
1646 org_u
<= hint
->org_pos
+ hint
->org_len
)
1660 /* the accepted shift for strong points in fractional pixels */
1661 #define PSH_STRONG_THRESHOLD 32
1663 /* the maximum shift value in font units */
1664 #define PSH_STRONG_THRESHOLD_MAXIMUM 30
1667 /* find strong points in a glyph */
1669 psh_glyph_find_strong_points( PSH_Glyph glyph
,
1672 /* a point is `strong' if it is located on a stem edge and */
1673 /* has an `in' or `out' tangent parallel to the hint's direction */
1675 PSH_Hint_Table table
= &glyph
->hint_tables
[dimension
];
1676 PS_Mask mask
= table
->hint_masks
->masks
;
1677 FT_UInt num_masks
= table
->hint_masks
->num_masks
;
1679 FT_Int major_dir
= dimension
== 0 ? PSH_DIR_VERTICAL
1680 : PSH_DIR_HORIZONTAL
;
1681 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1682 FT_Fixed scale
= dim
->scale_mult
;
1686 threshold
= (FT_Int
)FT_DivFix( PSH_STRONG_THRESHOLD
, scale
);
1687 if ( threshold
> PSH_STRONG_THRESHOLD_MAXIMUM
)
1688 threshold
= PSH_STRONG_THRESHOLD_MAXIMUM
;
1690 /* process secondary hints to `selected' points */
1691 if ( num_masks
> 1 && glyph
->num_points
> 0 )
1693 first
= mask
->end_point
;
1695 for ( ; num_masks
> 1; num_masks
--, mask
++ )
1701 next
= mask
->end_point
;
1702 count
= next
- first
;
1705 PSH_Point point
= glyph
->points
+ first
;
1708 psh_hint_table_activate_mask( table
, mask
);
1710 psh_hint_table_find_strong_points( table
, point
, count
,
1711 threshold
, major_dir
);
1717 /* process primary hints for all points */
1718 if ( num_masks
== 1 )
1720 FT_UInt count
= glyph
->num_points
;
1721 PSH_Point point
= glyph
->points
;
1724 psh_hint_table_activate_mask( table
, table
->hint_masks
->masks
);
1726 psh_hint_table_find_strong_points( table
, point
, count
,
1727 threshold
, major_dir
);
1730 /* now, certain points may have been attached to a hint and */
1731 /* not marked as strong; update their flags then */
1733 FT_UInt count
= glyph
->num_points
;
1734 PSH_Point point
= glyph
->points
;
1737 for ( ; count
> 0; count
--, point
++ )
1738 if ( point
->hint
&& !psh_point_is_strong( point
) )
1739 psh_point_set_strong( point
);
1744 /* find points in a glyph which are in a blue zone and have `in' or */
1745 /* `out' tangents parallel to the horizontal axis */
1747 psh_glyph_find_blue_points( PSH_Blues blues
,
1750 PSH_Blue_Table table
;
1752 FT_UInt glyph_count
= glyph
->num_points
;
1754 PSH_Point point
= glyph
->points
;
1757 for ( ; glyph_count
> 0; glyph_count
--, point
++ )
1762 /* check tangents */
1763 if ( !PSH_DIR_COMPARE( point
->dir_in
, PSH_DIR_HORIZONTAL
) &&
1764 !PSH_DIR_COMPARE( point
->dir_out
, PSH_DIR_HORIZONTAL
) )
1767 /* skip strong points */
1768 if ( psh_point_is_strong( point
) )
1773 /* look up top zones */
1774 table
= &blues
->normal_top
;
1775 blue_count
= table
->count
;
1776 zone
= table
->zones
;
1778 for ( ; blue_count
> 0; blue_count
--, zone
++ )
1780 FT_Pos delta
= y
- zone
->org_bottom
;
1783 if ( delta
< -blues
->blue_fuzz
)
1786 if ( y
<= zone
->org_top
+ blues
->blue_fuzz
)
1787 if ( blues
->no_overshoots
|| delta
<= blues
->blue_threshold
)
1789 point
->cur_u
= zone
->cur_bottom
;
1790 psh_point_set_strong( point
);
1791 psh_point_set_fitted( point
);
1795 /* look up bottom zones */
1796 table
= &blues
->normal_bottom
;
1797 blue_count
= table
->count
;
1798 zone
= table
->zones
+ blue_count
- 1;
1800 for ( ; blue_count
> 0; blue_count
--, zone
-- )
1802 FT_Pos delta
= zone
->org_top
- y
;
1805 if ( delta
< -blues
->blue_fuzz
)
1808 if ( y
>= zone
->org_bottom
- blues
->blue_fuzz
)
1809 if ( blues
->no_overshoots
|| delta
< blues
->blue_threshold
)
1811 point
->cur_u
= zone
->cur_top
;
1812 psh_point_set_strong( point
);
1813 psh_point_set_fitted( point
);
1820 /* interpolate strong points with the help of hinted coordinates */
1822 psh_glyph_interpolate_strong_points( PSH_Glyph glyph
,
1825 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1826 FT_Fixed scale
= dim
->scale_mult
;
1828 FT_UInt count
= glyph
->num_points
;
1829 PSH_Point point
= glyph
->points
;
1832 for ( ; count
> 0; count
--, point
++ )
1834 PSH_Hint hint
= point
->hint
;
1842 if ( psh_point_is_edge_min( point
) )
1843 point
->cur_u
= hint
->cur_pos
;
1845 else if ( psh_point_is_edge_max( point
) )
1846 point
->cur_u
= hint
->cur_pos
+ hint
->cur_len
;
1850 delta
= point
->org_u
- hint
->org_pos
;
1853 point
->cur_u
= hint
->cur_pos
+ FT_MulFix( delta
, scale
);
1855 else if ( delta
>= hint
->org_len
)
1856 point
->cur_u
= hint
->cur_pos
+ hint
->cur_len
+
1857 FT_MulFix( delta
- hint
->org_len
, scale
);
1859 else if ( hint
->org_len
> 0 )
1860 point
->cur_u
= hint
->cur_pos
+
1861 FT_MulDiv( delta
, hint
->cur_len
,
1864 point
->cur_u
= hint
->cur_pos
;
1866 psh_point_set_fitted( point
);
1872 #define PSH_MAX_STRONG_INTERNAL 16
1875 psh_glyph_interpolate_normal_points( PSH_Glyph glyph
,
1880 /* first technique: a point is strong if it is a local extremum */
1882 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
1883 FT_Fixed scale
= dim
->scale_mult
;
1884 FT_Memory memory
= glyph
->memory
;
1886 PSH_Point
* strongs
= NULL
;
1887 PSH_Point strongs_0
[PSH_MAX_STRONG_INTERNAL
];
1888 FT_UInt num_strongs
= 0;
1890 PSH_Point points
= glyph
->points
;
1891 PSH_Point points_end
= points
+ glyph
->num_points
;
1895 /* first count the number of strong points */
1896 for ( point
= points
; point
< points_end
; point
++ )
1898 if ( psh_point_is_strong( point
) )
1902 if ( num_strongs
== 0 ) /* nothing to do here */
1905 /* allocate an array to store a list of points, */
1906 /* stored in increasing org_u order */
1907 if ( num_strongs
<= PSH_MAX_STRONG_INTERNAL
)
1908 strongs
= strongs_0
;
1914 if ( FT_NEW_ARRAY( strongs
, num_strongs
) )
1919 for ( point
= points
; point
< points_end
; point
++ )
1924 if ( !psh_point_is_strong( point
) )
1927 for ( insert
= strongs
+ num_strongs
; insert
> strongs
; insert
-- )
1929 if ( insert
[-1]->org_u
<= point
->org_u
)
1932 insert
[0] = insert
[-1];
1938 /* now try to interpolate all normal points */
1939 for ( point
= points
; point
< points_end
; point
++ )
1941 if ( psh_point_is_strong( point
) )
1944 /* sometimes, some local extrema are smooth points */
1945 if ( psh_point_is_smooth( point
) )
1947 if ( point
->dir_in
== PSH_DIR_NONE
||
1948 point
->dir_in
!= point
->dir_out
)
1951 if ( !psh_point_is_extremum( point
) &&
1952 !psh_point_is_inflex( point
) )
1955 point
->flags
&= ~PSH_POINT_SMOOTH
;
1958 /* find best enclosing point coordinates then interpolate */
1960 PSH_Point before
, after
;
1964 for ( nn
= 0; nn
< num_strongs
; nn
++ )
1965 if ( strongs
[nn
]->org_u
> point
->org_u
)
1968 if ( nn
== 0 ) /* point before the first strong point */
1972 point
->cur_u
= after
->cur_u
+
1973 FT_MulFix( point
->org_u
- after
->org_u
,
1978 before
= strongs
[nn
- 1];
1980 for ( nn
= num_strongs
; nn
> 0; nn
-- )
1981 if ( strongs
[nn
- 1]->org_u
< point
->org_u
)
1984 if ( nn
== num_strongs
) /* point is after last strong point */
1986 before
= strongs
[nn
- 1];
1988 point
->cur_u
= before
->cur_u
+
1989 FT_MulFix( point
->org_u
- before
->org_u
,
1997 after
= strongs
[nn
];
1999 /* now interpolate point between before and after */
2002 if ( u
== before
->org_u
)
2003 point
->cur_u
= before
->cur_u
;
2005 else if ( u
== after
->org_u
)
2006 point
->cur_u
= after
->cur_u
;
2009 point
->cur_u
= before
->cur_u
+
2010 FT_MulDiv( u
- before
->org_u
,
2011 after
->cur_u
- before
->cur_u
,
2012 after
->org_u
- before
->org_u
);
2015 psh_point_set_fitted( point
);
2019 if ( strongs
!= strongs_0
)
2027 /* interpolate other points */
2029 psh_glyph_interpolate_other_points( PSH_Glyph glyph
,
2032 PSH_Dimension dim
= &glyph
->globals
->dimension
[dimension
];
2033 FT_Fixed scale
= dim
->scale_mult
;
2034 FT_Fixed delta
= dim
->scale_delta
;
2035 PSH_Contour contour
= glyph
->contours
;
2036 FT_UInt num_contours
= glyph
->num_contours
;
2039 for ( ; num_contours
> 0; num_contours
--, contour
++ )
2041 PSH_Point start
= contour
->start
;
2042 PSH_Point first
, next
, point
;
2046 /* count the number of strong points in this contour */
2047 next
= start
+ contour
->count
;
2051 for ( point
= start
; point
< next
; point
++ )
2052 if ( psh_point_is_fitted( point
) )
2060 /* if there are less than 2 fitted points in the contour, we */
2061 /* simply scale and eventually translate the contour points */
2062 if ( fit_count
< 2 )
2064 if ( fit_count
== 1 )
2065 delta
= first
->cur_u
- FT_MulFix( first
->org_u
, scale
);
2067 for ( point
= start
; point
< next
; point
++ )
2068 if ( point
!= first
)
2069 point
->cur_u
= FT_MulFix( point
->org_u
, scale
) + delta
;
2074 /* there are more than 2 strong points in this contour; we */
2075 /* need to interpolate weak points between them */
2081 /* skip consecutive fitted points */
2085 if ( next
== start
)
2088 if ( !psh_point_is_fitted( next
) )
2094 /* find next fitted point after unfitted one */
2098 if ( psh_point_is_fitted( next
) )
2102 /* now interpolate between them */
2104 FT_Pos org_a
, org_ab
, cur_a
, cur_ab
;
2105 FT_Pos org_c
, org_ac
, cur_c
;
2109 if ( first
->org_u
<= next
->org_u
)
2111 org_a
= first
->org_u
;
2112 cur_a
= first
->cur_u
;
2113 org_ab
= next
->org_u
- org_a
;
2114 cur_ab
= next
->cur_u
- cur_a
;
2118 org_a
= next
->org_u
;
2119 cur_a
= next
->cur_u
;
2120 org_ab
= first
->org_u
- org_a
;
2121 cur_ab
= first
->cur_u
- cur_a
;
2124 scale_ab
= 0x10000L
;
2126 scale_ab
= FT_DivFix( cur_ab
, org_ab
);
2128 point
= first
->next
;
2131 org_c
= point
->org_u
;
2132 org_ac
= org_c
- org_a
;
2136 /* on the left of the interpolation zone */
2137 cur_c
= cur_a
+ FT_MulFix( org_ac
, scale
);
2139 else if ( org_ac
>= org_ab
)
2141 /* on the right on the interpolation zone */
2142 cur_c
= cur_a
+ cur_ab
+ FT_MulFix( org_ac
- org_ab
, scale
);
2146 /* within the interpolation zone */
2147 cur_c
= cur_a
+ FT_MulFix( org_ac
, scale_ab
);
2150 point
->cur_u
= cur_c
;
2152 point
= point
->next
;
2154 } while ( point
!= next
);
2157 /* keep going until all points in the contours have been processed */
2160 } while ( first
!= start
);
2168 /*************************************************************************/
2169 /*************************************************************************/
2171 /***** HIGH-LEVEL INTERFACE *****/
2173 /*************************************************************************/
2174 /*************************************************************************/
2177 ps_hints_apply( PS_Hints ps_hints
,
2178 FT_Outline
* outline
,
2179 PSH_Globals globals
,
2180 FT_Render_Mode hint_mode
)
2182 PSH_GlyphRec glyphrec
;
2183 PSH_Glyph glyph
= &glyphrec
;
2191 /* something to do? */
2192 if ( outline
->n_points
== 0 || outline
->n_contours
== 0 )
2197 memory
= globals
->memory
;
2199 if ( ps_debug_glyph
)
2201 psh_glyph_done( ps_debug_glyph
);
2202 FT_FREE( ps_debug_glyph
);
2205 if ( FT_NEW( glyph
) )
2208 ps_debug_glyph
= glyph
;
2210 #endif /* DEBUG_HINTER */
2212 error
= psh_glyph_init( glyph
, outline
, ps_hints
, globals
);
2216 /* try to optimize the y_scale so that the top of non-capital letters
2217 * is aligned on a pixel boundary whenever possible
2220 PSH_Dimension dim_x
= &glyph
->globals
->dimension
[0];
2221 PSH_Dimension dim_y
= &glyph
->globals
->dimension
[1];
2223 FT_Fixed x_scale
= dim_x
->scale_mult
;
2224 FT_Fixed y_scale
= dim_y
->scale_mult
;
2226 FT_Fixed old_x_scale
= x_scale
;
2227 FT_Fixed old_y_scale
= y_scale
;
2232 FT_Bool rescale
= FALSE
;
2235 scaled
= FT_MulFix( globals
->blues
.normal_top
.zones
->org_ref
, y_scale
);
2236 fitted
= FT_PIX_ROUND( scaled
);
2238 if ( fitted
!= 0 && scaled
!= fitted
)
2242 y_scale
= FT_MulDiv( y_scale
, fitted
, scaled
);
2244 if ( fitted
< scaled
)
2245 x_scale
-= x_scale
/ 50;
2247 psh_globals_set_scale( glyph
->globals
, x_scale
, y_scale
, 0, 0 );
2250 glyph
->do_horz_hints
= 1;
2251 glyph
->do_vert_hints
= 1;
2253 glyph
->do_horz_snapping
= FT_BOOL( hint_mode
== FT_RENDER_MODE_MONO
||
2254 hint_mode
== FT_RENDER_MODE_LCD
);
2256 glyph
->do_vert_snapping
= FT_BOOL( hint_mode
== FT_RENDER_MODE_MONO
||
2257 hint_mode
== FT_RENDER_MODE_LCD_V
);
2259 glyph
->do_stem_adjust
= FT_BOOL( hint_mode
!= FT_RENDER_MODE_LIGHT
);
2261 for ( dimension
= 0; dimension
< 2; dimension
++ )
2263 /* load outline coordinates into glyph */
2264 psh_glyph_load_points( glyph
, dimension
);
2266 /* compute local extrema */
2267 psh_glyph_compute_extrema( glyph
);
2269 /* compute aligned stem/hints positions */
2270 psh_hint_table_align_hints( &glyph
->hint_tables
[dimension
],
2275 /* find strong points, align them, then interpolate others */
2276 psh_glyph_find_strong_points( glyph
, dimension
);
2277 if ( dimension
== 1 )
2278 psh_glyph_find_blue_points( &globals
->blues
, glyph
);
2279 psh_glyph_interpolate_strong_points( glyph
, dimension
);
2280 psh_glyph_interpolate_normal_points( glyph
, dimension
);
2281 psh_glyph_interpolate_other_points( glyph
, dimension
);
2283 /* save hinted coordinates back to outline */
2284 psh_glyph_save_points( glyph
, dimension
);
2287 psh_globals_set_scale( glyph
->globals
,
2288 old_x_scale
, old_y_scale
, 0, 0 );
2294 #ifndef DEBUG_HINTER
2295 psh_glyph_done( glyph
);