Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net --- v2: Fix the wine-tests build error at the debian9(Freetype v2.6.3). Because the build-time check is meaningless, v2 has modified it to check the supported interpreter version at the runtime.
v1: 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? Basically NOT.
https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions#ge...
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_INT... -- 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 | 104 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 5 deletions(-)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index 07de6f2f6c..dd94d22db5 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -130,6 +130,10 @@ typedef struct } FT_Version_t; static FT_Version_t FT_Version; static DWORD FT_SimpleVersion; +static FT_UInt forced_interpreter_version = 0; +static BOOL interpreter_v35_supported = FALSE; +static BOOL interpreter_v38_supported = FALSE; +static BOOL interpreter_v40_supported = FALSE;
static void *ft_handle = NULL;
@@ -158,6 +162,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 +405,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 +4108,49 @@ 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 void diagnose_freetype(void) +{ + if (pFT_Property_Set && pFT_Property_Get) + { + FT_UInt set_version, get_version; + + set_version = 35; + get_version = 0; + pFT_Property_Set( library, "truetype", "interpreter-version", &set_version ); + pFT_Property_Get( library, "truetype", "interpreter-version", &get_version ); + if (get_version == set_version) + interpreter_v35_supported = TRUE; + + set_version = 38; + get_version = 0; + pFT_Property_Set( library, "truetype", "interpreter-version", &set_version ); + pFT_Property_Get( library, "truetype", "interpreter-version", &get_version ); + if (get_version == set_version) + interpreter_v38_supported = TRUE; + + set_version = 40; + get_version = 0; + pFT_Property_Set( library, "truetype", "interpreter-version", &set_version ); + pFT_Property_Get( library, "truetype", "interpreter-version", &get_version ); + if (get_version == set_version) + interpreter_v40_supported = TRUE; + } +} + static BOOL init_freetype(void) { ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0); @@ -4137,6 +4187,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 +4216,9 @@ static BOOL init_freetype(void) ((FT_Version.minor << 8) & 0x00ff00) | ((FT_Version.patch ) & 0x0000ff);
+ diagnose_freetype(); + set_forced_interpreter_version(); + font_driver = &freetype_funcs; return TRUE;
@@ -5153,7 +5208,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 +5216,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 +5238,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 +5252,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 +5841,8 @@ found_face: done: if (ret) { + WORD gasp_version; + PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
switch (lf.lfQuality) @@ -5810,7 +5874,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 +5883,26 @@ done: } } } - TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags ); + + if (forced_interpreter_version == 0 && get_gasp_flags( ret, NULL, &gasp_version )) + { + ret->interpreter_version = 0; + + if (gasp_version == 1) + { + if (interpreter_v40_supported) + ret->interpreter_version = 40; + else if (interpreter_v38_supported) + ret->interpreter_version = 38; + } + else if (interpreter_v35_supported) + ret->interpreter_version = 35; + } + 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 +7084,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) {
Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net --- dlls/gdi32/dibdrv/dibdrv.h | 3 +- dlls/gdi32/dibdrv/graphics.c | 28 ++++++++++- dlls/gdi32/dibdrv/primitives.c | 88 ++++++++++++++++++++++------------ 3 files changed, 85 insertions(+), 34 deletions(-)
diff --git a/dlls/gdi32/dibdrv/dibdrv.h b/dlls/gdi32/dibdrv/dibdrv.h index 02164c9692..a5ff595496 100644 --- a/dlls/gdi32/dibdrv/dibdrv.h +++ b/dlls/gdi32/dibdrv/dibdrv.h @@ -87,6 +87,7 @@ struct intensity_range
struct font_intensities { + int max_level; struct intensity_range ranges[17]; struct font_gamma_ramp *gamma_ramp; }; @@ -196,7 +197,7 @@ typedef struct primitive_funcs void (* mask_rect)(const dib_info *dst, const RECT *rc, const dib_info *src, const POINT *origin, int rop2); void (* draw_glyph)(const dib_info *dst, const RECT *rc, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges); + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity); void (* draw_subpixel_glyph)(const dib_info *dst, const RECT *rc, const dib_info *glyph, const POINT *origin, DWORD text_pixel, const struct font_gamma_ramp *gamma_ramp); DWORD (* get_pixel)(const dib_info *dib, int x, int y); diff --git a/dlls/gdi32/dibdrv/graphics.c b/dlls/gdi32/dibdrv/graphics.c index 7d71fbb12a..9e10ffd4df 100644 --- a/dlls/gdi32/dibdrv/graphics.c +++ b/dlls/gdi32/dibdrv/graphics.c @@ -708,7 +708,7 @@ static void draw_glyph( dib_info *dib, int x, int y, const GLYPHMETRICS *metrics text_color, intensity->gamma_ramp ); else dib->funcs->draw_glyph( dib, &clipped_rect, glyph_dib, &src_origin, - text_color, intensity->ranges ); + text_color, intensity ); } } } @@ -734,6 +734,29 @@ static int get_glyph_depth( UINT aa_flags ) } }
+static int get_glyph_max_level( UINT aa_flags ) +{ + switch (aa_flags) + { + case GGO_BITMAP: /* we'll convert to GGO_GRAY4_BITMAP format */ + case GGO_GRAY4_BITMAP: return 16; + + /* FIXME: confirm the native Windows. */ + case GGO_GRAY2_BITMAP: return 16; + case GGO_GRAY8_BITMAP: return 16; + + case WINE_GGO_HRGB_BITMAP: + case WINE_GGO_HBGR_BITMAP: + case WINE_GGO_VRGB_BITMAP: + case WINE_GGO_VBGR_BITMAP: + case WINE_GGO_GRAY16_BITMAP: return 255; + + default: + ERR("Unexpected flags %08x\n", aa_flags); + return 0; + } +} + static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; static const int padding[4] = {0, 3, 2, 1};
@@ -826,7 +849,8 @@ static void render_string( DC *dc, dib_info *dib, struct cached_font *font, INT
text_color = get_pixel_color( dc, dib, dc->textColor, TRUE );
- if (glyph_dib.bit_count == 32) + intensity.max_level = get_glyph_max_level( font->aa_flags ); + if (intensity.max_level == 255) intensity.gamma_ramp = dc->font_gamma_ramp; else get_aa_ranges( dib->funcs->pixel_to_colorref( dib, text_color ), intensity.ranges ); diff --git a/dlls/gdi32/dibdrv/primitives.c b/dlls/gdi32/dibdrv/primitives.c index 01a1c7c1d8..e21c1d6ca7 100644 --- a/dlls/gdi32/dibdrv/primitives.c +++ b/dlls/gdi32/dibdrv/primitives.c @@ -6059,18 +6059,50 @@ static inline BYTE aa_color( BYTE dst, BYTE text, BYTE min_comp, BYTE max_comp ) } }
-static inline DWORD aa_rgb( BYTE r_dst, BYTE g_dst, BYTE b_dst, DWORD text, const struct intensity_range *range ) +static inline BYTE blend_color_gamma( BYTE dst, BYTE text, BYTE alpha, + const struct font_gamma_ramp *gamma_ramp ) { + if (alpha == 0) return dst; + if (alpha == 255) return text; + if (dst == text) return dst; + + return gamma_ramp->encode[ blend_color( gamma_ramp->decode[dst], + gamma_ramp->decode[text], + alpha ) ]; +} + +static inline DWORD aa_rgb( BYTE r_dst, BYTE g_dst, BYTE b_dst, DWORD text, const BYTE alpha, + const struct font_intensities *intensity ) +{ + const struct intensity_range *range; + const struct font_gamma_ramp *gamma_ramp; + + if (intensity->max_level == 255) + { + gamma_ramp = intensity->gamma_ramp; + if (gamma_ramp != NULL && gamma_ramp->gamma != 1000) + { + return blend_color_gamma( r_dst, text >> 16, alpha, gamma_ramp ) << 16 | + blend_color_gamma( g_dst, text >> 8, alpha, gamma_ramp ) << 8 | + blend_color_gamma( b_dst, text, alpha, gamma_ramp ); + } + return blend_color( r_dst, text >> 16, alpha ) << 16 | + blend_color( g_dst, text >> 8, alpha ) << 8 | + blend_color( b_dst, text, alpha ); + } + + range = &intensity->ranges[alpha]; return (aa_color( b_dst, text, range->b_min, range->b_max ) | aa_color( g_dst, text >> 8, range->g_min, range->g_max ) << 8 | aa_color( r_dst, text >> 16, range->r_min, range->r_max ) << 16); }
static void draw_glyph_8888( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { DWORD *dst_ptr = get_pixel_ptr_32( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); + const int max_level = intensity->max_level; int x, y;
for (y = rect->top; y < rect->bottom; y++) @@ -6078,8 +6110,9 @@ static void draw_glyph_8888( const dib_info *dib, const RECT *rect, const dib_in for (x = 0; x < rect->right - rect->left; x++) { if (glyph_ptr[x] <= 1) continue; - if (glyph_ptr[x] >= 16) { dst_ptr[x] = text_pixel; continue; } - dst_ptr[x] = aa_rgb( dst_ptr[x] >> 16, dst_ptr[x] >> 8, dst_ptr[x], text_pixel, ranges + glyph_ptr[x] ); + if (glyph_ptr[x] >= max_level) { dst_ptr[x] = text_pixel; continue; } + dst_ptr[x] = aa_rgb( dst_ptr[x] >> 16, dst_ptr[x] >> 8, dst_ptr[x], + text_pixel, glyph_ptr[x], intensity ); } dst_ptr += dib->stride / 4; glyph_ptr += glyph->stride; @@ -6087,10 +6120,11 @@ static void draw_glyph_8888( const dib_info *dib, const RECT *rect, const dib_in }
static void draw_glyph_32( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { DWORD *dst_ptr = get_pixel_ptr_32( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); + const int max_level = intensity->max_level; int x, y; DWORD text, val;
@@ -6103,11 +6137,11 @@ static void draw_glyph_32( const dib_info *dib, const RECT *rect, const dib_info for (x = 0; x < rect->right - rect->left; x++) { if (glyph_ptr[x] <= 1) continue; - if (glyph_ptr[x] >= 16) { dst_ptr[x] = text_pixel; continue; } + if (glyph_ptr[x] >= max_level) { dst_ptr[x] = text_pixel; continue; } val = aa_rgb( get_field(dst_ptr[x], dib->red_shift, dib->red_len), get_field(dst_ptr[x], dib->green_shift, dib->green_len), get_field(dst_ptr[x], dib->blue_shift, dib->blue_len), - text, ranges + glyph_ptr[x] ); + text, glyph_ptr[x], intensity ); dst_ptr[x] = rgb_to_pixel_masks( dib, val >> 16, val >> 8, val ); } dst_ptr += dib->stride / 4; @@ -6116,10 +6150,11 @@ static void draw_glyph_32( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_24( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { BYTE *dst_ptr = get_pixel_ptr_24( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); + const int max_level = intensity->max_level; int x, y; DWORD val;
@@ -6128,11 +6163,11 @@ static void draw_glyph_24( const dib_info *dib, const RECT *rect, const dib_info for (x = 0; x < rect->right - rect->left; x++) { if (glyph_ptr[x] <= 1) continue; - if (glyph_ptr[x] >= 16) + if (glyph_ptr[x] >= max_level) val = text_pixel; else val = aa_rgb( dst_ptr[x * 3 + 2], dst_ptr[x * 3 + 1], dst_ptr[x * 3], - text_pixel, ranges + glyph_ptr[x] ); + text_pixel, glyph_ptr[x], intensity ); dst_ptr[x * 3] = val; dst_ptr[x * 3 + 1] = val >> 8; dst_ptr[x * 3 + 2] = val >> 16; @@ -6143,10 +6178,11 @@ static void draw_glyph_24( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_555( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { WORD *dst_ptr = get_pixel_ptr_16( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); + const int max_level = intensity->max_level; int x, y; DWORD text, val;
@@ -6159,11 +6195,11 @@ static void draw_glyph_555( const dib_info *dib, const RECT *rect, const dib_inf for (x = 0; x < rect->right - rect->left; x++) { if (glyph_ptr[x] <= 1) continue; - if (glyph_ptr[x] >= 16) { dst_ptr[x] = text_pixel; continue; } + if (glyph_ptr[x] >= max_level) { dst_ptr[x] = text_pixel; continue; } val = aa_rgb( ((dst_ptr[x] >> 7) & 0xf8) | ((dst_ptr[x] >> 12) & 0x07), ((dst_ptr[x] >> 2) & 0xf8) | ((dst_ptr[x] >> 7) & 0x07), ((dst_ptr[x] << 3) & 0xf8) | ((dst_ptr[x] >> 2) & 0x07), - text, ranges + glyph_ptr[x] ); + text, glyph_ptr[x], intensity ); dst_ptr[x] = ((val >> 9) & 0x7c00) | ((val >> 6) & 0x03e0) | ((val >> 3) & 0x001f); } dst_ptr += dib->stride / 2; @@ -6172,10 +6208,11 @@ static void draw_glyph_555( const dib_info *dib, const RECT *rect, const dib_inf }
static void draw_glyph_16( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { WORD *dst_ptr = get_pixel_ptr_16( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); + const int max_level = intensity->max_level; int x, y; DWORD text, val;
@@ -6188,11 +6225,11 @@ static void draw_glyph_16( const dib_info *dib, const RECT *rect, const dib_info for (x = 0; x < rect->right - rect->left; x++) { if (glyph_ptr[x] <= 1) continue; - if (glyph_ptr[x] >= 16) { dst_ptr[x] = text_pixel; continue; } + if (glyph_ptr[x] >= max_level) { dst_ptr[x] = text_pixel; continue; } val = aa_rgb( get_field(dst_ptr[x], dib->red_shift, dib->red_len), get_field(dst_ptr[x], dib->green_shift, dib->green_len), get_field(dst_ptr[x], dib->blue_shift, dib->blue_len), - text, ranges + glyph_ptr[x] ); + text, glyph_ptr[x], intensity ); dst_ptr[x] = rgb_to_pixel_masks( dib, val >> 16, val >> 8, val ); } dst_ptr += dib->stride / 2; @@ -6201,7 +6238,7 @@ static void draw_glyph_16( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_8( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { BYTE *dst_ptr = get_pixel_ptr_8( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); @@ -6221,7 +6258,7 @@ static void draw_glyph_8( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_4( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { BYTE *dst_ptr = get_pixel_ptr_4( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); @@ -6246,7 +6283,7 @@ static void draw_glyph_4( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_1( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { BYTE *dst_ptr = get_pixel_ptr_1( dib, rect->left, rect->top ); const BYTE *glyph_ptr = get_pixel_ptr_8( glyph, origin->x, origin->y ); @@ -6268,21 +6305,10 @@ static void draw_glyph_1( const dib_info *dib, const RECT *rect, const dib_info }
static void draw_glyph_null( const dib_info *dib, const RECT *rect, const dib_info *glyph, - const POINT *origin, DWORD text_pixel, const struct intensity_range *ranges ) + const POINT *origin, DWORD text_pixel, const struct font_intensities *intensity ) { return; } -static inline BYTE blend_color_gamma( BYTE dst, BYTE text, BYTE alpha, - const struct font_gamma_ramp *gamma_ramp ) -{ - if (alpha == 0) return dst; - if (alpha == 255) return text; - if (dst == text) return dst; - - return gamma_ramp->encode[ blend_color( gamma_ramp->decode[dst], - gamma_ramp->decode[text], - alpha ) ]; -}
static inline DWORD blend_subpixel( BYTE r, BYTE g, BYTE b, DWORD text, DWORD alpha, const struct font_gamma_ramp *gamma_ramp )
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net --- XQuartz2.7.11 Freetyep2.7 is disabled the subpixel rendering. https://bugs.winehq.org/show_bug.cgi?id=41639#c35 https://bugs.winehq.org/show_bug.cgi?id=41639#c40
This improves the quality of the gray antialias rendering at the subpixel rendering *fallback* mode.
dlls/gdi32/freetype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index dd94d22db5..1b41e16c28 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -5865,7 +5865,7 @@ done: case WINE_GGO_VRGB_BITMAP: case WINE_GGO_VBGR_BITMAP: if (is_subpixel_rendering_enabled()) break; - *aa_flags = GGO_GRAY4_BITMAP; + *aa_flags = WINE_GGO_GRAY16_BITMAP; /* fall through */ case GGO_GRAY2_BITMAP: case GGO_GRAY4_BITMAP:
Byeongsik Jeon bsjeon@hanmail.net wrote:
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? Basically NOT.
Arguably this is a Freetype bug, applications should not fiddle with underlying interpreter versions or even pretend to know anything about truetype interpreter implementation details.
+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 );
- }
+}
Usually Wine tries to avoid such ugly hacks.
if (forced_interpreter_version == 0 && get_gasp_flags( ret, NULL, &gasp_version ))
{
ret->interpreter_version = 0;
if (gasp_version == 1)
{
if (interpreter_v40_supported)
ret->interpreter_version = 40;
else if (interpreter_v38_supported)
ret->interpreter_version = 38;
}
else if (interpreter_v35_supported)
ret->interpreter_version = 35;
}
Shouldn't Freetype itself set appropriate interpreter version based on GASP flags instead?
On Mon, 5 Nov 2018 09:49:09 +0300, Dmitry Timoshkov dmitry@baikal.ru wrote:
Byeongsik Jeon bsjeon@hanmail.net wrote:
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? Basically NOT.
Arguably this is a Freetype bug, applications should not fiddle with underlying interpreter versions or even pretend to know anything about truetype interpreter implementation details.
+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 );
- }
+}
Usually Wine tries to avoid such ugly hacks.
I put it because it is convenient and fast test, but it seems to be not appropriate for Wine. I'll delete it.
if (forced_interpreter_version == 0 && get_gasp_flags( ret, NULL, &gasp_version ))
{
ret->interpreter_version = 0;
if (gasp_version == 1)
{
if (interpreter_v40_supported)
ret->interpreter_version = 40;
else if (interpreter_v38_supported)
ret->interpreter_version = 38;
}
else if (interpreter_v35_supported)
ret->interpreter_version = 35;
}
Shouldn't Freetype itself set appropriate interpreter version based on GASP flags instead?
At first, I thought this was a Freetype bug. But when I look at the Freetype API design, it seems intentional. They know this mechanism and they have been keeping this API design for a long time.
In short, the fonts can be created considering version 35 or version 40 or both. And record that information on the gasp table.
When The program(or OS, etc) uses the fonts, it is optional to render which interpreter version is applied. In this way, MS GDI and DirectWrite rendering results may be different.
In fact, because the Freetype implementation is different from Microsoft, our choices are limited.
Byeongsik Jeon bsjeon@hanmail.net wrote:
Shouldn't Freetype itself set appropriate interpreter version based on GASP flags instead?
At first, I thought this was a Freetype bug. But when I look at the Freetype API design, it seems intentional. They know this mechanism and they have been keeping this API design for a long time.
Why not ask Freetype developers to take into account gasp flags and appropriately tune the rendering especially if it's known to cause the rendering bugs?
In short, the fonts can be created considering version 35 or version 40 or both. And record that information on the gasp table.
When The program(or OS, etc) uses the fonts, it is optional to render which interpreter version is applied. In this way, MS GDI and DirectWrite rendering results may be different.
In fact, because the Freetype implementation is different from Microsoft, our choices are limited.
Obviously applications running under MacOS or Windows don't have such a problem, and application developers shouldn't even bother or try to accomodate for font renderers bugs.
On Mon, 5 Nov 2018 11:51:06 +0300, Dmitry Timoshkov dmitry@baikal.ru wrote:
Byeongsik Jeon bsjeon@hanmail.net wrote:
Shouldn't Freetype itself set appropriate interpreter version based on GASP flags instead?
At first, I thought this was a Freetype bug. But when I look at the Freetype API design, it seems intentional. They know this mechanism and they have been keeping this API design for a long time.
Why not ask Freetype developers to take into account gasp flags and appropriately tune the rendering especially if it's known to cause the rendering bugs?
I didn't ask because I didn't think it was a bug. As you wrote below, how to use the gasp table information is an option.
Version 35 and Version 40 are interpreters of different properties with different purposes.
Version 35 was intended to implement a bytecode interpreter. The subpixel rendering through this is only adding the results of the gray antialias to the subpixel. Version 40 is the implementation of the *subpixel hinting*. This can be found to be similar to the DirectWrite font rendering.
Version 38 seems to contains the tweaks to support both. But it's growing code size and it looks dirty.
Of course, in the future version of the Freetype, something may change. The Freetype document is marked the version38 as a feature to be deleted. I'm going to pay attention to when version 38 is completely removed.
Until then, I think this patch can help the Wine. I think it is a problem that the text layout is very different from the same font.
Too much consideration makes this patch look dirty. In fact, the basics are simple.
Interpreter_version = gasp_version ? 40: 35;
In short, the fonts can be created considering version 35 or version 40 or both. And record that information on the gasp table.
When The program(or OS, etc) uses the fonts, it is optional to render which interpreter version is applied. In this way, MS GDI and DirectWrite rendering results may be different.
In fact, because the Freetype implementation is different from Microsoft, our choices are limited.
Obviously applications running under MacOS or Windows don't have such a problem, and application developers shouldn't even bother or try to accomodate for font renderers bugs.
This is because the OS does it, or as a result, it plays such a role. So the application developers does not have to worry about it.
In a sense, isn't Wine's position close to the OS?
If this problem is corrected in the future version of the Freetype, will not it support the past version of the Freetype?
Is not the Wine-Bug #41639 a problem because it is not updated?
Byeongsik Jeon bsjeon@hanmail.net wrote:
Shouldn't Freetype itself set appropriate interpreter version based on GASP flags instead?
At first, I thought this was a Freetype bug. But when I look at the Freetype API design, it seems intentional. They know this mechanism and they have been keeping this API design for a long time.
Why not ask Freetype developers to take into account gasp flags and appropriately tune the rendering especially if it's known to cause the rendering bugs?
I didn't ask because I didn't think it was a bug. As you wrote below, how to use the gasp table information is an option.
Version 35 and Version 40 are interpreters of different properties with different purposes.
Version 35 was intended to implement a bytecode interpreter. The subpixel rendering through this is only adding the results of the gray antialias to the subpixel. Version 40 is the implementation of the *subpixel hinting*. This can be found to be similar to the DirectWrite font rendering.
Version 38 seems to contains the tweaks to support both. But it's growing code size and it looks dirty.
It's not reasonable to expect application developers to dive into this kind of very specific and technical details. Developers just expect that things work. This is how it works under Mac and Windows.
Of course, in the future version of the Freetype, something may change. The Freetype document is marked the version38 as a feature to be deleted. I'm going to pay attention to when version 38 is completely removed.
Until then, I think this patch can help the Wine. I think it is a problem that the text layout is very different from the same font.
Too much consideration makes this patch look dirty. In fact, the basics are simple.
Interpreter_version = gasp_version ? 40: 35;
If that's this simple why freetype can't do that on its own?
In short, the fonts can be created considering version 35 or version 40 or both. And record that information on the gasp table.
When The program(or OS, etc) uses the fonts, it is optional to render which interpreter version is applied. In this way, MS GDI and DirectWrite rendering results may be different.
In fact, because the Freetype implementation is different from Microsoft, our choices are limited.
Obviously applications running under MacOS or Windows don't have such a problem, and application developers shouldn't even bother or try to accomodate for font renderers bugs.
This is because the OS does it, or as a result, it plays such a role. So the application developers does not have to worry about it.
In a sense, isn't Wine's position close to the OS?
There is nothing special in fonts handling that Wine does that other applications don't or can't do.
On Mon, 5 Nov 2018 13:32:18 +0300, Dmitry Timoshkov dmitry@baikal.ru wrote:
It's not reasonable to expect application developers to dive into this kind of very specific and technical details. Developers just expect that things work. This is how it works under Mac and Windows.
I'm not saying that all application developers should do this. Also, the developers does not need to do this on Mac or Windows. Because it works as they expected.
This solution is a way to fit the definition of the gasp table. Unlike general application development, it is not a special technique in the development that directly accesses the fonts.
Interpreter_version = gasp_version ? 40: 35;
If that's this simple why freetype can't do that on its own?
It is an option to decide how to operate. Even if the Freetype does it automatically, the Freetype should be made selectable it. If the action is not matched to Windows, we will have to consider another solution.
Is it a problem that it solve the problem using the API provided by the Freetype? It is a solution that can be widely applied to the Freetype already distributed, including MacOS XQuartz v2.7.11.
This problem may be solved in some next version of the Freetype. Then we can add some patch. But the patch for the past versions will still need.
There is nothing special in fonts handling that Wine does that other applications don't or can't do.
A typical Linux application does not refer to the gasp table. But isn't the Wine already referring to it?
I didn't think this solution would be a controversial issue. Maybe it's the result of my awkward English expression. In fact, I don’t think I’ve heard an answer that makes sense to why this solution is wrong.
This can be solved by Wine, even if it is not a bug caused by Wine itself. Then the effect is more extensive. I am grateful for the comments.
There is no need for application using freetype to change the interpreter version at compile time - you can do that at runtime by setting the FREETYPE_PROPERTIES environment variable. This was introduced in freetype 2.7/2.8-ish. On Monday, 5 November 2018, 21:53:48 GMT+8, Byeongsik Jeon bsjeon@hanmail.net wrote:
On Mon, 5 Nov 2018 13:32:18 +0300, Dmitry Timoshkov dmitry@baikal.ru wrote:
It's not reasonable to expect application developers to dive into this kind of very specific and technical details. Developers just expect that things work. This is how it works under Mac and Windows.
I'm not saying that all application developers should do this. Also, the developers does not need to do this on Mac or Windows. Because it works as they expected.
This solution is a way to fit the definition of the gasp table. Unlike general application development, it is not a special technique in the development that directly accesses the fonts.
Interpreter_version = gasp_version ? 40: 35;
If that's this simple why freetype can't do that on its own?
It is an option to decide how to operate. Even if the Freetype does it automatically, the Freetype should be made selectable it. If the action is not matched to Windows, we will have to consider another solution.
Is it a problem that it solve the problem using the API provided by the Freetype? It is a solution that can be widely applied to the Freetype already distributed, including MacOS XQuartz v2.7.11.
This problem may be solved in some next version of the Freetype. Then we can add some patch. But the patch for the past versions will still need.
There is nothing special in fonts handling that Wine does that other applications don't or can't do.
A typical Linux application does not refer to the gasp table. But isn't the Wine already referring to it?
I didn't think this solution would be a controversial issue. Maybe it's the result of my awkward English expression. In fact, I don’t think I’ve heard an answer that makes sense to why this solution is wrong.
This can be solved by Wine, even if it is not a bug caused by Wine itself. Then the effect is more extensive. I am grateful for the comments.
On Mon, 5 Nov 2018 14:06:32 +0000 (UTC), Hin-tak Leung hintak_leung@yahoo.co.uk wrote:
There is no need for application using freetype to change the interpreter version at compile time - you can do that at runtime by setting the FREETYPE_PROPERTIES environment variable. This was introduced in freetype 2.7/2.8-ish.
On Mon, 5 Nov 2018 14:08:50 +0900, Byeongsik Jeon bsjeon@hanmail.net wrote:>
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.
When you fix the interpreter version, it does not satisfy both types of the fonts together.
On Monday, 5 November 2018, 21:53:48 GMT+8, Byeongsik Jeon bsjeon@hanmail.net wrote:
On Mon, 5 Nov 2018 13:32:18 +0300, Dmitry Timoshkov <dmitry@baikal.ru mailto:dmitry@baikal.ru> wrote:
It's not reasonable to expect application developers to dive into this kind of very specific and technical details. Developers just expect that things work. This is how it works under Mac and Windows.
I'm not saying that all application developers should do this. Also, the developers does not need to do this on Mac or Windows. Because it works as they expected.
This solution is a way to fit the definition of the gasp table. Unlike general application development, it is not a special technique in the development that directly accesses the fonts.
Interpreter_version = gasp_version ? 40: 35;
If that's this simple why freetype can't do that on its own?
It is an option to decide how to operate. Even if the Freetype does it automatically, the Freetype should be made selectable it. If the action is not matched to Windows, we will have to consider another solution.
Is it a problem that it solve the problem using the API provided by the Freetype? It is a solution that can be widely applied to the Freetype already distributed, including MacOS XQuartz v2.7.11.
This problem may be solved in some next version of the Freetype. Then we can add some patch. But the patch for the past versions will still need.
There is nothing special in fonts handling that Wine does that other
applications
don't or can't do.
A typical Linux application does not refer to the gasp table. But isn't the Wine already referring to it?
I didn't think this solution would be a controversial issue. Maybe it's the result of my awkward English expression. In fact, I don’t think I’ve heard an answer that makes sense to why this solution is wrong.
This can be solved by Wine, even if it is not a bug caused by Wine itself. Then the effect is more extensive. I am grateful for the comments.
On Monday, 5 November 2018, 22:16:56 GMT+8, Byeongsik Jeon bsjeon@hanmail.net wrote:
On Mon, 5 Nov 2018 14:06:32 +0000 (UTC), Hin-tak Leung hintak_leung@yahoo.co.uk wrote:
There is no need for application using freetype to change the interpreter version at compile time - you can do that at runtime by setting the FREETYPE_PROPERTIES environment variable. This was introduced in freetype 2.7/2.8-ish.
When you fix the interpreter version, it does not satisfy both types of the fonts together.
The idea of FREETYPE_PROPERTIES is that you should choose on an application-by-application basis. And maybe also that using Microsoft's Tahoma instead of wine's is a bad idea? You are basically advocating switching the interpreter version based on font vintage/age , using a heuristics on the presence/absence of a table to tell you how old a font is. I think that is bad. Fix the font, or using a better way of telling the age of a font :-).
On Mon, 5 Nov 2018 14:33:35 +0000 (UTC), Hin-tak Leung hintak_leung@yahoo.co.uk wrote:
On Monday, 5 November 2018, 22:16:56 GMT+8, Byeongsik Jeon bsjeon@hanmail.net wrote:
On Mon, 5 Nov 2018 14:06:32 +0000 (UTC), Hin-tak Leung <hintak_leung@yahoo.co.uk mailto:hintak_leung@yahoo.co.uk> wrote:
There is no need for application using freetype to change the interpreter version at compile time - you can do that at runtime by setting the FREETYPE_PROPERTIES environment variable. This was introduced in freetype 2.7/2.8-ish.
When you fix the interpreter version, it does not satisfy both types of the fonts together.
The idea of FREETYPE_PROPERTIES is that you should choose on an application-by-application basis. And maybe also that using Microsoft's Tahoma instead of wine's is a bad idea?
On Mon, 5 Nov 2018 17:17:04 +0900, Byeongsik Jeon bsjeon@hanmail.net wrote:>
The problem arises in native Tahoma. MacOS has Tahoma builtin. and Wine's Tahoma has a low priority.
In the Freetype interpreter version 40, the native Tahoma returns a different glyph metrics than Windows GDI.
This patch is about the situation in which one application uses Tahoma and Consolas together. Also, make sure you do not need to add individual settings based on the font type.
Arial, Courier New, etc. These fonts have the same problem, though there are differences in degree.
You are basically advocating switching the interpreter version based on font vintage/age , using a heuristics on the presence/absence of a table to tell you how old a font is. I think that is bad. Fix the font, or using a better way of telling the age of a font :-).
The gasp table is not like that. It is the information that reflects the intention of the font creator.
I have discovered the possibility that this problem has been caused by something else. When I looked at the Tahoma through the ftview, I found something different from what I saw at the Wine. It may have seen wrong, but I will look more closely.
Thanks those who gave me comments.
On 11/05/2018 08:08 AM, Byeongsik Jeon wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net
v2: Fix the wine-tests build error at the debian9(Freetype v2.6.3). Because the build-time check is meaningless, v2 has modified it to check the supported interpreter version at the runtime.
v1: 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? Basically NOT.
I think this went away from original reported issue, and the patch is basically the same workaround suggested there.
The question is what changed in freetype 2.7 that caused broken rendering in wine's Tahoma, as an example that's easy to test.
Not that wine fonts don't use freetype instructions, so it's not obvious why interpreter mode could make a difference if we're still going to use embedded bitmaps for small sizes and original outline for the rest.
https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions#ge...
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_INT...
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 | 104 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 5 deletions(-)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index 07de6f2f6c..dd94d22db5 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -130,6 +130,10 @@ typedef struct } FT_Version_t; static FT_Version_t FT_Version; static DWORD FT_SimpleVersion; +static FT_UInt forced_interpreter_version = 0; +static BOOL interpreter_v35_supported = FALSE; +static BOOL interpreter_v38_supported = FALSE; +static BOOL interpreter_v40_supported = FALSE;
static void *ft_handle = NULL;
@@ -158,6 +162,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 +405,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 +4108,49 @@ 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 );
- }
+}
I don't think we want that.
+static void diagnose_freetype(void) +{
- if (pFT_Property_Set && pFT_Property_Get)
- {
FT_UInt set_version, get_version;
set_version = 35;
get_version = 0;
pFT_Property_Set( library, "truetype", "interpreter-version", &set_version );
pFT_Property_Get( library, "truetype", "interpreter-version", &get_version );
if (get_version == set_version)
interpreter_v35_supported = TRUE;
set_version = 38;
get_version = 0;
pFT_Property_Set( library, "truetype", "interpreter-version", &set_version );
pFT_Property_Get( library, "truetype", "interpreter-version", &get_version );
if (get_version == set_version)
interpreter_v38_supported = TRUE;
set_version = 40;
get_version = 0;
pFT_Property_Set( library, "truetype", "interpreter-version", &set_version );
pFT_Property_Get( library, "truetype", "interpreter-version", &get_version );
if (get_version == set_version)
interpreter_v40_supported = TRUE;
- }
+}
Have you actually tested older freetype versions? E.g. does ftview with wine's Tahoma show same corruption or not.
On Mon, 5 Nov 2018 10:21:24 +0300, Nikolay Sivov nsivov@codeweavers.com wrote:
On 11/05/2018 08:08 AM, Byeongsik Jeon wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net
v2: Fix the wine-tests build error at the debian9(Freetype v2.6.3). Because the build-time check is meaningless, v2 has modified it to check the supported interpreter version at the runtime.
v1: 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? Basically NOT.
I think this went away from original reported issue, and the patch is basically the same workaround suggested there.
The question is what changed in freetype 2.7 that caused broken rendering in wine's Tahoma, as an example that's easy to test.
Not that wine fonts don't use freetype instructions, so it's not obvious why interpreter mode could make a difference if we're still going to use embedded bitmaps for small sizes and original outline for the rest.
https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions#ge...
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_INT...
-- 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.
Have you actually tested older freetype versions? E.g. does ftview with wine's Tahoma show same corruption or not.
The problem arises in native Tahoma. MacOS has Tahoma builtin. and Wine's Tahoma has a low priority.
In the Freetype interpreter version 40, the native Tahoma returns a different glyph metrics than Windows GDI.