Done! https://github.com/madig/freetype2/commits/stem-darkening-autohinter-clean
One issue I came across is that I want to reuse the property names but docmaker.py assigns them to what the CFF driver header defines. Hm.
>From 2ec9b1884ce55c4fc44e4c0cb8c5ec80948fb80f Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <[email protected]> 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
>From 3986a7fd7b0c7f8f98545a8e38b05c4f255a3d51 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <[email protected]> Date: Sat, 24 Oct 2015 15:54:24 +0200 Subject: [PATCH 4/5] Implement darkening computation function Crude adaption of the original cf2_computeDarkening(). --- src/autofit/afloader.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ src/autofit/afloader.h | 5 ++ 2 files changed, 133 insertions(+) diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c index 722ffd3..99523f4 100644 --- a/src/autofit/afloader.c +++ b/src/autofit/afloader.c @@ -405,4 +405,132 @@ } + /* Compute amount of font units the face should be emboldened by, analogous + * to the CFF driver's cf2_computeDarkening(). See there for details of the + * 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, + FT_Pos standard_width ) + { + FT_UShort units_per_EM; + FT_Fixed ppem, emRatio; + FT_Fixed stemWidth, stemWidthPer1000, scaledStem, darkenAmount; + FT_Int logBase2; + FT_Int x1, y1, x2, y2, x3, y3, x4, y4; + + ppem = FT_MAX( cf2_intToFixed( 4 ), + cf2_intToFixed( face->size->metrics.x_ppem ) ); + units_per_EM = face->units_per_EM; + + if ( units_per_EM < 0 ) + /* If something goes wrong, don't embolden. */ + return 0; + + emRatio = FT_DivFix( cf2_intToFixed( 1000 ), cf2_intToFixed ( units_per_EM ) ); + if ( emRatio < cf2_floatToFixed( .01 ) ) + { + /* If something goes wrong, don't embolden. */ + return 0; + } + + x1 = module->darken_params[0]; + y1 = module->darken_params[1]; + x2 = module->darken_params[2]; + y2 = module->darken_params[3]; + x3 = module->darken_params[4]; + y3 = module->darken_params[5]; + x4 = module->darken_params[6]; + y4 = module->darken_params[7]; + + if ( standard_width <= 0) + { + stemWidth = cf2_intToFixed( 75 ); /* Taken from cf2font.c */ + stemWidthPer1000 = stemWidth; + } + else + { + stemWidth = cf2_intToFixed( standard_width ); + stemWidthPer1000 = FT_MulFix( stemWidth, emRatio ); + } + + logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) + + FT_MSB( (FT_UInt32)ppem ); + + if ( logBase2 >= 46 ) + /* possible overflow */ + scaledStem = cf2_intToFixed( x4 ); + else + scaledStem = FT_MulFix( stemWidthPer1000, ppem ); + + /* now apply the darkening parameters */ + if ( scaledStem < cf2_intToFixed( x1 ) ) + darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem ); + + else if ( scaledStem < cf2_intToFixed( x2 ) ) + { + FT_Int xdelta = x2 - x1; + FT_Int ydelta = y2 - y1; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x1 ), ppem ); + + + if ( !xdelta ) + goto Try_x3; + + darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y1 ), ppem ); + } + + else if ( scaledStem < cf2_intToFixed( x3 ) ) + { +Try_x3: + { + FT_Int xdelta = x3 - x2; + FT_Int ydelta = y3 - y2; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x2 ), ppem ); + + + if ( !xdelta ) + goto Try_x4; + + darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y2 ), ppem ); + } + } + + else if ( scaledStem < cf2_intToFixed( x4 ) ) + { +Try_x4: + { + FT_Int xdelta = x4 - x3; + FT_Int ydelta = y4 - y3; + FT_Int x = stemWidthPer1000 - + FT_DivFix( cf2_intToFixed( x3 ), ppem ); + + + if ( !xdelta ) + goto Use_y4; + + darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + + FT_DivFix( cf2_intToFixed( y3 ), ppem ); + } + } + + else + { +Use_y4: + darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem ); + } + + /* Convert darkenAmount from per 1000 em to true character space. */ + return cf2_fixedToInt( FT_DivFix( darkenAmount, emRatio ) ); + } + /* END */ diff --git a/src/autofit/afloader.h b/src/autofit/afloader.h index 37cfd14..f2963ce 100644 --- a/src/autofit/afloader.h +++ b/src/autofit/afloader.h @@ -75,6 +75,11 @@ FT_BEGIN_HEADER FT_UInt gindex, FT_Int32 load_flags ); + FT_LOCAL_DEF( FT_Int32 ) + af_loader_compute_darkening( AF_Module module, + FT_Face face, + FT_Pos standard_width ); + /* */ -- 2.4.3
>From 50e8bb2d7dc42223228464aa14a0b86f9112cb3c Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <[email protected]> Date: Sat, 24 Oct 2015 15:46:20 +0200 Subject: [PATCH 3/5] Add and implement af_*_get_standard_widths for AF_WritingSystem We need the computed standard horizontal and vertical widths for the emboldening calculation. This method provides a convenient way to extract it from writing-system-specific metrics structures, all script definitions must implement it. --- src/autofit/afcjk.c | 17 +++++++++++++++++ src/autofit/afdummy.c | 1 + src/autofit/afindic.c | 17 +++++++++++++++++ src/autofit/aflatin.c | 16 ++++++++++++++++ src/autofit/aflatin2.c | 16 ++++++++++++++++ src/autofit/aftypes.h | 9 +++++++++ 6 files changed, 76 insertions(+) diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c index 0ade4be..74c200b 100644 --- a/src/autofit/afcjk.c +++ b/src/autofit/afcjk.c @@ -688,6 +688,21 @@ } + /* Extract standard_width from writing system/script specific metrics class */ + + FT_LOCAL_DEF( void ) + af_cjk_get_standard_widths( AF_CJKMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -2280,6 +2295,7 @@ (AF_WritingSystem_InitMetricsFunc) af_cjk_metrics_init, (AF_WritingSystem_ScaleMetricsFunc)af_cjk_metrics_scale, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_cjk_get_standard_widths, (AF_WritingSystem_InitHintsFunc) af_cjk_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_cjk_hints_apply @@ -2299,6 +2315,7 @@ (AF_WritingSystem_InitMetricsFunc) NULL, (AF_WritingSystem_ScaleMetricsFunc)NULL, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, (AF_WritingSystem_InitHintsFunc) NULL, (AF_WritingSystem_ApplyHintsFunc) NULL diff --git a/src/autofit/afdummy.c b/src/autofit/afdummy.c index 18dd301..9142c78 100644 --- a/src/autofit/afdummy.c +++ b/src/autofit/afdummy.c @@ -65,6 +65,7 @@ (AF_WritingSystem_InitMetricsFunc) NULL, (AF_WritingSystem_ScaleMetricsFunc)NULL, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, (AF_WritingSystem_InitHintsFunc) af_dummy_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_dummy_hints_apply diff --git a/src/autofit/afindic.c b/src/autofit/afindic.c index 05b6bdd..2a578a4 100644 --- a/src/autofit/afindic.c +++ b/src/autofit/afindic.c @@ -89,6 +89,21 @@ } + /* Extract standard_width from writing system/script specific metrics class */ + + static void + af_indic_get_standard_widths( AF_CJKMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -108,6 +123,7 @@ (AF_WritingSystem_InitMetricsFunc) af_indic_metrics_init, (AF_WritingSystem_ScaleMetricsFunc)af_indic_metrics_scale, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_indic_get_standard_widths, (AF_WritingSystem_InitHintsFunc) af_indic_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_indic_hints_apply @@ -127,6 +143,7 @@ (AF_WritingSystem_InitMetricsFunc) NULL, (AF_WritingSystem_ScaleMetricsFunc)NULL, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)NULL, (AF_WritingSystem_InitHintsFunc) NULL, (AF_WritingSystem_ApplyHintsFunc) NULL diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 363f721..ddf3b6c 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -1149,6 +1149,21 @@ } + /* Extract standard_width from writing system/script specific metrics class */ + + FT_LOCAL_DEF( void ) + af_latin_get_standard_widths( AF_LatinMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -2944,6 +2959,7 @@ (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc) af_latin_get_standard_widths, (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply diff --git a/src/autofit/aflatin2.c b/src/autofit/aflatin2.c index 0380ffc..0996e7f 100644 --- a/src/autofit/aflatin2.c +++ b/src/autofit/aflatin2.c @@ -693,6 +693,21 @@ } + /* Extract standard_width from writing system/script specific metrics class */ + + FT_LOCAL_DEF( void ) + af_latin2_get_standard_widths( AF_LatinMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -2392,6 +2407,7 @@ (AF_WritingSystem_InitMetricsFunc) af_latin2_metrics_init, (AF_WritingSystem_ScaleMetricsFunc)af_latin2_metrics_scale, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_latin2_get_standard_widths, (AF_WritingSystem_InitHintsFunc) af_latin2_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_latin2_hints_apply diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h index 43b3800..86152de 100644 --- a/src/autofit/aftypes.h +++ b/src/autofit/aftypes.h @@ -211,6 +211,11 @@ extern void* _af_debug_hints; typedef void (*AF_WritingSystem_DoneMetricsFunc)( AF_StyleMetrics metrics ); + typedef void + (*AF_WritingSystem_GetStdWidthsFunc)( AF_StyleMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ); + typedef FT_Error (*AF_WritingSystem_InitHintsFunc)( AF_GlyphHints hints, @@ -276,6 +281,7 @@ extern void* _af_debug_hints; AF_WritingSystem_InitMetricsFunc style_metrics_init; AF_WritingSystem_ScaleMetricsFunc style_metrics_scale; AF_WritingSystem_DoneMetricsFunc style_metrics_done; + AF_WritingSystem_GetStdWidthsFunc style_metrics_getstdw; AF_WritingSystem_InitHintsFunc style_hints_init; AF_WritingSystem_ApplyHintsFunc style_hints_apply; @@ -489,6 +495,7 @@ extern void* _af_debug_hints; m_init, \ m_scale, \ m_done, \ + m_stdw, \ h_init, \ h_apply ) \ FT_CALLBACK_TABLE_DEF \ @@ -501,6 +508,7 @@ extern void* _af_debug_hints; m_init, \ m_scale, \ m_done, \ + m_stdw, \ \ h_init, \ h_apply \ @@ -577,6 +585,7 @@ extern void* _af_debug_hints; ac->style_metrics_init = m_init; \ ac->style_metrics_scale = m_scale; \ ac->style_metrics_done = m_done; \ + ac->style_metrics_getstdw = m_stdw; \ \ ac->style_hints_init = h_init; \ ac->style_hints_apply = h_apply; \ -- 2.4.3
>From 8f5a5927dc8ab01a6456b02443ce37620f6dfe1d Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <[email protected]> Date: Sat, 24 Oct 2015 15:33:39 +0200 Subject: [PATCH 2/5] Extend AF_FaceGlobalsRec_ to hold emboldening data And zero everything upon initialization and freeing. --- src/autofit/afglobal.c | 12 ++++++++++++ src/autofit/afglobal.h | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c index b071cc7..631cb64 100644 --- a/src/autofit/afglobal.c +++ b/src/autofit/afglobal.c @@ -355,6 +355,12 @@ /* right after the globals structure come the glyph styles */ globals->glyph_styles = (FT_UShort*)( globals + 1 ); globals->module = module; + globals->stem_darkening_for_ppem = 0; + globals->darken_x = 0; + globals->darken_y = 0; + globals->standard_vertical_width = 0; + globals->standard_horizontal_width = 0; + globals->scale_down_factor = 0; #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ globals->hb_font = hb_ft_font_create( face, NULL ); @@ -407,6 +413,12 @@ #endif globals->glyph_count = 0; + globals->stem_darkening_for_ppem = 0; + globals->darken_x = 0; + globals->darken_y = 0; + globals->standard_vertical_width = 0; + globals->standard_horizontal_width = 0; + globals->scale_down_factor = 0; globals->glyph_styles = NULL; /* no need to free this one! */ globals->face = NULL; diff --git a/src/autofit/afglobal.h b/src/autofit/afglobal.h index ffb2f86..f35ef7b 100644 --- a/src/autofit/afglobal.h +++ b/src/autofit/afglobal.h @@ -117,6 +117,24 @@ FT_BEGIN_HEADER AF_StyleMetrics metrics[AF_STYLE_MAX]; + FT_UShort stem_darkening_for_ppem; /* Compute darkening amount once + per size. Use this to check if + darken_(x|y) needs to be + recomputed. */ + FT_Pos standard_vertical_width; /* Copy from e.g. + AF_LatinMetrics.axis[AF_DIMENSION_HORZ], + to compute the darkening + amount. */ + FT_Pos standard_horizontal_width; /* Copy from e.g. + AF_LatinMetrics.axis[AF_DIMENSION_VERT], + to compute the darkening + amount. */ + FT_Pos darken_x; /* The actual amount to darken a glyph by (X axis). */ + FT_Pos darken_y; /* The actual amount to darken a glyph by (Y axis). */ + FT_Fixed scale_down_factor; /* Amount to scale down by to keep + emboldened points on the Y-axis in + pre-computed blue zones. */ + AF_Module module; /* to access global properties */ } AF_FaceGlobalsRec; -- 2.4.3
>From a0a298f9528e9ca0d9011c41c721bd1158579a5f Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler <[email protected]> Date: Sat, 24 Oct 2015 15:21:01 +0200 Subject: [PATCH 1/5] Properties for setting up stem darkening in the autohinter - Property setters and getters for no-stem-darkening and darkening-parameters. - Documentation in ftautoh.h --- include/freetype/ftautoh.h | 51 +++++++++++++++++++++++++++++++ src/autofit/afmodule.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ src/autofit/afmodule.h | 3 +- 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/include/freetype/ftautoh.h b/include/freetype/ftautoh.h index ab39c21..fdfc3cb 100644 --- a/include/freetype/ftautoh.h +++ b/include/freetype/ftautoh.h @@ -439,6 +439,57 @@ FT_BEGIN_HEADER */ + /************************************************************************** + * + * @property: + * no-stem-darkening + * + * @description: + * *Experimental* *only* + * + * The main purpose of emboldening glyphs or "stem darkening" is to enhance + * readability at smaller sizes. The smaller the size, the more emboldening + * is applied to keep glyphs from "thinning out". All glyphs that pass + * through the autohinter will be emboldened unless this property is set to + * TRUE. + * + * See the description of the CFF driver for algorithmic details. Total + * consistency with the CFF driver is currently not achieved because the + * emboldening method differs and glyphs must be scaled down on the Y-axis + * to keep outline points inside their precomputed blue zones. The smaller + * the size (especially 9ppem and down), the higher the loss of emboldening + * versus the CFF driver. + * + * *ATTENTION*: This feature has been developed with linear alpha + * blending and gamma correction of glyphs in mind. This means, a + * rendering library must apply linear alpha blending when compositing + * glyph bitmaps onto a surface and then apply gamma correction to the + * glyph pixels to get from linear space to display space (unless the + * display works in linear space). Internal testing at Adobe found that a + * gamma correction value of 1.8 gives good results across a wide range of + * displays with a sRGB gamma curve or a similar one. + * + * If this is not possible, it might be better to disable stem darkening. + * Currently, this can only be done globally. + * + */ + + + /************************************************************************** + * + * @property: + * darkening-parameters + * + * @description: + * *Experimental* *only* + * + * See the description of the CFF driver for details. This implementation + * appropriates the CFF_CONFIG_OPTION_DARKENING_PARAMETER_* #defines for + * consistency. Note the differences described in @no-stem-darkening. + * + */ + + /* */ diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c index bbf1372..6a318ba 100644 --- a/src/autofit/afmodule.c +++ b/src/autofit/afmodule.c @@ -177,6 +177,45 @@ return error; } #endif /* AF_CONFIG_OPTION_USE_WARPER */ + else if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = (FT_Int*)value; + + FT_Int x1 = darken_params[0]; + FT_Int y1 = darken_params[1]; + FT_Int x2 = darken_params[2]; + FT_Int y2 = darken_params[3]; + FT_Int x3 = darken_params[4]; + FT_Int y3 = darken_params[5]; + FT_Int x4 = darken_params[6]; + FT_Int y4 = darken_params[7]; + + if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || + y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 || + x1 > x2 || x2 > x3 || x3 > x4 || + y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 ) + return FT_THROW( Invalid_Argument ); + + module->darken_params[0] = x1; + module->darken_params[1] = y1; + module->darken_params[2] = x2; + module->darken_params[3] = y2; + module->darken_params[4] = x3; + module->darken_params[5] = y3; + module->darken_params[6] = x4; + module->darken_params[7] = y4; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool* no_stem_darkening = (FT_Bool*)value; + + + module->no_stem_darkening = *no_stem_darkening; + + return error; + } FT_TRACE0(( "af_property_set: missing property `%s'\n", property_name )); @@ -253,6 +292,33 @@ return error; } #endif /* AF_CONFIG_OPTION_USE_WARPER */ + else if ( !ft_strcmp( property_name, "darkening-parameters" ) ) + { + FT_Int* darken_params = module->darken_params; + FT_Int* val = (FT_Int*)value; + + + val[0] = darken_params[0]; + val[1] = darken_params[1]; + val[2] = darken_params[2]; + val[3] = darken_params[3]; + val[4] = darken_params[4]; + val[5] = darken_params[5]; + val[6] = darken_params[6]; + val[7] = darken_params[7]; + + return error; + } + else if ( !ft_strcmp( property_name, "no-stem-darkening" ) ) + { + FT_Bool no_stem_darkening = module->no_stem_darkening; + FT_Bool* val = (FT_Bool*)value; + + + *val = no_stem_darkening; + + return error; + } FT_TRACE0(( "af_property_get: missing property `%s'\n", property_name )); @@ -304,6 +370,16 @@ #ifdef AF_CONFIG_OPTION_USE_WARPER module->warping = 0; #endif + module->no_stem_darkening = FALSE; + + module->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1; + module->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1; + module->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2; + module->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2; + module->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3; + module->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3; + module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4; + module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4; return FT_Err_Ok; } diff --git a/src/autofit/afmodule.h b/src/autofit/afmodule.h index b9c2fd8..d23018a 100644 --- a/src/autofit/afmodule.h +++ b/src/autofit/afmodule.h @@ -41,7 +41,8 @@ FT_BEGIN_HEADER #ifdef AF_CONFIG_OPTION_USE_WARPER FT_Bool warping; #endif - + FT_Bool no_stem_darkening; + FT_Int darken_params[8]; } AF_ModuleRec, *AF_Module; -- 2.4.3
_______________________________________________ Freetype-devel mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/freetype-devel
