The next iteration.
- Execution of glyph instructions is stopped after IUP[x] and IUP[y] are
called. This saves some backwards compatibility hack work.
- SHPIX now denies execution under the same circumstances as DELTAP.
Before, this blew up Rokkitt, but the freshly released v1.2 in Google's
font directory works fine. Hurrah!
- Code that reset x-coordinates after hinting was removed, I found it
made no discernible difference. Saves some memory writes.
- Extended documentation somewhat.
Known problems: Several glyphs of the DejaVu family get no hinting at
any size, as if it was disabled for the affected glyphs. Must investigate.
>From 5d4de24ea08ed5754cc783fe16ed99770026a751 Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <madig...@gmail.com>
Date: Thu, 10 Mar 2016 16:34:31 +0100
Subject: [PATCH 1/2] Implement v38 bytecode interpreter and make it the
default [1/2]
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).
FreeType employs a small list of font-agnostic hacks to bludgeon
non-native-ClearType fonts (except tricky ones[1]) into submission.
FreeType 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 will be ignored if the freedom vector
is parallel to the X-axis (cf. Direct_Move() and Direct_Move_X()).
This has the smallest code footprint and single biggest effect.
- Glyph instruction execution will stop after both IUP[x] and IUP[y]
were called (c.f. TT_RunIns()).
- All rounding instructions are turned into Round_None if the freedom
vector is parallel to the X-axis (c.f. Round_*()).
- The hdmx table and modifications to phantom points are ignored.
Bearings and advance widths remain unchanged (except rounding them
outside the interpreter!). C.f. compute_glyph_metrics() and
TT_Hint_Glyph().
- SHPIX and DELTAP will not execute unless moving a composite on the
Y-axis or moving a previously Y-touched point. SHPIX will additionally
deny movement on the X-axis (c.f. Ins_SHPIX() and Ins_DELTAP()).
Minor hacks:
- SHP will deny movement on the X-axis (c.f. Ins_SHP())
- MSIRP, MIAP, MDRP and MIRP will see a control value cut-in and minimum
distance of 0 if the freedom vector is parallel to the X-axis (c.f.
Ins_MSIRP(), Ins_MIAP(), Ins_MDRP() and Ins_MIRP()).
- ALIGNRP will not execute after IUP[x] has been called (c.f.
Ins_ALIGNRP()).
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-display, the worse the
results.
Microsoft defines a way to turn off backwards compatibility and
interpret instructions as before ("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 v38 interpreter assumes backwards
compatibility by default, which can be turned of the same way by
executing the following in the control program:
#PUSH 4,3
INSTCTRL[]
(cf. Ins_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 (v38 with backwards
compatibility turned off). C.f. TT_RunIns().
[2]: Proposed by Microsoft's Greg Hitchcock in
https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx#Toc227035738
[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.
---
src/truetype/ttdriver.c | 5 +--
src/truetype/ttgload.c | 69 ++++++++++++++++++++++++++++------
src/truetype/ttinterp.h | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/truetype/ttobjs.c | 5 ---
4 files changed, 158 insertions(+), 20 deletions(-)
diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
index bbebabd..a856fed 100644
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -72,11 +72,10 @@
FT_UInt* interpreter_version = (FT_UInt*)value;
-#ifndef TT_CONFIG_OPTION_SUBPIXEL_HINTING
- if ( *interpreter_version != TT_INTERPRETER_VERSION_35 )
+ if ( *interpreter_version != TT_INTERPRETER_VERSION_35 &&
+ *interpreter_version != TT_INTERPRETER_VERSION_38 )
error = FT_ERR( Unimplemented_Feature );
else
-#endif
driver->interpreter_version = *interpreter_version;
return error;
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index 4ab6603..5635303 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -815,11 +815,16 @@
#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];
+ /* Save possibly modified glyph phantom points unless in v38 backwards
+ * compatibility mode, where no movement on the X-axis means no reason to
+ * change bearings or advance widths. */
+ if ( !loader->exec->backwards_compatibility )
+ {
+ 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_CONFIG_OPTION_SUBPIXEL_HINTING
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
@@ -1969,10 +1974,14 @@
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 ) &&
+ /* Adjust advance width to the value contained in the hdmx table unless
+ * FT_LOAD_COMPUTE_METRICS is set or backwards compatibility mode of the
+ * v38 interpreter is active. Non-"native ClearType" fonts should be
+ * stopped from changing advance widths or spacing may suffer, "native
+ * ClearType" fonts are trusted to know what they're doing. */
+ if ( !( loader->exec && loader->exec->backwards_compatibility ) &&
+ !face->postscript.isFixedPitch &&
+ IS_HINTED( loader->load_flags ) &&
!( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) )
{
FT_Byte* widthp;
@@ -2186,6 +2195,7 @@
TT_Face face;
FT_Stream stream;
+ TT_Driver driver;
#ifdef TT_USE_BYTECODE_INTERPRETER
FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC );
#endif
@@ -2193,6 +2203,7 @@
face = (TT_Face)glyph->face;
stream = face->root.stream;
+ driver = (TT_Driver)FT_FACE_DRIVER( face );
FT_MEM_ZERO( loader, sizeof ( TT_LoaderRec ) );
@@ -2203,6 +2214,8 @@
{
TT_ExecContext exec;
FT_Bool grayscale;
+ FT_Bool subpixel_hinting;
+ FT_Bool grayscale_cleartype;
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face );
@@ -2239,6 +2252,18 @@
if ( !exec )
return FT_THROW( Could_Not_Find_Context );
+ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
+ {
+ subpixel_hinting = TRUE;
+ grayscale_cleartype = ! FT_BOOL( load_flags & FT_LOAD_TARGET_LCD ||
+ load_flags & FT_LOAD_TARGET_LCD_V );
+ exec->vertical_lcd = FT_BOOL( load_flags & FT_LOAD_TARGET_LCD_V );
+ } else {
+ subpixel_hinting = FALSE;
+ grayscale_cleartype = FALSE;
+ exec->vertical_lcd = FALSE;
+ }
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
@@ -2299,8 +2324,8 @@
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
{
- grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) !=
- FT_RENDER_MODE_MONO );
+ grayscale = FT_BOOL( ! subpixel_hinting &&
+ FT_LOAD_TARGET_MODE( load_flags ) != FT_RENDER_MODE_MONO );
}
error = TT_Load_Context( exec, face, size );
@@ -2338,6 +2363,28 @@
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
{
+ /* a change from mono to subpixel rendering (and vice versa) */
+ /* requires a re-execution of the CVT program */
+ if ( subpixel_hinting != exec->subpixel_hinting )
+ {
+ FT_TRACE4(( "tt_loader_init: subpixel hinting change,"
+ " re-executing `prep' table\n" ));
+
+ exec->subpixel_hinting = subpixel_hinting;
+ 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;
+ }
+
/* a change from mono to grayscale rendering (and vice versa) */
/* requires a re-execution of the CVT program */
if ( grayscale != exec->grayscale )
diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h
index e5a02b9..4812d00 100644
--- a/src/truetype/ttinterp.h
+++ b/src/truetype/ttinterp.h
@@ -247,7 +247,104 @@ 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? */
+ FT_Bool grayscale; /* Bi-level hinting and grayscale
+ rendering */
+
+ /* 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).
+ *
+ * FreeType employs a small list of font-agnostic hacks to bludgeon
+ * non-native-ClearType fonts (except tricky ones[1]) into submission.
+ * FreeType 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 will be ignored if the freedom vector
+ * is parallel to the X-axis (cf. Direct_Move() and Direct_Move_X()).
+ * This has the smallest code footprint and single biggest effect.
+ * - Glyph instruction execution will stop after both IUP[x] and IUP[y]
+ * were called (c.f. TT_RunIns()).
+ * - All rounding instructions are turned into Round_None if the freedom
+ * vector is parallel to the X-axis (c.f. Round_*()).
+ * - The hdmx table and modifications to phantom points are ignored.
+ * Bearings and advance widths remain unchanged (except rounding them
+ * outside the interpreter!). C.f. compute_glyph_metrics() and
+ * TT_Hint_Glyph().
+ * - SHPIX and DELTAP will not execute unless moving a composite on the
+ * Y-axis or moving a previously Y-touched point. SHPIX will additionally
+ * deny movement on the X-axis (c.f. Ins_SHPIX() and Ins_DELTAP()).
+ *
+ * Minor hacks:
+ * - SHP will deny movement on the X-axis (c.f. Ins_SHP())
+ * - MSIRP, MIAP, MDRP and MIRP will see a control value cut-in and minimum
+ * distance of 0 if the freedom vector is parallel to the X-axis (c.f.
+ * Ins_MSIRP(), Ins_MIAP(), Ins_MDRP() and Ins_MIRP()).
+ * - ALIGNRP will not execute after IUP[x] has been called (c.f.
+ * Ins_ALIGNRP()).
+ *
+ * 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-display, the worse the
+ * results.
+ *
+ * Microsoft defines a way to turn off backwards compatibility and
+ * interpret instructions as before ("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 v38 interpreter assumes backwards
+ * compatibility by default, which can be turned of the same way by
+ * executing the following in the control program:
+ *
+ * #PUSH 4,3
+ * INSTCTRL[]
+ *
+ * (cf. Ins_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 (v38 with
+ * backwards compatibility turned off). C.f. TT_RunIns().
+ * [2]: Proposed by Microsoft's Greg Hitchcock in
+ * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx#Toc227035738
+ * [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.
+ */
+ FT_Bool subpixel_hinting; /* Using v38 implies this. */
+ FT_Bool vertical_lcd; /* long side of LCD subpixel */
+ /* rectangles is horizontal */
+
+ /* Defaults to true with v38 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;
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
TT_Round_Func func_round_sphn; /* subpixel rounding function */
diff --git a/src/truetype/ttobjs.c b/src/truetype/ttobjs.c
index a05f216..953931f 100644
--- a/src/truetype/ttobjs.c
+++ b/src/truetype/ttobjs.c
@@ -1286,12 +1286,7 @@
#ifdef TT_USE_BYTECODE_INTERPRETER
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;
-#endif
#else /* !TT_USE_BYTECODE_INTERPRETER */
--
2.5.0
>From 6b0fee13604f9cf208d959a2bdac914345d26a7f Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler <madig...@gmail.com>
Date: Thu, 10 Mar 2016 16:38:53 +0100
Subject: [PATCH 2/2] Implement v38 bytecode interpreter and make it the
default [2/2]
Implement the changes in the interpreter.
---
src/truetype/ttinterp.c | 291 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 279 insertions(+), 12 deletions(-)
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
index ccbb1d7..070dc1c 100644
--- a/src/truetype/ttinterp.c
+++ b/src/truetype/ttinterp.c
@@ -1642,6 +1642,10 @@
/* <InOut> */
/* zone :: The affected glyph zone. */
/* */
+ /* <Note> */
+ /* Will not move points on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. Will still `touch' the point. */
+ /* */
static void
Direct_Move( TT_ExecContext exc,
TT_GlyphZone zone,
@@ -1660,6 +1664,9 @@
( !exc->ignore_x_mode ||
( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Non-native ClearType fonts cannot be trusted to responsibly move
+ * points on the X-axis. */
+ if ( ! exc->backwards_compatibility )
zone->cur[point].x += FT_MulDiv( distance, v, exc->F_dot_P );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
@@ -1721,6 +1728,9 @@
/* The following versions are used whenever both vectors are both */
/* along one of the coordinate unit vectors, i.e. in 90% of the cases. */
/* */
+ /* Will not move points on the X-axis if using the v38 interpreter */
+ /* in backwards compatibility mode. Will still `touch' the point. */
+ /* */
/*************************************************************************/
@@ -1736,6 +1746,10 @@
if ( !SUBPIXEL_HINTING ||
!exc->ignore_x_mode )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Some non-native ClearType fonts seem to shuffle x-coordinates around,
+ * presumably to clean up rounding results. This creates dents in the
+ * outline. Deny, but still `touch' point so IUP works as expected. */
+ if ( ! exc->backwards_compatibility )
zone->cur[point].x += distance;
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
@@ -1853,6 +1867,10 @@
/* <Return> */
/* Rounded distance. */
/* */
+ /* <Note> */
+ /* Will not round on the X-axis if using the v38 interpreter */
+ /* in backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -1860,8 +1878,13 @@
{
FT_F26Dot6 val;
- FT_UNUSED( exc );
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
if ( distance >= 0 )
{
@@ -1896,6 +1919,10 @@
/* <Return> */
/* Rounded distance. */
/* */
+ /* <Note> */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_To_Half_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -1903,8 +1930,13 @@
{
FT_F26Dot6 val;
- FT_UNUSED( exc );
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
if ( distance >= 0 )
{
@@ -1939,6 +1971,10 @@
/* <Return> */
/* Rounded distance. */
/* */
+ /* <Note> */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_Down_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -1946,8 +1982,13 @@
{
FT_F26Dot6 val;
- FT_UNUSED( exc );
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
if ( distance >= 0 )
{
@@ -1982,6 +2023,10 @@
/* <Return> */
/* Rounded distance. */
/* */
+ /* <Note> */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_Up_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -1989,8 +2034,13 @@
{
FT_F26Dot6 val;
- FT_UNUSED( exc );
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
if ( distance >= 0 )
{
@@ -2025,6 +2075,10 @@
/* <Return> */
/* Rounded distance. */
/* */
+ /* <Note> */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_To_Double_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -2032,8 +2086,13 @@
{
FT_F26Dot6 val;
- FT_UNUSED( exc );
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
if ( distance >= 0 )
{
@@ -2074,6 +2133,9 @@
/* the description of super round that we should add the compensation */
/* before rounding. */
/* */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_Super( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -2082,6 +2144,13 @@
FT_F26Dot6 val;
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
+
if ( distance >= 0 )
{
val = ( distance - exc->phase + exc->threshold + compensation ) &
@@ -2123,6 +2192,9 @@
/* There is a separate function for Round_Super_45() as we may need */
/* greater precision. */
/* */
+ /* Will not round on the X-axis if using the v38 interpreter in */
+ /* backwards compatibility mode. */
+ /* */
static FT_F26Dot6
Round_Super_45( TT_ExecContext exc,
FT_F26Dot6 distance,
@@ -2131,6 +2203,13 @@
FT_F26Dot6 val;
+ /* Given ClearType's virtual increase of resolution on the X-axis, rounding
+ * to the physical pixel grid there doesn't make much sense. Native ClearType
+ * fonts know this, the rest (where backwards compatibility applies) may or
+ * may not. Short out rounding if freedom vector is parallel to X-axis. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ return Round_None( exc, distance, compensation );
+
if ( distance >= 0 )
{
val = ( ( distance - exc->phase + exc->threshold + compensation ) /
@@ -5049,6 +5128,11 @@
if ( K == 3 )
exc->ignore_x_mode = FT_BOOL( L == 4 );
#endif
+ /* Native ClearType fonts sign a waiver that turns off all backwards
+ * compatibility hacks and lets them program points to the grid like it's
+ * 1996. They might sign a waiver for just one glyph though. */
+ if ( K == 3 )
+ exc->backwards_compatibility = ! FT_BOOL( L == 4 );
}
@@ -5337,7 +5421,12 @@
Move_Zp2_Point( exc, point, 0, dy, TRUE );
else
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
- Move_Zp2_Point( exc, point, dx, dy, TRUE );
+ /* Stop movement on the X-axis when using the v38 interpreter in
+ * backwards compatibility mode. Prevents dents in outlines. */
+ if ( exc->backwards_compatibility )
+ Move_Zp2_Point( exc, point, 0, dy, TRUE );
+ else
+ Move_Zp2_Point( exc, point, dx, dy, TRUE );
exc->GS.loop--;
}
@@ -5468,7 +5557,6 @@
FT_Int B1, B2;
#endif
-
if ( exc->top < exc->GS.loop + 1 )
{
if ( exc->pedantic_hinting )
@@ -5574,7 +5662,22 @@
#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
- Move_Zp2_Point( exc, point, dx, dy, TRUE );
+ /* In backwards compatibility mode, block SHPIX under the same
+ * circumstances as DELTAP and additionally stop movement on the X-axis.
+ * This prevents dents in outlines by overeager designers back in the bad
+ * old days ("superhinting").
+ *
+ * XXX: breaks Rokkitt < v1.2 (glyphs explode vertically on ALIGNRP, but
+ * blocking ALIGNRP after SHPIX disables all hinting).
+ */
+ if ( exc->backwards_compatibility )
+ {
+ if ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
+ ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) )
+ Move_Zp2_Point( exc, point, 0, dy, TRUE );
+ }
+ else
+ Move_Zp2_Point( exc, point, dx, dy, TRUE );
#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
@@ -5616,6 +5719,17 @@
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ FT_F26Dot6 control_value_cutin = exc->GS.control_value_cutin;
+
+ /* From "Backwards Compatibility of TrueType Instructions with Microsoft
+ * ClearType" by Greg Hitchcock, 2009: "Some fonts pre-calculate stroke
+ * weights and subsequently use MSIRP[.], which involves neither rounding
+ * nor CVT cut-ins. Therefore MSIRP[.] now respects the CVT cut-in". Cut-in
+ * is set to zero because there's no movement on the X-axis anyway
+ * ("infinite resolution"). */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ control_value_cutin = 0;
+
point = (FT_UShort)args[0];
if ( BOUNDS( point, exc->zp1.n_points ) ||
@@ -5646,6 +5760,10 @@
distance = args[1];
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ if ( exc->backwards_compatibility &&
+ FT_ABS( distance - args[1] ) >= control_value_cutin )
+ distance = args[1];
+
exc->func_move( exc, &exc->zp1, point, args[1] - distance );
exc->GS.rp1 = exc->GS.rp0;
@@ -5738,6 +5856,16 @@
control_value_cutin = 0;
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* From "Backwards Compatibility of TrueType Instructions with Microsoft
+ * ClearType" by Greg Hitchcock, 2009: "In some fonts fractional stroke
+ * weights were calculated and used in MIRP[â¦râ¦]. While this provides some
+ * extra granularity in font smoothing, it unnecessarily quantizes stroke
+ * weights in ClearType, because un-rounded MIRPs now respect CVT cut-in.
+ * [...] with ClearType on, do CVT cut-in always". Cut-in is set to zero
+ * because there's no movement on the X-axis anyway ("infinite resolution"). */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ control_value_cutin = 0;
+
if ( BOUNDS( point, exc->zp0.n_points ) ||
BOUNDSL( cvtEntry, exc->cvtSize ) )
{
@@ -5845,6 +5973,11 @@
minimum_distance = 0;
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Like in MSIRP and MIRP, no movement on the X-axis ("infinite
+ * resolution") means minimum_distance might as well be zero. */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ minimum_distance = 0;
+
point = (FT_UShort)args[0];
if ( BOUNDS( point, exc->zp1.n_points ) ||
@@ -5998,6 +6131,17 @@
control_value_cutin = minimum_distance = 0;
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* From "Backwards Compatibility of TrueType Instructions with Microsoft
+ * ClearType" by Greg Hitchcock, 2009: "In some fonts fractional stroke
+ * weights were calculated and used in MIRP[â¦râ¦]. While this provides some
+ * extra granularity in font smoothing, it unnecessarily quantizes stroke
+ * weights in ClearType, because un-rounded MIRPs now respect CVT cut-in.
+ * [...] with ClearType on, do CVT cut-in always". Cut-in and minimum
+ * distance are set to zero because there's no movement on the X-axis
+ * anyway ("infinite resolution"). */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ control_value_cutin = minimum_distance = 0;
+
/* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
if ( BOUNDS( point, exc->zp1.n_points ) ||
@@ -6106,6 +6250,13 @@
}
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ if ( exc->backwards_compatibility &&
+ exc->GS.gep0 == exc->GS.gep1 )
+ {
+ if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin )
+ cvt_dist = org_dist;
+ }
+
distance = Round_None(
exc,
cvt_dist,
@@ -6211,6 +6362,13 @@
}
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Prevents severe dents in Arial's 'D' and 'G'. */
+ if ( exc->backwards_compatibility && exc->iupx_called )
+ {
+ exc->error = FT_THROW( Invalid_Reference );
+ goto Fail;
+ }
+
if ( exc->top < exc->GS.loop ||
BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
{
@@ -6752,6 +6910,18 @@
}
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Useful to detect attempts at "superhinting", e.g. shuffling outlines
+ * around after IUP to produce a specific black-and-white pixel pattern the
+ * designer wanted to see. Bad for everything except monochrome rendering.
+ */
+ if ( exc->backwards_compatibility )
+ {
+ if ( exc->opcode & 1 )
+ exc->iupx_called = TRUE;
+ else
+ exc->iupy_called = TRUE;
+ }
+
do
{
end_point = exc->pts.contours[contour] - exc->pts.first_point;
@@ -6945,8 +7115,24 @@
}
else
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
-
- exc->func_move( exc, &exc->zp0, A, B );
+ /* Like with SHPIX, DELTAP was/is often used to pop pixels on or
+ * off to produce some specific black-and-white pixel pattern the
+ * designer wanted to see ("superhinting"). Bad for everything
+ * except monochrome rendering. Short out as much as possible in
+ * backwards compatibility mode, except if we are DELTAPing in a
+ * composite glyph and on the Y-axis (e.g. offsetting diacritics
+ * from base glyphs) or if we're moving previously y-touched points
+ * (moves entire outline instead of denting it, that's ok).
+ * Details in "Backwards Compatibility of TrueType Instructions
+ * with Microsoft ClearType" by Greg Hitchcock, 2009.
+ */
+ if ( exc->backwards_compatibility )
+ {
+ if ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
+ ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) )
+ exc->func_move( exc, &exc->zp0, A, B );
+ } else
+ exc->func_move( exc, &exc->zp0, A, B );
}
}
else
@@ -7063,9 +7249,11 @@
FT_Long* args )
{
FT_Long K;
+ TT_Driver driver;
K = 0;
+ driver = (TT_Driver)FT_FACE_DRIVER( exc->face );
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
/********************************/
@@ -7091,7 +7279,7 @@
else
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
if ( ( args[0] & 1 ) != 0 )
- K = TT_INTERPRETER_VERSION_35;
+ K = driver->interpreter_version;
/********************************/
/* GLYPH ROTATED */
@@ -7110,13 +7298,65 @@
K |= 1 << 8;
/********************************/
- /* HINTING FOR GRAYSCALE */
+ /* BI-LEVEL HINTING AND */
+ /* GRAYSCALE RENDERING */
/* Selector Bit: 5 */
/* Return Bit(s): 12 */
/* */
if ( ( args[0] & 32 ) != 0 && exc->grayscale )
K |= 1 << 12;
+
+ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
+ {
+ /********************************/
+ /* HINTING FOR SUBPIXEL */
+ /* Selector Bit: 6 */
+ /* Return Bit(s): 13 */
+ /* */
+ /* v38 will do subpixel hinting by default. */
+ if ( ( args[0] & 64 ) != 0 )
+ K |= 1 << 13;
+
+ /********************************/
+ /* VERTICAL LCD SUBPIXELS? */
+ /* Selector Bit: 8 */
+ /* Return Bit(s): 15 */
+ /* */
+ if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd )
+ K |= 1 << 15;
+
+ /********************************/
+ /* SUBPIXEL POSITIONED? */
+ /* Selector Bit: 10 */
+ /* Return Bit(s): 17 */
+ /* */
+ /* XXX: FreeType supports it, dependant on what client does? */
+ if ( ( args[0] & 1024 ) != 0 )
+ K |= 1 << 17;
+
+ /********************************/
+ /* SYMMETRICAL SMOOTHING */
+ /* Selector Bit: 11 */
+ /* Return Bit(s): 18 */
+ /* */
+ /* The only smoothing method FreeType supports unless someone set
+ * FT_LOAD_TARGET_MONO. */
+ if ( ( args[0] & 2048 ) != 0 )
+ K |= 1 << 18;
+
+ /********************************/
+ /* CLEARTYPE HINTING AND */
+ /* GRAYSCALE RENDERING */
+ /* Selector Bit: 12 */
+ /* Return Bit(s): 19 */
+ /* */
+ /* Grayscale rendering is what FreeType does anyway unless someone set
+ * FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V) */
+ if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype )
+ K |= 1 << 19;
+ }
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( SUBPIXEL_HINTING &&
@@ -7274,6 +7514,8 @@
FT_Long ins_counter = 0; /* executed instructions counter */
FT_UShort i;
+ TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( exc->face );
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
FT_Byte opcode_pattern[1][2] = {
/* #8 TypeMan Talk Align */
@@ -7291,6 +7533,18 @@
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
exc->iup_called = FALSE;
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Toggle backwards compatibility according to what font says, except when
+ * it's a 'tricky' font that heavily relies on the interpreter to render
+ * glyphs correctly, e.g. DFKai-SB. Backwards compatibility hacks may break
+ * it. */
+ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 &&
+ !FT_IS_TRICKY( (&exc->face->root) ) )
+ exc->backwards_compatibility = ! (exc->GS.instruct_control & 4);
+ else
+ exc->backwards_compatibility = FALSE;
+
+ exc->iupx_called = FALSE;
+ exc->iupy_called = FALSE;
/* set PPEM and CVT functions */
exc->tt_metrics.ratio = 0;
@@ -7316,6 +7570,19 @@
do
{
+ /* Stop executing glyph instructions immediately after IUP[x] and IUP[y].
+ *
+ * They usually mark the end of the grid-fitting process. Many fonts
+ * (e.g. Microsoft's core web fonts) will keep going after that, though,
+ * usually to "fix" pixel patterns ("superhinting"). That's not useful in
+ * a subpixel rendering context and can wreck a glyph unless caught by
+ * other backwards compatibility hacks. Stopping execution here means
+ * less work.
+ */
+ if ( exc->backwards_compatibility &&
+ ( exc->iupy_called && exc->iupx_called ) )
+ break;
+
exc->opcode = exc->code[exc->IP];
#ifdef FT_DEBUG_LEVEL_TRACE
--
2.5.0
_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel