From 0d23ac479b587ba1e048fa77e66504cfa3d9e286 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 31 Oct 2005 10:05:52 +0000 Subject: [PATCH] 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. --- dlls/gdi/dc.c | 29 +++++----- dlls/gdi/enhmfdrv/dc.c | 22 +++---- dlls/gdi/enhmfdrv/init.c | 3 + dlls/gdi/tests/.cvsignore | 1 + dlls/gdi/tests/Makefile.in | 1 + dlls/gdi/tests/dc.c | 88 ++++++++++++++++++++++++++++ dlls/gdi/tests/metafile.c | 115 +++++++++++++++++++++++++++++++++++-- 7 files changed, 227 insertions(+), 32 deletions(-) create mode 100644 dlls/gdi/tests/dc.c diff --git a/dlls/gdi/dc.c b/dlls/gdi/dc.c index 02f57f6355c..0f51bc0c61b 100644 --- a/dlls/gdi/dc.c +++ b/dlls/gdi/dc.c @@ -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 level ) TRACE("%p %d\n", hdc, level ); dc = DC_GetDCUpdate( hdc ); if(!dc) return FALSE; - if(dc->funcs->pRestoreDC) - { - success = dc->funcs->pRestoreDC( dc->physDev, level ); - GDI_ReleaseObj( hdc ); - return success; - } - 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(abs(level) > dc->saveLevel || level == 0) { GDI_ReleaseObj( hdc ); return FALSE; } + + 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 success; + } + if (level < 0) level = dc->saveLevel + level + 1; success=TRUE; while (dc->saveLevel >= level) { diff --git a/dlls/gdi/enhmfdrv/dc.c b/dlls/gdi/enhmfdrv/dc.c index fc320c7071c..9eadb05410b 100644 --- a/dlls/gdi/enhmfdrv/dc.c +++ b/dlls/gdi/enhmfdrv/dc.c @@ -38,19 +38,15 @@ BOOL EMFDRV_RestoreDC( PHYSDEV dev, INT level ) 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 ) diff --git a/dlls/gdi/enhmfdrv/init.c b/dlls/gdi/enhmfdrv/init.c index 9476849e204..8fe665c643d 100644 --- a/dlls/gdi/enhmfdrv/init.c +++ b/dlls/gdi/enhmfdrv/init.c @@ -422,6 +422,9 @@ HENHMETAFILE WINAPI CloseEnhMetaFile(HDC hdc) /* [in] metafile DC */ 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; diff --git a/dlls/gdi/tests/.cvsignore b/dlls/gdi/tests/.cvsignore index 38c8561ef32..6df380b2121 100644 --- a/dlls/gdi/tests/.cvsignore +++ b/dlls/gdi/tests/.cvsignore @@ -2,6 +2,7 @@ Makefile bitmap.ok brush.ok clipping.ok +dc.ok gdiobj.ok generated.ok mapping.ok diff --git a/dlls/gdi/tests/Makefile.in b/dlls/gdi/tests/Makefile.in index 6d2516d4ba0..bcb0be71ad2 100644 --- a/dlls/gdi/tests/Makefile.in +++ b/dlls/gdi/tests/Makefile.in @@ -9,6 +9,7 @@ CTESTS = \ bitmap.c \ brush.c \ clipping.c \ + dc.c \ gdiobj.c \ generated.c \ mapping.c \ diff --git a/dlls/gdi/tests/dc.c b/dlls/gdi/tests/dc.c new file mode 100644 index 00000000000..e6eeff92d1b --- /dev/null +++ b/dlls/gdi/tests/dc.c @@ -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 +#include +#include + +#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(); +} diff --git a/dlls/gdi/tests/metafile.c b/dlls/gdi/tests/metafile.c index 59813bba2f6..0b2aa665b53 100644 --- a/dlls/gdi/tests/metafile.c +++ b/dlls/gdi/tests/metafile.c @@ -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();