* src/autofit/afloader.c (af_loader_load_g): Implement emboldening.

This commit is contained in:
Nikolaus Waxweiler 2015-11-03 10:40:58 +01:00 committed by Werner Lemberg
parent bf2ba9e3d4
commit b6fd5bc06c
2 changed files with 140 additions and 7 deletions

View File

@ -1,3 +1,7 @@
2015-11-02 Nikolaus Waxweiler <madigens@gmail.com>
* src/autofit/afloader.c (af_loader_load_g): Implement emboldening.
2015-11-02 Nikolaus Waxweiler <madigens@gmail.com>
[autofit] Implement darkening computation function.

View File

@ -76,6 +76,14 @@
}
#define af_intToFixed( i ) \
( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
#define af_fixedToInt( x ) \
( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
#define af_floatToFixed( f ) \
( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
/* Do the main work of `af_loader_load_glyph'. Note that we never */
/* have to deal with composite glyphs as those get loaded into */
/* FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. */
@ -88,6 +96,8 @@
FT_UInt glyph_index,
FT_Int32 load_flags )
{
AF_Module module = loader->globals->module;
FT_Error error;
FT_Face face = loader->face;
AF_StyleMetrics metrics = loader->metrics;
@ -103,6 +113,132 @@
if ( error )
goto Exit;
/*
* Apply stem darkening (emboldening) here before hints are applied to
* the outline. Glyphs are scaled down proportionally to the
* emboldening so that curve points don't fall outside their precomputed
* blue zones.
*
* Any emboldening done by the font driver (e.g., the CFF driver)
* doesn't reach here because the autohinter loads the unprocessed
* glyphs in font units for analysis (functions `af_*_metrics_init_*')
* and then above to prepare it for the rasterizers by itself,
* independently of the font driver. So emboldening must be done here,
* within the autohinter.
*
* All glyphs to be autohinted pass through here one by one. The
* standard widths can therefore change from one glyph to the next,
* depending on what script a glyph is assigned to (each script has its
* own set of standard widths and other metrics). The darkening amount
* must therefore be recomputed for each size and
* `standard_{vertical,horizontal}_width' change.
*/
if ( !module->no_stem_darkening )
{
AF_FaceGlobals globals = loader->globals;
AF_WritingSystemClass writing_system_class;
FT_Pos stdVW = 0;
FT_Pos stdHW = 0;
FT_Bool size_changed = face->size->metrics.x_ppem
!= globals->stem_darkening_for_ppem;
FT_Fixed em_size = af_intToFixed( face->units_per_EM );
FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size );
FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };
/* Skip stem darkening for broken fonts. */
if ( !face->units_per_EM )
goto After_Emboldening;
/*
* We depend on the writing system (script analyzers) to supply
* standard widths for the script of the glyph we are looking at. If
* it can't deliver, stem darkening is effectively disabled.
*/
writing_system_class =
AF_WRITING_SYSTEM_CLASSES_GET[metrics->style_class->writing_system];
if ( writing_system_class->style_metrics_getstdw )
writing_system_class->style_metrics_getstdw( metrics,
&stdHW,
&stdVW );
else
goto After_Emboldening;
if ( size_changed ||
( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
{
FT_Fixed darken_by_font_units_x, darken_x;
darken_by_font_units_x =
af_intToFixed( af_loader_compute_darkening( loader,
face,
stdVW ) );
darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
face->size->metrics.x_scale ),
em_ratio );
globals->standard_vertical_width = stdVW;
globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
globals->darken_x = af_fixedToInt( darken_x );
}
if ( size_changed ||
( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
{
FT_Fixed darken_by_font_units_y, darken_y;
darken_by_font_units_y =
af_intToFixed( af_loader_compute_darkening( loader,
face,
stdHW ) );
darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
face->size->metrics.y_scale ),
em_ratio );
globals->standard_horizontal_width = stdHW;
globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
globals->darken_y = af_fixedToInt( darken_y );
/*
* Scale outlines down on the Y-axis to keep them inside their blue
* zones. The stronger the emboldening, the stronger the
* downscaling (plus heuristical padding to prevent outlines still
* falling out their zones due to rounding).
*
* Reason: `FT_Outline_Embolden' works by shifting the rightmost
* points of stems farther to the right, and topmost points farther
* up. This positions points on the Y-axis outside their
* pre-computed blue zones and leads to distortion when applying the
* hints in the code further below. Code outside this emboldening
* block doesn't know we are presenting it with modified outlines
* the analyzer didn't see!
*
* An unfortunate side effect of downscaling is that the emboldening
* effect is slightly decreased. The loss becomes more pronounced
* versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
*/
globals->scale_down_factor =
FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ),
em_size );
}
FT_Outline_EmboldenXY( &slot->outline,
globals->darken_x,
globals->darken_y );
scale_down_matrix.yy = globals->scale_down_factor;
FT_Outline_Transform( &slot->outline, &scale_down_matrix );
}
After_Emboldening:
loader->transformed = internal->glyph_transformed;
if ( loader->transformed )
{
@ -412,13 +548,6 @@
*
* XXX: Currently a crude adaption of the original algorithm. Do better?
*/
#define af_intToFixed( i ) \
( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
#define af_fixedToInt( x ) \
( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
#define af_floatToFixed( f ) \
( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
FT_LOCAL_DEF( FT_Int32 )
af_loader_compute_darkening( AF_Loader loader,
FT_Face face,