Add new `slight' auto-hinting mode.

This mode uses fractional advance widths and doesn't scale glyphs
horizontally, only applying vertical scaling and hinting.

At the same time, the behaviour of the `light' auto-hinter gets
restored for backwards compatibility: Both vertical and horizontal
scaling is again based on rounded metrics values (this was changed
in a commit from 2017-03-30 as a side effect).  To be more precise,
the behaviour is restored for TrueType fonts only; for other font
formats like Type 1, this is a new feature of the `light' hinting
mode.

* include/freetype/freetype.h (FT_LOAD_TARGET_SLIGHT): New macro.
(FT_RENDER_MODE_SLIGHT): New render mode.

* include/freetype/internal/ftobjs.h (FT_Size_InternalRec): Add
`autohint_mode' and `autohint_metrics' fields.

* src/autofit/afcjk.c (af_cjk_hints_init), src/autofit/aflatin.c
(af_latin_hints_init), src/autofit/aflatin2 (af_latin2_hints_init):
Updated.

* src/autofit/afloader.c (af_loader_embolden_glyph_in_slot): Use
`autohint_metrics'.
(af_loader_load_glyph): s/internal/slot_internal/.
Initialize `autohint_metrics' and `autohint_mode' depending on
current auto-hint mode.
Use `autohint_metrics'.
Updated.

* src/base/ftadvanc.c (LOAD_ADVANCE_FAST_CHECK): Updated.

* src/base/ftobjs.c (FT_Load_Glyph): Updated.
(FT_New_Size): Allocate `internal' object.

* src/pshinter/pshalgo.c (ps_hints_apply): Updated.

* src/smooth/ftsmooth.c (ft_smooth_render): Updated.
This commit is contained in:
Werner Lemberg 2017-04-22 13:27:21 +02:00
parent 69da54cacc
commit 5aa6716a5e
11 changed files with 187 additions and 41 deletions

View File

