ref: 5aa6716a5eb8409f606886b5d4e63476d85a3519
parent: 69da54cacc0056a40fc7f88278c4c60fd35e7bb9
author: Werner Lemberg <[email protected]>
date: Sat Apr 22 09:27:21 EDT 2017
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.
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,47 @@
2017-04-22 Werner Lemberg <[email protected]>
+ 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 <[email protected]>
+
Introduce `FT_Size_InternalRec' structure.
We are going to extend this later on.
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -221,6 +221,7 @@
/* */
/* 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 @@
/* `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 @@
* 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_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 @@
/* 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_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,
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -443,13 +443,20 @@
/* 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. */
+ /* */
/*************************************************************************/
typedef struct FT_Size_InternalRec_
{
void* module_data;
+
+ FT_Render_Mode autohint_mode;
+ FT_Size_Metrics autohint_metrics;
} FT_Size_InternalRec;
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -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 )
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -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
--- a/src/autofit/aflatin2.c
+++ b/src/autofit/aflatin2.c
@@ -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
--- a/src/autofit/afloader.c
+++ b/src/autofit/afloader.c
@@ -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;
--- a/src/base/ftadvanc.c
+++ b/src/base/ftadvanc.c
@@ -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 */
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -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 &&
--- a/src/pshinter/pshalgo.c
+++ b/src/pshinter/pshalgo.c
@@ -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++ )
{
--- a/src/smooth/ftsmooth.c
+++ b/src/smooth/ftsmooth.c
@@ -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,