These patches make a test case attached to the bug https://bugs.winehq.org/show_bug.cgi?id=33190 work.
-- v3: win32u: NtGdiExtTextOutW() should translate x,y from logical to device units at the last step. win32u: Fix device<->world width/height converters. gdi32/tests: Add some tests for rotated font metrics. win32u: Use slightly more readable names for DP/LP converters. win32u: Use correct helper for converting width to device units.
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/win32u/font.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 7d7aa26adc7..96f091ff77c 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -262,6 +262,16 @@ static inline INT height_to_LP( DC *dc, INT height ) return GDI_ROUND( (double)height * fabs( dc->xformVport2World.eM22 )); }
+static inline INT INTERNAL_XWSTODS(DC *dc, INT width) +{ + POINT pt[2]; + pt[0].x = pt[0].y = 0; + pt[1].x = width; + pt[1].y = 0; + lp_to_dp(dc, pt, 2); + return pt[1].x - pt[0].x; +} + static inline INT INTERNAL_YWSTODS(DC *dc, INT height) { POINT pt[2]; @@ -5741,7 +5751,7 @@ BOOL nulldrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *rect */ static inline int get_line_width( DC *dc, int metric_size ) { - int width = abs( INTERNAL_YWSTODS( dc, metric_size )); + int width = abs( INTERNAL_XWSTODS( dc, metric_size )); if (width == 0) width = 1; if (metric_size < 0) width = -width; return width;
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/win32u/font.c | 56 +++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 41 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 96f091ff77c..6e7cef3daa2 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -224,32 +224,6 @@ static inline int facename_compare( const WCHAR *str1, const WCHAR *str2, SIZE_T
/* Device -> World size conversion */
-/* Performs a device to world transformation on the specified width (which - * is in integer format). - */ -static inline INT INTERNAL_XDSTOWS(DC *dc, INT width) -{ - double floatWidth; - - /* Perform operation with floating point */ - floatWidth = (double)width * dc->xformVport2World.eM11; - /* Round to integers */ - return GDI_ROUND(floatWidth); -} - -/* Performs a device to world transformation on the specified size (which - * is in integer format). - */ -static inline INT INTERNAL_YDSTOWS(DC *dc, INT height) -{ - double floatHeight; - - /* Perform operation with floating point */ - floatHeight = (double)height * dc->xformVport2World.eM22; - /* Round to integers */ - return GDI_ROUND(floatHeight); -} - /* scale width and height but don't mirror them */
static inline INT width_to_LP( DC *dc, INT width ) @@ -262,7 +236,7 @@ static inline INT height_to_LP( DC *dc, INT height ) return GDI_ROUND( (double)height * fabs( dc->xformVport2World.eM22 )); }
-static inline INT INTERNAL_XWSTODS(DC *dc, INT width) +static inline INT width_to_DP(DC *dc, INT width) { POINT pt[2]; pt[0].x = pt[0].y = 0; @@ -272,7 +246,7 @@ static inline INT INTERNAL_XWSTODS(DC *dc, INT width) return pt[1].x - pt[0].x; }
-static inline INT INTERNAL_YWSTODS(DC *dc, INT height) +static inline INT height_to_DP(DC *dc, INT height) { POINT pt[2]; pt[0].x = pt[0].y = 0; @@ -5258,7 +5232,7 @@ BOOL WINAPI NtGdiGetTextExtentExW( HDC hdc, const WCHAR *str, INT count, INT max { for (i = 0; i < count; i++) { - unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + + unsigned int dx = abs( width_to_LP( dc, pos[i] )) + (i + 1) * dc->attr->char_extra; if (nfit && dx > (unsigned int)max_ext) break; if (dxs) dxs[i] = dx; @@ -5266,8 +5240,8 @@ BOOL WINAPI NtGdiGetTextExtentExW( HDC hdc, const WCHAR *str, INT count, INT max if (nfit) *nfit = i; }
- size->cx = abs( INTERNAL_XDSTOWS( dc, size->cx )) + count * dc->attr->char_extra; - size->cy = abs( INTERNAL_YDSTOWS( dc, size->cy )); + size->cx = abs( width_to_LP( dc, size->cx )) + count * dc->attr->char_extra; + size->cy = abs( height_to_LP( dc, size->cy )); }
if (pos != buffer && pos != dxs) free( pos ); @@ -5371,16 +5345,16 @@ UINT WINAPI NtGdiGetOutlineTextMetricsInternalW( HDC hdc, UINT cbData, output->otmTextMetrics.tmOverhang = width_to_LP( dc, output->otmTextMetrics.tmOverhang ); output->otmAscent = height_to_LP( dc, output->otmAscent); output->otmDescent = height_to_LP( dc, output->otmDescent); - output->otmLineGap = INTERNAL_YDSTOWS(dc, output->otmLineGap); - output->otmsCapEmHeight = INTERNAL_YDSTOWS(dc, output->otmsCapEmHeight); - output->otmsXHeight = INTERNAL_YDSTOWS(dc, output->otmsXHeight); + output->otmLineGap = height_to_LP(dc, output->otmLineGap); + output->otmsCapEmHeight = height_to_LP(dc, output->otmsCapEmHeight); + output->otmsXHeight = height_to_LP(dc, output->otmsXHeight); output->otmrcFontBox.top = height_to_LP( dc, output->otmrcFontBox.top); output->otmrcFontBox.bottom = height_to_LP( dc, output->otmrcFontBox.bottom); output->otmrcFontBox.left = width_to_LP( dc, output->otmrcFontBox.left); output->otmrcFontBox.right = width_to_LP( dc, output->otmrcFontBox.right); output->otmMacAscent = height_to_LP( dc, output->otmMacAscent); output->otmMacDescent = height_to_LP( dc, output->otmMacDescent); - output->otmMacLineGap = INTERNAL_YDSTOWS(dc, output->otmMacLineGap); + output->otmMacLineGap = height_to_LP(dc, output->otmMacLineGap); output->otmptSubscriptSize.x = width_to_LP( dc, output->otmptSubscriptSize.x); output->otmptSubscriptSize.y = height_to_LP( dc, output->otmptSubscriptSize.y); output->otmptSubscriptOffset.x = width_to_LP( dc, output->otmptSubscriptOffset.x); @@ -5389,7 +5363,7 @@ UINT WINAPI NtGdiGetOutlineTextMetricsInternalW( HDC hdc, UINT cbData, output->otmptSuperscriptSize.y = height_to_LP( dc, output->otmptSuperscriptSize.y); output->otmptSuperscriptOffset.x = width_to_LP( dc, output->otmptSuperscriptOffset.x); output->otmptSuperscriptOffset.y = height_to_LP( dc, output->otmptSuperscriptOffset.y); - output->otmsStrikeoutSize = INTERNAL_YDSTOWS(dc, output->otmsStrikeoutSize); + output->otmsStrikeoutSize = height_to_LP(dc, output->otmsStrikeoutSize); output->otmsStrikeoutPosition = height_to_LP( dc, output->otmsStrikeoutPosition); output->otmsUnderscoreSize = height_to_LP( dc, output->otmsUnderscoreSize); output->otmsUnderscorePosition = height_to_LP( dc, output->otmsUnderscorePosition); @@ -5751,7 +5725,7 @@ BOOL nulldrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *rect */ static inline int get_line_width( DC *dc, int metric_size ) { - int width = abs( INTERNAL_XWSTODS( dc, metric_size )); + int width = abs( width_to_DP( dc, metric_size )); if (width == 0) width = 1; if (metric_size < 0) width = -width; return width; @@ -5982,8 +5956,8 @@ BOOL WINAPI NtGdiExtTextOutW( HDC hdc, INT x, INT y, UINT flags, const RECT *lpr width = desired[1]; }
- tm.tmAscent = abs(INTERNAL_YWSTODS(dc, tm.tmAscent)); - tm.tmDescent = abs(INTERNAL_YWSTODS(dc, tm.tmDescent)); + tm.tmAscent = abs(height_to_DP(dc, tm.tmAscent)); + tm.tmDescent = abs(height_to_DP(dc, tm.tmDescent)); switch( align & (TA_LEFT | TA_RIGHT | TA_CENTER) ) { case TA_LEFT: @@ -6080,10 +6054,10 @@ done: { otm = malloc( size ); NtGdiGetOutlineTextMetricsInternalW( hdc, size, otm, 0 ); - underlinePos = abs( INTERNAL_YWSTODS( dc, otm->otmsUnderscorePosition )); + underlinePos = abs( height_to_DP( dc, otm->otmsUnderscorePosition )); if (otm->otmsUnderscorePosition < 0) underlinePos = -underlinePos; underlineWidth = get_line_width( dc, otm->otmsUnderscoreSize ); - strikeoutPos = abs( INTERNAL_YWSTODS( dc, otm->otmsStrikeoutPosition )); + strikeoutPos = abs( height_to_DP( dc, otm->otmsStrikeoutPosition )); if (otm->otmsStrikeoutPosition < 0) strikeoutPos = -strikeoutPos; strikeoutWidth = get_line_width( dc, otm->otmsStrikeoutSize ); free( otm );
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/gdi32/tests/font.c | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 362ddd381f1..7b66ee9e37c 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -22,6 +22,7 @@ #include <stdarg.h> #include <stdio.h> #include <assert.h> +#include <math.h>
#include "windef.h" #include "winbase.h" @@ -7862,6 +7863,104 @@ static void test_font_weight(void) ok(bret, "got error %ld\n", GetLastError()); }
+static void test_rotated_metrics(void) +{ + static const WCHAR str[] = L"Hello World!"; + static const struct + { + float angle_g; + POINT pt; + LONG tmHeight, tmAscent, tmDescent; + SIZE ext; + } test[] = + { + { 0.0f, {50,50}, 87,72,15, {392,87} }, + { 45.0f, {94,24}, 107,76,31, {391,107} }, + { 90.0f, {100,-50}, 90,74,16, {391,90} }, + { 135.0f, {0,-71}, 108,76,32, {391,108} }, + { 180.0f, {-50,-50}, 87,72,15, {392,87} } + }; + float angle_r; + HDC hdc; + LOGFONTW lf; + HFONT hfont; + XFORM xform; + TEXTMETRICW tm; + POINT pt; + SIZE sz; + int i; + + hdc = CreateCompatibleDC(0); + + memset(&lf, 0, sizeof(lf)); + wcscpy(lf.lfFaceName, L"Tahoma"); + lf.lfHeight = -72; + hfont = CreateFontIndirectW(&lf); + + SetGraphicsMode(hdc, GM_ADVANCED); + SetMapMode(hdc, MM_TEXT); + + hfont = SelectObject(hdc, hfont); + + pt.x = 100; + pt.y = 100; + DPtoLP(hdc, &pt, 1); + ok(pt.x == 100, "got %ld\n", pt.x); + ok(pt.y == 100, "got %ld\n", pt.y); + + GetTextMetricsW(hdc, &tm); + ok(match_off_by_n(tm.tmHeight, 87, 5), "got %ld\n", tm.tmHeight); + ok(match_off_by_n(tm.tmAscent, 72, 5), "got %ld\n", tm.tmAscent); + ok(match_off_by_n(tm.tmDescent, 15, 5), "got %ld\n", tm.tmDescent); + + GetTextExtentPoint32W(hdc, str, wcslen(str), &sz); + ok(match_off_by_n(sz.cx, 391, 5), "got %ld\n", sz.cx); + ok(match_off_by_n(sz.cy, 87, 5), "got %ld\n", sz.cy); + + for (i = 0; i < ARRAY_SIZE(test); i++) + { + winetest_push_context("%d", i); + + angle_r = test[i].angle_g * 3.141592f / 180.0f; + + xform.eM11 = cosf(angle_r) * 2.0f; + xform.eM12 = sinf(angle_r); + xform.eM21 = -xform.eM12 * 2.0f; + xform.eM22 = xform.eM11; + xform.eDx = 0.0f; + xform.eDy = 0.0f; + SetWorldTransform(hdc, &xform); + + pt.x = 100; + pt.y = 100; + DPtoLP(hdc, &pt, 1); + ok(pt.x == test[i].pt.x, "got %ld\n", pt.x); + ok(pt.y == test[i].pt.y, "got %ld\n", pt.y); + + GetTextMetricsW(hdc, &tm); + /* Windows produces noticeable diff for angles 45.0 and 135.0 */ + todo_wine_if(i != 0 && i != 4) + ok(match_off_by_n(tm.tmHeight, test[i].tmHeight, 25), "got %ld\n", tm.tmHeight); + todo_wine_if(i != 0 && i != 4) + ok(match_off_by_n(tm.tmAscent, test[i].tmAscent, 10), "got %ld\n", tm.tmAscent); + todo_wine_if(i != 0 && i != 2 && i != 4) + ok(match_off_by_n(tm.tmDescent, test[i].tmDescent, 17), "got %ld\n", tm.tmDescent); + + GetTextExtentPoint32W(hdc, str, wcslen(str), &sz); + todo_wine_if(i != 0 && i != 4) + ok(match_off_by_n(sz.cx, test[i].ext.cx, 10), "got %ld\n", sz.cx); + todo_wine_if(i != 0 && i != 4) + ok(match_off_by_n(sz.cy, test[i].ext.cy, 21), "got %ld\n", sz.cy); + + winetest_pop_context(); + } + + hfont = SelectObject(hdc, hfont); + DeleteObject(hfont); + + DeleteDC(hdc); +} + START_TEST(font) { static const char *test_names[] = @@ -7883,6 +7982,7 @@ START_TEST(font) return; }
+ test_rotated_metrics(); test_stock_fonts(); test_logfont(); test_bitmap_font();
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/gdi32/tests/font.c | 5 ----- dlls/win32u/font.c | 38 ++++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 23 deletions(-)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 7b66ee9e37c..090092029dc 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -7939,17 +7939,12 @@ static void test_rotated_metrics(void)
GetTextMetricsW(hdc, &tm); /* Windows produces noticeable diff for angles 45.0 and 135.0 */ - todo_wine_if(i != 0 && i != 4) ok(match_off_by_n(tm.tmHeight, test[i].tmHeight, 25), "got %ld\n", tm.tmHeight); - todo_wine_if(i != 0 && i != 4) ok(match_off_by_n(tm.tmAscent, test[i].tmAscent, 10), "got %ld\n", tm.tmAscent); - todo_wine_if(i != 0 && i != 2 && i != 4) ok(match_off_by_n(tm.tmDescent, test[i].tmDescent, 17), "got %ld\n", tm.tmDescent);
GetTextExtentPoint32W(hdc, str, wcslen(str), &sz); - todo_wine_if(i != 0 && i != 4) ok(match_off_by_n(sz.cx, test[i].ext.cx, 10), "got %ld\n", sz.cx); - todo_wine_if(i != 0 && i != 4) ok(match_off_by_n(sz.cy, test[i].ext.cy, 21), "got %ld\n", sz.cy);
winetest_pop_context(); diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 6e7cef3daa2..45d89b7aa0d 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -228,32 +228,34 @@ static inline int facename_compare( const WCHAR *str1, const WCHAR *str2, SIZE_T
static inline INT width_to_LP( DC *dc, INT width ) { - return GDI_ROUND( (double)width * fabs( dc->xformVport2World.eM11 )); + float scale_x; + + scale_x = hypotf( dc->xformWorld2Vport.eM11, dc->xformWorld2Vport.eM12 ); + return GDI_ROUND( (float)width / scale_x ); }
static inline INT height_to_LP( DC *dc, INT height ) { - return GDI_ROUND( (double)height * fabs( dc->xformVport2World.eM22 )); + float scale_y; + + scale_y = hypotf( dc->xformWorld2Vport.eM21, dc->xformWorld2Vport.eM22 ); + return GDI_ROUND( (float)height / scale_y ); }
static inline INT width_to_DP(DC *dc, INT width) { - POINT pt[2]; - pt[0].x = pt[0].y = 0; - pt[1].x = width; - pt[1].y = 0; - lp_to_dp(dc, pt, 2); - return pt[1].x - pt[0].x; + float scale_x; + + scale_x = hypotf( dc->xformWorld2Vport.eM11, dc->xformWorld2Vport.eM12 ); + return GDI_ROUND( (float)width * scale_x ); }
static inline INT height_to_DP(DC *dc, INT height) { - POINT pt[2]; - pt[0].x = pt[0].y = 0; - pt[1].x = 0; - pt[1].y = height; - lp_to_dp(dc, pt, 2); - return pt[1].y - pt[0].y; + float scale_y; + + scale_y = hypotf( dc->xformWorld2Vport.eM21, dc->xformWorld2Vport.eM22 ); + return GDI_ROUND( (float)height * scale_y ); }
static INT FONT_GetObjectW( HGDIOBJ handle, INT count, LPVOID buffer ); @@ -4087,8 +4089,8 @@ static void scale_outline_font_metrics( const struct gdi_font *font, OUTLINETEXT else scale_x = font->scale_y;
- scale_x *= fabs(font->matrix.eM11); - scale_y = font->scale_y * fabs(font->matrix.eM22); + scale_x *= hypotf(font->matrix.eM11, font->matrix.eM12); + scale_y = font->scale_y * hypotf(font->matrix.eM21, font->matrix.eM22);
/* Windows scales these values as signed integers even if they are unsigned */ #define SCALE_X(x) (x) = GDI_ROUND((int)(x) * (scale_x)) @@ -4302,8 +4304,8 @@ static void scale_font_metrics( struct gdi_font *font, TEXTMETRICW *tm ) else scale_x = font->scale_y;
- scale_x *= fabs(font->matrix.eM11); - scale_y = font->scale_y * fabs(font->matrix.eM22); + scale_x *= hypotf(font->matrix.eM11, font->matrix.eM12); + scale_y = font->scale_y * hypotf(font->matrix.eM21, font->matrix.eM22);
#define SCALE_X(x) (x) = GDI_ROUND((x) * scale_x) #define SCALE_Y(y) (y) = GDI_ROUND((y) * scale_y)
From: Dmitry Timoshkov dmitry@baikal.ru
Bug: https://bugs.winehq.org/show_bug.cgi?id=33190
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/win32u/font.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 45d89b7aa0d..a7f2056efe1 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -5856,12 +5856,6 @@ BOOL WINAPI NtGdiExtTextOutW( HDC hdc, INT x, INT y, UINT flags, const RECT *lpr goto done; }
- pt.x = x; - pt.y = y; - lp_to_dp(dc, &pt, 1); - x = pt.x; - y = pt.y; - char_extra = dc->attr->char_extra; if (char_extra && lpDx && NtGdiGetDeviceCaps( hdc, TECHNOLOGY ) == DT_RASPRINTER) char_extra = 0; /* Printer drivers don't add char_extra if lpDx is supplied */ @@ -5958,8 +5952,6 @@ BOOL WINAPI NtGdiExtTextOutW( HDC hdc, INT x, INT y, UINT flags, const RECT *lpr width = desired[1]; }
- tm.tmAscent = abs(height_to_DP(dc, tm.tmAscent)); - tm.tmDescent = abs(height_to_DP(dc, tm.tmDescent)); switch( align & (TA_LEFT | TA_RIGHT | TA_CENTER) ) { case TA_LEFT: @@ -6021,12 +6013,19 @@ BOOL WINAPI NtGdiExtTextOutW( HDC hdc, INT x, INT y, UINT flags, const RECT *lpr text_box.bottom = y + tm.tmDescent;
if (flags & ETO_CLIPPED) intersect_rect( &text_box, &text_box, &rc ); + lp_to_dp(dc, (POINT *)&text_box, 2); if (!IsRectEmpty( &text_box )) physdev->funcs->pExtTextOut( physdev, 0, 0, ETO_OPAQUE, &text_box, NULL, 0, NULL ); } } }
+ pt.x = x; + pt.y = y; + lp_to_dp(dc, &pt, 1); + x = pt.x; + y = pt.y; + ret = physdev->funcs->pExtTextOut( physdev, x, y, (flags & ~ETO_OPAQUE), &rc, str, count, (INT*)deltas );
On Mon Apr 7 08:07:43 2025 +0000, Huw Davies wrote:
There are existing tests for outline text metrics and world
transforms, and this MR shouldn't influence that. What kind of new tests would like to be added? Ones that show that your proposed changes are needed.
Does new version of the patches meet your suggestions?
Is there anything else that could be added or improved here?