@ -1,3 +1,45 @@
2017-04-22 Werner Lemberg <wl@gnu.org>
Add new `slight' auto-hinting mode.
This mode uses fractional advance widths and doesn't scale glyphs
horizontally, only applying vertical scaling and hinting.
At the same time, the behaviour of the `light' auto-hinter gets
restored for backwards compatibility: Both vertical and horizontal
scaling is again based on rounded metrics values (this was changed
in a commit from 2017-03-30 as a side effect). To be more precise,
the behaviour is restored for TrueType fonts only; for other font
formats like Type 1, this is a new feature of the `light' hinting
mode.
* include/freetype/freetype.h (FT_LOAD_TARGET_SLIGHT): New macro.
(FT_RENDER_MODE_SLIGHT): New render mode.
* include/freetype/internal/ftobjs.h (FT_Size_InternalRec): Add
`autohint_mode' and `autohint_metrics' fields.
* src/autofit/afcjk.c (af_cjk_hints_init), src/autofit/aflatin.c
(af_latin_hints_init), src/autofit/aflatin2 (af_latin2_hints_init):
Updated.
* src/autofit/afloader.c (af_loader_embolden_glyph_in_slot): Use
`autohint_metrics'.
(af_loader_load_glyph): s/internal/slot_internal/.
Initialize `autohint_metrics' and `autohint_mode' depending on
current auto-hint mode.
Use `autohint_metrics'.
Updated.
* src/base/ftadvanc.c (LOAD_ADVANCE_FAST_CHECK): Updated.
* src/base/ftobjs.c (FT_Load_Glyph): Updated.
(FT_New_Size): Allocate `internal' object.
* src/pshinter/pshalgo.c (ps_hints_apply): Updated.
* src/smooth/ftsmooth.c (ft_smooth_render): Updated.
2017-04-22 Werner Lemberg <wl@gnu.org>
Introduce `FT_Size_InternalRec' structure.

View File

@ -221,6 +221,7 @@ FT_BEGIN_HEADER
/* */
/* FT_LOAD_TARGET_NORMAL */
/* FT_LOAD_TARGET_LIGHT */
/* FT_LOAD_TARGET_SLIGHT */
/* FT_LOAD_TARGET_MONO */
/* FT_LOAD_TARGET_LCD */
/* FT_LOAD_TARGET_LCD_V */
@ -1755,7 +1756,8 @@ FT_BEGIN_HEADER
/* `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP. */
/* */
/* Here is a small pseudo code fragment that shows how to use */
/* `lsb_delta' and `rsb_delta': */
/* `lsb_delta' and `rsb_delta' to improve (integer) positioning of */
/* glyphs: */
/* */
/* { */
/* FT_Pos origin_x = 0; */
@ -2941,6 +2943,18 @@ FT_BEGIN_HEADER
* driver, if the driver itself and the font support it, or by the
* auto-hinter.
*
* Use this hinting mode if you mainly need integer advance widths
* and want to avoid sub-pixel rendering.
*
* FT_LOAD_TARGET_SLIGHT ::
* This is similar to @FT_LOAD_TARGET_LIGHT with a main difference:
* Advance widths are not rounded to integer values; instead, the
* linearly scaled values are used. In particular this implies that
* you have to apply sub-pixel rendering.
*
* In general, this mode yields better results than
* @FT_LOAD_TARGET_LIGHT.
*
* FT_LOAD_TARGET_MONO ::
* Strong hinting algorithm that should only be used for monochrome
* output. The result is probably unpleasant if the glyph is rendered
@ -2975,11 +2989,19 @@ FT_BEGIN_HEADER
* FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD );
* }
*
* In general, you should stick with one rendering mode. For example,
* switching between @FT_LOAD_TARGET_LIGHT and @FT_LOAD_TARGET_SLIGHT
* enforces a lot of recomputation, which is slow. Another reason is
* caching: Selecting a different mode usually causes changes in both
* the outlines and the rasterized bitmaps; it is thus necessary to
* empty the cache after a mode switch to avoid false hits.
*
*/
#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 )
#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL )
#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT )
#define FT_LOAD_TARGET_SLIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_SLIGHT )
#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO )
#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD )
#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V )
@ -3060,6 +3082,12 @@ FT_BEGIN_HEADER
/* indirectly to define hinting algorithm selectors. See */
/* @FT_LOAD_TARGET_XXX for details. */
/* */
/* FT_RENDER_MODE_SLIGHT :: */
/* This is equivalent to @FT_RENDER_MODE_NORMAL. It is only */
/* defined as a separate value because render modes are also used */
/* indirectly to define hinting algorithm selectors. See */
/* @FT_LOAD_TARGET_XXX for details. */
/* */
/* FT_RENDER_MODE_MONO :: */
/* This mode corresponds to 1-bit bitmaps (with 2~levels of */
/* opacity). */
@ -3092,6 +3120,7 @@ FT_BEGIN_HEADER
{
FT_RENDER_MODE_NORMAL = 0,
FT_RENDER_MODE_LIGHT,
FT_RENDER_MODE_SLIGHT,
FT_RENDER_MODE_MONO,
FT_RENDER_MODE_LCD,
FT_RENDER_MODE_LCD_V,

View File

@ -443,7 +443,11 @@ FT_BEGIN_HEADER
/* object. */
/* */
/* <Fields> */
/* module_data :: Data specific to a driver module. */
/* module_data :: Data specific to a driver module. */
/* */
/* autohint_mode :: The used auto-hinting mode. */
/* */
/* autohint_metrics :: Metrics used by the auto-hinter. */
/* */
/*************************************************************************/
@ -451,6 +455,9 @@ FT_BEGIN_HEADER
{
void* module_data;
FT_Render_Mode autohint_mode;
FT_Size_Metrics autohint_metrics;
} FT_Size_InternalRec;

View File

@ -1398,9 +1398,12 @@
other_flags |= AF_LATIN_HINTS_VERT_SNAP;
/*
* We adjust stems to full pixels unless in `light' or `lcd' mode.
* We adjust stems to full pixels unless in `light', `slight',
* or `lcd' mode.
*/
if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
if ( mode != FT_RENDER_MODE_LIGHT &&
mode != FT_RENDER_MODE_SLIGHT &&
mode != FT_RENDER_MODE_LCD )
other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
if ( mode == FT_RENDER_MODE_MONO )

View File

@ -2577,7 +2577,9 @@
/*
* We adjust stems to full pixels unless in `light' or `lcd' mode.
*/
if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
if ( mode != FT_RENDER_MODE_LIGHT &&
mode != FT_RENDER_MODE_SLIGHT &&
mode != FT_RENDER_MODE_LCD )
other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
if ( mode == FT_RENDER_MODE_MONO )
@ -2590,8 +2592,10 @@
* However, if warping is enabled (which only works in `light' hinting
* mode), advance widths get adjusted, too.
*/
if ( mode == FT_RENDER_MODE_LIGHT || mode == FT_RENDER_MODE_LCD ||
( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
if ( mode == FT_RENDER_MODE_LIGHT ||
mode == FT_RENDER_MODE_SLIGHT ||
mode == FT_RENDER_MODE_LCD ||
( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
#ifdef AF_CONFIG_OPTION_USE_WARPER

View File

@ -1560,7 +1560,9 @@
/*
* We adjust stems to full pixels unless in `light' or `lcd' mode.
*/
if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
if ( mode != FT_RENDER_MODE_LIGHT &&
mode != FT_RENDER_MODE_SLIGHT &&
mode != FT_RENDER_MODE_LCD )
other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
if ( mode == FT_RENDER_MODE_MONO )
@ -1570,8 +1572,10 @@
* In `light' or `lcd' mode we disable horizontal hinting completely.
* We also do it if the face is italic.
*/
if ( mode == FT_RENDER_MODE_LIGHT || mode == FT_RENDER_MODE_LCD ||
( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
if ( mode == FT_RENDER_MODE_LIGHT ||
mode == FT_RENDER_MODE_SLIGHT ||
mode == FT_RENDER_MODE_LCD ||
( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
#ifdef AF_CONFIG_OPTION_USE_WARPER

View File

@ -97,11 +97,13 @@
AF_FaceGlobals globals = loader->globals;
AF_WritingSystemClass writing_system_class;
FT_Size_Metrics* size_metrics = &face->size->internal->autohint_metrics;
FT_Pos stdVW = 0;
FT_Pos stdHW = 0;
FT_Bool size_changed = face->size->metrics.x_ppem
!= globals->stem_darkening_for_ppem;
FT_Bool size_changed = 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 );
@ -145,11 +147,11 @@
face,
stdVW ) );
darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
face->size->metrics.x_scale ),
size_metrics->x_scale ),
em_ratio );
globals->standard_vertical_width = stdVW;
globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
globals->stem_darkening_for_ppem = size_metrics->x_ppem;
globals->darken_x = af_fixedToInt( darken_x );
}
@ -164,11 +166,11 @@
face,
stdHW ) );
darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
face->size->metrics.y_scale ),
size_metrics->y_scale ),
em_ratio );
globals->standard_horizontal_width = stdHW;
globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
globals->stem_darkening_for_ppem = size_metrics->x_ppem;
globals->darken_y = af_fixedToInt( darken_y );
/*
@ -217,10 +219,11 @@
{
FT_Error error;
FT_Size size = face->size;
FT_GlyphSlot slot = face->glyph;
FT_Slot_Internal internal = slot->internal;
FT_GlyphLoader gloader = internal->loader;
FT_Size size = face->size;
FT_Size_Internal size_internal = size->internal;
FT_GlyphSlot slot = face->glyph;
FT_Slot_Internal slot_internal = slot->internal;
FT_GlyphLoader gloader = slot_internal->loader;
AF_GlyphHints hints = loader->hints;
AF_ScalerRec scaler;
@ -239,6 +242,44 @@
FT_ZERO( &scaler );
if ( !size_internal->autohint_metrics.x_scale ||
size_internal->autohint_mode != FT_LOAD_TARGET_MODE( load_flags ) )
{
/* switching between LIGHT and SLIGHT (and vice versa) usually means */
/* different scaling values; this later on enforces recomputation of */
/* everything related to the current size */
size_internal->autohint_mode = FT_LOAD_TARGET_MODE( load_flags );
size_internal->autohint_metrics = size->metrics;
if ( size_internal->autohint_mode != FT_RENDER_MODE_SLIGHT )
{
FT_Size_Metrics* size_metrics = &size_internal->autohint_metrics;
/* set metrics to integer values and adjust scaling accordingly; */
/* this is the same setup as with TrueType fonts, cf. function */
/* `tt_size_reset' in file `ttobjs.c' */
size_metrics->ascender = FT_PIX_ROUND(
FT_MulFix( face->ascender,
size_metrics->y_scale ) );
size_metrics->descender = FT_PIX_ROUND(
FT_MulFix( face->descender,
size_metrics->y_scale ) );
size_metrics->height = FT_PIX_ROUND(
FT_MulFix( face->height,
size_metrics->y_scale ) );
size_metrics->x_scale = FT_DivFix( size_metrics->x_ppem << 6,
face->units_per_EM );
size_metrics->y_scale = FT_DivFix( size_metrics->y_ppem << 6,
face->units_per_EM );
size_metrics->max_advance = FT_PIX_ROUND(
FT_MulFix( face->max_advance_width,
size_metrics->x_scale ) );
}
}
/*
* TODO: This code currently doesn't support fractional advance widths,
* i.e., placing hinted glyphs at anything other than integer
@ -249,9 +290,9 @@
* values of the scaler would need to be adjusted.
*/
scaler.face = face;
scaler.x_scale = size->metrics.x_scale;
scaler.x_scale = size_internal->autohint_metrics.x_scale;
scaler.x_delta = 0;
scaler.y_scale = size->metrics.y_scale;
scaler.y_scale = size_internal->autohint_metrics.y_scale;
scaler.y_delta = 0;
scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
@ -339,21 +380,22 @@
*
*/
/* stem darkening only works well in `light' mode */
if ( scaler.render_mode == FT_RENDER_MODE_LIGHT &&
/* stem darkening only works well in `light' and `slight' modes */
if ( ( scaler.render_mode == FT_RENDER_MODE_LIGHT ||
scaler.render_mode == FT_RENDER_MODE_SLIGHT ) &&
( !face->internal->no_stem_darkening ||
( face->internal->no_stem_darkening < 0 &&
!module->no_stem_darkening ) ) )
!module->no_stem_darkening ) ) )
af_loader_embolden_glyph_in_slot( loader, face, style_metrics );
loader->transformed = internal->glyph_transformed;
loader->transformed = slot_internal->glyph_transformed;
if ( loader->transformed )
{
FT_Matrix inverse;
loader->trans_matrix = internal->glyph_matrix;
loader->trans_delta = internal->glyph_delta;
loader->trans_matrix = slot_internal->glyph_matrix;
loader->trans_delta = slot_internal->glyph_delta;
inverse = loader->trans_matrix;
if ( !FT_Matrix_Invert( &inverse ) )
@ -391,7 +433,8 @@
/* 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 )
if ( scaler.render_mode != FT_RENDER_MODE_LIGHT &&
scaler.render_mode != FT_RENDER_MODE_SLIGHT )
{
FT_Pos old_rsb, old_lsb, new_lsb;
FT_Pos pp1x_uh, pp2x_uh;
@ -448,7 +491,10 @@
slot->rsb_delta = loader->pp2.x - pp2x;
}
}
else
/* `light' mode uses integer advance widths */
/* (but sets `lsb_delta' and `rsb_delta'), */
/* `slight' mode uses fractional values */
else if ( scaler.render_mode == FT_RENDER_MODE_LIGHT )
{
FT_Pos pp1x = loader->pp1.x;
FT_Pos pp2x = loader->pp2.x;
@ -460,6 +506,11 @@
slot->lsb_delta = loader->pp1.x - pp1x;
slot->rsb_delta = loader->pp2.x - pp2x;
}
else
{
slot->lsb_delta = 0;
slot->rsb_delta = 0;
}
break;
@ -510,6 +561,7 @@
/* to keep the original rounded advance width; ditto for */
/* digits if all have the same advance width */
if ( scaler.render_mode != FT_RENDER_MODE_LIGHT &&
scaler.render_mode != FT_RENDER_MODE_SLIGHT &&
( FT_IS_FIXED_WIDTH( slot->face ) ||
( af_face_globals_is_digit( loader->globals, glyph_index ) &&
style_metrics->digits_have_same_width ) ) )
@ -533,7 +585,8 @@
slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
style_metrics->scaler.y_scale );
slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
if ( scaler.render_mode != FT_RENDER_MODE_SLIGHT )
slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
slot->format = FT_GLYPH_FORMAT_OUTLINE;

