diff --git a/src/pshinter/pshalgo3.c b/src/pshinter/pshalgo3.c index 12a0f169b..39e9ca1fb 100644 --- a/src/pshinter/pshalgo3.c +++ b/src/pshinter/pshalgo3.c @@ -456,7 +456,9 @@ return; } - /* perform stem snapping when requested */ + /* perform stem snapping when requested - this is necessary + * for monochrome and LCD hinting modes only + */ do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) || ( dimension == 1 && glyph->do_vert_snapping ); @@ -516,14 +518,24 @@ hint->cur_pos = pos; hint->cur_len = fit_len; +#if 0 + /* stem adjustment tries to snap stem widths to standard + * ones. this is important to prevent unpleasant rounding + * artefacts... + */ if ( glyph->do_stem_adjust ) { if ( len <= 64 ) { - /* the stem is less than one pixel, we will center it */ - /* around the nearest pixel center */ - /* */ + /* the stem is less than one pixel, we will center it + * around the nearest pixel center + */ +#if 1 + pos = ( pos + (len >> 1) ) & -64; +#else + /* this seems to be a bug !! */ pos = ( pos + ( (len >> 1) & -64 ) ); +#endif len = 64; } else @@ -531,6 +543,7 @@ len = psh3_dimension_quantize_len( dim, len, 0 ); } } +#endif /* 0 */ /* now that we have a good hinted stem width, try to position */ /* the stem along a pixel grid integer coordinate */ @@ -541,7 +554,7 @@ if ( do_snapping ) { - pos = hint->cur_pos; + pos = hint->cur_pos; len = hint->cur_len; if ( len < 64 ) @@ -587,6 +600,186 @@ } + /* + * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT) + * of stems + */ + static void + psh3_hint_align_light( PSH3_Hint hint, + PSH_Globals globals, + FT_Int dimension, + PSH3_Glyph glyph ) + { + PSH_Dimension dim = &globals->dimension[dimension]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + + if ( !psh3_hint_is_fitted(hint) ) + { + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_center; + FT_Pos fit_len; + + PSH_AlignmentRec align; + + /* ignore stem alignments when requested through the hint flags */ + if ( ( dimension == 0 && !glyph->do_horz_hints ) || + ( dimension == 1 && !glyph->do_vert_hints ) ) + { + hint->cur_pos = pos; + hint->cur_len = len; + + psh3_hint_set_fitted( hint ); + return; + } + + fit_len = len; + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = PSH_BLUE_ALIGN_NONE; + align.align_bot = align.align_top = 0; + + if ( dimension == 1 ) + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + + switch ( align.align ) + { + case PSH_BLUE_ALIGN_TOP: + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + + case PSH_BLUE_ALIGN_BOT: + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + break; + + default: + { + PSH3_Hint parent = hint->parent; + + + if ( parent ) + { + FT_Pos par_org_center, par_cur_center; + FT_Pos cur_org_center, cur_delta; + + + /* ensure that parent is already fitted */ + if ( !psh3_hint_is_fitted( parent ) ) + psh3_hint_align_light( parent, globals, dimension, glyph ); + + par_org_center = parent->org_pos + ( parent->org_len / 2); + par_cur_center = parent->cur_pos + ( parent->cur_len / 2); + cur_org_center = hint->org_pos + ( hint->org_len / 2); + + cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); + pos = par_cur_center + cur_delta - ( len >> 1 ); + } + + /* Stems less than one pixel wide are easy - we want to + * make them as dark as possible, so they must fall within + * one pixel. If the stem is split between two pixels + * then snap the edge that is nearer to the pixel boundary + * to the pixel boundary + */ + if (len <= 64) + { + if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 ) + pos += psh3_hint_snap_stem_side_delta ( pos, len ); + } + /* Position stems other to minimize the amount of mid-grays. + * There are, in general, two positions that do this, + * illustrated as A) and B) below. + * + * + + + + + * + * A) |--------------------------------| + * B) |--------------------------------| + * C) |--------------------------------| + * + * Position A) (split the excess stem equally) should be better + * for stems of width N + f where f < 0.5 + * + * Position B) (split the deficiency equally) should be better + * for stems of width N + f where f > 0.5 + * + * It turns out though that minimizing the total number of lit + * pixels is also important, so position C), with one edge + * aligned with a pixel boundary is actually preferable + * to A). There are also more possibile positions for C) than + * for A) or B), so it involves less distortion of the overall + * character shape. + */ + else /* len > 64 */ + { + FT_Fixed frac_len = len & 63; + FT_Fixed center = pos + ( len >> 1 ); + FT_Fixed delta_a, delta_b; + + if ( ( len / 64 ) & 1 ) + { + delta_a = ( center & -64 ) + 32 - center; + delta_b = ( ( center + 32 ) & - 64 ) - center; + } + else + { + delta_a = ( ( center + 32 ) & - 64 ) - center; + delta_b = ( center & -64 ) + 32 - center; + } + + /* We choose between B) and C) above based on the amount + * of fractinal stem width; for small amounts, choose + * C) always, for large amounts, B) always, and inbetween, + * pick whichever one involves less stem movement. + */ + if (frac_len < 32) + { + pos += psh3_hint_snap_stem_side_delta ( pos, len ); + } + else if (frac_len < 48) + { + FT_Fixed side_delta = psh3_hint_snap_stem_side_delta ( pos, len ); + + if ( ABS( side_delta ) < ABS( delta_b ) ) + pos += side_delta; + else + pos += delta_b; + } + else + { + pos += delta_b; + } + } + + hint->cur_pos = pos; + } + } /* switch */ + + psh3_hint_set_fitted( hint ); + +#ifdef DEBUG_HINTER + if ( ps3_debug_hint_func ) + ps3_debug_hint_func( hint, dimension ); +#endif + } + } + + static void psh3_hint_table_align_hints( PSH3_Hint_Table table, PSH_Globals globals,