[PATCH 1/3] gdi32: Fix the truetype interpreter version problem.

Byeongsik Jeon bsjeon at hanmail.net
Sun Nov 4 18:23:24 CST 2018


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639
Signed-off-by: Byeongsik Jeon <bsjeon at hanmail.net>
---
This is also a problem in Linux. It just seemed to stand out in 
MacOS(Tahoma builtined) XQuartz2.7.11 Freetype2.7(subpixel turned off).

The truetype bytecode interpreter of the Freetype appears to have the 
following problems depending on the version.
- v35: MS cleartype font(ex. Consolas) rendering is ugly.
- v40: MS legacy font (ex. Tahoma) rendering is ugly. Some fonts return 
the wrong values even for the glyph metrics. 

Is this a Freetype bug? Bagically NOT.

https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions#get-information

The bytecode interptreter is a stack-based virtual maching the interprets 
the truetype bytecode instructions. Ths bytecode program receives the 
interpreter version information through the GETINFO instruction. GETINFO 
also provided the information of the fontsmoothing, cleartype, etc.
The bytecode program actually changes the location of the glyph points 
based on this information.

So, what looks like a bug above is that the wrong version information was 
delivered to the bytecode program. To determine this version value, this 
patch use the gasp table version. This idea is inspired by the following 
documentation.

https://www.freetype.org/freetype2/docs/reference/ft2-properties.html#TT_INTERPRETER_VERSION_XXX
--
Note that ‘Gray ClearType’ is essentially the same as v1.6's 
grayscale rendering. However, the GETINFO instruction handles it 
differently: v1.6 returns bit 12 (hinting for grayscale), while v2.1 
returns bits 13 (hinting for ClearType), 18 (symmetrical smoothing), and 
19 (Gray ClearType). Also, this mode respects bits 2 and 3 for the 
version 1 gasp table exclusively (like Color ClearType), while v1.6 only 
respects the values of version 0 (bits 0 and 1).
--
I think the maximum value that can be applied to the gasp version 0 font
is the interpreter version 37.

The Freetype interpreter version 38 includes tweaks based on the font
names. I was able to use the v38 by modifying the build option of the
Freetype.
#define TT_CONFIG_OPTION_SUBPIXEL_HINTING     ( 1 | 2 )

This allows the gasp version 0 fonts to be rendered more similar to the 
Windows old cleartype in the subpixel rendering mode. However, this is 
not applied because the build option must be modified and the Freetype 
document is marked as a feature to be deleted.

To quickly check changes with the Freetype version up, I have added the
WINE_GDI_PROPERTIES code.
WINE_GDI_PROPERTIES="truetype:interpreter-version=35" winecfg


 dlls/gdi32/freetype.c | 69 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 64 insertions(+), 5 deletions(-)

diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index 07de6f2f6c..1e54e65ed4 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -92,6 +92,15 @@
 #ifdef FT_LCD_FILTER_H
 #include FT_LCD_FILTER_H
 #endif
+
+#ifdef FT_DRIVER_H
+#include FT_DRIVER_H
+#else
+#ifdef FT_TRUETYPE_DRIVER_H /* Deprecated since version 2.9 */
+#include FT_TRUETYPE_DRIVER_H
+#endif
+#endif
+
 #endif /* HAVE_FT2BUILD_H */
 
 #include "windef.h"
@@ -130,6 +139,7 @@ typedef struct
 } FT_Version_t;
 static FT_Version_t FT_Version;
 static DWORD FT_SimpleVersion;
+static FT_UInt forced_interpreter_version = 0;
 
 static void *ft_handle = NULL;
 
@@ -158,6 +168,8 @@ MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
 MAKE_FUNCPTR(FT_Outline_Get_CBox);
 MAKE_FUNCPTR(FT_Outline_Transform);
 MAKE_FUNCPTR(FT_Outline_Translate);
+MAKE_FUNCPTR(FT_Property_Get);
+MAKE_FUNCPTR(FT_Property_Set);
 MAKE_FUNCPTR(FT_Render_Glyph);
 MAKE_FUNCPTR(FT_Set_Charmap);
 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
@@ -399,6 +411,7 @@ struct tagGdiFont {
     DWORD total_kern_pairs;
     KERNINGPAIR *kern_pairs;
     struct list child_fonts;
+    FT_UInt interpreter_version;
 
     /* the following members can be accessed without locking, they are never modified after creation */
     FT_Face ft_face;
@@ -4101,6 +4114,20 @@ static void update_font_info(void)
     }
 }
 
