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

Reply via email to