 
            Supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/9183.
-- v2: win32u: Use floats for map_monitor_rect computation.
 
            From: Rémi Bernon rbernon@codeweavers.com
Avoiding rounding errors caused by dpi_from and dpi * 2 denominators. It's possible to tweak the operations to move the division up to the end but it increases the risk of overflows. Let's just use floats for this.
Based on a patch from Conor McCarthy. --- dlls/win32u/sysparams.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 17432ef867d..d6ee5d643e2 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1923,10 +1923,12 @@ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from,
if (monitor->source) { - DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}, *mode_from, *mode_to; + float points[4] = {rect.left, rect.top, rect.right, rect.bottom}, from[2], to[2]; + DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}, physical_mode; UINT num, den, dpi;
source_get_current_settings( monitor->source, ¤t_mode ); + physical_mode = monitor->source->physical;
dpi = monitor_get_dpi( monitor, MDT_DEFAULT, &x, &y ); if (!dpi_from) dpi_from = dpi; @@ -1935,23 +1937,31 @@ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from, if (type_from == MDT_RAW_DPI) { monitor_virt_to_raw_ratio( monitor, &den, &num ); - mode_from = &monitor->source->physical; - mode_to = ¤t_mode; + from[0] = physical_mode.dmPosition.x + physical_mode.dmPelsWidth / 2.0; + from[1] = physical_mode.dmPosition.y + physical_mode.dmPelsHeight / 2.0; + to[0] = current_mode.dmPosition.x + current_mode.dmPelsWidth / 2.0; + to[1] = current_mode.dmPosition.y + current_mode.dmPelsHeight / 2.0; } else { monitor_virt_to_raw_ratio( monitor, &num, &den ); - mode_from = ¤t_mode; - mode_to = &monitor->source->physical; + from[0] = current_mode.dmPosition.x + current_mode.dmPelsWidth / 2.0; + from[1] = current_mode.dmPosition.y + current_mode.dmPelsHeight / 2.0; + to[0] = physical_mode.dmPosition.x + physical_mode.dmPelsWidth / 2.0; + to[1] = physical_mode.dmPosition.y + physical_mode.dmPelsHeight / 2.0; }
- rect = map_dpi_rect( rect, dpi_from, dpi * 2 ); - OffsetRect( &rect, -mode_from->dmPosition.x * 2 - mode_from->dmPelsWidth, - -mode_from->dmPosition.y * 2 - mode_from->dmPelsHeight ); - rect = map_dpi_rect( rect, den, num ); - OffsetRect( &rect, mode_to->dmPosition.x * 2 + mode_to->dmPelsWidth, - mode_to->dmPosition.y * 2 + mode_to->dmPelsHeight ); - return map_dpi_rect( rect, dpi * 2, dpi_to ); + for (int i = 0; i < ARRAY_SIZE(points); i++) + { + points[i] *= (float)dpi / dpi_from; + points[i] -= from[i & 1]; + points[i] *= (float)num / den; + points[i] += to[i & 1]; + points[i] *= (float)dpi_to / dpi; + } + + SetRect( &rect, round( points[0] ), round( points[1] ), round( points[2] ), round( points[3] ) ); + return rect; }
if (!dpi_from) dpi_from = monitor_get_dpi( monitor, type_from, &x, &y );
 
            v2: Drop the test for now, as it's sometimes failing on Windows. We don't want more spurious failures.

