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

Andrew Eikum aeikum at codeweavers.com
Wed Jul 19 14:06:29 CDT 2017


This is a partial revert of d1fe894a. The Chrome browser print function
implements its 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 original transform is restored
before the record is played back.

Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
 dlls/gdi32/enhmetafile.c    |  33 ++-
 dlls/gdi32/tests/metafile.c | 505 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 533 insertions(+), 5 deletions(-)

diff --git a/dlls/gdi32/enhmetafile.c b/dlls/gdi32/enhmetafile.c
index cffac375f9..299b896bf0 100644
--- a/dlls/gdi32/enhmetafile.c
+++ b/dlls/gdi32/enhmetafile.c
@@ -729,6 +729,21 @@ static BOOL emr_produces_output(int type)
     }
 }
 
+static BOOL emr_modifies_transform(int type)
+{
+    switch(type) {
+    case EMR_SCALEVIEWPORTEXTEX:
+    case EMR_SCALEWINDOWEXTEX:
+    case EMR_SETVIEWPORTEXTEX:
+    case EMR_SETVIEWPORTORGEX:
+    case EMR_SETWINDOWEXTEX:
+    case EMR_SETWINDOWORGEX:
+    case EMR_SETWORLDTRANSFORM:
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
 
 /*****************************************************************************
  *           PlayEnhMetaFileRecord  (GDI32.@)
@@ -1443,14 +1458,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);
@@ -2216,6 +2237,13 @@ BOOL WINAPI PlayEnhMetaFileRecord(
   LPtoDP(hdc, (POINT*)&tmprc, 2);
   TRACE("L:0,0 - 1000,1000 -> D:%s\n", wine_dbgstr_rect(&tmprc));
 
+  if (emr_modifies_transform( mr->iType ) && !IS_WIN9X())
+  {
+      /* WinNT - update the transform (win9x updates when the next graphics
+         output record is played). */
+      EMF_Update_MF_Xform(hdc, info);
+  }
+
   return TRUE;
 }
 
@@ -2427,11 +2455,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..eaad666361 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,506 @@ static void test_emf_GradientFill(void)
     DeleteEnhMetaFile( hemf );
 }
 
