[v2 PATCH] gdi32: Update metafile world transform immediately in winnt mode

Andrew Eikum aeikum at codeweavers.com
Fri Jul 21 13:42:53 CDT 2017


The Chrome browser print function implements their own handling for
EMR_MODIFYWORLDTRANSFORM which calls ModifyWorldTransform on the HDC
directly without ever calling PlayEnhMetaFileRecord. In Wine, this
transformation would get discarded when the callback function returned,
causing the page to be printed at the wrong scale.

Tests show that the transform is updated immediately during
PlayEnhMetaFileRecord. In addition, a modified transform persists
between callbacks until PlayEnhMetaFileRecord is called on a relevant
type of callback, at which point the transform is reverted before
playing back the record.

Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---

v2: Rearrange changes to enhmetafile.c and clean up re-used code in the tests.

 dlls/gdi32/enhmetafile.c    |  37 +++++++-
 dlls/gdi32/tests/metafile.c | 216 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 248 insertions(+), 5 deletions(-)

diff --git a/dlls/gdi32/enhmetafile.c b/dlls/gdi32/enhmetafile.c
index cffac375f9..c5c7c570c9 100644
--- a/dlls/gdi32/enhmetafile.c
+++ b/dlls/gdi32/enhmetafile.c
@@ -886,6 +886,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
         info->state.wndOrgY = pSetWindowOrgEx->ptlOrigin.y;
 
         TRACE("SetWindowOrgEx: %d,%d\n", info->state.wndOrgX, info->state.wndOrgY);
+
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
         break;
       }
     case EMR_SETWINDOWEXTEX:
@@ -900,6 +904,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
             EMF_FixIsotropic(hdc, info);
 
         TRACE("SetWindowExtEx: %d,%d\n",info->state.wndExtX, info->state.wndExtY);
+
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
 	break;
       }
     case EMR_SETVIEWPORTORGEX:
@@ -909,6 +917,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
         info->state.vportOrgX = pSetViewportOrgEx->ptlOrigin.x;
         info->state.vportOrgY = pSetViewportOrgEx->ptlOrigin.y;
         TRACE("SetViewportOrgEx: %d,%d\n", info->state.vportOrgX, info->state.vportOrgY);
+
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
 	break;
       }
     case EMR_SETVIEWPORTEXTEX:
@@ -922,6 +934,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
         if (info->state.mode == MM_ISOTROPIC)
             EMF_FixIsotropic(hdc, info);
         TRACE("SetViewportExtEx: %d,%d\n", info->state.vportExtX, info->state.vportExtY);
+
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
 	break;
       }
     case EMR_CREATEPEN:
@@ -1251,6 +1267,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       {
         const EMRSETWORLDTRANSFORM *lpXfrm = (const EMRSETWORLDTRANSFORM *)mr;
         info->state.world_transform = lpXfrm->xform;
+
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
         break;
       }
 
@@ -1406,6 +1426,9 @@ BOOL WINAPI PlayEnhMetaFileRecord(
              lpScaleViewportExtEx->xNum,lpScaleViewportExtEx->xDenom,
              lpScaleViewportExtEx->yNum,lpScaleViewportExtEx->yDenom);
 
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
         break;
       }
 
@@ -1431,6 +1454,9 @@ BOOL WINAPI PlayEnhMetaFileRecord(
              lpScaleWindowExtEx->xNum,lpScaleWindowExtEx->xDenom,
              lpScaleWindowExtEx->yNum,lpScaleWindowExtEx->yDenom);
 
+        if (!IS_WIN9X())
+            EMF_Update_MF_Xform(hdc, info);
+
         break;
       }
 
