v7: Clean up everything that got through the backwards compatibility
mode by resetting all x-coordinates to their previous positions in
TT_Hint_Glyph(). Only do this for glyphs that are instructed, otherwise
we get garbled output.
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..7689595 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -815,11 +815,26 @@
#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];
+ } else {
+ FT_UShort n;
+
+ /* Clean up everything that got through the backwards compatibility mode
+ * by resetting all x-coordinates to their previous positions. Only do
+ * this for glyphs that are instructed, otherwise we get garbled output.
+ */
+ if ( n_ins > 0 )
+ for ( n = 0; n < zone->n_points - 4; n++ )
+ zone->cur[n].x = zone->org[n].x;
+ }
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
@@ -1969,10 +1984,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 +2205,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 +2213,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 +2224,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 +2262,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 +2334,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 +2373,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: 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..218aa46 100644
--- a/src/truetype/ttinterp.h
+++ b/src/truetype/ttinterp.h
@@ -247,7 +247,68 @@ 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). Microsoft describes a way to turn
+ * off backwards compatibility and interpret instructions as before
+ * ("native ClearType")[1]. 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[2].
+ *
+ * Of the hacks implemented in FreeType, ignoring any point movement on the
+ * X-axis if the freedom vector is parallel to the X-axis has the smallest
+ * code footprint and single biggest effect (cf. Direct_Move() and
+ * Direct_Move_X()). 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. The harder the designer tried to produce very specific
+ * black-and-white pixel patterns ("superhinting") for
+ * pre-ClearType-display, the worse the results. Most web fonts seen in the
+ * wild are made for ClearType display.
+ *
+ * The v38 interpreter assumes backwards compatibility by default. Fonts
+ * can turn it off and go "native ClearType" by using the following
+ * bytecode sequence at the beginning of the CVT program[1]:
+ *
+ * #PUSH 4,3
+ * INSTCTRL[]
+ *
+ * (cf. Ins_INSTCTRL()).
+ *
+ * [1]: Proposed by Microsoft's Greg Hitchcock in
+ * https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx#Toc227035738
+ * [2]: 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 */
+
+ FT_Bool backwards_compatibility; /* Defaults to true with v38
+ interpreter. If this is
+ false, it implies the
+ interpreter is in v35 or in
+ native ClearType mode. */
+ FT_Bool iup_called; /* Useful for detecting and denying post-iup trickery. */
+
+ /* 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 cd4e294..4b43bb4 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 */
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
index ccbb1d7..c0da814 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--;
}
@@ -5574,7 +5663,13 @@
#else /* !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 by overeager
+ * designers back in the bad old days ("superhinting"). */
+ if ( exc->backwards_compatibility )
+ 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 +5711,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 +5752,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 +5848,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 +5965,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 +6123,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 +6242,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 +6354,13 @@
}
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ /* Prevents severe dents in Arial's 'D' and 'G'. */
+ if ( exc->backwards_compatibility && exc->iup_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 +6902,13 @@
}
#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 )
+ exc->iup_called = TRUE;
+
do
{
end_point = exc->pts.contours[contour] - exc->pts.first_point;
@@ -6945,8 +7102,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 +7236,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 +7266,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 +7285,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 +7501,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 +7520,17 @@
#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->iup_called = FALSE;
/* set PPEM and CVT functions */
exc->tt_metrics.ratio = 0;
_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel