>From 0d62556870ccd939782141a2ba7aa3150a1b03b8 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Sun, 23 Oct 2016 22:43:25 +0200 Subject: [PATCH] Minor clean-up of af_loader_load_glyph(). Merge af_loader_load_g into af_loader_load_glyph, move emboldening into own function, minor documentation improvements. --- src/autofit/afloader.c | 600 ++++++++++++++++++++++++------------------------- src/autofit/afloader.h | 5 + 2 files changed, 304 insertions(+), 301 deletions(-) diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c index 26bba06..e593db8 100644 --- a/src/autofit/afloader.c +++ b/src/autofit/afloader.c @@ -86,161 +86,135 @@ ( (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. */ - /* In the rare cases where FT_LOAD_NO_RECURSE is set, it implies */ - /* FT_LOAD_NO_SCALE and as such the auto-hinter is never called. */ - - static FT_Error - af_loader_load_g( AF_Loader loader, - AF_Scaler scaler, - FT_UInt glyph_index, - FT_Int32 load_flags ) + /* Load the glyph at index into the current slot of a face and hint it. */ + + FT_LOCAL_DEF( FT_Error ) + af_loader_load_glyph( AF_Loader loader, + AF_Module module, + FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ) { - AF_Module module = loader->globals->module; + FT_Error error; + FT_Size size = face->size; + FT_GlyphSlot slot = face->glyph; + FT_Slot_Internal internal = slot->internal; + FT_GlyphLoader gloader = internal->loader; + AF_GlyphHints hints = loader->hints; + AF_ScalerRec scaler; + AF_StyleMetrics style_metrics; + FT_UInt style_options = AF_STYLE_NONE_DFLT; + AF_StyleClass style_class; + AF_WritingSystemClass writing_system_class; +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = loader->globals; +#endif - FT_Error error; - FT_Face face = loader->face; - AF_StyleMetrics metrics = loader->metrics; - AF_GlyphHints hints = loader->hints; - FT_GlyphSlot slot = face->glyph; - FT_Slot_Internal internal = slot->internal; - FT_GlyphLoader gloader = internal->loader; - FT_Int32 flags; + if ( !size ) + return FT_THROW( Invalid_Size_Handle ); - flags = load_flags | FT_LOAD_LINEAR_DESIGN; - error = FT_Load_Glyph( face, glyph_index, flags ); - if ( error ) - goto Exit; + FT_ZERO( &scaler ); /* - * 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. + * TODO: This code currently doesn't support fractional advance widths, + * i.e. placing hinted glyphs at anything other than integer x-positions. + * This is only relevant for the warper code, which scales and shifts + * glyphs to optimize blackness of stems (hinting on the x-axis by nature + * places things on pixel integers, hinting on the y-axis only, i.e. LIGHT + * mode, doesn't touch the x-axis). The delta values of the scaler would + * need to be adjusted. */ - 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; + scaler.face = face; + scaler.x_scale = size->metrics.x_scale; + scaler.x_delta = 0; + scaler.y_scale = size->metrics.y_scale; + scaler.y_delta = 0; - FT_Fixed em_size = af_intToFixed( face->units_per_EM ); - FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size ); + scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); + scaler.flags = 0; - FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; + error = af_loader_reset( loader, module, face ); + if ( error ) + goto Exit; - /* Skip stem darkening for broken fonts. */ - if ( !face->units_per_EM ) - goto After_Emboldening; +#ifdef FT_OPTION_AUTOFIT2 + /* XXX: undocumented hook to activate the latin2 writing system. */ + if ( load_flags & ( 1UL << 20 ) ) + style_options = AF_STYLE_LTN2_DFLT; +#endif - /* - * 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]; + /* + * Glyphs (really code points) are assigned scripts. Script analysis is + * done lazily: For each glyph that passes through here, the corresponding + * script analyzer is called, but returns immediately if it has been run + * already. + */ + error = af_face_globals_get_metrics( loader->globals, glyph_index, + style_options, &style_metrics ); + if ( error ) + goto Exit; - if ( writing_system_class->style_metrics_getstdw ) - writing_system_class->style_metrics_getstdw( metrics, - &stdHW, - &stdVW ); - else - goto After_Emboldening; + style_class = style_metrics->style_class; + writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + loader->metrics = style_metrics; - if ( size_changed || - ( stdVW > 0 && stdVW != globals->standard_vertical_width ) ) - { - FT_Fixed darken_by_font_units_x, darken_x; + if ( writing_system_class->style_metrics_scale ) + writing_system_class->style_metrics_scale( style_metrics, &scaler ); + else + style_metrics->scaler = scaler; + if ( writing_system_class->style_hints_init ) + { + error = writing_system_class->style_hints_init( hints, + style_metrics ); + if ( error ) + goto Exit; + } - 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 ); + /* + * 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. In + * the rare cases where FT_LOAD_NO_RECURSE is set, it implies + * FT_LOAD_NO_SCALE and as such the auto-hinter is never called. + */ + load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM | + FT_LOAD_LINEAR_DESIGN; + load_flags &= ~FT_LOAD_RENDER; - globals->standard_vertical_width = stdVW; - globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; - globals->darken_x = af_fixedToInt( darken_x ); - } + error = FT_Load_Glyph( face, glyph_index, load_flags ); - 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 ); - } + if ( error ) + goto Exit; - FT_Outline_EmboldenXY( &slot->outline, - globals->darken_x, - globals->darken_y ); + /* + * 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. + * + * Ignore Errors and carry on without emboldening. + */ + if ( !module->no_stem_darkening ) + af_loader_embolden_glyph_in_slot( loader, face, style_metrics ); - 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 ) { @@ -257,87 +231,100 @@ switch ( slot->format ) { - case FT_GLYPH_FORMAT_OUTLINE: - /* translate the loaded glyph when an internal transform is needed */ - if ( loader->transformed ) - FT_Outline_Translate( &slot->outline, - loader->trans_delta.x, - loader->trans_delta.y ); - - /* compute original horizontal phantom points (and ignore */ - /* vertical ones) */ - loader->pp1.x = hints->x_delta; - loader->pp1.y = hints->y_delta; - loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, - hints->x_scale ) + hints->x_delta; - loader->pp2.y = hints->y_delta; - - /* be sure to check for spacing glyphs */ - if ( slot->outline.n_points == 0 ) - goto Hint_Metrics; - - /* now load the slot image into the auto-outline and run the */ - /* automatic hinting process */ - { + case FT_GLYPH_FORMAT_OUTLINE: + /* translate the loaded glyph when an internal transform is needed */ + if ( loader->transformed ) + FT_Outline_Translate( &slot->outline, + loader->trans_delta.x, + loader->trans_delta.y ); + + /* compute original horizontal phantom points (and ignore */ + /* vertical ones) */ + loader->pp1.x = hints->x_delta; + loader->pp1.y = hints->y_delta; + loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, + hints->x_scale ) + hints->x_delta; + loader->pp2.y = hints->y_delta; + + /* be sure to check for spacing glyphs */ + if ( slot->outline.n_points == 0 ) + goto Hint_Metrics; + + /* now load the slot image into the auto-outline and run the */ + /* automatic hinting process */ + { #ifdef FT_CONFIG_OPTION_PIC - AF_FaceGlobals globals = loader->globals; + AF_FaceGlobals globals = loader->globals; #endif - AF_StyleClass style_class = metrics->style_class; - AF_WritingSystemClass writing_system_class = - AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + AF_StyleClass style_class = style_metrics->style_class; + AF_WritingSystemClass writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; - if ( writing_system_class->style_hints_apply ) - writing_system_class->style_hints_apply( glyph_index, - hints, - &gloader->base.outline, - metrics ); - } + if ( writing_system_class->style_hints_apply ) + writing_system_class->style_hints_apply( glyph_index, + hints, + &gloader->base.outline, + style_metrics ); + } - /* we now need to adjust the metrics according to the change in */ - /* width/positioning that occurred during the hinting process */ - if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) - { - FT_Pos old_rsb, old_lsb, new_lsb; - FT_Pos pp1x_uh, pp2x_uh; - AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; - AF_Edge edge1 = axis->edges; /* leftmost edge */ - AF_Edge edge2 = edge1 + - axis->num_edges - 1; /* rightmost edge */ + /* we now need to adjust the metrics according to the change in */ + /* width/positioning that occurred during the hinting process */ + if ( scaler.render_mode != FT_RENDER_MODE_LIGHT ) + { + FT_Pos old_rsb, old_lsb, new_lsb; + FT_Pos pp1x_uh, pp2x_uh; + AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; + AF_Edge edge1 = axis->edges; /* leftmost edge */ + AF_Edge edge2 = edge1 + + axis->num_edges - 1; /* rightmost edge */ - if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) - { - old_rsb = loader->pp2.x - edge2->opos; - old_lsb = edge1->opos; - new_lsb = edge1->pos; + if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) + { + old_rsb = loader->pp2.x - edge2->opos; + old_lsb = edge1->opos; + new_lsb = edge1->pos; + + /* remember unhinted values to later account */ + /* for rounding errors */ - /* remember unhinted values to later account */ - /* for rounding errors */ + pp1x_uh = new_lsb - old_lsb; + pp2x_uh = edge2->pos + old_rsb; - pp1x_uh = new_lsb - old_lsb; - pp2x_uh = edge2->pos + old_rsb; + /* prefer too much space over too little space */ + /* for very small sizes */ - /* prefer too much space over too little space */ - /* for very small sizes */ + if ( old_lsb < 24 ) + pp1x_uh -= 8; - if ( old_lsb < 24 ) - pp1x_uh -= 8; + if ( old_rsb < 24 ) + pp2x_uh += 8; - if ( old_rsb < 24 ) - pp2x_uh += 8; + loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); + loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); - loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); - loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); + if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) + loader->pp1.x -= 64; - if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) - loader->pp1.x -= 64; + if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) + loader->pp2.x += 64; - if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) - loader->pp2.x += 64; + slot->lsb_delta = loader->pp1.x - pp1x_uh; + slot->rsb_delta = loader->pp2.x - pp2x_uh; + } + else + { + FT_Pos pp1x = loader->pp1.x; + FT_Pos pp2x = loader->pp2.x; - slot->lsb_delta = loader->pp1.x - pp1x_uh; - slot->rsb_delta = loader->pp2.x - pp2x_uh; + + loader->pp1.x = FT_PIX_ROUND( pp1x ); + loader->pp2.x = FT_PIX_ROUND( pp2x ); + + slot->lsb_delta = loader->pp1.x - pp1x; + slot->rsb_delta = loader->pp2.x - pp2x; + } } else { @@ -345,34 +332,21 @@ FT_Pos pp2x = loader->pp2.x; - loader->pp1.x = FT_PIX_ROUND( pp1x ); - loader->pp2.x = FT_PIX_ROUND( pp2x ); + loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); + loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); slot->lsb_delta = loader->pp1.x - pp1x; slot->rsb_delta = loader->pp2.x - pp2x; } - } - else - { - FT_Pos pp1x = loader->pp1.x; - FT_Pos pp2x = loader->pp2.x; - - - loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); - loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); - - slot->lsb_delta = loader->pp1.x - pp1x; - slot->rsb_delta = loader->pp2.x - pp2x; - } - break; + break; - default: - /* we don't support other formats (yet?) */ - error = FT_THROW( Unimplemented_Feature ); + default: + /* we don't support other formats (yet?) */ + error = FT_THROW( Unimplemented_Feature ); } - Hint_Metrics: +Hint_Metrics: { FT_BBox bbox; FT_Vector vvector; @@ -380,8 +354,8 @@ vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; - vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); - vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); + vvector.x = FT_MulFix( vvector.x, style_metrics->scaler.x_scale ); + vvector.y = FT_MulFix( vvector.y, style_metrics->scaler.y_scale ); /* transform the hinted outline if needed */ if ( loader->transformed ) @@ -389,12 +363,12 @@ FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); FT_Vector_Transform( &vvector, &loader->trans_matrix ); } -#if 1 + /* we must translate our final outline by -pp1.x and compute */ /* the new metrics */ if ( loader->pp1.x ) FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); -#endif + FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); @@ -413,20 +387,13 @@ /* for mono-width fonts (like Andale, Courier, etc.) we need */ /* to keep the original rounded advance width; ditto for */ /* digits if all have the same advance width */ -#if 0 - if ( !FT_IS_FIXED_WIDTH( slot->face ) ) - slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; - else - slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, - x_scale ); -#else - if ( scaler->render_mode != FT_RENDER_MODE_LIGHT && + if ( scaler.render_mode != FT_RENDER_MODE_LIGHT && ( FT_IS_FIXED_WIDTH( slot->face ) || ( af_face_globals_is_digit( loader->globals, glyph_index ) && - metrics->digits_have_same_width ) ) ) + style_metrics->digits_have_same_width ) ) ) { slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, - metrics->scaler.x_scale ); + style_metrics->scaler.x_scale ); /* Set delta values to 0. Otherwise code that uses them is */ /* going to ruin the fixed advance width. */ @@ -439,106 +406,137 @@ if ( slot->metrics.horiAdvance ) slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; } -#endif slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, - metrics->scaler.y_scale ); + style_metrics->scaler.y_scale ); slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); -#if 0 - /* reassign all outline fields except flags to protect them */ - slot->outline.n_contours = internal->loader->base.outline.n_contours; - slot->outline.n_points = internal->loader->base.outline.n_points; - slot->outline.points = internal->loader->base.outline.points; - slot->outline.tags = internal->loader->base.outline.tags; - slot->outline.contours = internal->loader->base.outline.contours; -#endif - slot->format = FT_GLYPH_FORMAT_OUTLINE; } - - Exit: +Exit: return error; } - /* Load a glyph. */ - FT_LOCAL_DEF( FT_Error ) - af_loader_load_glyph( AF_Loader loader, - AF_Module module, - FT_Face face, - FT_UInt gindex, - FT_Int32 load_flags ) + af_loader_embolden_glyph_in_slot( AF_Loader loader, + FT_Face face, + AF_StyleMetrics style_metrics ) { - FT_Error error; - FT_Size size = face->size; - AF_ScalerRec scaler; + FT_Error error = FT_Err_Ok; + FT_GlyphSlot slot = face->glyph; + AF_FaceGlobals globals = loader->globals; + AF_WritingSystemClass writing_system_class; + FT_Pos stdVW = 0; + FT_Pos stdHW = 0; - if ( !size ) - return FT_THROW( Invalid_Size_Handle ); + FT_Bool size_changed = face->size->metrics.x_ppem + != globals->stem_darkening_for_ppem; - FT_ZERO( &scaler ); + FT_Fixed em_size = af_intToFixed( face->units_per_EM ); + FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size ); - scaler.face = face; - scaler.x_scale = size->metrics.x_scale; - scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ - scaler.y_scale = size->metrics.y_scale; - scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ + FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; - scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); - scaler.flags = 0; /* XXX: fix this */ - error = af_loader_reset( loader, module, face ); - if ( !error ) + /* Skip stem darkening for broken fonts. */ + if ( !face->units_per_EM ) { - AF_StyleMetrics metrics; - FT_UInt options = AF_STYLE_NONE_DFLT; + error = FT_Err_Corrupted_Font_Header; + goto Exit; + } + /* + * 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 disabled. + */ + writing_system_class = + AF_WRITING_SYSTEM_CLASSES_GET[style_metrics->style_class->writing_system]; -#ifdef FT_OPTION_AUTOFIT2 - /* XXX: undocumented hook to activate the latin2 writing system */ - if ( load_flags & ( 1UL << 20 ) ) - options = AF_STYLE_LTN2_DFLT; -#endif + if ( writing_system_class->style_metrics_getstdw ) + { + writing_system_class->style_metrics_getstdw( style_metrics, + &stdHW, + &stdVW ); + } + else + { + error = FT_Err_Unimplemented_Feature; + goto Exit; + } - error = af_face_globals_get_metrics( loader->globals, gindex, - options, &metrics ); - if ( !error ) - { -#ifdef FT_CONFIG_OPTION_PIC - AF_FaceGlobals globals = loader->globals; -#endif - AF_StyleClass style_class = metrics->style_class; - AF_WritingSystemClass writing_system_class = - AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; + if ( size_changed || + ( stdVW > 0 && stdVW != globals->standard_vertical_width ) ) + { + FT_Fixed darken_by_font_units_x, darken_x; - loader->metrics = metrics; - if ( writing_system_class->style_metrics_scale ) - writing_system_class->style_metrics_scale( metrics, &scaler ); - else - metrics->scaler = scaler; + 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 ); - load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; - load_flags &= ~FT_LOAD_RENDER; + globals->standard_vertical_width = stdVW; + globals->stem_darkening_for_ppem = face->size->metrics.x_ppem; + globals->darken_x = af_fixedToInt( darken_x ); + } - if ( writing_system_class->style_hints_init ) - { - error = writing_system_class->style_hints_init( loader->hints, - metrics ); - if ( error ) - goto Exit; - } + if ( size_changed || + ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) ) + { + FT_Fixed darken_by_font_units_y, darken_y; - error = af_loader_load_g( loader, &scaler, gindex, load_flags ); - } + + 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 ); } - Exit: + + 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 ); + +Exit: return error; } diff --git a/src/autofit/afloader.h b/src/autofit/afloader.h index 0062eb9..2e6f36b 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_Error ) + af_loader_embolden_glyph_in_slot( AF_Loader loader, + FT_Face face, + AF_StyleMetrics style_metrics ); + FT_LOCAL_DEF( FT_Int32 ) af_loader_compute_darkening( AF_Loader loader, FT_Face face, -- 2.7.4