gdi32: Save/restore internal EMF playing state on EMR_SAVEDC/EMR_RESTOREDC, add a test for this

Dmitry Timoshkov dmitry at codeweavers.com
Mon Apr 28 02:02:31 CDT 2008


Hello,

this patch makes another part of an EMF sent to me by a customer look similar
to the one under my XP box.

This implementation is based on the SaveDC/RestoreDC one.

Changelog:
    gdi32: Save/restore internal EMF playing state on EMR_SAVEDC/EMR_RESTOREDC,
    add a test for this.
---
 dlls/gdi32/enhmetafile.c    |  244 ++++++++++++++++++++++++++-----------------
 dlls/gdi32/tests/metafile.c |  210 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 355 insertions(+), 99 deletions(-)

diff --git a/dlls/gdi32/enhmetafile.c b/dlls/gdi32/enhmetafile.c
index a3e7aa5..7c6068d 100644
--- a/dlls/gdi32/enhmetafile.c
+++ b/dlls/gdi32/enhmetafile.c
@@ -491,10 +491,9 @@ UINT WINAPI GetEnhMetaFileBits(
     return size;
 }
 
-typedef struct enum_emh_data
+typedef struct EMF_dc_state
 {
     INT   mode;
-    XFORM init_transform;
     XFORM world_transform;
     INT   wndOrgX;
     INT   wndOrgY;
@@ -504,6 +503,15 @@ typedef struct enum_emh_data
     INT   vportOrgY;
     INT   vportExtX;
     INT   vportExtY;
+    struct EMF_dc_state *next;
+} EMF_dc_state;
+
+typedef struct enum_emh_data
+{
+    XFORM init_transform;
+    EMF_dc_state state;
+    INT save_level;
+    EMF_dc_state *saved_state;
 } enum_emh_data;
 
 #define ENUM_GET_PRIVATE_DATA(ht) \
@@ -519,16 +527,16 @@ static void EMF_Update_MF_Xform(HDC hdc, const enum_emh_data *info)
     XFORM mapping_mode_trans, final_trans;
     FLOAT scaleX, scaleY;
 
-    scaleX = (FLOAT)info->vportExtX / (FLOAT)info->wndExtX;
-    scaleY = (FLOAT)info->vportExtY / (FLOAT)info->wndExtY;
+    scaleX = (FLOAT)info->state.vportExtX / (FLOAT)info->state.wndExtX;
+    scaleY = (FLOAT)info->state.vportExtY / (FLOAT)info->state.wndExtY;
     mapping_mode_trans.eM11 = scaleX;
     mapping_mode_trans.eM12 = 0.0;
     mapping_mode_trans.eM21 = 0.0;
     mapping_mode_trans.eM22 = scaleY;
-    mapping_mode_trans.eDx  = (FLOAT)info->vportOrgX - scaleX * (FLOAT)info->wndOrgX;
-    mapping_mode_trans.eDy  = (FLOAT)info->vportOrgY - scaleY * (FLOAT)info->wndOrgY;
+    mapping_mode_trans.eDx  = (FLOAT)info->state.vportOrgX - scaleX * (FLOAT)info->state.wndOrgX;
+    mapping_mode_trans.eDy  = (FLOAT)info->state.vportOrgY - scaleY * (FLOAT)info->state.wndOrgY;
 
-    CombineTransform(&final_trans, &info->world_transform, &mapping_mode_trans);
+    CombineTransform(&final_trans, &info->state.world_transform, &mapping_mode_trans);
     CombineTransform(&final_trans, &final_trans, &info->init_transform);
  
     if (!SetWorldTransform(hdc, &final_trans))
@@ -537,6 +545,40 @@ static void EMF_Update_MF_Xform(HDC hdc, const enum_emh_data *info)
     }
 }
 