View File

@ -59,14 +59,15 @@
/* */
/* - unscaled load */
/* - unhinted load */
/* - light-hinted load */
/* - light-hinted and slight-hinted load */
/* - if a variations font, it must have an `HVAR' or `VVAR' */
/* table (thus the old MM or GX fonts don't qualify; this */
/* gets checked by the driver-specific functions) */
#define LOAD_ADVANCE_FAST_CHECK( face, flags ) \
( flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) || \
FT_LOAD_TARGET_MODE( flags ) == FT_RENDER_MODE_LIGHT )
#define LOAD_ADVANCE_FAST_CHECK( face, flags ) \
( flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) || \
( FT_LOAD_TARGET_MODE( flags ) == FT_RENDER_MODE_LIGHT || \
FT_LOAD_TARGET_MODE( flags ) == FT_RENDER_MODE_SLIGHT ) )
/* documentation is in ftadvanc.h */

View File

@ -668,8 +668,8 @@
* - Then, auto-hint if FT_LOAD_FORCE_AUTOHINT is set or if we don't
* have a native font hinter.
*
* - Otherwise, auto-hint for LIGHT hinting mode or if there isn't
* any hinting bytecode in the TrueType/OpenType font.
* - Otherwise, auto-hint for LIGHT or SLIGHT hinting mode or if there
* isn't any hinting bytecode in the TrueType/OpenType font.
*
* - Exception: The font is `tricky' and requires the native hinter to
* load properly.
@ -702,8 +702,9 @@
/* check the size of the `fpgm' and `prep' tables, too -- */
/* the assumption is that there don't exist real TTFs where */
/* both `fpgm' and `prep' tables are missing */
if ( ( mode == FT_RENDER_MODE_LIGHT &&
!FT_DRIVER_HINTS_LIGHTLY( driver ) ) ||
if ( ( ( mode == FT_RENDER_MODE_LIGHT ||
mode == FT_RENDER_MODE_SLIGHT ) &&
!FT_DRIVER_HINTS_LIGHTLY( driver ) ) ||
( FT_IS_SFNT( face ) &&
ttface->num_locations &&
ttface->max_profile.maxSizeOfInstructions == 0 &&

View File

@ -2149,7 +2149,8 @@
glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
hint_mode == FT_RENDER_MODE_LCD_V );
glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT &&
hint_mode != FT_RENDER_MODE_SLIGHT );
for ( dimension = 0; dimension < 2; dimension++ )
{

View File

@ -433,7 +433,8 @@
FT_Render_Mode mode,
const FT_Vector* origin )
{
if ( mode == FT_RENDER_MODE_LIGHT )
if ( mode == FT_RENDER_MODE_LIGHT ||
mode == FT_RENDER_MODE_SLIGHT )
mode = FT_RENDER_MODE_NORMAL;
return ft_smooth_render_generic( render, slot, mode, origin,