@@ -1443,14 +1469,20 @@ BOOL WINAPI PlayEnhMetaFileRecord(
             info->state.world_transform.eM11 = info->state.world_transform.eM22 = 1;
             info->state.world_transform.eM12 = info->state.world_transform.eM21 = 0;
             info->state.world_transform.eDx  = info->state.world_transform.eDy  = 0;
+            if (!IS_WIN9X())
+                EMF_Update_MF_Xform(hdc, info);
             break;
         case MWT_LEFTMULTIPLY:
             CombineTransform(&info->state.world_transform, &lpModifyWorldTrans->xform,
                              &info->state.world_transform);
+            if (!IS_WIN9X())
+                ModifyWorldTransform(hdc, &lpModifyWorldTrans->xform, MWT_LEFTMULTIPLY);
             break;
         case MWT_RIGHTMULTIPLY:
             CombineTransform(&info->state.world_transform, &info->state.world_transform,
                              &lpModifyWorldTrans->xform);
+            if (!IS_WIN9X())
+                EMF_Update_MF_Xform(hdc, info);
             break;
         default:
             FIXME("Unknown imode %d\n", lpModifyWorldTrans->iMode);
@@ -2427,11 +2459,6 @@ BOOL WINAPI EnumEnhMetaFile(
 	TRACE("Calling EnumFunc with record %s, size %d\n", get_emr_name(emr->iType), emr->nSize);
 	ret = (*callback)(hdc, ht, emr, emh->nHandles, (LPARAM)data);
 	offset += emr->nSize;
-
-        /* WinNT - update the transform (win9x updates when the next graphics
-           output record is played). */
-        if (hdc && !IS_WIN9X())
-            EMF_Update_MF_Xform(hdc, info);
     }
 
     if (hdc)
diff --git a/dlls/gdi32/tests/metafile.c b/dlls/gdi32/tests/metafile.c
index eeec79c800..a6c8386f27 100644
--- a/dlls/gdi32/tests/metafile.c
+++ b/dlls/gdi32/tests/metafile.c
@@ -28,6 +28,8 @@
 #include "winuser.h"
 #include "winerror.h"
 
+#define IS_WIN9X() (GetVersion() & 0x80000000)
+
 static LOGFONTA orig_lf;
 static BOOL emr_processed = FALSE;
 
@@ -3912,6 +3914,219 @@ static void test_emf_GradientFill(void)
     DeleteEnhMetaFile( hemf );
 }
 
