diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index 4ab6603..87592a6 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -1945,6 +1945,7 @@ #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); #endif + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); FT_BBox bbox; FT_Fixed y_scale; @@ -1969,10 +1970,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. Interpreter v38 uses subpixel + * hinting and indicates subpixel positioning and therefore ignores any + * changes to the horizontal advance width. XXX: does this clash with any + * non-bytecode-advance-width-changing-feature? */ + if ( driver->interpreter_version != TT_INTERPRETER_VERSION_38 && + !face->postscript.isFixedPitch && + IS_HINTED( loader->load_flags ) && !( loader->load_flags & FT_LOAD_COMPUTE_METRICS ) ) { FT_Byte* widthp; @@ -2186,6 +2191,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 +2199,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 +2210,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 +2248,16 @@ 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 ); + } else { + subpixel_hinting = FALSE; + grayscale_cleartype = FALSE; + } + #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) @@ -2299,8 +2318,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 +2357,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.c b/src/truetype/ttinterp.c index ccbb1d7..edfe021 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--; } @@ -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,57 @@ 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; + + /********************************/ + /* 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 +7493,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 +7512,13 @@ #ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING exc->iup_called = FALSE; #endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + /* Toggle backwards compatibility according to what font says */ + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) + 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; diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h index e5a02b9..79ad1ab 100644 --- a/src/truetype/ttinterp.h +++ b/src/truetype/ttinterp.h @@ -247,7 +247,62 @@ 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 backwards_compatibility; /* Defaults to true with v38 interpreter. */ + FT_Bool iup_called; /* Useful for detecting and denying post-iup trickery. */ + + /* ClearType hinting and grayscale rendering, as used 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 */