|
|
|
@ -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, |
|
|
|
|