gdi: SaveDC/RestoreDC

Huw D M Davies h.davies1 at physics.ox.ac.uk
Sun Oct 30 12:24:58 CST 2005


        Huw Davies <huw at codeweavers.com>
        Fix handling of relative state indices in RestoreDC.
        Fix return value of SaveDC when writing to an emf.
        Before writing the EMR_EOF record we should ensure that we
        clear the state stack.

Index: dlls/gdi/dc.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/dc.c,v
retrieving revision 1.13
diff -u -p -r1.13 dc.c
--- dlls/gdi/dc.c	29 Oct 2005 10:29:56 -0000	1.13
+++ dlls/gdi/dc.c	30 Oct 2005 18:15:30 -0000
@@ -504,8 +504,9 @@ INT WINAPI SaveDC( HDC hdc )
     if(dc->funcs->pSaveDC)
     {
         ret = dc->funcs->pSaveDC( dc->physDev );
+        if(ret)
+            ret = ++dc->saveLevel;
         GDI_ReleaseObj( hdc );
-        /* FIXME: ret is just a success flag, we should return a proper value */
         return ret;
     }
 
@@ -551,26 +552,24 @@ BOOL WINAPI RestoreDC( HDC hdc, INT leve
     TRACE("%p %d\n", hdc, level );
     dc = DC_GetDCUpdate( hdc );
     if(!dc) return FALSE;
-    if(dc->funcs->pRestoreDC)
+
+    if(abs(level) > dc->saveLevel || level == 0)
     {
-        success = dc->funcs->pRestoreDC( dc->physDev, level );
         GDI_ReleaseObj( hdc );
-        return success;
+        return FALSE;
     }
-
-    if (level == -1) level = dc->saveLevel;
-    if ((level < 1)
-            /* This pair of checks disagrees with MSDN "Platform SDK:
-               Windows GDI" July 2000 which says all negative values
-               for level will be interpreted as an instance relative
-               to the current state.  Restricting it to just -1 does
-               not satisfy this */
-	|| (level > dc->saveLevel))
+        
+    if(dc->funcs->pRestoreDC)
     {
+        success = dc->funcs->pRestoreDC( dc->physDev, level );
+        if(level < 0) level = dc->saveLevel + level + 1;
+        if(success)
+            dc->saveLevel = level - 1;
         GDI_ReleaseObj( hdc );
-        return FALSE;
+        return success;
     }
 
+    if (level < 0) level = dc->saveLevel + level + 1;
     success=TRUE;
     while (dc->saveLevel >= level)
     {
Index: dlls/gdi/enhmfdrv/dc.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/enhmfdrv/dc.c,v
retrieving revision 1.9
diff -u -p -r1.9 dc.c
--- dlls/gdi/enhmfdrv/dc.c	10 Oct 2003 00:06:59 -0000	1.9
+++ dlls/gdi/enhmfdrv/dc.c	30 Oct 2005 18:15:30 -0000
@@ -38,19 +38,15 @@ BOOL EMFDRV_RestoreDC( PHYSDEV dev, INT 
 
     emr.emr.iType = EMR_RESTOREDC;
     emr.emr.nSize = sizeof(emr);
-    emr.iRelative = -1;
-    if (level == -1) 
-        return EMFDRV_WriteRecord( dev, &emr.emr );
-    else if (level > 0 && level <= physDev->dc->saveLevel) 
-    {
-        while (level >= physDev->dc->saveLevel) 
-        {
-            EMFDRV_WriteRecord( dev, &emr.emr );
-            level--;
-        }
-        return TRUE;
-    }
-    return FALSE;
+
+    if (level < 0)
+        emr.iRelative = level;
+    else
+        emr.iRelative = level - physDev->dc->saveLevel - 1;
+
+    EMFDRV_WriteRecord( dev, &emr.emr );
+
+    return TRUE;
 }
 
 UINT EMFDRV_SetTextAlign( PHYSDEV dev, UINT align )
Index: dlls/gdi/enhmfdrv/init.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/enhmfdrv/init.c,v
retrieving revision 1.32
diff -u -p -r1.32 init.c
--- dlls/gdi/enhmfdrv/init.c	24 Mar 2005 21:01:38 -0000	1.32
+++ dlls/gdi/enhmfdrv/init.c	30 Oct 2005 18:15:31 -0000
@@ -422,6 +422,9 @@ HENHMETAFILE WINAPI CloseEnhMetaFile(HDC
     if (!(dc = (DC *) GDI_GetObjPtr( hdc, ENHMETAFILE_DC_MAGIC ))) return 0;
     physDev = (EMFDRV_PDEVICE *)dc->physDev;
 
+    if(dc->saveLevel)
+        RestoreDC(hdc, 1);
+
     emr.emr.iType = EMR_EOF;
     emr.emr.nSize = sizeof(emr);
     emr.nPalEntries = 0;
Index: dlls/gdi/tests/.cvsignore
===================================================================
RCS file: /home/wine/wine/dlls/gdi/tests/.cvsignore,v
retrieving revision 1.9
diff -u -p -r1.9 .cvsignore
--- dlls/gdi/tests/.cvsignore	28 Oct 2005 16:41:25 -0000	1.9
+++ dlls/gdi/tests/.cvsignore	30 Oct 2005 18:15:31 -0000
@@ -2,6 +2,7 @@ Makefile
 bitmap.ok
 brush.ok
 clipping.ok
+dc.ok
 gdiobj.ok
 generated.ok
 mapping.ok
Index: dlls/gdi/tests/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/gdi/tests/Makefile.in,v
retrieving revision 1.9
diff -u -p -r1.9 Makefile.in
--- dlls/gdi/tests/Makefile.in	28 Oct 2005 16:41:25 -0000	1.9
+++ dlls/gdi/tests/Makefile.in	30 Oct 2005 18:15:31 -0000
@@ -9,6 +9,7 @@ CTESTS = \
 	bitmap.c \
 	brush.c \
 	clipping.c \
+	dc.c \
 	gdiobj.c \
 	generated.c \
 	mapping.c \
Index: dlls/gdi/tests/metafile.c
===================================================================
RCS file: /home/wine/wine/dlls/gdi/tests/metafile.c,v
retrieving revision 1.18
diff -u -p -r1.18 metafile.c
--- dlls/gdi/tests/metafile.c	26 Oct 2005 10:05:07 -0000	1.18
+++ dlls/gdi/tests/metafile.c	30 Oct 2005 18:15:32 -0000
@@ -56,7 +56,7 @@ static void init_function_pointers(void)
     GDI_GET_PROC(SetRelAbs);
 }
 
-static int CALLBACK emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
+static int CALLBACK eto_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
     const ENHMETARECORD *emr, int n_objs, LPARAM param)
 {
     static int n_record;
@@ -234,7 +234,7 @@ static void test_ExtTextOut(void)
     if(pSetRelAbs) pSetRelAbs(hdcDisplay, RELATIVE);
     SetBkMode(hdcDisplay, OPAQUE);
 
-    ret = EnumEnhMetaFile(hdcDisplay, hMetafile, emf_enum_proc, dx, &rc);
+    ret = EnumEnhMetaFile(hdcDisplay, hMetafile, eto_emf_enum_proc, dx, &rc);
     ok( ret, "EnumEnhMetaFile error %ld\n", GetLastError());
 
     ok( GetTextAlign(hdcDisplay) == (TA_UPDATECP | TA_CENTER | TA_BASELINE | TA_RTLREADING),
@@ -248,16 +248,122 @@ static void test_ExtTextOut(void)
 
     ok(emr_processed, "EnumEnhMetaFile couldn't find EMR_EXTTEXTOUTA or EMR_EXTTEXTOUTW record\n");
 
-    ok(!EnumEnhMetaFile(hdcDisplay, hMetafile, emf_enum_proc, dx, NULL),
+    ok(!EnumEnhMetaFile(hdcDisplay, hMetafile, eto_emf_enum_proc, dx, NULL),
        "A valid hdc has to require a valid rc\n");
 
-    ok(EnumEnhMetaFile(NULL, hMetafile, emf_enum_proc, dx, NULL),
+    ok(EnumEnhMetaFile(NULL, hMetafile, eto_emf_enum_proc, dx, NULL),
        "A null hdc does not require a valid rc\n");
 
     ret = DeleteEnhMetaFile(hMetafile);
     ok( ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
     ret = ReleaseDC(hwnd, hdcDisplay);
     ok( ret, "ReleaseDC error %ld\n", GetLastError());
+    DestroyWindow(hwnd);
+}
+
+static int CALLBACK savedc_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
+                                         const ENHMETARECORD *emr, int n_objs, LPARAM param)
+{
+    static int save_state;
+    static int restore_no;
+
+    switch (emr->iType)
+    {
+    case EMR_HEADER:
+        save_state = 0;
+        restore_no = 0;
+        break;
+
+    case EMR_SAVEDC:
+        save_state++;
+        break;
+
+    case EMR_RESTOREDC:
+        {
+            EMRRESTOREDC *restoredc = (EMRRESTOREDC *)emr;
+            switch(++restore_no)
+            {
+            case 1:
+                ok(restoredc->iRelative == -1, "first restore %ld\n", restoredc->iRelative);
+                break;
+
+            case 2:
+                ok(restoredc->iRelative == -3, "second restore %ld\n", restoredc->iRelative);
+                break;
+            case 3:
+                ok(restoredc->iRelative == -2, "third restore %ld\n", restoredc->iRelative);
+                break;
+            }
+            ok(restore_no <= 3, "restore_no %d\n", restore_no);
+            save_state += restoredc->iRelative;
+            break;
+        }
+    case EMR_EOF:
+        ok(save_state == 0, "EOF save_state %d\n", save_state);
+        break;
+    }
+
+
+    return 1;        
+}
+
+void test_SaveDC(void)
+{
+    HDC hdcMetafile, hdcDisplay;
+    HENHMETAFILE hMetafile;
+    HWND hwnd;
+    int ret;
+    static const RECT rc = { 0, 0, 100, 100 };
+
+    /* Win9x doesn't play EMFs on invisible windows */
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+                           0, 0, 200, 200, 0, 0, 0, NULL);
+    ok(hwnd != 0, "CreateWindowExA error %ld\n", GetLastError());
+
+    hdcDisplay = GetDC(hwnd);
+    ok(hdcDisplay != 0, "GetDC error %ld\n", GetLastError());
+
+    hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
+    ok(hdcMetafile != 0, "CreateEnhMetaFileA error %ld\n", GetLastError());
+
+    /* Need to write something to the emf, otherwise Windows won't play it back */
+    LineTo(hdcMetafile, 100, 100);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 1, "ret = %d\n", ret);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 2, "ret = %d\n", ret);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 3, "ret = %d\n", ret);
+
+    ret = RestoreDC(hdcMetafile, -1);
+    ok(ret, "ret = %d\n", ret);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 3, "ret = %d\n", ret);
+
+    ret = RestoreDC(hdcMetafile, 1);
+    ok(ret, "ret = %d\n", ret);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 1, "ret = %d\n", ret);
+
+    ret = SaveDC(hdcMetafile);
+    ok(ret == 2, "ret = %d\n", ret);
+
+    hMetafile = CloseEnhMetaFile(hdcMetafile);
+    ok(hMetafile != 0, "CloseEnhMetaFile error %ld\n", GetLastError());
+
+    ret = EnumEnhMetaFile(hdcDisplay, hMetafile, savedc_emf_enum_proc, 0, &rc);
+    ok( ret == 1, "EnumEnhMetaFile rets %d\n", ret);
+
+    ret = DeleteEnhMetaFile(hMetafile);
+    ok( ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
+    ret = ReleaseDC(hwnd, hdcDisplay);
+    ok( ret, "ReleaseDC error %ld\n", GetLastError());
+    DestroyWindow(hwnd);
 }
 
 /* Win-format metafile (mfdrv) tests */
