Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net ---
As a result of analyzing the native Tahoma font in the ftview, I realized that the bug #41639 is a result of not specifying the appropriate hinting algorithm.
https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT... --- .. values to select a specific hinting algorithm for the hinter. .. You can use a hinting algorithm that doesn't correspond to the same rendering mode. As an example, it is possible to use the ‘light’ hinting algorithm and have the results rendered in horizontal LCD pixel mode, with code like
FT_Load_Glyph( face, glyph_index, load_flags | FT_LOAD_TARGET_LIGHT ); FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD ); ---
FT_LOAD_TARGET_NORMAL is 0. So, we have been applying FT_LOAD_TARGET_NORMAL to all load_flags.
--- FT_LOAD_TARGET_NORMAL : Default render mode; it corresponds to 8-bit anti-aliased bitmaps. FT_LOAD_TARGET_MONO : Strong hinting algorithm that should only be used for monochrome output. ---
We usually do not see this problem because we use the Wine's Tahoma with the embeded bitmap.
MacOS XQuartz 2.7.11, Freetype 2.7: - subpixel rendering disabled Freetype. - MacOS builtined Tahoma. This bug appears because of the combination of two situations.
This is about the Freetype interpreter version 40. Version 40 is the default.
dlls/gdi32/freetype.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index 07de6f2f6c..56d1e99873 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -7001,6 +7001,30 @@ 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;
+ if (!(load_flags & FT_LOAD_NO_HINTING)) + { + switch (format) + { + case GGO_BITMAP: + load_flags |= FT_LOAD_TARGET_MONO; + break; + case GGO_GRAY2_BITMAP: + case GGO_GRAY4_BITMAP: + case GGO_GRAY8_BITMAP: + case WINE_GGO_GRAY16_BITMAP: + load_flags |= FT_LOAD_TARGET_NORMAL; + break; + case WINE_GGO_HRGB_BITMAP: + case WINE_GGO_HBGR_BITMAP: + load_flags |= FT_LOAD_TARGET_LCD; + break; + case WINE_GGO_VRGB_BITMAP: + case WINE_GGO_VBGR_BITMAP: + load_flags |= FT_LOAD_TARGET_LCD_V; + break; + } + } + 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 )
On Tue, Nov 06, 2018 at 04:08:07PM +0900, Byeongsik Jeon wrote:
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(-)
Hmm, I'm not convinced we want to add this custom behaviour to the dibdrv; especially as its motivation is a fallback in a fairly niche case.
Huw.
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 56d1e99873..9f0808ec6e 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -5801,7 +5801,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:
On Tue, Nov 06, 2018 at 04:08:06PM +0900, Byeongsik Jeon wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41639 Signed-off-by: Byeongsik Jeon bsjeon@hanmail.net
As a result of analyzing the native Tahoma font in the ftview, I realized that the bug #41639 is a result of not specifying the appropriate hinting algorithm.
https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT...
.. values to select a specific hinting algorithm for the hinter. .. You can use a hinting algorithm that doesn't correspond to the same rendering mode. As an example, it is possible to use the ‘light’ hinting algorithm and have the results rendered in horizontal LCD pixel mode, with code like
FT_Load_Glyph( face, glyph_index, load_flags | FT_LOAD_TARGET_LIGHT ); FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD );
FT_LOAD_TARGET_NORMAL is 0. So, we have been applying FT_LOAD_TARGET_NORMAL to all load_flags.
FT_LOAD_TARGET_NORMAL : Default render mode; it corresponds to 8-bit anti-aliased bitmaps. FT_LOAD_TARGET_MONO : Strong hinting algorithm that should only be used for monochrome output.
We usually do not see this problem because we use the Wine's Tahoma with the embeded bitmap.
MacOS XQuartz 2.7.11, Freetype 2.7:
- subpixel rendering disabled Freetype.
- MacOS builtined Tahoma.
This bug appears because of the combination of two situations.
This is about the Freetype interpreter version 40. Version 40 is the default.
dlls/gdi32/freetype.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index 07de6f2f6c..56d1e99873 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -7001,6 +7001,30 @@ 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;
- if (!(load_flags & FT_LOAD_NO_HINTING))
- {
switch (format)
{
case GGO_BITMAP:
load_flags |= FT_LOAD_TARGET_MONO;
break;
case GGO_GRAY2_BITMAP:
case GGO_GRAY4_BITMAP:
case GGO_GRAY8_BITMAP:
case WINE_GGO_GRAY16_BITMAP:
load_flags |= FT_LOAD_TARGET_NORMAL;
break;
case WINE_GGO_HRGB_BITMAP:
case WINE_GGO_HBGR_BITMAP:
load_flags |= FT_LOAD_TARGET_LCD;
break;
case WINE_GGO_VRGB_BITMAP:
case WINE_GGO_VBGR_BITMAP:
load_flags |= FT_LOAD_TARGET_LCD_V;
break;
}
- }
This seems worthwhile, however could you make this a helper function (e.g. get_load_flags() and call it earlier on, right before where GGO_UNHINTED is handled? The helper would also return FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH.
Huw.