+struct emf_WorldTransform_test_data {
+    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_simple(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, "PlayEnhMetaFileRecord failed\n");
+
+                test_data->expected.eM11 *= lpXfrm->xform.eM11;
+                test_data->expected.eM22 *= lpXfrm->xform.eM22;
+
+                /* verify it is updated immediately */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "After SWT playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                break;
+            }
+
+        case EMR_MODIFYWORLDTRANSFORM:
+            {
+                const EMRMODIFYWORLDTRANSFORM *lpXfrm = (const EMRMODIFYWORLDTRANSFORM *)emr;
+
+                /* make sure we're still in sync */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "On MWT entry, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                /* play back record */
+                ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+                if(lpXfrm->iMode == MWT_LEFTMULTIPLY ||
+                        lpXfrm->iMode == MWT_RIGHTMULTIPLY)
+                {
+                    test_data->expected.eM11 *= lpXfrm->xform.eM11;
+                    test_data->expected.eM22 *= lpXfrm->xform.eM22;
+                }
+
+                /* verify it is updated immediately */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "After MWT playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                break;
+            }
+
+        case EMR_LINETO:
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "Before LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+            /* transform doesn't change during LINETO playback */
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "After LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            break;
+
+        default:
+            PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            break;
+    }
+
+    return 1;
+}
+
+/* verify simple playback of left & right multiply modifications */
+static void test_emf_WorldTransform_simple(void)
+{
+    HDC hdcMetafile, hdc;
+    HWND hwnd;
+    HENHMETAFILE hemf;
+    XFORM xform;
+    BOOL ret;
+    RECT rect = { 0, 0, 100, 100 };
+    struct emf_WorldTransform_test_data test_data;
+
+    if (IS_WIN9X())
+        /* win9x doesn't support world transforms */
+        return;
+
+    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_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_simple, &test_data, &rect);
+    ok(ret == TRUE, "EnumEnhMetaFile failed: %u\n", GetLastError());
+
+    ReleaseDC(hwnd, hdc);
+    DestroyWindow(hwnd);
+
+    DeleteEnhMetaFile(hemf);
+}
+
+static INT CALLBACK enum_emf_WorldTransform_modify(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:
+            {
+                /* play back record */
+                ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+                /* initialize test_data->expected */
+                GetWorldTransform(hdc, &test_data->expected);
+
+                break;
+            }
+
+        case EMR_MODIFYWORLDTRANSFORM:
+            {
+                /* make sure we're still in sync */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "On MWT entry, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                /* don't play back the record, instead modify directly */
+                memset(&xform, 0, sizeof(xform));
+                xform.eM11 = 0.5;
+                xform.eM22 = 0.5;
+                ret = ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+                ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+                /* the modified transform persists across callback calls */
+                test_data->expected.eM11 *= xform.eM11;
+                test_data->expected.eM22 *= xform.eM22;
+
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "After ModifyWT, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                break;
+            }
+
+        case EMR_LINETO:
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "Before LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+            /* transform doesn't change during LINETO playback */
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "After LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            break;
+
+        default:
+            PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            break;
+    }
+
+    return 1;
+}
+
+/* demonstrate that modifications during callbacks persist across callbacks */
+static void test_emf_WorldTransform_modify(void)
+{
+    HDC hdcMetafile, hdc;
+    HWND hwnd;
+    HENHMETAFILE hemf;
+    XFORM xform;
+    BOOL ret;
+    RECT rect = { 0, 0, 100, 100 };
+    struct emf_WorldTransform_test_data test_data;
+
+    if (IS_WIN9X())
+        /* win9x doesn't support world transforms */
+        return;
+
+    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 = 1.0;
+    xform.eM22 = 1.0;
+    ret = SetWorldTransform(hdcMetafile, &xform); /* EMR_SETWORLDTRANSFORM */
+    ok(ret == TRUE, "SetWorldTransform failed\n");
+
+    memset(&xform, 0, sizeof(xform));
+    xform.eM11 = 1.0;
+    xform.eM22 = 1.0;
+    ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_LEFTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+    ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+    memset(&xform, 0, sizeof(xform));
+    xform.eM11 = 1.0;
+    xform.eM22 = 1.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_modify, &test_data, &rect);
+    ok(ret == TRUE, "EnumEnhMetaFile failed: %u\n", GetLastError());
+
+    ReleaseDC(hwnd, hdc);
+    DestroyWindow(hwnd);
+
+    DeleteEnhMetaFile(hemf);
+}
+
+static INT CALLBACK enum_emf_WorldTransform_modified_playback(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:
+            {
+                /* play back record */
+                ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+                /* initialize test_data->expected */
+                GetWorldTransform(hdc, &test_data->expected);
+                test_data->stored = test_data->expected;
+
+                break;
+            }
+
+        case EMR_MODIFYWORLDTRANSFORM:
+            {
+                const EMRMODIFYWORLDTRANSFORM *lpXfrm = (const EMRMODIFYWORLDTRANSFORM *)emr;
+
+                /* make sure we're still in sync */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "On MWT entry, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                /* play back record */
+                ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+                ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+                if(lpXfrm->iMode == MWT_LEFTMULTIPLY)
+                {
+                    /* left multiply does not discard direct modifications */
+                    test_data->expected.eM11 *= lpXfrm->xform.eM11;
+                    test_data->expected.eM22 *= lpXfrm->xform.eM22;
+
+                    /* and updates the stored matrix separately */
+                    test_data->stored.eM11 *= lpXfrm->xform.eM11;
+                    test_data->stored.eM22 *= lpXfrm->xform.eM22;
+                }
+                else if(lpXfrm->iMode == MWT_RIGHTMULTIPLY)
+                {
+                    /* but right multiply does discard */
+                    test_data->expected.eM11 = test_data->stored.eM11 * lpXfrm->xform.eM11;
+                    test_data->expected.eM22 = test_data->stored.eM22 * lpXfrm->xform.eM22;
+                }
+
+                /* verify it is updated immediately */
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "After MWT playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                /* modify directly */
+                memset(&xform, 0, sizeof(xform));
+                xform.eM11 = 0.5;
+                xform.eM22 = 0.5;
+                ret = ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
+                ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+                /* the modified transform persists across callback calls */
+                test_data->expected.eM11 *= xform.eM11;
+                test_data->expected.eM22 *= xform.eM22;
+
+                ret = GetWorldTransform(hdc, &xform);
+                ok(ret == TRUE, "GetWorldTransform failed\n");
+                ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                        fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                        "After ModifyWT, got wrong world transform: %f, %f; expected: %f, %f\n",
+                        xform.eM11, xform.eM22,
+                        test_data->expected.eM11, test_data->expected.eM22);
+
+                break;
+            }
+
+        case EMR_LINETO:
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "Before LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            ret = PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            ok(ret == TRUE, "PlayEnhMetaFileRecord failed\n");
+
+            /* transform doesn't change during LINETO playback */
+            ret = GetWorldTransform(hdc, &xform);
+            ok(ret == TRUE, "GetWorldTransform failed\n");
+            ok(fabs(xform.eM11 - test_data->expected.eM11) < 0.1 &&
+                    fabs(xform.eM22 - test_data->expected.eM22) < 0.1,
+                    "After LINETO playback, got wrong world transform: %f, %f; expected: %f, %f\n",
+                    xform.eM11, xform.eM22,
+                    test_data->expected.eM11, test_data->expected.eM22);
+
+            break;
+
+        default:
+            PlayEnhMetaFileRecord(hdc, ht, emr, nobj);
+            break;
+    }
+
+    return 1;
+}
+
+/* demonstrate that right multiplication record playback discards direct
+ * modifications during callbacks, but left playback doesn't */
+static void test_emf_WorldTransform_modified_playback(void)
+{
+    HDC hdcMetafile, hdc;
+    HWND hwnd;
+    HENHMETAFILE hemf;
+    XFORM xform;
+    BOOL ret;
+    RECT rect = { 0, 0, 100, 100 };
+    struct emf_WorldTransform_test_data test_data;
+
+    if (IS_WIN9X())
+        /* win9x doesn't support world transforms */
+        return;
+
+    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 = 1.0;
+    xform.eM22 = 1.0;
+    ret = SetWorldTransform(hdcMetafile, &xform); /* EMR_SETWORLDTRANSFORM */
+    ok(ret == TRUE, "SetWorldTransform failed\n");
+
+    memset(&xform, 0, sizeof(xform));
+    xform.eM11 = 1.0;
+    xform.eM22 = 1.0;
+    ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_LEFTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+    ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+    memset(&xform, 0, sizeof(xform));
+    xform.eM11 = 1.0;
+    xform.eM22 = 1.0;
+    ret = ModifyWorldTransform(hdcMetafile, &xform, MWT_LEFTMULTIPLY); /* EMR_MODIFYWORLDTRANSFORM */
+    ok(ret == TRUE, "ModifyWorldTransform failed\n");
+
+    memset(&xform, 0, sizeof(xform));
+    xform.eM11 = 1.0;
+    xform.eM22 = 1.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_modified_playback, &test_data, &rect);
+    ok(ret == TRUE, "EnumEnhMetaFile failed: %u\n", GetLastError());
+
+    ReleaseDC(hwnd, hdc);
+    DestroyWindow(hwnd);
+
+    DeleteEnhMetaFile(hemf);
+}
+
 START_TEST(metafile)
 {
     init_function_pointers();
@@ -3928,6 +4430,9 @@ START_TEST(metafile)
     test_emf_paths();
     test_emf_PolyPolyline();
     test_emf_GradientFill();
+    test_emf_WorldTransform_simple();
+    test_emf_WorldTransform_modify();
+    test_emf_WorldTransform_modified_playback();
 
     /* For win-format metafiles (mfdrv) */
     test_mf_SaveDC();
-- 
2.13.3




More information about the wine-patches mailing list