@@ -813,6 +919,7 @@ START_TEST(metafile)
 
     /* For enhanced metafiles (enhmfdrv) */
     test_ExtTextOut();
+    test_SaveDC();
 
     /* For win-format metafiles (mfdrv) */
     test_mf_Blank();
--- /dev/null	2005-10-30 09:50:24.716595750 +0000
+++ dlls/gdi/tests/dc.c	2005-10-30 18:06:36.000000000 +0000
@@ -0,0 +1,88 @@
+/*
+ * Unit tests for dc functions
+ *
+ * Copyright (c) 2005 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+
+void test_savedc(void)
+{
+    HDC hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
+    int ret;
+
+    ok(hdc != NULL, "CreateDC rets %p\n", hdc);
+
+    ret = SaveDC(hdc);
+    ok(ret == 1, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 2, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 3, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, -1);
+    ok(ret, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 3, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, 1);
+    ok(ret, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 1, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 2, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 3, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, -2);
+    ok(ret, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 2, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, -2);
+    ok(ret, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 1, "ret = %d\n", ret);
+    ret = SaveDC(hdc);
+    ok(ret == 2, "ret = %d\n", ret); 
+    ret = RestoreDC(hdc, -4);
+    ok(!ret, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, 3);
+    ok(!ret, "ret = %d\n", ret);
+
+    /* Under win98 the following two succeed and both clear the save stack
+    ret = RestoreDC(hdc, -3);
+    ok(!ret, "ret = %d\n", ret);
+    ret = RestoreDC(hdc, 0);
+    ok(!ret, "ret = %d\n", ret);
+    */
+
+    ret = RestoreDC(hdc, 1);
+    ok(ret, "ret = %d\n", ret);
+
+    DeleteDC(hdc);
+}
+
+START_TEST(dc)
+{
+    test_savedc();
+}



More information about the wine-patches mailing list