>From c2fa5d7b9193b045dc16e8ccc20bcb2f28b206c3 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Sat, 30 Apr 2016 22:53:01 +0200 Subject: [PATCH 1/3] Prepare landing of new minimal TrueType subpixel hinting code (v40). Modern TrueType fonts are usually rendered through Microsoft's collection of rendering techniques called ClearType (e.g., subpixel rendering and subpixel hinting). When ClearType was introduced, most fonts were not ready. Microsoft decided to implement a backwards compatibility mode that employed several simple to complicated assumptions and tricks that modified the interpretation of the bytecode contained in these fonts to make them look ClearType-y somehow. Most (web)fonts that were released since then have come to rely on these hacks to render correctly, even some of Microsoft's flagship ClearType fonts (Calibri, Cambria, Segoe UI). The minimal subpixel hinting code (interpreter version 40) employs a small list of font-agnostic hacks to bludgeon non-native-ClearType fonts (except tricky ones[1]) into submission. It will not try to toggle hacks for specific fonts for performance and complexity reasons. The focus is on modern (web)fonts rather than legacy fonts that were made for black-and-white rendering. Major hacks - Any point movement on the X-axis is ignored (cf. `Direct_Move' and `Direct_Move_X'). This has the smallest code footprint and single biggest effect. The ClearType way to increase resolution is supersampling the x-axis, the FreeType way is ignoring instructions on the x-axis, which gives the same result in the majority of cases. - Points are not moved post-IUP (neither on the x-, nor on the y-axis), except the x component of diagonal moves post-IUP (cf. `Direct_Move', `Direct_Move_Y', `Move_Zp2_Point'). Post-IUP changes are commonly used to `fix' pixel patterns which has little use outside monochrome rendering. - SHPIX and DELTAP don't execute unless moving a composite on the y axis or moving a previously y touched point. SHPIX additionally denies movement on the x axis (cf. `Ins_SHPIX' and `Ins_DELTAP'). Both instructions are commonly used to `fix' pixel patterns for monochrome or Windows's GDI rendering but make little sense for FreeType rendering. Both can distort the outline. See [2] for details. - The hdmx table and modifications to phantom points are ignored. Bearings and advance widths remain unchanged (except rounding them outside the interpreter!), cf. `compute_glyph_metrics' and `TT_Hint_Glyph'. Letting non-native-ClearType fonts modify spacing might mess up spacing. Minor hacks - FLIPRGON, FLIPRGOFF, and FLIPPT don't execute post-IUP. This prevents dents in e.g. Arial-Regular's `D' and `G' glyphs at various sizes. (Post-IUP is the state after both IUP[x] and IUP[y] have been executed.) The best results are achieved for fonts that were from the outset designed with ClearType in mind, meaning they leave the x axis mostly alone and don't mess with the `final' outline to produce more pleasing pixel patterns. The harder the designer tried to produce very specific patterns (`superhinting') for pre-ClearType-displays, the worse the results. Microsoft defines a way to turn off backwards compatibility and interpret instructions as before (called `native ClearType')[2][3]. The font designer then regains full control and is responsible for making the font work correctly with ClearType without any hand-holding by the interpreter or rasterizer[4]. The v40 interpreter assumes backwards compatibility by default, which can be turned off the same way by executing the following in the control program (cf. `Ins_INSTCTRL'). #PUSH 4,3 INSTCTRL[] [1] Tricky fonts as FreeType defines them rely on the bytecode interpreter to display correctly. Hacks can interfere with them, so they get treated like native ClearType fonts (v40 with backwards compatibility turned off). Cf. `TT_RunIns'. [2] Proposed by Microsoft's Greg Hitchcock in https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx [3] Beat Stamm describes it in more detail: http://www.beatstamm.com/typography/RTRCh4.htm#Sec12 [4] The list of `native ClearType' fonts is small at the time of this writing; I found the following on a Windows 10 Update 1511 installation: Constantia, Corbel, Sitka, Malgun Gothic, Microsoft JhengHei (Bold and UI Bold), Microsoft YaHei (Bold and UI Bold), SimSun, NSimSun, and Yu Gothic. --- devel/ftoption.h | 67 +++++++++++---- include/freetype/config/ftoption.h | 67 +++++++++++---- include/freetype/ftcffdrv.h | 8 +- include/freetype/ftttdrv.h | 48 +++++++---- include/freetype/internal/tttypes.h | 4 +- src/truetype/ttdriver.c | 5 +- src/truetype/ttgload.c | 159 +++++++++++++++++++++++++++--------- src/truetype/ttinterp.h | 138 +++++++++++++++++++++++++++++-- src/truetype/ttobjs.c | 8 +- src/truetype/ttsubpix.c | 6 +- src/truetype/ttsubpix.h | 4 +- 11 files changed, 408 insertions(+), 106 deletions(-) diff --git a/devel/ftoption.h b/devel/ftoption.h index b354efd..d145eeb 100644 --- a/devel/ftoption.h +++ b/devel/ftoption.h @@ -585,24 +585,55 @@ FT_BEGIN_HEADER /*************************************************************************/ /* */ - /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ - /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ - /* replaces the native TrueType hinting mechanism when anything but */ - /* FT_RENDER_MODE_MONO is requested. */ - /* */ - /* Enabling this causes the TrueType driver to ignore instructions under */ - /* certain conditions. This is done in accordance with the guide here, */ - /* with some minor differences: */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* subpixel hinting support into the TrueType driver. This modifies the */ + /* TrueType hinting mechanism when anything but FT_RENDER_MODE_MONO is */ + /* requested. */ + /* */ + /* In particular, it modifies the bytecode interpreter to interpret (or */ + /* not) instructions in a certain way so that all TrueType fonts look */ + /* like they do in a Windows ClearType (DirectWrite) environment. See */ + /* [1] for a technical overview on what this means. See `ttinterp.h' */ + /* for more details on the LEAN option. */ + /* */ + /* There are three options. */ + /* */ + /* - 1: This option is associated with the */ + /* `Infinality' moniker. Contributed by an individual nicknamed */ + /* Infinality with the goal of making TrueType fonts render better */ + /* than on Windows. A high amount of configurability and flexibility, */ + /* down to rules for single glyphs in fonts, but also very slow. Its */ + /* experimental and slow nature and the original developer losing */ + /* interest meant that this option was never enabled in default */ + /* builds. */ + /* */ + /* - 2: The new default mode for */ + /* the TrueType driver. The Infinality code base was stripped to the */ + /* bare minimum and all configurability removed in the name of speed */ + /* and simplicity. The configurability was mainly aimed at legacy */ + /* fonts like Arial, Times New Roman, or Courier. Legacy fonts are */ + /* fonts that modify vertical stems to achieve clean black-and-white */ + /* bitmaps. The new mode focuses on applying a minimal set of rules */ + /* to all fonts indiscriminately so that modern and web fonts render */ + /* well while legacy fonts render okay. */ + /* + * - 3: Compile both. */ /* */ - /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* By undefining these, you get rendering behavior like on Windows */ + /* without ClearType, i.e., Windows XP without ClearType enabled and */ + /* Win9x (interpreter version v35). Or not, depending on how much */ + /* hinting blood and testing tears the font designer put into a given */ + /* font. If you define one or both subpixel hinting options, you can switch between between v35 */ + /* and the ones you define. */ /* */ - /* By undefining this, you only compile the code necessary to hint */ - /* TrueType glyphs with native TT hinting. */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ /* */ - /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ - /* defined. */ + /* [1] http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ /* */ -#define TT_CONFIG_OPTION_SUBPIXEL_HINTING +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */ +#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2 +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 | 2 */ /*************************************************************************/ @@ -811,6 +842,14 @@ FT_BEGIN_HEADER */ #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER #define TT_USE_BYTECODE_INTERPRETER + +#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 1 +#define TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY +#endif + +#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 2 +#define TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL +#endif #endif diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index a8097fe..a29567b 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -585,24 +585,55 @@ FT_BEGIN_HEADER /*************************************************************************/ /* */ - /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ - /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ - /* replaces the native TrueType hinting mechanism when anything but */ - /* FT_RENDER_MODE_MONO is requested. */ - /* */ - /* Enabling this causes the TrueType driver to ignore instructions under */ - /* certain conditions. This is done in accordance with the guide here, */ - /* with some minor differences: */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* subpixel hinting support into the TrueType driver. This modifies the */ + /* TrueType hinting mechanism when anything but FT_RENDER_MODE_MONO is */ + /* requested. */ + /* */ + /* In particular, it modifies the bytecode interpreter to interpret (or */ + /* not) instructions in a certain way so that all TrueType fonts look */ + /* like they do in a Windows ClearType (DirectWrite) environment. See */ + /* [1] for a technical overview on what this means. See `ttinterp.h' */ + /* for more details on the LEAN option. */ + /* */ + /* There are three options. */ + /* */ + /* - 1: This option is associated with the */ + /* `Infinality' moniker. Contributed by an individual nicknamed */ + /* Infinality with the goal of making TrueType fonts render better */ + /* than on Windows. A high amount of configurability and flexibility, */ + /* down to rules for single glyphs in fonts, but also very slow. Its */ + /* experimental and slow nature and the original developer losing */ + /* interest meant that this option was never enabled in default */ + /* builds. */ + /* */ + /* - 2: The new default mode for */ + /* the TrueType driver. The Infinality code base was stripped to the */ + /* bare minimum and all configurability removed in the name of speed */ + /* and simplicity. The configurability was mainly aimed at legacy */ + /* fonts like Arial, Times New Roman, or Courier. Legacy fonts are */ + /* fonts that modify vertical stems to achieve clean black-and-white */ + /* bitmaps. The new mode focuses on applying a minimal set of rules */ + /* to all fonts indiscriminately so that modern and web fonts render */ + /* well while legacy fonts render okay. */ + /* + * - 3: Compile both. */ /* */ - /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* By undefining these, you get rendering behavior like on Windows */ + /* without ClearType, i.e., Windows XP without ClearType enabled and */ + /* Win9x (interpreter version v35). Or not, depending on how much */ + /* hinting blood and testing tears the font designer put into a given */ + /* font. If you define one or both subpixel hinting options, you can switch between between v35 */ + /* and the ones you define. */ /* */ - /* By undefining this, you only compile the code necessary to hint */ - /* TrueType glyphs with native TT hinting. */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ /* */ - /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ - /* defined. */ + /* [1] http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ /* */ -/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */ +#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2 +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 | 2 */ /*************************************************************************/ @@ -811,6 +842,14 @@ FT_BEGIN_HEADER */ #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER #define TT_USE_BYTECODE_INTERPRETER + +#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 1 +#define TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY +#endif + +#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 2 +#define TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL +#endif #endif diff --git a/include/freetype/ftcffdrv.h b/include/freetype/ftcffdrv.h index b0b4299..ad34541 100644 --- a/include/freetype/ftcffdrv.h +++ b/include/freetype/ftcffdrv.h @@ -110,7 +110,7 @@ FT_BEGIN_HEADER * to minimize distortion. * * @order: - * hinting-engine + * hinting-engine[cff] * no-stem-darkening[cff] * darkening-parameters[cff] * @@ -120,7 +120,7 @@ FT_BEGIN_HEADER /************************************************************************** * * @property: - * hinting-engine + * hinting-engine[cff] * * @description: * Thanks to Adobe, which contributed a new hinting (and parsing) @@ -157,8 +157,8 @@ FT_BEGIN_HEADER * FT_CFF_HINTING_XXX * * @description: - * A list of constants used for the @hinting-engine property to select - * the hinting engine for CFF fonts. + * A list of constants used for the @hinting-engine[cff] property to + * select the hinting engine for CFF fonts. * * @values: * FT_CFF_HINTING_FREETYPE :: diff --git a/include/freetype/ftttdrv.h b/include/freetype/ftttdrv.h index 6c02e65..e61ee85 100644 --- a/include/freetype/ftttdrv.h +++ b/include/freetype/ftttdrv.h @@ -138,31 +138,36 @@ FT_BEGIN_HEADER * interpreter-version * * @description: - * Currently, two versions are available, representing the bytecode - * interpreter with and without subpixel hinting support, + * Currently, three versions are available, two representing the bytecode + * interpreter with subpixel hinting support (old `Infinality' code and new + * stripped-down and higher performance `minimal' code) and one without, * respectively. The default is subpixel support if - * TT_CONFIG_OPTION_SUBPIXEL_HINTING is defined, and no subpixel - * support otherwise (since it isn't available then). + * TT_CONFIG_OPTION_SUBPIXEL_HINTING is defined, and no subpixel support + * otherwise (since it isn't available then). * * If subpixel hinting is on, many TrueType bytecode instructions behave * differently compared to B/W or grayscale rendering (except if `native - * ClearType' is selected by the font). The main idea is to render at a - * much increased horizontal resolution, then sampling down the created - * output to subpixel precision. However, many older fonts are not + * ClearType' is selected by the font). Microsoft's main idea is to render + * at a much increased horizontal resolution, then sampling down the + * created output to subpixel precision. However, many older fonts are not * suited to this and must be specially taken care of by applying - * (hardcoded) font-specific tweaks. + * (hardcoded) tweaks in Microsoft's interpreter. * * Details on subpixel hinting and some of the necessary tweaks can be * found in Greg Hitchcock's whitepaper at * `http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx'. + * Note that FreeType currently doesn't really "subpixel hint" (6x1, 6x2 or + * 6x5 supersampling) like discussed in the paper. Depending in the chosen + * interpreter, it simply ignores instructions on vertical stems to arrive + * at very similar results. * - * The following example code demonstrates how to activate subpixel + * The following example code demonstrates how to deactivate subpixel * hinting (omitting the error handling). * * { * FT_Library library; * FT_Face face; - * FT_UInt interpreter_version = TT_INTERPRETER_VERSION_38; + * FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35; * * * FT_Init_FreeType( &library ); @@ -199,7 +204,15 @@ FT_BEGIN_HEADER * Version~38 corresponds to MS rasterizer v.1.9; it is roughly * equivalent to the hinting provided by DirectWrite ClearType (as * can be found, for example, in the Internet Explorer~9 running on - * Windows~7). + * Windows~7). It is used in FreeType to select the `Infinality' subpixel + * hinting code. The code may be removed in a future version. + * + * TT_INTERPRETER_VERSION_40 :: + * Version~40 corresponds to MS rasterizer v.2.1; it is roughly + * equivalent to the hinting provided by DirectWrite ClearType (as can be + * found, for example, in Microsoft's Edge Browser on Windows~10). It is + * used in FreeType to select the `minimal' subpixel hinting code, a + * stripped-down and higher performance version of the `Infinality' code. * * @note: * This property controls the behaviour of the bytecode interpreter @@ -207,9 +220,9 @@ FT_BEGIN_HEADER * get rasterized! In particular, it does not control subpixel color * filtering. * - * If FreeType has not been compiled with configuration option - * FT_CONFIG_OPTION_SUBPIXEL_HINTING, selecting version~38 causes an - * `FT_Err_Unimplemented_Feature' error. + * If FreeType has not been compiled with the configuration option + * FT_CONFIG_OPTION_SUBPIXEL_HINTING, selecting version~38 or 40 + * causes an `FT_Err_Unimplemented_Feature' error. * * Depending on the graphics framework, Microsoft uses different * bytecode and rendering engines. As a consequence, the version @@ -290,13 +303,14 @@ FT_BEGIN_HEADER * the version~1 gasp table exclusively (like Color ClearType), while * v1.6 only respects the values of version~0 (bits 0 and~1). * - * FreeType doesn't provide all capabilities of the most recent - * ClearType incarnation, thus we identify our subpixel support as - * version~38. + * Keep in mind that the features of above interpreter versions might not + * map exactly to FreeType features or behavior because it is a + * fundamentally different library with different internals. * */ #define TT_INTERPRETER_VERSION_35 35 #define TT_INTERPRETER_VERSION_38 38 +#define TT_INTERPRETER_VERSION_40 40 /* */ diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 8f1a642..4110d50 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -1386,12 +1386,12 @@ FT_BEGIN_HEADER FT_ULong horz_metrics_offset; FT_ULong vert_metrics_offset; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY /* since 2.4.12 */ FT_ULong sph_found_func_flags; /* special functions found */ /* for this face */ FT_Bool sph_compatibility_mode; -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ } TT_FaceRec; diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index abbd127..c9d4081 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -73,9 +73,12 @@ if ( *interpreter_version == TT_INTERPRETER_VERSION_35 -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || *interpreter_version == TT_INTERPRETER_VERSION_38 #endif +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + || *interpreter_version == TT_INTERPRETER_VERSION_40 +#endif ) driver->interpreter_version = *interpreter_version; else diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index c4038ee..517b5aa 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -122,7 +122,7 @@ FT_UInt glyph_index ) { TT_Face face = loader->face; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); #endif @@ -153,7 +153,7 @@ loader->top_bearing = top_bearing; loader->vadvance = advance_height; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && loader->exec ) { @@ -165,7 +165,7 @@ /* backwards compatibility mode on and off. */ sph_set_tweaks( loader, glyph_index ); } -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ if ( !loader->linear_def ) { @@ -733,7 +733,8 @@ TT_Hint_Glyph( TT_Loader loader, FT_Bool is_composite ) { -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#if defined TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || \ + defined TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL TT_Face face = loader->face; TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); #endif @@ -816,13 +817,23 @@ #endif - /* save glyph phantom points */ - loader->pp1 = zone->cur[zone->n_points - 4]; - loader->pp2 = zone->cur[zone->n_points - 3]; - loader->pp3 = zone->cur[zone->n_points - 2]; - loader->pp4 = zone->cur[zone->n_points - 1]; +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + /* Save possibly modified glyph phantom points unless in v40 backwards */ + /* compatibility mode, where no movement on the x axis means no reason */ + /* to change bearings or advance widths. */ + if ( !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && + !loader->exec->backwards_compatibility ) ) + { +#endif + loader->pp1 = zone->cur[zone->n_points - 4]; + loader->pp2 = zone->cur[zone->n_points - 3]; + loader->pp3 = zone->cur[zone->n_points - 2]; + loader->pp4 = zone->cur[zone->n_points - 1]; +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + } +#endif -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { if ( loader->exec->sph_tweak_flags & SPH_TWEAK_DEEMBOLDEN ) @@ -831,7 +842,7 @@ else if ( loader->exec->sph_tweak_flags & SPH_TWEAK_EMBOLDEN ) FT_Outline_EmboldenXY( &loader->gloader->current.outline, 24, 0 ); } -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ return FT_Err_Ok; } @@ -897,7 +908,7 @@ } { -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY TT_Face face = loader->face; TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); @@ -916,7 +927,7 @@ FT_Bool do_scale = FALSE; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { @@ -947,7 +958,7 @@ } else -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ { /* scale the glyph */ @@ -1078,7 +1089,7 @@ : -subglyph->transform.yx; int c = subglyph->transform.xy > 0 ? subglyph->transform.xy : -subglyph->transform.xy; - int d = subglyph->transform.yy > 0 ? subglyph->transform.yy + int d = subglyph->transform.yy > 0 ? subglyph->transform.yy : -subglyph->transform.yy; int m = a > b ? a : b; int n = c > d ? c : d; @@ -1324,7 +1335,8 @@ * (3) for everything else. * */ -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* XXX merge infinality + lean datafields? */ +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY #define TT_LOADER_SET_PP( loader ) \ do \ @@ -1350,7 +1362,7 @@ (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance; \ } while ( 0 ) -#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#else /* !TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ #define TT_LOADER_SET_PP( loader ) \ do \ @@ -1366,7 +1378,7 @@ (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance; \ } while ( 0 ) -#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* !TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ /*************************************************************************/ @@ -1946,7 +1958,8 @@ FT_UInt glyph_index ) { TT_Face face = loader->face; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#if defined TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || \ + defined TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); #endif @@ -1973,11 +1986,18 @@ glyph->metrics.horiBearingY = bbox.yMax; glyph->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; - /* adjust advance width to the value contained in the hdmx table */ - /* unless FT_LOAD_COMPUTE_METRICS is set */ - if ( !face->postscript.isFixedPitch && - IS_HINTED( loader->load_flags ) && - !( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) ) + /* Adjust advance width to the value contained in the hdmx table */ + /* unless FT_LOAD_COMPUTE_METRICS is set or backwards compatibility */ + /* mode of the v40 interpreter is active. See `ttinterp.h' for */ + /* details on backwards compatibility mode. */ + if ( +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && + ( loader->exec && loader->exec->backwards_compatibility ) ) && +#endif + !face->postscript.isFixedPitch && + IS_HINTED( loader->load_flags ) && + !( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) ) { FT_Byte* widthp; @@ -1986,7 +2006,7 @@ size->root.metrics.x_ppem, glyph_index ); -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { @@ -2004,7 +2024,7 @@ } else -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ { if ( widthp ) @@ -2193,6 +2213,10 @@ #ifdef TT_USE_BYTECODE_INTERPRETER FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); #endif +#if defined TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || \ + defined TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( (TT_Face)glyph->face ); +#endif face = (TT_Face)glyph->face; @@ -2206,11 +2230,13 @@ if ( IS_HINTED( load_flags ) && !glyf_table_only ) { TT_ExecContext exec; - FT_Bool grayscale; - -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); + FT_Bool grayscale = TRUE; +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + FT_Bool subpixel_hinting_lean; + FT_Bool grayscale_cleartype; +#endif +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY FT_Bool subpixel_hinting = FALSE; #if 0 @@ -2222,7 +2248,7 @@ FT_Bool subpixel_positioned; FT_Bool gray_cleartype; #endif -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ FT_Bool reexecute = FALSE; @@ -2243,7 +2269,26 @@ if ( !exec ) return FT_THROW( Could_Not_Find_Context ); -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) + { + subpixel_hinting_lean = TRUE; + grayscale_cleartype = !FT_BOOL( load_flags & + FT_LOAD_TARGET_LCD || + load_flags & + FT_LOAD_TARGET_LCD_V ); + exec->vertical_lcd_lean = FT_BOOL( load_flags & + FT_LOAD_TARGET_LCD_V ); + } + else + { + subpixel_hinting_lean = FALSE; + grayscale_cleartype = FALSE; + exec->vertical_lcd_lean = FALSE; + } +#endif + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { @@ -2300,18 +2345,23 @@ } else -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - { +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) + grayscale = FT_BOOL( !subpixel_hinting_lean && + FT_LOAD_TARGET_MODE( load_flags ) != + FT_RENDER_MODE_MONO ); + else +#endif grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ); - } + FT_RENDER_MODE_MONO ); error = TT_Load_Context( exec, face, size ); if ( error ) return error; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { @@ -2339,9 +2389,37 @@ } else -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ { + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) + { + /* a change from mono to subpixel rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( subpixel_hinting_lean != exec->subpixel_hinting_lean ) + { + FT_TRACE4(( "tt_loader_init: subpixel hinting change," + " re-executing `prep' table\n" )); + + exec->subpixel_hinting_lean = subpixel_hinting_lean; + reexecute = TRUE; + } + + /* a change from colored to grayscale subpixel rendering (and */ + /* vice versa) requires a re-execution of the CVT program */ + if ( grayscale_cleartype != exec->grayscale_cleartype ) + { + FT_TRACE4(( "tt_loader_init: grayscale subpixel hinting change," + " re-executing `prep' table\n" )); + + exec->grayscale_cleartype = grayscale_cleartype; + reexecute = TRUE; + } + } +#endif + /* a change from mono to grayscale rendering (and vice versa) */ /* requires a re-execution of the CVT program */ if ( grayscale != exec->grayscale ) @@ -2374,10 +2452,11 @@ if ( exec->GS.instruct_control & 2 ) exec->GS = tt_default_graphics_state; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY /* check whether we have a font hinted for ClearType -- */ /* note that this flag can also be modified in a glyph's bytecode */ - if ( exec->GS.instruct_control & 4 ) + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && + exec->GS.instruct_control & 4 ) exec->ignore_x_mode = 0; #endif diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h index e5a02b9..5aaceea 100644 --- a/src/truetype/ttinterp.h +++ b/src/truetype/ttinterp.h @@ -99,7 +99,7 @@ FT_BEGIN_HEADER } TT_CallRec, *TT_CallStack; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY /*************************************************************************/ /* */ @@ -138,7 +138,7 @@ FT_BEGIN_HEADER } SPH_Font_Class; -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ /*************************************************************************/ @@ -247,9 +247,135 @@ FT_BEGIN_HEADER TT_Set_CVT_Func func_write_cvt; /* write a cvt entry (in pixels) */ TT_Set_CVT_Func func_move_cvt; /* incr a cvt entry (in pixels) */ - FT_Bool grayscale; /* are we hinting for grayscale? */ - -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Bool grayscale; /* bi-level hinting and */ + /* grayscale rendering */ + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + /* + * Modern TrueType fonts are usually rendered through Microsoft's + * collection of rendering techniques called ClearType (e.g., subpixel + * rendering and subpixel hinting). When ClearType was introduced, + * most fonts were not ready. Microsoft decided to implement a + * backwards compatibility mode that employed several simple to + * complicated assumptions and tricks that modified the interpretation + * of the bytecode contained in these fonts to make them look + * ClearType-y somehow. Most (web)fonts that were released since then + * have come to rely on these hacks to render correctly, even some of + * Microsoft's flagship ClearType fonts (Calibri, Cambria, Segoe UI). + * + * The minimal subpixel hinting code (interpreter version 40) employs a + * small list of font-agnostic hacks to bludgeon non-native-ClearType fonts + * (except tricky ones[1]) into submission. It will not try to toggle + * hacks for specific fonts for performance and complexity reasons. The + * focus is on modern (web)fonts rather than legacy fonts that were made + * for black-and-white rendering. + * + * Major hacks + * + * - Any point movement on the X-axis is ignored (cf. `Direct_Move' + * and `Direct_Move_X'). This has the smallest code footprint and + * single biggest effect. The ClearType way to increase resolution + * is supersampling the x-axis, the FreeType way is ignoring + * instructions on the x-axis, which gives the same result in the + * majority of cases. + * + * - Points are not moved post-IUP (neither on the x-, nor on the y-axis), + * except the x component of diagonal moves post-IUP (cf. `Direct_Move', + * `Direct_Move_Y', `Move_Zp2_Point'). Post-IUP changes are commonly + * used to `fix' pixel patterns which has little use outside monochrome + * rendering. + * + * - SHPIX and DELTAP don't execute unless moving a composite on the + * y axis or moving a previously y touched point. SHPIX + * additionally denies movement on the x axis (cf. `Ins_SHPIX' and + * `Ins_DELTAP'). Both instructions are commonly used to `fix' + * pixel patterns for monochrome or Windows's GDI rendering but make + * little sense for FreeType rendering. Both can distort the + * outline. See [2] for details. + * + * - The hdmx table and modifications to phantom points are ignored. + * Bearings and advance widths remain unchanged (except rounding + * them outside the interpreter!), cf. `compute_glyph_metrics' and + * `TT_Hint_Glyph'. Letting non-native-ClearType fonts modify + * spacing might mess up spacing. + * + * Minor hacks + * + * - FLIPRGON, FLIPRGOFF, and FLIPPT don't execute post-IUP. This + * prevents dents in e.g. Arial-Regular's `D' and `G' glyphs at + * various sizes. + * + * (Post-IUP is the state after both IUP[x] and IUP[y] have been + * executed.) + * + * The best results are achieved for fonts that were from the outset + * designed with ClearType in mind, meaning they leave the x axis + * mostly alone and don't mess with the `final' outline to produce + * more pleasing pixel patterns. The harder the designer tried to + * produce very specific patterns (`superhinting') for + * pre-ClearType-displays, the worse the results. + * + * Microsoft defines a way to turn off backwards compatibility and + * interpret instructions as before (called `native ClearType')[2][3]. + * The font designer then regains full control and is responsible for + * making the font work correctly with ClearType without any + * hand-holding by the interpreter or rasterizer[4]. The v40 + * interpreter assumes backwards compatibility by default, which can + * be turned off the same way by executing the following in the + * control program (cf. `Ins_INSTCTRL'). + * + * #PUSH 4,3 + * INSTCTRL[] + * + * [1] Tricky fonts as FreeType defines them rely on the bytecode + * interpreter to display correctly. Hacks can interfere with + * them, so they get treated like native ClearType fonts (v40 with + * backwards compatibility turned off). Cf. `TT_RunIns'. + * + * [2] Proposed by Microsoft's Greg Hitchcock in + * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx + * + * [3] Beat Stamm describes it in more detail: + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec12 + * + * [4] The list of `native ClearType' fonts is small at the time of + * this writing; I found the following on a Windows 10 Update 1511 + * installation: Constantia, Corbel, Sitka, Malgun Gothic, + * Microsoft JhengHei (Bold and UI Bold), Microsoft YaHei (Bold + * and UI Bold), SimSun, NSimSun, and Yu Gothic. + * + */ + + /* Using v40 implies subpixel hinting. Used to detect interpreter */ + /* version switches. `_lean' to differentiate from the Infinality */ + /* `subpixel_hinting' that is managed differently. */ + FT_Bool subpixel_hinting_lean; + + /* Long side of a LCD subpixel is vertical (e.g. screen is rotated). */ + /* `_lean' to differentiate from the Infinality `vertical_lcd' that is */ + /* managed differently. */ + FT_Bool vertical_lcd_lean; + + /* Default to backwards compatibility mode in v40 interpreter. If */ + /* this is false, it implies the interpreter is in v35 or in native */ + /* ClearType mode. */ + FT_Bool backwards_compatibility; + + /* Useful for detecting and denying post-IUP trickery that is usually */ + /* used to fix pixel patterns (`superhinting'). */ + FT_Bool iupx_called; + FT_Bool iupy_called; + + /* ClearType hinting and grayscale rendering, as used by Universal */ + /* Windows Platform apps (Windows 8 and above). Like the standard */ + /* colorful ClearType mode, it utilizes a vastly increased virtual */ + /* resolution on the x axis. Different from bi-level hinting and */ + /* grayscale rendering, the old mode from Win9x days that roughly */ + /* adheres to the physical pixel grid on both axes. */ + FT_Bool grayscale_cleartype; +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */ + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY TT_Round_Func func_round_sphn; /* subpixel rounding function */ FT_Bool subpixel_hinting; /* Using subpixel hinting? */ @@ -279,7 +405,7 @@ FT_BEGIN_HEADER FT_ULong sph_in_func_flags; /* flags to indicate if in */ /* special functions */ -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ } TT_ExecContextRec; diff --git a/src/truetype/ttobjs.c b/src/truetype/ttobjs.c index a05f216..ed3be2d 100644 --- a/src/truetype/ttobjs.c +++ b/src/truetype/ttobjs.c @@ -1287,10 +1287,12 @@ TT_Driver driver = (TT_Driver)ttdriver; -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING - driver->interpreter_version = TT_INTERPRETER_VERSION_38; -#else driver->interpreter_version = TT_INTERPRETER_VERSION_35; +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY + driver->interpreter_version = TT_INTERPRETER_VERSION_38; +#endif +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + driver->interpreter_version = TT_INTERPRETER_VERSION_40; #endif #else /* !TT_USE_BYTECODE_INTERPRETER */ diff --git a/src/truetype/ttsubpix.c b/src/truetype/ttsubpix.c index 5a8f262..0395096 100644 --- a/src/truetype/ttsubpix.c +++ b/src/truetype/ttsubpix.c @@ -27,7 +27,7 @@ #include "ttsubpix.h" -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY /*************************************************************************/ /* */ @@ -1000,12 +1000,12 @@ } } -#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#else /* !TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ /* ANSI C doesn't like empty source files */ typedef int _tt_subpix_dummy; -#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* !TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ /* END */ diff --git a/src/truetype/ttsubpix.h b/src/truetype/ttsubpix.h index 6313baa..86844da 100644 --- a/src/truetype/ttsubpix.h +++ b/src/truetype/ttsubpix.h @@ -27,7 +27,7 @@ FT_BEGIN_HEADER -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY /*************************************************************************/ /* */ @@ -100,7 +100,7 @@ FT_BEGIN_HEADER #define SPH_OPTION_SET_COMPATIBLE_WIDTHS FALSE #define SPH_OPTION_SET_RASTERIZER_VERSION 38 -#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ FT_END_HEADER -- 2.5.5