[PATCH 0/2] MR11217: gdi32: Convert inclusive logical bounds to inclusive device bounds in emfdc_update_bounds
The rectangles supplied to emfdc_update_bounds() by the EMFDC record emitters are inclusive in logical units, but the previous code simply fed the two corners through LPtoDP as if they were independent points. That collapses the rectangle to the device pixel hit by the inclusive corner and silently drops everything else within the same logical unit. Unlike with MM_TEXT and similiar, MM_ANSIOTROPIC/MM_ISOTROPIC mappings may span many device units per single logical unit, causing the bounds to be short on each axis. This then gets propagated to GDI+ and produces a much smaller frame than Windows does. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11217
From: Rose Hellsing <rose@pinkro.se> The rectangles supplied to emfdc_update_bounds() by the EMFDC record emitters are inclusive in logical units, but the previous code simply fed the two corners through LPtoDP as if they were independent points. That collapses the rectangle to the device pixel hit by the inclusive corner and silently drops everything else within the same logical unit. Unlike with MM_TEXT and similiar, MM_ANSIOTROPIC/MM_ISOTROPIC mappings may span many device units per single logical unit, causing the bounds to be short on each axis. This then gets propagated to GDI+ and produces a much smaller frame than Windows does. --- dlls/gdi32/emfdc.c | 59 +++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/dlls/gdi32/emfdc.c b/dlls/gdi32/emfdc.c index 1633d617bb9..6e7530815d2 100644 --- a/dlls/gdi32/emfdc.c +++ b/dlls/gdi32/emfdc.c @@ -121,36 +121,63 @@ static BOOL emfdc_record( struct emf *emf, EMR *emr ) static void emfdc_update_bounds( struct emf *emf, RECTL *rect ) { RECTL *bounds = &emf->dc_attr->emf_bounds; - RECTL vport_rect = *rect; - - LPtoDP( dc_attr_handle( emf->dc_attr ), (POINT *)&vport_rect, 2 ); + POINT pts[4]; + LONG dx, dy; + + /* The caller-provided rectangle is inclusive in logical units. To get an + * inclusive device rectangle that covers the full extent of the logical + * rectangle, the endpoint corresponding to the inclusive logical edge + * needs to be extended by the device width of one logical unit minus + * one. The per-unit device width is computed from a (0,0)-(1,1) probe + * rather than as LPtoDP(N+1)-LPtoDP(N) so that round-half-up + * floating-point asymmetry at .5 boundaries do not cause the bounds + * to lose isotropy when the mapping mode is intended to be isotropic. */ + pts[0].x = rect->left; pts[0].y = rect->top; + pts[1].x = rect->right; pts[1].y = rect->bottom; + pts[2].x = 0; pts[2].y = 0; + pts[3].x = 1; pts[3].y = 1; + LPtoDP( dc_attr_handle( emf->dc_attr ), pts, 4 ); + + dx = pts[3].x - pts[2].x; + dy = pts[3].y - pts[2].y; + + /* Extend the inclusive logical endpoint to cover the full device width of + * one logical unit. The sign of the delta handles mirrored coordinate + * systems. */ + if (dx >= 0) pts[1].x += dx - 1; + else pts[1].x += dx + 1; + if (dy >= 0) pts[1].y += dy - 1; + else pts[1].y += dy + 1; /* The coordinate systems may be mirrored (LPtoDP handles points, not rectangles) */ - if (vport_rect.left > vport_rect.right) + if (pts[0].x > pts[1].x) { - LONG temp = vport_rect.right; - vport_rect.right = vport_rect.left; - vport_rect.left = temp; + LONG temp = pts[0].x; + pts[0].x = pts[1].x; + pts[1].x = temp; } - if (vport_rect.top > vport_rect.bottom) + if (pts[0].y > pts[1].y) { - LONG temp = vport_rect.bottom; - vport_rect.bottom = vport_rect.top; - vport_rect.top = temp; + LONG temp = pts[0].y; + pts[0].y = pts[1].y; + pts[1].y = temp; } if (bounds->left > bounds->right) { /* first bounding rectangle */ - *bounds = vport_rect; + bounds->left = pts[0].x; + bounds->top = pts[0].y; + bounds->right = pts[1].x; + bounds->bottom = pts[1].y; } else { - bounds->left = min(bounds->left, vport_rect.left); - bounds->top = min(bounds->top, vport_rect.top); - bounds->right = max(bounds->right, vport_rect.right); - bounds->bottom = max(bounds->bottom, vport_rect.bottom); + bounds->left = min(bounds->left, pts[0].x); + bounds->top = min(bounds->top, pts[0].y); + bounds->right = max(bounds->right, pts[1].x); + bounds->bottom = max(bounds->bottom, pts[1].y); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11217
From: Rose Hellsing <rose@pinkro.se> --- dlls/gdi32/tests/metafile.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dlls/gdi32/tests/metafile.c b/dlls/gdi32/tests/metafile.c index 6d33a9eb90d..3f21db10af9 100644 --- a/dlls/gdi32/tests/metafile.c +++ b/dlls/gdi32/tests/metafile.c @@ -9553,24 +9553,18 @@ static void test_SetWinMetaFileBits(void) diffy = rclBoundsAnisotropic.bottom - GetDeviceCaps(dc, VERTRES) / 2; if (diffx < 0) diffx = -diffx; if (diffy < 0) diffy = -diffy; - todo_wine - { ok(diffx <= 1 && diffy <= 1, "SetWinMetaFileBits (MM_ANISOTROPIC): Reference bounds: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n", GetDeviceCaps(dc, HORZRES) / 2, GetDeviceCaps(dc, VERTRES) / 2, rclBoundsAnisotropic.right, rclBoundsAnisotropic.bottom); - } /* Allow 1 mm difference (rounding errors) */ diffx = rclFrameAnisotropic.right / 100 - GetDeviceCaps(dc, HORZSIZE) / 2; diffy = rclFrameAnisotropic.bottom / 100 - GetDeviceCaps(dc, VERTSIZE) / 2; if (diffx < 0) diffx = -diffx; if (diffy < 0) diffy = -diffy; - todo_wine - { ok(diffx <= 1 && diffy <= 1, "SetWinMetaFileBits (MM_ANISOTROPIC): Reference frame: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n", GetDeviceCaps(dc, HORZSIZE) / 2, GetDeviceCaps(dc, VERTSIZE) / 2, rclFrameAnisotropic.right / 100, rclFrameAnisotropic.bottom / 100); - } DeleteDC(dc); /* If the METAFILEPICT pointer is NULL, the MM_ANISOTROPIC mapping mode and the whole device surface are used */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11217
participants (2)
-
Rose Hellsing -
Rose Hellsing (@axtlos)