>From e487d3464d89a11b7703bd171b765015444bd01c Mon Sep 17 00:00:00 2001
From: Nikolaus Waxweiler
Date: Thu, 10 Mar 2016 19:51:23 +0100
Subject: [PATCH 2/2] Implement v38 bytecode interpreter and make it the
default [2/2]
Implement interpreter changes.
---
src/truetype/ttinterp.c | 283 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 271 insertions(+), 12 deletions(-)
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
index ccbb1d7..fcc168e 100644
--- a/src/truetype/ttinterp.c
+++ b/src/truetype/ttinterp.c
@@ -1642,6 +1642,10 @@
/* */
/* zone :: The affected glyph zone. */
/* */
+ /* */
+ /* 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 @@
/* */
/* Rounded distance. */
/* */
+ /* */
+ /* 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 @@
/* */
/* Rounded distance. */
/* */
+ /* */
+ /* 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 @@
/* */
/* Rounded distance. */
/* */
+ /* */
+ /* 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 @@
/* */
/* Rounded distance. */
/* */
+ /* */
+ /* 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 @@
/* */
/* Rounded distance. */
/* */
+ /* */
+ /* 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,23 @@
#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->iupx_called && exc->iupy_called ) &&
+ ( ( 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 +5720,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 +5761,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 +5857,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 +5974,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 +6132,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 +6251,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,
@@ -6219,6 +6371,16 @@
goto Fail;
}
+ /* Prevents severe dents in Arial's 'D' and 'G' and a few Times New Roman
+ * glyphs, among others. Only block after both IUP[x] and IUP[y] are
+ * called, results in distortion otherwise. The error is important, results
+ * in distortion otherwise. */
+ if ( exc->backwards_compatibility && exc->iupx_called && exc->iupy_called )
+ {
+ exc->error = FT_THROW( Invalid_Reference );
+ goto Fail;
+ }
+
while ( exc->GS.loop > 0 )
{
exc->args--;
@@ -6752,6 +6914,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 +7119,25 @@
}
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->iupx_called && exc->iupy_called ) &&
+ ( ( 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 +7254,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 +7284,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 +7303,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 +7519,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 +7538,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;
--
2.5.0