+static void set_forced_interpreter_version(void)
+{
+    static const char property_name[] = "truetype:interpreter-version=";
+    const char *env;
+
+    env = getenv( "WINE_GDI_PROPERTIES" );
+    if ( env && (env = strstr( env, property_name )) )
+    {
+        forced_interpreter_version = atoi( env + sizeof(property_name) - 1 );
+
+        TRACE( "forced_interpreter_version = %d\n" , forced_interpreter_version );
+    }
+}
+
 static BOOL init_freetype(void)
 {
     ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
@@ -4137,6 +4164,8 @@ static BOOL init_freetype(void)
     LOAD_FUNCPTR(FT_Outline_Get_CBox)
     LOAD_FUNCPTR(FT_Outline_Transform)
     LOAD_FUNCPTR(FT_Outline_Translate)
+    LOAD_FUNCPTR(FT_Property_Get)
+    LOAD_FUNCPTR(FT_Property_Set)
     LOAD_FUNCPTR(FT_Render_Glyph)
     LOAD_FUNCPTR(FT_Set_Charmap)
     LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
@@ -4164,6 +4193,8 @@ static BOOL init_freetype(void)
                        ((FT_Version.minor <<  8) & 0x00ff00) |
                        ((FT_Version.patch      ) & 0x0000ff);
 
+    set_forced_interpreter_version();
+
     font_driver = &freetype_funcs;
     return TRUE;
 
@@ -5153,7 +5184,7 @@ static FT_Encoding pick_charmap( FT_Face face, int charset )
     return *encs;
 }
 
-static BOOL get_gasp_flags( GdiFont *font, WORD *flags )
+static BOOL get_gasp_flags( GdiFont *font, WORD *flags, WORD *gasp_version )
 {
     DWORD size;
     WORD buf[16]; /* Enough for seven ranges before we need to alloc */
@@ -5161,7 +5192,8 @@ static BOOL get_gasp_flags( GdiFont *font, WORD *flags )
     WORD num_recs, version;
     BOOL ret = FALSE;
 
-    *flags = 0;
+    if ( !flags && !gasp_version ) return FALSE;
+
     size = get_font_data( font, MS_GASP_TAG,  0, NULL, 0 );
     if (size == GDI_ERROR) return FALSE;
     if (size < 4 * sizeof(WORD)) return FALSE;
@@ -5182,6 +5214,13 @@ static BOOL get_gasp_flags( GdiFont *font, WORD *flags )
         goto done;
     }
 
+    if (gasp_version)
+        *gasp_version = version;
+
+    ret = TRUE;
+    if (!flags) goto done;
+
+    *flags = 0;
     while (num_recs--)
     {
         *flags = GET_BE_WORD( *(ptr + 1) );
@@ -5189,7 +5228,6 @@ static BOOL get_gasp_flags( GdiFont *font, WORD *flags )
         ptr += 2;
     }
     TRACE( "got flags %04x for ppem %d\n", *flags, font->ft_face->size->metrics.y_ppem );
-    ret = TRUE;
 
 done:
     HeapFree( GetProcessHeap(), 0, alloced );
@@ -5779,6 +5817,8 @@ found_face:
 done:
     if (ret)
     {
+        WORD gasp_version;
+
         PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
 
         switch (lf.lfQuality)
@@ -5810,7 +5850,7 @@ done:
                 if ((!antialias_fakes || (!ret->fake_bold && !ret->fake_italic)) && is_hinting_enabled())
                 {
                     WORD gasp_flags;
-                    if (get_gasp_flags( ret, &gasp_flags ) && !(gasp_flags & GASP_DOGRAY))
+                    if (get_gasp_flags( ret, &gasp_flags, NULL ) && !(gasp_flags & GASP_DOGRAY))
                     {
                         TRACE( "font %s %d aa disabled by GASP\n",
                                debugstr_w(lf.lfFaceName), lf.lfHeight );
@@ -5819,7 +5859,15 @@ done:
                 }
             }
         }
-        TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags );
+
+        if (!forced_interpreter_version && get_gasp_flags( ret, NULL, &gasp_version ))
+            ret->interpreter_version = gasp_version == 0 ? TT_INTERPRETER_VERSION_35
+                                                         : TT_INTERPRETER_VERSION_40;
+        else
+            ret->interpreter_version = forced_interpreter_version;
+
+        TRACE( "%p %s %d aa_flags %x interpreter_version %d\n", hfont,
+               debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags, ret->interpreter_version );
         release_font( physdev->font );
         physdev->font = ret;
     }
@@ -7001,6 +7049,17 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
     if (needsTransform || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP;
     if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT;
 
+    /* FT_Property_Set and FT_Load_Glyph must be in the same locking section. */
+    if (font->interpreter_version && pFT_Property_Set && pFT_Property_Get)
+    {
+        FT_UInt interpreter_version = 0;
+
+        pFT_Property_Set( library, "truetype", "interpreter-version", &font->interpreter_version );
+        pFT_Property_Get( library, "truetype", "interpreter-version", &interpreter_version );
+        if ( interpreter_version != font->interpreter_version )
+            FIXME( "truetype:interpreter-version: expected %d, got %d\n",
+                   font->interpreter_version, interpreter_version );
+    }
     err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
 
     if(err) {
-- 
2.19.1




More information about the wine-devel mailing list