+struct emf_WorldTransform_test_data {
+    const char *name;
+
+    BOOL do_modify;
+    BOOL do_playback;
+
+    XFORM stored; /* this is the "hidden" transform used in PlayEnhMetaFileRecord */
+    XFORM expected; /* this is what we expect for the "actual" transform for the HDC */
+};
+
+static INT CALLBACK enum_emf_WorldTransform(HDC hdc, HANDLETABLE *ht,
+        const ENHMETARECORD *emr, INT nobj, LPARAM param)
+{
+    XFORM xform = {0};
+    struct emf_WorldTransform_test_data *test_data = (struct emf_WorldTransform_test_data *)param;
+    BOOL ret;
+
+    switch(emr->iType){
+        case EMR_SETWORLDTRANSFORM:
+            {
+                const EMRSETWORLDTRANSFORM *lpXfrm = (const EMRSETWORLDTRANSFORM *)emr;
+
+                /* initialize test_data->expected */
+                GetWorldTransform(hdc, &test_data->expected);
+
+                /* play back record */
+                ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                ok(ret == TRUE, "%s: PlayEnhMetaFileRecord failed\n", test_data->name);
+
+                CombineTransform(&test_data->expected, &test_data->expected, &lpXfrm->xform);
+
+                /* verify it is updated immediately */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "%s: After SWT playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        test_data->name, xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                test_data->stored = xform;
+
+                break;
+            }
+
+        case EMR_MODIFYWORLDTRANSFORM:
+            {
+                const EMRMODIFYWORLDTRANSFORM *lpXfrm = (const EMRMODIFYWORLDTRANSFORM *)emr;
+
+                /* make sure we're still in sync */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "%s: On MWT entry, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        test_data->name, xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                if(test_data->do_playback){
+                    /* play back record */
+                    ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                    ok(ret == TRUE, "%s: PlayEnhMetaFileRecord failed\n", test_data->name);
+
+                    if(lpXfrm->iMode == MWT_LEFTMULTIPLY){
+                        /* left multiply does not discard direct modifications */
+                        CombineTransform(&test_data->expected, &lpXfrm->xform, &test_data->expected);
+
+                        /* and updates the stored matrix separately */
+                        CombineTransform(&test_data->stored, &lpXfrm->xform, &test_data->stored);
+
+                    }else if(lpXfrm->iMode == MWT_RIGHTMULTIPLY){
+                        /* but right multiply does discard */
+                        CombineTransform(&test_data->stored, &test_data->stored, &lpXfrm->xform);
+                        test_data->expected = test_data->stored;
+                    }
+
+                    /* verify it is updated immediately */
+                    ret = GetWorldTransform(hdc, &xform);
+                    ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+                    ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                            fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                            "%s: After MWT playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                            test_data->name, xform.eM11, xform.eM22,
+                            test_data->expected.eM11, test_data->expected.eM22);
+                }
+
+                if(test_data->do_modify){
+                    /* modify directly */
+                    memset(&xform, 0, sizeof(xform));
+                    xform.eM11 = 0.5;
+                    xform.eM22 = 0.5;
+                    ret = ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+                    ok(ret == TRUE, "%s: ModifyWorldTransform failed\n", test_data->name);
+
+                    /* the modified transform persists across callback calls */
+                    CombineTransform(&test_data->expected, &xform, &test_data->expected);
+
+                    ret = GetWorldTransform(hdc, &xform);
+                    ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+                    ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                            fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                            "%s: After ModifyWT, got wrong world transform: %f, %f; expected: %f, %f\n",
+                            test_data->name, xform.eM11, xform.eM22,
+                            test_data->expected.eM11, test_data->expected.eM22);
+                }
+
+                break;
+            }
+
+        case EMR_LINETO:
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "%s: Before LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    test_data->name, xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            ok(ret == TRUE, "%s: PlayEnhMetaFileRecord failed\n", test_data->name);
+
+            /* transform doesn't change during LINETO playback */
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "%s: GetWorldTransform failed\n", test_data->name);
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "%s: After LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    test_data->name, xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            break;
+
+        default:
+            PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            break;
+    }
+
+    return 1;
+}
+
+static void test_emf_WorldTransform(void)
+{
+    HDC hdcMetafile, hdc;
+    HWND hwnd;
+    HENHMETAFILE hemf;
+    XFORM xform;
+    BOOL ret;
+    RECT rect = { 0, 0, 100, 100 };
+    int i;
+    struct emf_WorldTransform_test_data test_data[] = {
+        { "normal", FALSE, TRUE },
+        { "playback and modify", TRUE, TRUE },
+        { "manual modify", TRUE, FALSE }
+    };
+
+    if (IS_WIN9X())
+        /* win9x doesn't support world transforms */
+        return;
+
+    for(i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i){
+        hdcMetafile = CreateEnhMetaFileA(GetDC(0), NULL, NULL, NULL);
+        ok(hdcMetafile != 0, "CreateEnhMetaFileA error %d\n", GetLastError());
+
+        ret = SetGraphicsMode(hdcMetafile, GM_ADVANCED);
+        ok(ret == TRUE, "SetGraphicsMode failed\n");
+
+        memset(&xform, 0, sizeof(xform));
+        xform.eM11 = 0.5;
+        xform.eM22 = 0.5;
+        ret = SetWorldTransform(hdcMetafile, &xform); /* EMR_SETWORLDTRANSFORM */
+        ok(ret == TRUE, "SetWorldTransform failed\n");
+
+        memset(&xform, 0, sizeof(xform));
+        xform.eM11 = 2.0;
+        xform.eM22 = 2.0;
+        ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_LEFTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+        ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+        memset(&xform, 0, sizeof(xform));
+        xform.eM11 = 3.0;
+        xform.eM22 = 3.0;
+        ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_LEFTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+        ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+        memset(&xform, 0, sizeof(xform));
+        xform.eM11 = 4.0;
+        xform.eM22 = 4.0;
+        ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_RIGHTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+        ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+        ret = LineTo(hdcMetafile, 1, 1);
+        ok(ret == TRUE, "LineTo failed\n");
+
+        hemf = CloseEnhMetaFile(hdcMetafile);
+        ok(hemf != 0, "CloseEnhMetaFile error %d\n", GetLastError());
+
+        hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+                               0, 0, 200, 200, 0, 0, 0, NULL);
+        ok(hwnd != 0, "CreateWindowExA error %d\n", GetLastError());
+
+        hdc = GetDC(hwnd);
+        ok(hdc != 0, "GetDC failed\n");
+
+        ret = EnumEnhMetaFile(hdc, hemf, enum_emf_WorldTransform, &test_data[i], &rect);
+        ok(ret == TRUE, "EnumEnhMetaFile failed: %u\n", GetLastError());
+
+        ReleaseDC(hwnd, hdc);
+        DestroyWindow(hwnd);
+
+        DeleteEnhMetaFile(hemf);
+    }
+}
+
 START_TEST(metafile)
 {
     init_function_pointers();
@@ -3928,6 +4143,7 @@ START_TEST(metafile)
     test_emf_paths();
     test_emf_PolyPolyline();
     test_emf_GradientFill();
+    test_emf_WorldTransform();
 
     /* For win-format metafiles (mfdrv) */
     test_mf_SaveDC();
-- 
2.13.3




More information about the wine-patches mailing list