+static void EMF_RestoreDC( enum_emh_data *info, INT level )
+{
+    if (abs(level) > info->save_level || level == 0) return;
+
+    if (level < 0) level = info->save_level + level + 1;
+
+    while (info->save_level >= level)
+    {
+        EMF_dc_state *state = info->saved_state;
+        info->saved_state = state->next;
+        state->next = NULL;
+        if (--info->save_level < level)
+        {
+            EMF_dc_state *next = info->state.next;
+            info->state = *state;
+            info->state.next = next;
+        }
+        HeapFree( GetProcessHeap(), 0, state );
+    }
+}
+
+static void EMF_SaveDC( enum_emh_data *info )
+{
+    EMF_dc_state *state = HeapAlloc( GetProcessHeap(), 0, sizeof(*state));
+    if (state)
+    {
+        *state = info->state;
+        state->next = info->saved_state;
+        info->saved_state = state;
+        info->save_level++;
+        TRACE("save_level %d\n", info->save_level);
+    }
+}
+
 static void EMF_SetMapMode(HDC hdc, enum_emh_data *info)
 {
     INT horzSize = GetDeviceCaps( hdc, HORZSIZE );
@@ -544,46 +586,46 @@ static void EMF_SetMapMode(HDC hdc, enum_emh_data *info)
     INT horzRes  = GetDeviceCaps( hdc, HORZRES );
     INT vertRes  = GetDeviceCaps( hdc, VERTRES );
 
-    TRACE("%d\n",info->mode);
+    TRACE("%d\n", info->state.mode);
 
-    switch(info->mode)
+    switch(info->state.mode)
     {
     case MM_TEXT:
-        info->wndExtX   = 1;
-        info->wndExtY   = 1;
-        info->vportExtX = 1;
-        info->vportExtY = 1;
+        info->state.wndExtX   = 1;
+        info->state.wndExtY   = 1;
+        info->state.vportExtX = 1;
+        info->state.vportExtY = 1;
         break;
     case MM_LOMETRIC:
     case MM_ISOTROPIC:
-        info->wndExtX   = horzSize * 10;
-        info->wndExtY   = vertSize * 10;
-        info->vportExtX = horzRes;
-        info->vportExtY = -vertRes;
+        info->state.wndExtX   = horzSize * 10;
+        info->state.wndExtY   = vertSize * 10;
+        info->state.vportExtX = horzRes;
+        info->state.vportExtY = -vertRes;
         break;
     case MM_HIMETRIC:
-        info->wndExtX   = horzSize * 100;
-        info->wndExtY   = vertSize * 100;
-        info->vportExtX = horzRes;
-        info->vportExtY = -vertRes;
+        info->state.wndExtX   = horzSize * 100;
+        info->state.wndExtY   = vertSize * 100;
+        info->state.vportExtX = horzRes;
+        info->state.vportExtY = -vertRes;
         break;
     case MM_LOENGLISH:
-        info->wndExtX   = MulDiv(1000, horzSize, 254);
-        info->wndExtY   = MulDiv(1000, vertSize, 254);
-        info->vportExtX = horzRes;
-        info->vportExtY = -vertRes;
+        info->state.wndExtX   = MulDiv(1000, horzSize, 254);
+        info->state.wndExtY   = MulDiv(1000, vertSize, 254);
+        info->state.vportExtX = horzRes;
+        info->state.vportExtY = -vertRes;
         break;
     case MM_HIENGLISH:
-        info->wndExtX   = MulDiv(10000, horzSize, 254);
-        info->wndExtY   = MulDiv(10000, vertSize, 254);
-        info->vportExtX = horzRes;
-        info->vportExtY = -vertRes;
+        info->state.wndExtX   = MulDiv(10000, horzSize, 254);
+        info->state.wndExtY   = MulDiv(10000, vertSize, 254);
+        info->state.vportExtX = horzRes;
+        info->state.vportExtY = -vertRes;
         break;
     case MM_TWIPS:
-        info->wndExtX   = MulDiv(14400, horzSize, 254);
-        info->wndExtY   = MulDiv(14400, vertSize, 254);
-        info->vportExtX = horzRes;
-        info->vportExtY = -vertRes;
+        info->state.wndExtX   = MulDiv(14400, horzSize, 254);
+        info->state.wndExtY   = MulDiv(14400, vertSize, 254);
+        info->state.vportExtX = horzRes;
+        info->state.vportExtY = -vertRes;
         break;
     case MM_ANISOTROPIC:
         break;
@@ -600,22 +642,22 @@ static void EMF_SetMapMode(HDC hdc, enum_emh_data *info)
 
 static void EMF_FixIsotropic(HDC hdc, enum_emh_data *info)
 {
-    double xdim = fabs((double)info->vportExtX * GetDeviceCaps( hdc, HORZSIZE ) /
-                  (GetDeviceCaps( hdc, HORZRES ) * info->wndExtX));
-    double ydim = fabs((double)info->vportExtY * GetDeviceCaps( hdc, VERTSIZE ) /
-                  (GetDeviceCaps( hdc, VERTRES ) * info->wndExtY));
+    double xdim = fabs((double)info->state.vportExtX * GetDeviceCaps( hdc, HORZSIZE ) /
+                  (GetDeviceCaps( hdc, HORZRES ) * info->state.wndExtX));
+    double ydim = fabs((double)info->state.vportExtY * GetDeviceCaps( hdc, VERTSIZE ) /
+                  (GetDeviceCaps( hdc, VERTRES ) * info->state.wndExtY));
 
     if (xdim > ydim)
     {
-        INT mincx = (info->vportExtX >= 0) ? 1 : -1;
-        info->vportExtX = floor(info->vportExtX * ydim / xdim + 0.5);
-        if (!info->vportExtX) info->vportExtX = mincx;
+        INT mincx = (info->state.vportExtX >= 0) ? 1 : -1;
+        info->state.vportExtX = floor(info->state.vportExtX * ydim / xdim + 0.5);
+        if (!info->state.vportExtX) info->state.vportExtX = mincx;
     }
     else
     {
-        INT mincy = (info->vportExtY >= 0) ? 1 : -1;
-        info->vportExtY = floor(info->vportExtY * xdim / ydim + 0.5);
-        if (!info->vportExtY) info->vportExtY = mincy;
+        INT mincy = (info->state.vportExtY >= 0) ? 1 : -1;
+        info->state.vportExtY = floor(info->state.vportExtY * xdim / ydim + 0.5);
+        if (!info->state.vportExtY) info->state.vportExtY = mincy;
     }
 }
 
@@ -740,9 +782,10 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       {
         const EMRSETMAPMODE *pSetMapMode = (const EMRSETMAPMODE *)mr;
 
-        if(info->mode == pSetMapMode->iMode && (info->mode == MM_ISOTROPIC || info->mode == MM_ANISOTROPIC))
+        if (info->state.mode == pSetMapMode->iMode &&
+            (info->state.mode == MM_ISOTROPIC || info->state.mode == MM_ANISOTROPIC))
             break;
-        info->mode = pSetMapMode->iMode;
+        info->state.mode = pSetMapMode->iMode;
         EMF_SetMapMode(hdc, info);
 	break;
       }
@@ -790,14 +833,16 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       }
     case EMR_SAVEDC:
       {
-	SaveDC(hdc);
+        if (SaveDC( hdc ))
+            EMF_SaveDC( info );
 	break;
       }
     case EMR_RESTOREDC:
       {
 	const EMRRESTOREDC *pRestoreDC = (const EMRRESTOREDC *)mr;
         TRACE("EMR_RESTORE: %d\n", pRestoreDC->iRelative);
-	RestoreDC(hdc, pRestoreDC->iRelative);
+        if (RestoreDC( hdc, pRestoreDC->iRelative ))
+            EMF_RestoreDC( info, pRestoreDC->iRelative );
 	break;
       }
     case EMR_INTERSECTCLIPRECT:
@@ -839,48 +884,46 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       {
     	const EMRSETWINDOWORGEX *pSetWindowOrgEx = (const EMRSETWINDOWORGEX *)mr;
 
-        info->wndOrgX = pSetWindowOrgEx->ptlOrigin.x;
-        info->wndOrgY = pSetWindowOrgEx->ptlOrigin.y;
+        info->state.wndOrgX = pSetWindowOrgEx->ptlOrigin.x;
+        info->state.wndOrgY = pSetWindowOrgEx->ptlOrigin.y;
 
-        TRACE("SetWindowOrgEx: %d,%d\n",info->wndOrgX,info->wndOrgY);
+        TRACE("SetWindowOrgEx: %d,%d\n", info->state.wndOrgX, info->state.wndOrgY);
         break;
       }
     case EMR_SETWINDOWEXTEX:
       {
 	const EMRSETWINDOWEXTEX *pSetWindowExtEx = (const EMRSETWINDOWEXTEX *)mr;
 	
-        if(info->mode != MM_ISOTROPIC && info->mode != MM_ANISOTROPIC)
+        if (info->state.mode != MM_ISOTROPIC && info->state.mode != MM_ANISOTROPIC)
 	    break;
-        info->wndExtX = pSetWindowExtEx->szlExtent.cx;
-        info->wndExtY = pSetWindowExtEx->szlExtent.cy;
-        if (info->mode == MM_ISOTROPIC)
+        info->state.wndExtX = pSetWindowExtEx->szlExtent.cx;
+        info->state.wndExtY = pSetWindowExtEx->szlExtent.cy;
+        if (info->state.mode == MM_ISOTROPIC)
             EMF_FixIsotropic(hdc, info);
 
-        TRACE("SetWindowExtEx: %d,%d\n",info->wndExtX,info->wndExtY);
+        TRACE("SetWindowExtEx: %d,%d\n",info->state.wndExtX, info->state.wndExtY);
 	break;
       }
     case EMR_SETVIEWPORTORGEX:
       {
 	const EMRSETVIEWPORTORGEX *pSetViewportOrgEx = (const EMRSETVIEWPORTORGEX *)mr;
-        enum_emh_data *info = ENUM_GET_PRIVATE_DATA(handletable);
 
-        info->vportOrgX = pSetViewportOrgEx->ptlOrigin.x;
-        info->vportOrgY = pSetViewportOrgEx->ptlOrigin.y;
-        TRACE("SetViewportOrgEx: %d,%d\n",info->vportOrgX,info->vportOrgY);
+        info->state.vportOrgX = pSetViewportOrgEx->ptlOrigin.x;
+        info->state.vportOrgY = pSetViewportOrgEx->ptlOrigin.y;
+        TRACE("SetViewportOrgEx: %d,%d\n", info->state.vportOrgX, info->state.vportOrgY);
 	break;
       }
     case EMR_SETVIEWPORTEXTEX:
       {
 	const EMRSETVIEWPORTEXTEX *pSetViewportExtEx = (const EMRSETVIEWPORTEXTEX *)mr;
-        enum_emh_data *info = ENUM_GET_PRIVATE_DATA(handletable);
 
-        if(info->mode != MM_ISOTROPIC && info->mode != MM_ANISOTROPIC)
+        if (info->state.mode != MM_ISOTROPIC && info->state.mode != MM_ANISOTROPIC)
 	    break;
-        info->vportExtX = pSetViewportExtEx->szlExtent.cx;
-        info->vportExtY = pSetViewportExtEx->szlExtent.cy;
-        if (info->mode == MM_ISOTROPIC)
+        info->state.vportExtX = pSetViewportExtEx->szlExtent.cx;
+        info->state.vportExtY = pSetViewportExtEx->szlExtent.cy;
+        if (info->state.mode == MM_ISOTROPIC)
             EMF_FixIsotropic(hdc, info);
-        TRACE("SetViewportExtEx: %d,%d\n",info->vportExtX,info->vportExtY);
+        TRACE("SetViewportExtEx: %d,%d\n", info->state.vportExtX, info->state.vportExtY);
 	break;
       }
     case EMR_CREATEPEN:
@@ -1193,7 +1236,7 @@ BOOL WINAPI PlayEnhMetaFileRecord(
     case EMR_SETWORLDTRANSFORM:
       {
         const EMRSETWORLDTRANSFORM *lpXfrm = (const EMRSETWORLDTRANSFORM *)mr;
-        info->world_transform = lpXfrm->xform;
+        info->state.world_transform = lpXfrm->xform;
         break;
       }
 
@@ -1331,18 +1374,18 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       {
         const EMRSCALEVIEWPORTEXTEX *lpScaleViewportExtEx = (const EMRSCALEVIEWPORTEXTEX *)mr;
 
-        if ((info->mode != MM_ISOTROPIC) && (info->mode != MM_ANISOTROPIC))
+        if ((info->state.mode != MM_ISOTROPIC) && (info->state.mode != MM_ANISOTROPIC))
 	    break;
         if (!lpScaleViewportExtEx->xNum || !lpScaleViewportExtEx->xDenom || 
             !lpScaleViewportExtEx->yNum || !lpScaleViewportExtEx->yDenom)
             break;
-        info->vportExtX = MulDiv(info->vportExtX, lpScaleViewportExtEx->xNum,
+        info->state.vportExtX = MulDiv(info->state.vportExtX, lpScaleViewportExtEx->xNum,
                                  lpScaleViewportExtEx->xDenom);
-        info->vportExtY = MulDiv(info->vportExtY, lpScaleViewportExtEx->yNum,
+        info->state.vportExtY = MulDiv(info->state.vportExtY, lpScaleViewportExtEx->yNum,
                                  lpScaleViewportExtEx->yDenom);
-        if (info->vportExtX == 0) info->vportExtX = 1;
-        if (info->vportExtY == 0) info->vportExtY = 1;
-        if (info->mode == MM_ISOTROPIC)
+        if (info->state.vportExtX == 0) info->state.vportExtX = 1;
+        if (info->state.vportExtY == 0) info->state.vportExtY = 1;
+        if (info->state.mode == MM_ISOTROPIC)
             EMF_FixIsotropic(hdc, info);
 
         TRACE("EMRSCALEVIEWPORTEXTEX %d/%d %d/%d\n",
@@ -1356,18 +1399,18 @@ BOOL WINAPI PlayEnhMetaFileRecord(
       {
         const EMRSCALEWINDOWEXTEX *lpScaleWindowExtEx = (const EMRSCALEWINDOWEXTEX *)mr;
 
-        if ((info->mode != MM_ISOTROPIC) && (info->mode != MM_ANISOTROPIC))
+        if ((info->state.mode != MM_ISOTROPIC) && (info->state.mode != MM_ANISOTROPIC))
 	    break;
         if (!lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->xDenom || 
             !lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->yDenom)
             break;
-        info->wndExtX = MulDiv(info->wndExtX, lpScaleWindowExtEx->xNum,
+        info->state.wndExtX = MulDiv(info->state.wndExtX, lpScaleWindowExtEx->xNum,
                                lpScaleWindowExtEx->xDenom);
-        info->wndExtY = MulDiv(info->wndExtY, lpScaleWindowExtEx->yNum,
+        info->state.wndExtY = MulDiv(info->state.wndExtY, lpScaleWindowExtEx->yNum,
                                lpScaleWindowExtEx->yDenom);
-        if (info->wndExtX == 0) info->wndExtX = 1;
-        if (info->wndExtY == 0) info->wndExtY = 1;
-        if (info->mode == MM_ISOTROPIC)
+        if (info->state.wndExtX == 0) info->state.wndExtX = 1;
+        if (info->state.wndExtY == 0) info->state.wndExtY = 1;
+        if (info->state.mode == MM_ISOTROPIC)
             EMF_FixIsotropic(hdc, info);
 
         TRACE("EMRSCALEWINDOWEXTEX %d/%d %d/%d\n",
@@ -1383,16 +1426,16 @@ BOOL WINAPI PlayEnhMetaFileRecord(
 
         switch(lpModifyWorldTrans->iMode) {
         case MWT_IDENTITY:
-            info->world_transform.eM11 = info->world_transform.eM22 = 1;
-            info->world_transform.eM12 = info->world_transform.eM21 = 0;
-            info->world_transform.eDx  = info->world_transform.eDy  = 0;
+            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;
             break;
         case MWT_LEFTMULTIPLY:
-            CombineTransform(&info->world_transform, &lpModifyWorldTrans->xform,
-                             &info->world_transform);
+            CombineTransform(&info->state.world_transform, &lpModifyWorldTrans->xform,
+                             &info->state.world_transform);
             break;
         case MWT_RIGHTMULTIPLY:
-            CombineTransform(&info->world_transform, &info->world_transform,
+            CombineTransform(&info->state.world_transform, &info->state.world_transform,
                              &lpModifyWorldTrans->xform);
             break;
         default:
@@ -2243,17 +2286,20 @@ BOOL WINAPI EnumEnhMetaFile(
 	SetLastError(ERROR_NOT_ENOUGH_MEMORY);
 	return FALSE;
     }
-    info->wndOrgX = 0;
-    info->wndOrgY = 0;
-    info->wndExtX = 1;
-    info->wndExtY = 1;
-    info->vportOrgX = 0;
-    info->vportOrgY = 0;
-    info->vportExtX = 1;
-    info->vportExtY = 1;
-    info->world_transform.eM11 = info->world_transform.eM22 = 1;
-    info->world_transform.eM12 = info->world_transform.eM21 = 0;
-    info->world_transform.eDx  = info->world_transform.eDy =  0;
+    info->state.wndOrgX = 0;
+    info->state.wndOrgY = 0;
+    info->state.wndExtX = 1;
+    info->state.wndExtY = 1;
+    info->state.vportOrgX = 0;
+    info->state.vportOrgY = 0;
+    info->state.vportExtX = 1;
+    info->state.vportExtY = 1;
+    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;
+
+    info->save_level = 0;
+    info->saved_state = NULL;
 
     ht = (HANDLETABLE*) &info[1];
     ht->objectHandle[0] = hmf;
@@ -2282,7 +2328,7 @@ BOOL WINAPI EnumEnhMetaFile(
         old_stretchblt = SetStretchBltMode(hdc, BLACKONWHITE);
     }
 
-    info->mode = MM_TEXT;
+    info->state.mode = MM_TEXT;
 
     if ( IS_WIN9X() )
     {
@@ -2386,6 +2432,12 @@ BOOL WINAPI EnumEnhMetaFile(
         if( (ht->objectHandle)[i] )
 	    DeleteObject( (ht->objectHandle)[i] );
 
+    while (info->saved_state)
+    {
+        EMF_dc_state *state = info->saved_state;
+        info->saved_state = info->saved_state->next;
+        HeapFree( GetProcessHeap(), 0, state );
+    }
     HeapFree( GetProcessHeap(), 0, info );
     return ret;
 }
diff --git a/dlls/gdi32/tests/metafile.c b/dlls/gdi32/tests/metafile.c
index 473f7e3..0b50d58 100644
--- a/dlls/gdi32/tests/metafile.c
+++ b/dlls/gdi32/tests/metafile.c
@@ -261,37 +261,174 @@ static void test_ExtTextOut(void)
     DestroyWindow(hwnd);
 }
 
+static void check_dc_state(HDC hdc, int restore_no,
+                           int wnd_org_x, int wnd_org_y, int wnd_ext_x, int wnd_ext_y,
+                           int vp_org_x, int vp_org_y, int vp_ext_x, int vp_ext_y)
+{
+    BOOL ret;
+    XFORM xform;
+    POINT vp_org, win_org;
+    SIZE vp_size, win_size;
+    FLOAT xscale, yscale, edx, edy;
+
+    SetLastError(0xdeadbeef);
+    ret = GetWorldTransform(hdc, &xform);
+    if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) goto win9x_here;
+    ok(ret, "GetWorldTransform error %u\n", GetLastError());
+
+    trace("%d: eM11 %f, eM22 %f, eDx %f, eDy %f\n", restore_no, xform.eM11, xform.eM22, xform.eDx, xform.eDy);
+
+    ok(xform.eM12 == 0.0, "%d: expected eM12 0.0, got %f\n", restore_no, xform.eM12);
+    ok(xform.eM21 == 0.0, "%d: expected eM21 0.0, got %f\n", restore_no, xform.eM21);
+
+    xscale = (FLOAT)vp_ext_x / (FLOAT)wnd_ext_x;
+    trace("x scale %f\n", xscale);
+    ok(fabs(xscale - xform.eM11) < 0.01, "%d: vp_ext_x %d, wnd_ext_cx %d, eM11 %f\n",
+       restore_no, vp_ext_x, wnd_ext_x, xform.eM11);
+
+    yscale = (FLOAT)vp_ext_y / (FLOAT)wnd_ext_y;
+    trace("y scale %f\n", yscale);
+    ok(fabs(yscale - xform.eM22) < 0.01, "%d: vp_ext_y %d, wnd_ext_y %d, eM22 %f\n",
+       restore_no, vp_ext_y, wnd_ext_y, xform.eM22);
+
+    edx = (FLOAT)vp_org_x - xform.eM11 * (FLOAT)wnd_org_x;
+    ok(fabs(edx - xform.eDx) < 0.01, "%d: edx %f != eDx %f\n", restore_no, edx, xform.eDx);
+    edy = (FLOAT)vp_org_y - xform.eM22 * (FLOAT)wnd_org_y;
+    ok(fabs(edy - xform.eDy) < 0.01, "%d: edy %f != eDy %f\n", restore_no, edy, xform.eDy);
+
+    return;
+
+win9x_here:
+
+    GetWindowOrgEx(hdc, &win_org);
+    GetViewportOrgEx(hdc, &vp_org);
+    GetWindowExtEx(hdc, &win_size);
+    GetViewportExtEx(hdc, &vp_size);
+
+    ok(wnd_org_x == win_org.x, "%d: wnd_org_x: %d != %d\n", restore_no, wnd_org_x, win_org.x);
+    ok(wnd_org_y == win_org.y, "%d: wnd_org_y: %d != %d\n", restore_no, wnd_org_y, win_org.y);
+
+    ok(vp_org_x == vp_org.x, "%d: vport_org_x: %d != %d\n", restore_no, vp_org_x, vp_org.x);
+    ok(vp_org_y == vp_org.y, "%d: vport_org_y: %d != %d\n", restore_no, vp_org_y, vp_org.y);
+
+    ok(wnd_ext_x == win_size.cx, "%d: wnd_ext_x: %d != %d\n", restore_no, wnd_ext_x, win_size.cx);
+    ok(wnd_ext_y == win_size.cy, "%d: wnd_ext_y: %d != %d\n", restore_no, wnd_ext_y, win_size.cy);
+
+    ok(vp_ext_x == vp_size.cx, "%d: vport_ext_x: %d != %d\n", restore_no, vp_ext_x, vp_size.cx);
+    ok(vp_ext_y == vp_size.cy, "%d: vport_ext_y: %d != %d\n", restore_no, vp_ext_y, vp_size.cy);
+}
+
 static int CALLBACK savedc_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
                                          const ENHMETARECORD *emr, int n_objs, LPARAM param)
 {
+    BOOL ret;
+    XFORM xform;
+    POINT pt;
+    SIZE size;
     static int save_state;
     static int restore_no;
 
+    trace("hdc %p, emr->iType %d, emr->nSize %d, param %p\n",
+           hdc, emr->iType, emr->nSize, (void *)param);
+
+    trace("BEFORE:\n");
+    SetLastError(0xdeadbeef);
+    ret = GetWorldTransform(hdc, &xform);
+    if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        ok(GetWindowOrgEx(hdc, &pt), "GetWindowOrgEx error %u\n", GetLastError());
+        trace("window org (%d,%d)\n", pt.x, pt.y);
+        ok(GetViewportOrgEx(hdc, &pt), "GetViewportOrgEx error %u\n", GetLastError());
+        trace("vport org (%d,%d)\n", pt.x, pt.y);
+        ok(GetWindowExtEx(hdc, &size), "GetWindowExtEx error %u\n", GetLastError());
+        trace("window ext (%d,%d)\n", size.cx, size.cy);
+        ok(GetViewportExtEx(hdc, &size), "GetViewportExtEx error %u\n", GetLastError());
+        trace("vport ext (%d,%d)\n", size.cx, size.cy);
+    }
+    else
+    {
+        ok(ret, "GetWorldTransform error %u\n", GetLastError());
+        trace("eM11 %f, eM22 %f, eDx %f, eDy %f\n", xform.eM11, xform.eM22, xform.eDx, xform.eDy);
+    }
+
+    PlayEnhMetaFileRecord(hdc, handle_table, emr, n_objs);
+
     switch (emr->iType)
     {
     case EMR_HEADER:
+    {
+        static RECT exp_bounds = { 0, 0, 150, 150 };
+        RECT bounds;
+        const ENHMETAHEADER *emf = (const ENHMETAHEADER *)emr;
+
+        trace("bounds %d,%d-%d,%d, frame %d,%d-%d,%d\n",
+               emf->rclBounds.left, emf->rclBounds.top, emf->rclBounds.right, emf->rclBounds.bottom,
+               emf->rclFrame.left, emf->rclFrame.top, emf->rclFrame.right, emf->rclFrame.bottom);
+        trace("mm %d x %d, device %d x %d\n", emf->szlMillimeters.cx, emf->szlMillimeters.cy,
+               emf->szlDevice.cx, emf->szlDevice.cy);
+
+        SetRect(&bounds, emf->rclBounds.left, emf->rclBounds.top, emf->rclBounds.right, emf->rclBounds.bottom);
+        ok(EqualRect(&bounds, &exp_bounds), "wrong bounds\n");
+
         save_state = 0;
         restore_no = 0;
+        check_dc_state(hdc, restore_no, 0, 0, 1, 1, 0, 0, 1, 1);
         break;
+    }
 
+    case EMR_LINETO:
+        {
+            const EMRLINETO *line = (const EMRLINETO *)emr;
+            trace("EMR_LINETO %d,%d\n", line->ptl.x, line->ptl.x);
+            break;
+        }
+    case EMR_SETWINDOWORGEX:
+        {
+            const EMRSETWINDOWORGEX *org = (const EMRSETWINDOWORGEX *)emr;
+            trace("EMR_SETWINDOWORGEX: %d,%d\n", org->ptlOrigin.x, org->ptlOrigin.y);
+            break;
+        }
+    case EMR_SETWINDOWEXTEX:
+        {
+            const EMRSETWINDOWEXTEX *ext = (const EMRSETWINDOWEXTEX *)emr;
+            trace("EMR_SETWINDOWEXTEX: %d,%d\n", ext->szlExtent.cx, ext->szlExtent.cy);
+            break;
+        }
+    case EMR_SETVIEWPORTORGEX:
+        {
+            const EMRSETVIEWPORTORGEX *org = (const EMRSETVIEWPORTORGEX *)emr;
+            trace("EMR_SETVIEWPORTORGEX: %d,%d\n", org->ptlOrigin.x, org->ptlOrigin.y);
+            break;
+        }
+    case EMR_SETVIEWPORTEXTEX:
+        {
+            const EMRSETVIEWPORTEXTEX *ext = (const EMRSETVIEWPORTEXTEX *)emr;
+            trace("EMR_SETVIEWPORTEXTEX: %d,%d\n", ext->szlExtent.cx, ext->szlExtent.cy);
+            break;
+        }
     case EMR_SAVEDC:
         save_state++;
+        trace("EMR_SAVEDC\n");
         break;
 
     case EMR_RESTOREDC:
         {
             const EMRRESTOREDC *restoredc = (const EMRRESTOREDC *)emr;
+            trace("EMR_RESTOREDC: %d\n", restoredc->iRelative);
+
             switch(++restore_no)
             {
             case 1:
                 ok(restoredc->iRelative == -1, "first restore %d\n", restoredc->iRelative);
+                check_dc_state(hdc, restore_no, -2, -2, 8192, 8192, 20, 20, 20479, 20478);
                 break;
-
             case 2:
                 ok(restoredc->iRelative == -3, "second restore %d\n", restoredc->iRelative);
+                check_dc_state(hdc, restore_no, 0, 0, 16384, 16384, 0, 0, 17873, 17872);
                 break;
             case 3:
                 ok(restoredc->iRelative == -2, "third restore %d\n", restoredc->iRelative);
+                check_dc_state(hdc, restore_no, -4, -4, 32767, 32767, 40, 40, 3276, 3276);
                 break;
             }
             ok(restore_no <= 3, "restore_no %d\n", restore_no);
@@ -302,6 +439,27 @@ static int CALLBACK savedc_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
         ok(save_state == 0, "EOF save_state %d\n", save_state);
         break;
     }
+
+    trace("AFTER:\n");
+    SetLastError(0xdeadbeef);
+    ret = GetWorldTransform(hdc, &xform);
+    if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        ok(GetWindowOrgEx(hdc, &pt), "GetWindowOrgEx error %u\n", GetLastError());
+        trace("window org (%d,%d)\n", pt.x, pt.y);
+        ok(GetViewportOrgEx(hdc, &pt), "GetViewportOrgEx error %u\n", GetLastError());
+        trace("vport org (%d,%d)\n", pt.x, pt.y);
+        ok(GetWindowExtEx(hdc, &size), "GetWindowExtEx error %u\n", GetLastError());
+        trace("window ext (%d,%d)\n", size.cx, size.cy);
+        ok(GetViewportExtEx(hdc, &size), "GetViewportExtEx error %u\n", GetLastError());
+        trace("vport ext (%d,%d)\n", size.cx, size.cy);
+    }
+    else
+    {
+        ok(ret, "GetWorldTransform error %u\n", GetLastError());
+        trace("eM11 %f, eM22 %f, eDx %f, eDy %f\n", xform.eM11, xform.eM22, xform.eDx, xform.eDy);
+    }
+
     return 1;
 }
 
@@ -311,7 +469,7 @@ static void test_SaveDC(void)
     HENHMETAFILE hMetafile;
     HWND hwnd;
     int ret;
-    static const RECT rc = { 0, 0, 100, 100 };
+    static const RECT rc = { 0, 0, 150, 150 };
 
     /* Win9x doesn't play EMFs on invisible windows */
     hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
@@ -324,18 +482,52 @@ static void test_SaveDC(void)
     hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
     ok(hdcMetafile != 0, "CreateEnhMetaFileA error %d\n", GetLastError());
 
+    SetMapMode(hdcMetafile, MM_ANISOTROPIC);
+
     /* Need to write something to the emf, otherwise Windows won't play it back */
-    LineTo(hdcMetafile, 100, 100);
+    LineTo(hdcMetafile, 150, 150);
+
+    SetWindowOrgEx(hdcMetafile, 0, 0, NULL);
+    SetViewportOrgEx(hdcMetafile, 0, 0, NULL);
+    SetWindowExtEx(hdcMetafile, 110, 110, NULL );
+    SetViewportExtEx(hdcMetafile, 120, 120, NULL );
+
+    /* Force Win9x to update DC state */
+    SetPixelV(hdcMetafile, 50, 50, 0);
 
     ret = SaveDC(hdcMetafile);
     ok(ret == 1, "ret = %d\n", ret);
 
+    SetWindowOrgEx(hdcMetafile, -1, -1, NULL);
+    SetViewportOrgEx(hdcMetafile, 10, 10, NULL);
+    SetWindowExtEx(hdcMetafile, 150, 150, NULL );
+    SetViewportExtEx(hdcMetafile, 200, 200, NULL );
+
+    /* Force Win9x to update DC state */
+    SetPixelV(hdcMetafile, 50, 50, 0);
+
     ret = SaveDC(hdcMetafile);
     ok(ret == 2, "ret = %d\n", ret);
 
+    SetWindowOrgEx(hdcMetafile, -2, -2, NULL);
+    SetViewportOrgEx(hdcMetafile, 20, 20, NULL);
+    SetWindowExtEx(hdcMetafile, 120, 120, NULL );
+    SetViewportExtEx(hdcMetafile, 300, 300, NULL );
+
+    /* Force Win9x to update DC state */
+    SetPixelV(hdcMetafile, 50, 50, 0);
+
     ret = SaveDC(hdcMetafile);
     ok(ret == 3, "ret = %d\n", ret);
 
+    SetWindowOrgEx(hdcMetafile, -3, -3, NULL);
+    SetViewportOrgEx(hdcMetafile, 30, 30, NULL);
+    SetWindowExtEx(hdcMetafile, 200, 200, NULL );
+    SetViewportExtEx(hdcMetafile, 400, 400, NULL );
+
+    /* Force Win9x to update DC state */
+    SetPixelV(hdcMetafile, 50, 50, 0);
+
     ret = RestoreDC(hdcMetafile, -1);
     ok(ret, "ret = %d\n", ret);
 
@@ -345,6 +537,14 @@ static void test_SaveDC(void)
     ret = RestoreDC(hdcMetafile, 1);
     ok(ret, "ret = %d\n", ret);
 
+    SetWindowOrgEx(hdcMetafile, -4, -4, NULL);
+    SetViewportOrgEx(hdcMetafile, 40, 40, NULL);
+    SetWindowExtEx(hdcMetafile, 500, 500, NULL );
+    SetViewportExtEx(hdcMetafile, 50, 50, NULL );
+
+    /* Force Win9x to update DC state */
+    SetPixelV(hdcMetafile, 50, 50, 0);
+
     ret = SaveDC(hdcMetafile);
     ok(ret == 1, "ret = %d\n", ret);
 
@@ -1282,12 +1482,16 @@ static int CALLBACK clip_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
         rect = rgn2.data.rdh.rcBound;
         rc_transformed = *rc;
         translate((POINT *)&rc_transformed, 2, &xform);
+        trace("transformed (%d,%d-%d,%d)\n", rc_transformed.left, rc_transformed.top,
+              rc_transformed.right, rc_transformed.bottom);
         ok(EqualRect(&rect, &rc_transformed), "rects don't match\n");
 
         rect = *(const RECT *)rgn2.data.Buffer;
         trace("rect (%d,%d-%d,%d)\n", rect.left, rect.top, rect.right, rect.bottom);
         rc_transformed = *rc;
         translate((POINT *)&rc_transformed, 2, &xform);
+        trace("transformed (%d,%d-%d,%d)\n", rc_transformed.left, rc_transformed.top,
+              rc_transformed.right, rc_transformed.bottom);
         ok(EqualRect(&rect, &rc_transformed), "rects don't match\n");
 
         ok(rgn2.data.rdh.dwSize == sizeof(rgn1->data.rdh), "expected sizeof(rdh), got %u", rgn2.data.rdh.dwSize);
-- 
1.5.5.1






More information about the wine-patches mailing list