This is funny. I think I found a way to make bytecode rendering similar
to the autohinter in smoothness, it even helps making old fonts look
less ugly. in Ins_MIRP, I added:
if ( exc->backwards_compatibility && exc->GS.freeVector.x == 0x0 )
{
control_value_cutin = control_value_cutin / 2;
minimum_distance = minimum_distance / 2;
}
In Ins_MDRP, I added:
if ( exc->backwards_compatibility && exc->GS.freeVector.x == 0x0 )
{
minimum_distance = minimum_distance / 2;
}
(I know both values are actually FT_F26Dot6 but this works for my
proof-of-concept)
I'm browsing around the net and on https://typekit.com/fonts and am
quite pleased with what I see :D
I attached the new full patch again. It sure looks like I have to have a
harder look at this and find out if I should change more minimum
distances and CVT cutin values.
Uh, should I push a branch to GitHub?
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index 4ab6603..5a68052 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,18 @@
if ( !exec )
return FT_THROW( Could_Not_Find_Context );
+ if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
+ {
+ subpixel_hinting = TRUE;
+ exec->backwards_compatibility = ! (exec->GS.instruct_control & 4) ;
+ grayscale_cleartype = ! FT_BOOL( load_flags & FT_LOAD_TARGET_LCD ||
+ load_flags & FT_LOAD_TARGET_LCD_V );
+ } else {
+ subpixel_hinting = FALSE;
+ exec->backwards_compatibility = FALSE;
+ grayscale_cleartype = FALSE;
+ }
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
@@ -2299,8 +2320,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 +2359,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 1e34151..e539cda 100644
--- a/src/truetype/ttinterp.c
+++ b/src/truetype/ttinterp.c
@@ -1698,6 +1698,7 @@
( !exc->ignore_x_mode ||
( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y != 0x0 )
zone->cur[point].x += FT_MulDiv( distance, v, exc->F_dot_P );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
@@ -1778,6 +1779,7 @@
if ( !SUBPIXEL_HINTING ||
!exc->ignore_x_mode )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y != 0x0 )
zone->cur[point].x += distance;
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
@@ -5176,6 +5178,8 @@
if ( K == 3 )
exc->ignore_x_mode = FT_BOOL( L == 4 );
#endif
+ if ( K == 3 )
+ exc->backwards_compatibility = ! FT_BOOL( L == 4 );
}
@@ -5440,7 +5444,8 @@
if ( exc->GS.freeVector.x != 0 )
{
- exc->zp2.cur[point].x += dx;
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y != 0x0 )
+ exc->zp2.cur[point].x += dx;
if ( touch )
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
}
@@ -5911,6 +5916,9 @@
cvtEntry = (FT_ULong)args[1];
point = (FT_UShort)args[0];
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ control_value_cutin = 0;
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( SUBPIXEL_HINTING &&
exc->ignore_x_mode &&
@@ -6019,6 +6027,14 @@
minimum_distance = exc->GS.minimum_distance;
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ minimum_distance = 0;
+
+ if ( exc->backwards_compatibility && exc->GS.freeVector.x == 0x0 )
+ {
+ minimum_distance = minimum_distance / 2;
+ }
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( SUBPIXEL_HINTING &&
exc->ignore_x_mode &&
@@ -6172,6 +6188,15 @@
point = (FT_UShort)args[0];
cvtEntry = (FT_ULong)( args[1] + 1 );
+ if ( exc->backwards_compatibility && exc->GS.freeVector.y == 0x0 )
+ control_value_cutin = minimum_distance = 0;
+
+ if ( exc->backwards_compatibility && exc->GS.freeVector.x == 0x0 )
+ {
+ control_value_cutin = control_value_cutin / 2;
+ minimum_distance = minimum_distance / 2;
+ }
+
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( SUBPIXEL_HINTING &&
exc->ignore_x_mode &&
@@ -7286,9 +7311,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
/********************************/
@@ -7314,7 +7341,7 @@
else
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
if ( ( args[0] & 1 ) != 0 )
- K = TT_INTERPRETER_VERSION_35;
+ K = driver->interpreter_version;
/********************************/
/* GLYPH ROTATED */
@@ -7333,13 +7360,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 &&
diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h
index e5a02b9..3e2503f 100644
--- a/src/truetype/ttinterp.h
+++ b/src/truetype/ttinterp.h
@@ -247,7 +247,52 @@ 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. 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 by the 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(), also Move_Zp2_Point()). The best results are achieved
+ * for fonts that were from the outset designed with ClearType in mind,
+ * "classically" hinted fonts like Arial and Times fare far worse.
+ *
+ * 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;
+ FT_Bool backwards_compatibility;
+
+ /* ClearType hinting and grayscale rendering. Different from bi-level
+ * hinting and grayscale rendering! */
+ 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 419ce35..1fe11f7 100644
--- a/src/truetype/ttobjs.c
+++ b/src/truetype/ttobjs.c
@@ -1308,12 +1308,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 */
_______________________________________________
Freetype-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/freetype-devel