>From 2ec9b1884ce55c4fc44e4c0cb8c5ec80948fb80f Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Sat, 24 Oct 2015 16:04:24 +0200 Subject: [PATCH 5/5] Implement the actual emboldening in af_loader_load_g(). --- src/autofit/afloader.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c index 99523f4..6e9254a 100644 --- a/src/autofit/afloader.c +++ b/src/autofit/afloader.c @@ -84,6 +84,7 @@ static FT_Error af_loader_load_g( AF_Loader loader, + AF_Module module, AF_Scaler scaler, FT_UInt glyph_index, FT_Int32 load_flags ) @@ -103,6 +104,103 @@ 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 (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. + */ +#define cf2_intToFixed( i ) ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) +#define cf2_fixedToInt( x ) ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define cf2_floatToFixed( f ) ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) ) + if ( !module->no_stem_darkening ) + { + AF_FaceGlobals globals = loader->globals; + FT_Pos stdVW = 0; + FT_Pos stdHW = 0; + FT_Bool size_changed = face->size->metrics.x_ppem + != globals->stem_darkening_for_ppem; + FT_Fixed emSize = cf2_intToFixed( face->units_per_EM ); + FT_Fixed emRatio = FT_DivFix( cf2_intToFixed( 1000 ), emSize ); + FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; + AF_WritingSystemClass writing_system_class; + + /* Skip stem darkening for broken fonts. */ + if ( face->units_per_EM <= 0 ) + goto After_Emboldening; + + /* We depend on the writing system (script analyzers) to supply + * standard_widths for the script of the glyph we're 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; + + globals->standard_vertical_width = stdVW; + globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; + darken_by_font_units_x = cf2_intToFixed( af_loader_compute_darkening( module, face, stdVW ) ); + darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x, face->size->metrics.x_scale ), + emRatio ); + globals->darken_x = cf2_fixedToInt( darken_x ); + } + + if ( size_changed || ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) ) + { + FT_Fixed darken_by_font_units_y, darken_y; + + globals->standard_horizontal_width = stdHW; + globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; + darken_by_font_units_y = cf2_intToFixed( af_loader_compute_darkening( module, face, stdHW ) ); + darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y, face->size->metrics.y_scale ), + emRatio ); + globals->darken_y = cf2_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 lands + * 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're 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. 9ppem and below. */ + globals->scale_down_factor = FT_DivFix( emSize - (darken_by_font_units_y + cf2_intToFixed( 6 )), + emSize ); + } + + 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 ) { @@ -397,7 +495,7 @@ goto Exit; } - error = af_loader_load_g( loader, &scaler, gindex, load_flags ); + error = af_loader_load_g( loader, module, &scaler, gindex, load_flags ); } } Exit: @@ -410,9 +508,6 @@ * algorithm. * XXX: Currently a crude adaption of the original algorithm. Do better? */ -#define cf2_intToFixed( i ) ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) -#define cf2_fixedToInt( x ) ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) -#define cf2_floatToFixed( f ) ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) ) FT_LOCAL_DEF( FT_Int32 ) af_loader_compute_darkening( AF_Module module, FT_Face face, -- 2.4.3