Freetype's FT_Load_Glyph may return different glyph metrics (in particular, horiAdvance) depending on load target flags (FT_LOAD_TARGET_MONO, FT_LOAD_TARGET_NORMAL ...). Among the consequences of that are: - the size of, e. g, GetTextExtentPoint() doesn't match the size of actually rendered text; - DrawTextW() with DT_CALCRECT flag returns wrong bounding rectangle.
In the core of that is GetGlyphOutline() returning different values for GGO_METRICS format (used in various glyph metrics query functions) and the actual format used during rendering.
It probably make sense to use effective fonts rendering option for GGO_METRICS so that matches. I did some ad-hoc testing on Windows with currently problematic Tahoma font and quite expectedly GetGlyphOutline(GGO_METRICS) always returns the same metrics as other output format options. While all the options also have the same metrics between each other (which is still not the case with Wine). I guess it is not easily possible to make all the face load options match each other with freetype (nor I am sure that is always the case on Windows for all the possible fonts), but making GGO_METRICS return the metrics matching actual gdi device context setup looks more important.
Fixes Idle Spiral being unable to render typed text in save / load dialogs (which is using Winforms from Unity's Mono).
-- v3: win32u: Use font AA flags when querying glyph outline with GGO_METRICS. d3dx9/tests: Make test_ID3DXFont() less dependent on GetGlyphOutlineW(GGO_METRICS) behaviour. win32u: Set all glyph load flags in get_load_flags(). win32u: Store effective AA flags in font_physdev.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/win32u/font.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 9ee89f1b5c4..bf80babe6d9 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -55,6 +55,7 @@ struct font_physdev { struct gdi_physdev dev; struct gdi_font *font; + UINT aa_flags; };
static inline struct font_physdev *get_font_dev( PHYSDEV dev ) @@ -4671,6 +4672,7 @@ static HFONT font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) *aa_flags = font_smoothing; } *aa_flags = font_funcs->get_aa_flags( font, *aa_flags, antialias_fakes ); + physdev->aa_flags = *aa_flags; } TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), (int)lf.lfHeight, *aa_flags ); pthread_mutex_unlock( &font_lock );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/win32u/freetype.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 701e15c110d..acf29788550 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3403,10 +3403,13 @@ static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int b return needed; }
-static FT_Int get_load_flags( UINT format ) +static FT_Int get_load_flags( UINT format, BOOL vertical_metrics, BOOL force_no_bitmap ) { FT_Int load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+ if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT; + if (force_no_bitmap || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP; + if (format & GGO_UNHINTED) return load_flags | FT_LOAD_NO_HINTING;
@@ -3446,7 +3449,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Glyph_Metrics metrics; FT_Error err; FT_BBox bbox; - FT_Int load_flags = get_load_flags(format); + FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics;
@@ -3456,8 +3459,6 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT font->matrix.eM11, font->matrix.eM12, font->matrix.eM21, font->matrix.eM22);
- format &= ~GGO_UNHINTED; - matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices );
vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); @@ -3465,9 +3466,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - - if (matrices || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP; - if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT; + load_flags = get_load_flags(format, vertical_metrics, !!matrices);
err = pFT_Load_Glyph(ft_face, glyph, load_flags); if (err && !(load_flags & FT_LOAD_NO_HINTING)) @@ -3482,6 +3481,8 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT return GDI_ERROR; }
+ format &= ~GGO_UNHINTED; + metrics = ft_face->glyph->metrics; if(font->fake_bold) { if (!get_bold_glyph_outline(ft_face->glyph, font->ppem, &metrics) && metrics.width)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/d3dx9_36/tests/core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c index c3b80028f43..5b07fcd5ef7 100644 --- a/dlls/d3dx9_36/tests/core.c +++ b/dlls/d3dx9_36/tests/core.c @@ -356,7 +356,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device) IDirect3DTexture9 *texture; D3DSURFACE_DESC surf_desc; IDirect3DDevice9 *bufdev; - GLYPHMETRICS glyph_metrics; + GLYPHMETRICS glyph_metrics, gm_grayscale; D3DXFONT_DESCA desc; ID3DXSprite *sprite; RECT rect, blackbox; @@ -605,15 +605,27 @@ static void test_ID3DXFont(IDirect3DDevice9 *device) count = GetGlyphOutlineW(hdc, glyph, GGO_GLYPH_INDEX | GGO_METRICS, &glyph_metrics, 0, NULL, &mat); ok(count != GDI_ERROR, "Unexpected count %#lx.\n", count);
+ count = GetGlyphOutlineW(hdc, glyph, GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP, &gm_grayscale, 0, NULL, &mat); + ok(count != GDI_ERROR, "Unexpected count %#lx.\n", count); + ret = ID3DXFont_GetTextMetricsW(font, &tm); ok(ret, "Unexpected ret %#x.\n", ret);
- todo_wine ok(blackbox.right - blackbox.left == glyph_metrics.gmBlackBoxX + 2, "Got %ld, expected %d.\n", + todo_wine_if(blackbox.right - blackbox.left < glyph_metrics.gmBlackBoxX + 2) + ok(blackbox.right - blackbox.left == glyph_metrics.gmBlackBoxX + 2, "Got %ld, expected %d.\n", blackbox.right - blackbox.left, glyph_metrics.gmBlackBoxX + 2); - todo_wine ok(blackbox.bottom - blackbox.top == glyph_metrics.gmBlackBoxY + 2, "Got %ld, expected %d.\n", + + todo_wine_if(blackbox.bottom - blackbox.top < glyph_metrics.gmBlackBoxY + 2) + ok(blackbox.bottom - blackbox.top == glyph_metrics.gmBlackBoxY + 2, "Got %ld, expected %d.\n", blackbox.bottom - blackbox.top, glyph_metrics.gmBlackBoxY + 2); + + todo_wine_if(glyph_metrics.gmptGlyphOrigin.x != gm_grayscale.gmptGlyphOrigin.x + && cellinc.x == gm_grayscale.gmptGlyphOrigin.x - 1) ok(cellinc.x == glyph_metrics.gmptGlyphOrigin.x - 1, "Got %ld, expected %ld.\n", cellinc.x, glyph_metrics.gmptGlyphOrigin.x - 1); + + todo_wine_if(glyph_metrics.gmptGlyphOrigin.y != gm_grayscale.gmptGlyphOrigin.y + && cellinc.y == tm.tmAscent - gm_grayscale.gmptGlyphOrigin.y - 1) ok(cellinc.y == tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1, "Got %ld, expected %ld.\n", cellinc.y, tm.tmAscent - glyph_metrics.gmptGlyphOrigin.y - 1);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/win32u/font.c | 16 ++++++++-------- dlls/win32u/freetype.c | 13 +++++++++++-- dlls/win32u/ntgdi_private.h | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index bf80babe6d9..86c77f346fd 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -3833,7 +3833,7 @@ static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph )
static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf, - const MAT2 *mat ) + const MAT2 *mat, UINT aa_flags ) { GLYPHMETRICS gm; ABC abc; @@ -3867,7 +3867,7 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, if (format == GGO_METRICS && !mat && get_gdi_font_glyph_metrics( font, index, &gm, &abc )) goto done;
- ret = font_funcs->get_glyph_outline( font, index, format, &gm, &abc, buflen, buf, mat, tategaki ); + ret = font_funcs->get_glyph_outline( font, index, format, &gm, &abc, buflen, buf, mat, tategaki, aa_flags ); if (ret == GDI_ERROR) return ret;
if (format == GGO_METRICS && !mat) @@ -3916,7 +3916,7 @@ static BOOL font_GetCharABCWidths( PHYSDEV dev, UINT first, UINT count, WCHAR *c for (i = 0; i < count; i++) { c = chars ? chars[i] : first + i; - get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL ); + get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL, physdev->aa_flags ); } pthread_mutex_unlock( &font_lock ); return TRUE; @@ -3942,7 +3942,7 @@ static BOOL font_GetCharABCWidthsI( PHYSDEV dev, UINT first, UINT count, WORD *g pthread_mutex_lock( &font_lock ); for (c = 0; c < count; c++, buffer++) get_glyph_outline( physdev->font, gi ? gi[c] : first + c, GGO_METRICS | GGO_GLYPH_INDEX, - NULL, buffer, 0, NULL, NULL ); + NULL, buffer, 0, NULL, NULL, physdev->aa_flags ); pthread_mutex_unlock( &font_lock ); return TRUE; } @@ -3969,7 +3969,7 @@ static BOOL font_GetCharWidth( PHYSDEV dev, UINT first, UINT count, const WCHAR for (i = 0; i < count; i++) { c = chars ? chars[i] : i + first; - if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL ) == GDI_ERROR) + if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL, physdev->aa_flags ) == GDI_ERROR) buffer[i] = 0; else buffer[i] = abc.abcA + abc.abcB + abc.abcC; @@ -4148,7 +4148,7 @@ static DWORD font_GetGlyphOutline( PHYSDEV dev, UINT glyph, UINT format, return dev->funcs->pGetGlyphOutline( dev, glyph, format, gm, buflen, buf, mat ); } pthread_mutex_lock( &font_lock ); - ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat ); + ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat, physdev->aa_flags ); pthread_mutex_unlock( &font_lock ); return ret; } @@ -4328,7 +4328,7 @@ static BOOL font_GetTextExtentExPoint( PHYSDEV dev, const WCHAR *str, INT count, pthread_mutex_lock( &font_lock ); for (i = pos = 0; i < count; i++) { - get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL ); + get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL, physdev->aa_flags ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } @@ -4358,7 +4358,7 @@ static BOOL font_GetTextExtentExPointI( PHYSDEV dev, const WORD *indices, INT co for (i = pos = 0; i < count; i++) { get_glyph_outline( physdev->font, indices[i], GGO_METRICS | GGO_GLYPH_INDEX, - NULL, &abc, 0, NULL, NULL ); + NULL, &abc, 0, NULL, NULL, physdev->aa_flags ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index acf29788550..68a406d3583 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3442,7 +3442,7 @@ static FT_Int get_load_flags( UINT format, BOOL vertical_metrics, BOOL force_no_ */ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *lpgm, ABC *abc, UINT buflen, void *buf, - const MAT2 *lpmat, BOOL tategaki ) + const MAT2 *lpmat, BOOL tategaki, UINT aa_flags ) { struct gdi_font *base_font = font->base_font ? font->base_font : font; FT_Face ft_face = get_ft_face( font ); @@ -3452,6 +3452,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics; + UINT effective_format = format;
TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm, buflen, buf, lpmat);
@@ -3461,14 +3462,22 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT
matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices );
+ if (aa_flags && (format & ~GGO_GLYPH_INDEX) == GGO_METRICS) + effective_format = aa_flags | (format & GGO_GLYPH_INDEX); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - load_flags = get_load_flags(format, vertical_metrics, !!matrices); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices);
err = pFT_Load_Glyph(ft_face, glyph, load_flags); + if (err && format != effective_format) + { + WARN("Failed to load glyph %#x, retrying with GGO_METRICS. Error %#x.\n", glyph, err); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); + err = pFT_Load_Glyph(ft_face, glyph, load_flags); + } if (err && !(load_flags & FT_LOAD_NO_HINTING)) { WARN("Failed to load glyph %#x, retrying without hinting. Error %#x.\n", glyph, err); diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index 5c4f8279c87..eb82df750ba 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -327,7 +327,7 @@ struct font_backend_funcs UINT (*get_default_glyph)( struct gdi_font *gdi_font ); UINT (*get_glyph_outline)( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm, ABC *abc, UINT buflen, void *buf, - const MAT2 *mat, BOOL tategaki ); + const MAT2 *mat, BOOL tategaki, UINT aa_flags ); UINT (*get_unicode_ranges)( struct gdi_font *font, GLYPHSET *gs ); BOOL (*get_char_width_info)( struct gdi_font *font, struct char_width_info *info ); BOOL (*set_outline_text_metrics)( struct gdi_font *font );
v3: - store effective aa_flags in font_physdev instead of gdi_font (where they don't belong and break things when the cached font is selected to different contexts).