From e6a61fb3c7b5aa2efd288b07f24aeb0277bbbe6d Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Thu, 30 Apr 2026 09:39:48 +0300 Subject: [PATCH] [GDIPLUS_WINETEST] Sync to Wine-10.0 --- .../rostests/winetests/gdiplus/CMakeLists.txt | 1 - modules/rostests/winetests/gdiplus/brush.c | 331 ++- .../winetests/gdiplus/customlinecap.c | 93 +- modules/rostests/winetests/gdiplus/font.c | 505 +++- modules/rostests/winetests/gdiplus/graphics.c | 857 +++++- .../rostests/winetests/gdiplus/graphicspath.c | 893 +++++- modules/rostests/winetests/gdiplus/guid.c | 13 - modules/rostests/winetests/gdiplus/image.c | 2474 +++++++++++------ modules/rostests/winetests/gdiplus/matrix.c | 93 +- modules/rostests/winetests/gdiplus/metafile.c | 1452 +++++++++- .../rostests/winetests/gdiplus/pathiterator.c | 290 +- modules/rostests/winetests/gdiplus/pen.c | 75 +- modules/rostests/winetests/gdiplus/region.c | 325 ++- .../rostests/winetests/gdiplus/resource.rc | 11 + .../winetests/gdiplus/wine_mac_win.ttf | Bin 0 -> 1140 bytes .../winetests/gdiplus/wine_testfont0.ttf | Bin 0 -> 1656 bytes .../winetests/gdiplus/wine_unicode_mac.ttf | Bin 0 -> 1168 bytes 17 files changed, 6090 insertions(+), 1323 deletions(-) create mode 100644 modules/rostests/winetests/gdiplus/wine_mac_win.ttf create mode 100644 modules/rostests/winetests/gdiplus/wine_testfont0.ttf create mode 100644 modules/rostests/winetests/gdiplus/wine_unicode_mac.ttf diff --git a/modules/rostests/winetests/gdiplus/CMakeLists.txt b/modules/rostests/winetests/gdiplus/CMakeLists.txt index 5074555b052..a4f9ff1aaf0 100644 --- a/modules/rostests/winetests/gdiplus/CMakeLists.txt +++ b/modules/rostests/winetests/gdiplus/CMakeLists.txt @@ -27,7 +27,6 @@ add_executable(gdiplus_winetest set_module_type(gdiplus_winetest win32cui) add_importlibs(gdiplus_winetest gdiplus user32 gdi32 ole32 msvcrt kernel32) -add_pch(gdiplus_winetest precomp.h "${PCH_SKIP_SOURCE}") add_rostests_file(TARGET gdiplus_winetest) if(MSVC) diff --git a/modules/rostests/winetests/gdiplus/brush.c b/modules/rostests/winetests/gdiplus/brush.c index bb645ffef93..f79eb7bbedc 100644 --- a/modules/rostests/winetests/gdiplus/brush.c +++ b/modules/rostests/winetests/gdiplus/brush.c @@ -18,13 +18,18 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include #include "objbase.h" #include "gdiplus.h" #include "wine/test.h" -#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) +#define expect(expected,got) expect_(__LINE__, expected, got) +static inline void expect_(unsigned line, DWORD expected, DWORD got) +{ + ok_(__FILE__, line)(expected == got, "Expected %.8ld, got %.8ld\n", expected, got); +} #define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %.2f, got %.2f\n", expected, got) static HWND hwnd; @@ -274,6 +279,37 @@ static void test_transform(void) GpPointF start, end; GpRectF rectf; REAL elements[6]; + UINT buf[15]; + HBITMAP hbmp,old; + HDC dcmem; + BITMAP bm; + GpImage *image; + IStream *stream; + HGLOBAL hglob; + VOID *data; + HRESULT hr; + LONG size; + + /* 3*5 bmp 32bit*/ + static const unsigned char bmpimage[] = { + 0x42,0x4d,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00, + 0x00,0x00,0x28,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,0x00, + 0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x3e,0x00, + 0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x11,0xff,0x00,0xff,0x11,0xff,0x00,0xff,0x11,0xff,0x00, + 0xff,0x33,0xff,0x00,0xff,0x33,0xff,0x00,0xff,0x33,0xff,0x00, + 0xff,0x55,0xff,0x00,0xff,0x55,0xff,0x00,0xff,0x55,0xff,0x00, + 0xff,0xaa,0xff,0x00,0xff,0xaa,0xff,0x00,0xff,0xaa,0xff,0x00, + 0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x00}; + + static const UINT buf_rotate_180[15] = { + 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff11ff, 0xffff11ff, 0xffff11ff, + 0xffff33ff, 0xffff33ff, 0xffff33ff, + 0xffff55ff, 0xffff55ff, 0xffff55ff, + 0xffffaaff, 0xffffaaff, 0xffffaaff}; /* GpTexture */ status = GdipCreateMatrix2(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, &m); @@ -556,6 +592,38 @@ static void test_transform(void) expect(Ok, status); status = GdipDeleteBrush((GpBrush*)line); expect(Ok,status); + GdipDeleteGraphics(graphics); + + dcmem = CreateCompatibleDC(hdc); + hbmp = CreateBitmap(3, 5, 1, 32, NULL); + old = SelectObject(dcmem, hbmp); + status = GdipCreateFromHDC(dcmem, &graphics); + expect(Ok, status); + hglob = GlobalAlloc(0, sizeof(bmpimage)); + data = GlobalLock (hglob); + memcpy(data, bmpimage, sizeof(bmpimage)); + GlobalUnlock(hglob); + hr = CreateStreamOnHGlobal(hglob, TRUE, &stream); + expect(S_OK, hr); + status = GdipLoadImageFromStream(stream, &image); + expect(Ok, status); + status = GdipCreateTexture(image, WrapModeTile, &texture); + expect(Ok, status); + status = GdipRotateTextureTransform(texture, 180.0f, MatrixOrderPrepend); + expect(Ok, status); + status = GdipFillRectangle(graphics, (GpBrush*)texture, .0f, .0f, (REAL)3, (REAL)5); + expect(Ok, status); + GdipDeleteBrush((GpBrush*)texture); + GdipDisposeImage(image); + GdipDeleteGraphics(graphics); + IStream_Release(stream); + hbmp = SelectObject(dcmem, old); + GetObjectW(hbmp, sizeof(bm), &bm); + size = GetBitmapBits(hbmp, sizeof(buf), buf); + expect(sizeof(buf), size); + DeleteObject(hbmp); + DeleteDC(dcmem); + ok(!memcmp(buf, buf_rotate_180, sizeof(buf)), "Failed to rotate image.\n"); if(0){ /* enable to visually compare with Windows */ @@ -567,7 +635,6 @@ static void test_transform(void) } GdipDeleteMatrix(m); - GdipDeleteGraphics(graphics); ReleaseDC(0, hdc); } @@ -1299,6 +1366,7 @@ static void test_pathgradientcenterpoint(void) { static const GpPointF path_points[] = {{0,0}, {3,0}, {0,4}}; GpStatus status; + GpPath* path; GpPathGradient *grad; GpPointF point; @@ -1341,8 +1409,28 @@ static void test_pathgradientcenterpoint(void) status = GdipGetPathGradientCenterPoint(grad, &point); expect(Ok, status); - todo_wine expectf(1.0, point.X); - todo_wine expectf(4.0/3.0, point.Y); + expectf(1.0, point.X); + expectf(4.0/3.0, point.Y); + + status = GdipDeleteBrush((GpBrush*)grad); + expect(Ok, status); + + status = GdipCreatePath(FillModeWinding, &path); + expect(Ok, status); + + status = GdipAddPathEllipse(path, 0, 0, 100, 50); + expect(Ok, status); + + status = GdipCreatePathGradientFromPath(path, &grad); + expect(Ok, status); + + status = GdipGetPathGradientCenterPoint(grad, &point); + expect(Ok, status); + expectf(700.0/13.0, point.X); + expectf(25.0, point.Y); + + status = GdipDeletePath(path); + expect(Ok, status); status = GdipDeleteBrush((GpBrush*)grad); expect(Ok, status); @@ -1613,6 +1701,239 @@ static void test_getHatchStyle(void) GdipDeleteBrush((GpBrush *)brush); } +static ARGB COLORREF2ARGB(COLORREF color) +{ + return 0xff000000 | + (color & 0xff) << 16 | + (color & 0xff00) | + (color & 0xff0000) >> 16; +} + +extern BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff); + +static void test_hatchBrushStyles(void) +{ + static const struct + { + short pattern[8]; + GpHatchStyle hs; + } + styles[] = + { + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross }, + { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent }, + { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, + { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, + { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, + { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical }, + { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, + { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, + { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal }, + { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal }, + { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal }, + { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical }, + { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti }, + { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti }, + { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag }, + { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave }, + { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick }, + { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick }, + { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave }, + { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid }, + { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot }, + { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid }, + { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond }, + { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle }, + { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis }, + { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid }, + { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard }, + { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard }, + { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond }, + { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond }, + }; + static const ARGB exp_colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; + static const ARGB fore_color = 0xff000000; + static const ARGB back_color = 0xffffffff; + static const int width = 16, height = 16; + GpStatus status; + HDC hdc; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpBitmap *bitmap; + GpHatch *brush = NULL; + BOOL match_hdc; + BOOL match_image; + int x, y; + int i; + + hdc = GetDC(hwnd); + status = GdipCreateFromHDC(hdc, &graphics_hdc); + expect(Ok, status); + ok(graphics_hdc != NULL, "Expected the graphics context to be initialized.\n"); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + expect(Ok, status); + ok(graphics_image != NULL, "Expected the graphics context to be initialized.\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + status = GdipCreateHatchBrush(styles[i].hs, fore_color, back_color, &brush); + expect(Ok, status); + ok(brush != NULL, "Expected the brush to be initialized.\n"); + status = GdipFillRectangleI(graphics_hdc, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipFillRectangleI(graphics_image, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipDeleteBrush((GpBrush *)brush); + expect(Ok, status); + brush = NULL; + + match_hdc = TRUE; + match_image = TRUE; + for(y = 0; y < width && (match_hdc || match_image); y++) + { + for(x = 0; x < height && (match_hdc || match_image); x++) + { + ARGB color; + int cindex = (styles[i].pattern[7-(y%8)] >> (2*(7-(x%8)))) & 3; + + color = COLORREF2ARGB(GetPixel(hdc, x, y)); + if (!color_match(color, exp_colors[cindex], 1)) + match_hdc = FALSE; + + GdipBitmapGetPixel(bitmap, x, y, &color); + if (!color_match(color, exp_colors[cindex], 1)) + match_image = FALSE; + } + } + ok(match_hdc, "Unexpected pattern for hatch style %#x with hdc.\n", styles[i].hs); + ok(match_image, "Unexpected pattern for hatch style %#x with image.\n", styles[i].hs); + } + + status = GdipDeleteGraphics(graphics_image); + expect(Ok, status); + status = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, status); + + status = GdipDeleteGraphics(graphics_hdc); + expect(Ok, status); + ReleaseDC(hwnd, hdc); +} + +static void test_renderingOrigin(void) +{ + static const int width = 8, height = 8; + GpStatus status; + HDC hdc; + GpBitmap *bitmap; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpHatch *brush; + BOOL match_hdc; + BOOL match_image; + static const INT tests[][2] = {{3, 6}, {-7, -4}}; + static const ARGB fore_color = 0xff000000; + static const ARGB back_color = 0xffffffff; + INT x, y; + int i; + + hdc = GetDC(hwnd); + GdipCreateFromHDC(hdc, &graphics_hdc); + + GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + + GdipCreateHatchBrush(HatchStyleCross, fore_color, back_color, &brush); + + x = y = 0xdeadbeef; + status = GdipGetRenderingOrigin(graphics_image, &x, &y); + expect(Ok, status); + ok(x == 0 && y == 0, "Expected (%d, %d) got (%d, %d)\n", 0, 0, x, y); + x = y = 0xdeadbeef; + status = GdipGetRenderingOrigin(graphics_image, &x, &y); + expect(Ok, status); + ok(x == 0 && y == 0, "Expected (%d, %d) got (%d, %d)\n", 0, 0, x, y); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + const INT exp_x = tests[i][0] & 7; + const INT exp_y = tests[i][1] & 7; + + status = GdipSetRenderingOrigin(graphics_image, tests[i][0], tests[i][1]); + expect(Ok, status); + status = GdipSetRenderingOrigin(graphics_hdc, tests[i][0], tests[i][1]); + expect(Ok, status); + + status = GdipGetRenderingOrigin(graphics_image, &x, &y); + expect(Ok, status); + ok(x == tests[i][0] && y == tests[i][1], "Expected (%d, %d) got (%d, %d)\n", + tests[i][0], tests[i][1], x, y); + status = GdipGetRenderingOrigin(graphics_image, &x, &y); + expect(Ok, status); + ok(x == tests[i][0] && y == tests[i][1], "Expected (%d, %d) got (%d, %d)\n", + tests[i][0], tests[i][1], x, y); + + GdipFillRectangleI(graphics_image, (GpBrush *)brush, 0, 0, width, height); + GdipFillRectangleI(graphics_hdc, (GpBrush *)brush, 0, 0, width, height); + + match_hdc = TRUE; + match_image = TRUE; + for (y = 0; y < height && (match_hdc || match_image); y++) + { + for (x = 0; x < width && (match_hdc || match_image); x++) + { + ARGB color; + const ARGB exp_color = (x == exp_x || y == exp_y) ? fore_color : back_color; + + color = COLORREF2ARGB(GetPixel(hdc, x, y)); + if (color != exp_color) + match_hdc = FALSE; + + GdipBitmapGetPixel(bitmap, x, y, &color); + if (color != exp_color) + match_image = FALSE; + } + } + ok(match_hdc, "Hatch brush rendered incorrectly on hdc with rendering origin (%d, %d).\n", + tests[i][0], tests[i][1]); + ok(match_image, "Hatch brush rendered incorrectly on image with rendering origin (%d, %d).\n", + tests[i][0], tests[i][1]); + } + + GdipDeleteBrush((GpBrush *)brush); + + GdipDeleteGraphics(graphics_image); + GdipDisposeImage((GpImage*)bitmap); + + GdipDeleteGraphics(graphics_hdc); + ReleaseDC(hwnd, hdc); +} + START_TEST(brush) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1665,6 +1986,8 @@ START_TEST(brush) test_pathgradientpresetblend(); test_pathgradientblend(); test_getHatchStyle(); + test_hatchBrushStyles(); + test_renderingOrigin(); GdiplusShutdown(gdiplusToken); DestroyWindow(hwnd); diff --git a/modules/rostests/winetests/gdiplus/customlinecap.c b/modules/rostests/winetests/gdiplus/customlinecap.c index e4ec329b935..8c3ecef886f 100644 --- a/modules/rostests/winetests/gdiplus/customlinecap.c +++ b/modules/rostests/winetests/gdiplus/customlinecap.c @@ -26,6 +26,13 @@ #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) #define expectf(expected, got) ok(got == expected, "Expected %.2f, got %.2f\n", expected, got) +static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) +{ + unsigned int diff = x > y ? x - y : y - x; + + return diff <= max_diff; +} + static BOOL compare_float(float f, float g, unsigned int ulps) { int x = *(int *)&f; @@ -36,26 +43,28 @@ static BOOL compare_float(float f, float g, unsigned int ulps) if (y < 0) y = INT_MIN - y; - if (abs(x - y) > ulps) - return FALSE; - - return TRUE; + return compare_uint(x, y, ulps); } static void test_constructor_destructor(void) { GpCustomLineCap *custom; - GpPath *path, *path2; + GpPath *path, *path2, *pathFarAway; GpStatus stat; stat = GdipCreatePath(FillModeAlternate, &path); expect(Ok, stat); - stat = GdipAddPathRectangle(path, 5.0, 5.0, 10.0, 10.0); + stat = GdipAddPathRectangle(path, -5.0, -4.0, 10.0, 8.0); expect(Ok, stat); stat = GdipCreatePath(FillModeAlternate, &path2); expect(Ok, stat); - stat = GdipAddPathRectangle(path2, 5.0, 5.0, 10.0, 10.0); + stat = GdipAddPathRectangle(path2, -5.0, -5.0, 10.0, 10.0); + expect(Ok, stat); + + stat = GdipCreatePath(FillModeAlternate, &pathFarAway); + expect(Ok, stat); + stat = GdipAddPathRectangle(pathFarAway, 5.0, 5.0, 10.0, 10.0); expect(Ok, stat); /* NULL args */ @@ -70,18 +79,39 @@ static void test_constructor_destructor(void) stat = GdipDeleteCustomLineCap(NULL); expect(InvalidParameter, stat); - /* valid args */ - stat = GdipCreateCustomLineCap(NULL, path2, LineCapFlat, 0.0, &custom); + /* If both parameters are provided, then fillPath will be ignored. */ + custom = NULL; + stat = GdipCreateCustomLineCap(path, path2, LineCapFlat, 0.0, &custom); expect(Ok, stat); + ok(custom != NULL, "Custom line cap was not created\n"); stat = GdipDeleteCustomLineCap(custom); expect(Ok, stat); - /* it's strange but native returns NotImplemented on stroke == NULL */ + + /* valid args */ custom = NULL; - stat = GdipCreateCustomLineCap(path, NULL, LineCapFlat, 10.0, &custom); + stat = GdipCreateCustomLineCap(NULL, path2, LineCapFlat, 0.0, &custom); + expect(Ok, stat); + ok(custom != NULL, "Custom line cap was not created\n"); + stat = GdipDeleteCustomLineCap(custom); + expect(Ok, stat); + + custom = NULL; + stat = GdipCreateCustomLineCap(path, NULL, LineCapFlat, 0.0, &custom); + expect(Ok, stat); + ok(custom != NULL, "Custom line cap was not created\n"); + stat = GdipDeleteCustomLineCap(custom); + expect(Ok, stat); + + /* Custom line cap position (0, 0) is a place corresponding to the end of line. + * If Custom Line Cap is too big and too far from position (0, 0), + * then NotImplemented will be returned, due to floating point precision limitation. */ + custom = NULL; + stat = GdipCreateCustomLineCap(pathFarAway, NULL, LineCapFlat, 10.0, &custom); todo_wine expect(NotImplemented, stat); todo_wine ok(custom == NULL, "Expected a failure on creation\n"); if(stat == Ok) GdipDeleteCustomLineCap(custom); + GdipDeletePath(pathFarAway); GdipDeletePath(path2); GdipDeletePath(path); } @@ -165,6 +195,14 @@ static void test_inset(void) expect(Ok, stat); expectf(0.0, inset); + stat = GdipSetCustomLineCapBaseInset(custom, 2.0); + expect(Ok, stat); + + inset = (REAL)0xdeadbeef; + stat = GdipGetCustomLineCapBaseInset(custom, &inset); + expect(Ok, stat); + ok(inset == 2.0, "Unexpected inset value %f\n", inset); + GdipDeleteCustomLineCap(custom); GdipDeletePath(path); } @@ -283,14 +321,15 @@ static void test_create_adjustable_cap(void) ok(base == LineCapTriangle, "Unexpected base cap %d\n", base); stat = GdipSetCustomLineCapBaseCap((GpCustomLineCap*)cap, LineCapSquare); -todo_wine ok(stat == Ok, "Unexpected return code, %d\n", stat); stat = GdipGetCustomLineCapBaseCap((GpCustomLineCap*)cap, &base); ok(stat == Ok, "Unexpected return code, %d\n", stat); -todo_wine ok(base == LineCapSquare, "Unexpected base cap %d\n", base); + stat = GdipSetCustomLineCapBaseCap((GpCustomLineCap*)cap, LineCapSquareAnchor); + ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat); + /* Base inset */ stat = GdipGetAdjustableArrowCapWidth(cap, &width); ok(stat == Ok, "Unexpected return code, %d\n", stat); @@ -371,6 +410,33 @@ static void test_captype(void) GdipDeleteCustomLineCap((GpCustomLineCap*)arrowcap); } +static void test_strokecap(void) +{ + GpCustomLineCap *cap; + GpStatus stat; + GpPath *path; + + /* default cap */ + stat = GdipCreatePath(FillModeAlternate, &path); + ok(stat == Ok, "Failed to create path, %d\n", stat); + stat = GdipAddPathRectangle(path, 5.0, 5.0, 10.0, 10.0); + ok(stat == Ok, "AddPathRectangle failed, %d\n", stat); + + stat = GdipCreateCustomLineCap(NULL, path, LineCapFlat, 0.0, &cap); + ok(stat == Ok, "Failed to create cap, %d\n", stat); + + stat = GdipSetCustomLineCapStrokeCaps(cap, LineCapSquare, LineCapFlat); + ok(stat == Ok, "Unexpected return code, %d\n", stat); + + stat = GdipSetCustomLineCapStrokeCaps(cap, LineCapSquareAnchor, LineCapFlat); + ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat); + + stat = GdipSetCustomLineCapStrokeCaps(cap, LineCapFlat, LineCapSquareAnchor); + ok(stat == InvalidParameter, "Unexpected return code, %d\n", stat); + GdipDeleteCustomLineCap(cap); + GdipDeletePath(path); +} + START_TEST(customlinecap) { struct GdiplusStartupInput gdiplusStartupInput; @@ -396,6 +462,7 @@ START_TEST(customlinecap) test_scale(); test_create_adjustable_cap(); test_captype(); + test_strokecap(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/font.c b/modules/rostests/winetests/gdiplus/font.c index 03e7a04e081..1bbb18f4c6d 100644 --- a/modules/rostests/winetests/gdiplus/font.c +++ b/modules/rostests/winetests/gdiplus/font.c @@ -25,16 +25,16 @@ #include "gdiplus.h" #include "wine/test.h" -#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) -#define expect_(expected, got, precision) ok(abs((expected) - (got)) <= (precision), "Expected %d, got %d\n", (expected), (got)) +#define expect(expected,got) expect_inline(__LINE__, expected, got) +static inline void expect_inline(unsigned line, DWORD expected, DWORD got) +{ + ok_(__FILE__, line)(expected == got, "Expected %ld, got %ld\n", expected, got); +} + +#define expect_(expected, got, precision) ok(abs((expected) - (got)) <= (precision), "Expected %d, got %ld\n", (expected), (got)) #define expectf_(expected, got, precision) ok(fabs((expected) - (got)) <= (precision), "Expected %f, got %f\n", (expected), (got)) #define expectf(expected, got) expectf_((expected), (got), 0.001) -static const WCHAR nonexistent[] = {'T','h','i','s','F','o','n','t','s','h','o','u','l','d','N','o','t','E','x','i','s','t','\0'}; -static const WCHAR MSSansSerif[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f','\0'}; -static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'}; -static const WCHAR Tahoma[] = {'T','a','h','o','m','a',0}; - static void set_rect_empty(RectF *rc) { rc->X = 0.0; @@ -43,24 +43,32 @@ static void set_rect_empty(RectF *rc) rc->Height = 0.0; } +#define load_resource(a, b, c) _load_resource(__LINE__, a, b, c) +static void _load_resource(int line, const WCHAR *filename, BYTE **data, DWORD *size) +{ + HRSRC resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); + ok_(__FILE__, line)(!!resource, "FindResourceW failed, error %lu\n", GetLastError()); + *data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + ok_(__FILE__, line)(!!*data, "LockResource failed, error %lu\n", GetLastError()); + *size = SizeofResource(GetModuleHandleW(NULL), resource); + ok_(__FILE__, line)(*size > 0, "SizeofResource failed, error %lu\n", GetLastError()); +} + static void create_testfontfile(const WCHAR *filename, int resource, WCHAR pathW[MAX_PATH]) { - DWORD written; + DWORD written, length; HANDLE file; - HRSRC res; void *ptr; GetTempPathW(MAX_PATH, pathW); lstrcatW(pathW, filename); file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW), GetLastError()); + ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %ld\n", wine_dbgstr_w(pathW), GetLastError()); - res = FindResourceA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(resource), (LPCSTR)RT_RCDATA); - ok(res != 0, "couldn't find resource\n"); - ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); - WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL); - ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n"); + load_resource(MAKEINTRESOURCEW(resource), (BYTE **)&ptr, &length); + WriteFile(file, ptr, length, &written, NULL); + ok(written == length, "couldn't write resource\n"); CloseHandle(file); } @@ -68,27 +76,38 @@ static void create_testfontfile(const WCHAR *filename, int resource, WCHAR pathW static void _delete_testfontfile(const WCHAR *filename, int line) { BOOL ret = DeleteFileW(filename); - ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError()); + ok_(__FILE__,line)(ret, "failed to delete file %s, error %ld\n", wine_dbgstr_w(filename), GetLastError()); } static void test_long_name(void) { WCHAR path[MAX_PATH]; - static const WCHAR path_longname[] = {'w','i','n','e','_','l','o','n','g','n','a','m','e','.','t','t','f',0}; GpStatus stat; GpFontCollection *fonts; + HANDLE file; INT num_families; - GpFontFamily *family; + GpFontFamily *family, *cloned_family; WCHAR family_name[LF_FACESIZE]; GpFont *font; stat = GdipNewPrivateFontCollection(&fonts); ok(stat == Ok, "GdipNewPrivateFontCollection failed: %d\n", stat); - create_testfontfile(path_longname, 1, path); + create_testfontfile(L"wine_longname.ttf", 1, path); + + file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFileW failed: %ld\n", GetLastError()); stat = GdipPrivateAddFontFile(fonts, path); - ok(stat == Ok, "GdipPrivateAddFontFile failed: %d\n", stat); + ok(stat == Ok, "GdipPrivateAddFontFile failed with open file handle: %d\n", stat); + + CloseHandle(file); + + if (stat != Ok) { + /* try again without opened file handle */ + stat = GdipPrivateAddFontFile(fonts, path); + ok(stat == Ok, "GdipPrivateAddFontFile failed: %d\n", stat); + } stat = GdipGetFontCollectionFamilyCount(fonts, &num_families); ok(stat == Ok, "GdipGetFontCollectionFamilyCount failed: %d\n", stat); @@ -104,6 +123,10 @@ static void test_long_name(void) stat = GdipCreateFont(family, 256.0, FontStyleRegular, UnitPixel, &font); ok(stat == Ok, "GdipCreateFont failed: %d\n", stat); + stat = GdipCloneFontFamily(family, &cloned_family); + ok(stat == Ok, "GdipCloneFontFamily failed: %d\n", stat); + ok(family == cloned_family, "GdipCloneFontFamily returned new object\n"); + /* Cleanup */ stat = GdipDeleteFont(font); @@ -112,6 +135,13 @@ static void test_long_name(void) stat = GdipDeletePrivateFontCollection(&fonts); ok(stat == Ok, "GdipDeletePrivateFontCollection failed: %d\n", stat); + /* Cloned family survives after collection is deleted */ + stat = GdipGetFamilyName(cloned_family, family_name, LANG_NEUTRAL); + ok(stat == Ok, "GdipGetFamilyName failed: %d\n", stat); + + stat = GdipDeleteFontFamily(cloned_family); + ok(stat == Ok, "GdipDeleteFontFamily failed: %d\n", stat); + DELETE_FONTFILE(path); } @@ -125,11 +155,11 @@ static void test_createfont(void) REAL size; WCHAR familyname[LF_FACESIZE]; - stat = GdipCreateFontFamilyFromName(nonexistent, NULL, &fontfamily); + stat = GdipCreateFontFamilyFromName(L"ThisFontShouldNotExist", NULL, &fontfamily); expect (FontFamilyNotFound, stat); stat = GdipDeleteFont(font); expect (InvalidParameter, stat); - stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &fontfamily); + stat = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &fontfamily); expect (Ok, stat); stat = GdipCreateFont(fontfamily, 12, FontStyleRegular, UnitPoint, &font); expect (Ok, stat); @@ -141,7 +171,8 @@ static void test_createfont(void) expect(Ok, stat); stat = GdipGetFamilyName(fontfamily2, familyname, 0); expect(Ok, stat); - ok (lstrcmpiW(Tahoma, familyname) == 0, "Expected Tahoma, got %s\n", + ok (fontfamily == fontfamily2, "Unexpected family instance.\n"); + ok (lstrcmpiW(L"Tahoma", familyname) == 0, "Expected Tahoma, got %s\n", wine_dbgstr_w(familyname)); stat = GdipDeleteFontFamily(fontfamily2); expect(Ok, stat); @@ -168,6 +199,76 @@ static void test_createfont(void) GdipDeleteFontFamily(fontfamily); } +static void test_createfont_charset(void) +{ + GpFontFamily* fontfamily = NULL; + GpGraphics *graphics; + GpFont* font = NULL; + GpStatus stat; + LOGFONTW lf; + HDC hdc; + UINT i; + + static const struct { + LPCWSTR family_name; + BYTE char_set; + } td[] = + { + {L"Tahoma", ANSI_CHARSET}, + {L"Symbol", SYMBOL_CHARSET}, + {L"Marlett", SYMBOL_CHARSET}, + {L"Wingdings", SYMBOL_CHARSET}, + }; + + hdc = CreateCompatibleDC(0); + stat = GdipCreateFromHDC(hdc, &graphics); + expect (Ok, stat); + + for (i = 0; i < ARRAY_SIZE(td); i++) + { + winetest_push_context("%u", i); + + stat = GdipCreateFontFamilyFromName(td[i].family_name, NULL, &fontfamily); + expect (Ok, stat); + stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitPoint, &font); + expect (Ok, stat); + + stat = GdipGetLogFontW(font, graphics, &lf); + expect(Ok, stat); + + if (lstrcmpiW(lf.lfFaceName, td[i].family_name) != 0) + { + skip("%s not installed\n", wine_dbgstr_w(td[i].family_name)); + } + else + { + ok(lf.lfHeight < 0, "Expected negative height, got %ld\n", lf.lfHeight); + expect(0, lf.lfWidth); + expect(0, lf.lfEscapement); + expect(0, lf.lfOrientation); + ok((lf.lfWeight >= 100) && (lf.lfWeight <= 900), "Expected weight to be set\n"); + expect(0, lf.lfItalic); + expect(0, lf.lfUnderline); + expect(0, lf.lfStrikeOut); + ok(td[i].char_set == lf.lfCharSet || + (td[i].char_set == ANSI_CHARSET && lf.lfCharSet == GetTextCharset(hdc)), + "got %#x\n", lf.lfCharSet); + expect(0, lf.lfOutPrecision); + expect(0, lf.lfClipPrecision); + expect(0, lf.lfQuality); + expect(0, lf.lfPitchAndFamily); + } + + GdipDeleteFont(font); + GdipDeleteFontFamily(fontfamily); + + winetest_pop_context(); + } + + GdipDeleteGraphics(graphics); + DeleteDC(hdc); +} + static void test_logfont(void) { LOGFONTA lfa, lfa2; @@ -299,6 +400,56 @@ static void test_logfont(void) GdipDeleteFontFamily(family); GdipDeleteFont(font); + font = NULL; + + /* The next test must be done with a font where tmHeight - + tmInternalLeading != tmAscent. Times New Roman is such a font, + so make sure we really have it before continuing. */ + memset(&lfa, 0, sizeof(lfa)); + lstrcpyA(lfa.lfFaceName, "Times New Roman"); + + stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font); + expect(Ok, stat); + + memset(&lfa2, 0, sizeof(lfa2)); + stat = GdipGetLogFontA(font, graphics, &lfa2); + expect(Ok, stat); + + GdipDeleteFont(font); + font = NULL; + + if (!lstrlenA(lfa.lfFaceName) || lstrcmpA(lfa.lfFaceName, lfa2.lfFaceName)) + { + skip("Times New Roman not installed\n"); + } + else + { + static const struct + { + INT input; + REAL expected; + } test_sizes[] = {{12, 9.0}, {36, 32.0}, {48, 42.0}, {72, 63.0}, {144, 127.0}}; + + UINT i; + + memset(&lfa, 0, sizeof(lfa)); + lstrcpyA(lfa.lfFaceName, "Times New Roman"); + + for (i = 0; i < ARRAY_SIZE(test_sizes); ++i) + { + lfa.lfHeight = test_sizes[i].input; + + stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font); + expect(Ok, stat); + + stat = GdipGetFontSize(font, &rval); + expect(Ok, stat); + expectf(test_sizes[i].expected, rval); + + GdipDeleteFont(font); + font = NULL; + } + } GdipDeleteGraphics(graphics); ReleaseDC(0, hdc); @@ -311,42 +462,40 @@ static void test_fontfamily (void) GpStatus stat; /* FontFamily cannot be NULL */ - stat = GdipCreateFontFamilyFromName (Tahoma , NULL, NULL); + stat = GdipCreateFontFamilyFromName (L"Tahoma" , NULL, NULL); expect (InvalidParameter, stat); /* FontFamily must be able to actually find the family. * If it can't, any subsequent calls should fail. */ - stat = GdipCreateFontFamilyFromName (nonexistent, NULL, &family); + stat = GdipCreateFontFamilyFromName (L"ThisFontShouldNotExist", NULL, &family); expect (FontFamilyNotFound, stat); /* Bitmap fonts are not found */ - stat = GdipCreateFontFamilyFromName (MSSansSerif, NULL, &family); + stat = GdipCreateFontFamilyFromName (L"MS Sans Serif", NULL, &family); expect (FontFamilyNotFound, stat); if(stat == Ok) GdipDeleteFontFamily(family); - stat = GdipCreateFontFamilyFromName (Tahoma, NULL, &family); + stat = GdipCreateFontFamilyFromName (L"Tahoma", NULL, &family); expect (Ok, stat); stat = GdipGetFamilyName (family, itsName, LANG_NEUTRAL); expect (Ok, stat); - expect (0, lstrcmpiW(itsName, Tahoma)); + expect (0, lstrcmpiW(itsName, L"Tahoma")); - if (0) - { - /* Crashes on Windows XP SP2, Vista, and so Wine as well */ - stat = GdipGetFamilyName (family, NULL, LANG_NEUTRAL); - expect (Ok, stat); - } + /* Crashes on Windows XP SP2 and Vista */ + stat = GdipGetFamilyName (family, NULL, LANG_NEUTRAL); + expect (Ok, stat); /* Make sure we don't read old data */ ZeroMemory (itsName, sizeof(itsName)); stat = GdipCloneFontFamily(family, &clonedFontFamily); expect (Ok, stat); + ok (family == clonedFontFamily, "Unexpected family instance.\n"); GdipDeleteFontFamily(family); stat = GdipGetFamilyName(clonedFontFamily, itsName, LANG_NEUTRAL); expect(Ok, stat); - expect(0, lstrcmpiW(itsName, Tahoma)); + expect(0, lstrcmpiW(itsName, L"Tahoma")); GdipDeleteFontFamily(clonedFontFamily); } @@ -357,7 +506,7 @@ static void test_fontfamily_properties (void) GpStatus stat; UINT16 result = 0; - stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &FontFamily); + stat = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &FontFamily); expect(Ok, stat); stat = GdipGetLineSpacing(FontFamily, FontStyleRegular, &result); @@ -377,7 +526,7 @@ static void test_fontfamily_properties (void) ok(result == 423, "Expected 423, got %d\n", result); GdipDeleteFontFamily(FontFamily); - stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, &FontFamily); + stat = GdipCreateFontFamilyFromName(L"Times New Roman", NULL, &FontFamily); if(stat == FontFamilyNotFound) skip("Times New Roman not installed\n"); else @@ -481,7 +630,7 @@ static void test_heightgivendpi(void) REAL height; Unit unit; - stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &fontfamily); + stat = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &fontfamily); expect(Ok, stat); stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitPixel, &font); @@ -682,7 +831,7 @@ static void test_font_metrics(void) memset(&lf, 0, sizeof(lf)); /* Tahoma,-13 */ - lstrcpyW(lf.lfFaceName, Tahoma); + lstrcpyW(lf.lfFaceName, L"Tahoma"); lf.lfHeight = -13; stat = GdipCreateFontFromLogfontW(hdc, &lf, &font); expect(Ok, stat); @@ -693,14 +842,14 @@ static void test_font_metrics(void) gdip_get_font_metrics(font, &fm_gdip); trace("gdiplus:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent, fm_gdip.font_height, fm_gdip.font_size); gdi_get_font_metrics(&lf, &fm_gdi); trace("gdi:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent, fm_gdi.font_height, fm_gdi.font_size); @@ -709,10 +858,10 @@ static void test_font_metrics(void) stat = GdipGetLogFontW(font, graphics, &lf); expect(Ok, stat); - ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight); + ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %ld\n", lf.lfHeight); gdi_get_font_metrics(&lf, &fm_gdi); trace("gdi:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent, fm_gdi.font_height, fm_gdi.font_size); @@ -723,7 +872,7 @@ static void test_font_metrics(void) GdipDeleteFont(font); /* Tahoma,13 */ - lstrcpyW(lf.lfFaceName, Tahoma); + lstrcpyW(lf.lfFaceName, L"Tahoma"); lf.lfHeight = 13; stat = GdipCreateFontFromLogfontW(hdc, &lf, &font); expect(Ok, stat); @@ -734,14 +883,14 @@ static void test_font_metrics(void) gdip_get_font_metrics(font, &fm_gdip); trace("gdiplus:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent, fm_gdip.font_height, fm_gdip.font_size); gdi_get_font_metrics(&lf, &fm_gdi); trace("gdi:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent, fm_gdi.font_height, fm_gdi.font_size); @@ -750,10 +899,10 @@ static void test_font_metrics(void) stat = GdipGetLogFontW(font, graphics, &lf); expect(Ok, stat); - ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight); + ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %ld\n", lf.lfHeight); gdi_get_font_metrics(&lf, &fm_gdi); trace("gdi:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent, fm_gdi.font_height, fm_gdi.font_size); @@ -763,7 +912,7 @@ static void test_font_metrics(void) GdipDeleteFont(font); - stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &family); + stat = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, stat); /* Tahoma,13 */ @@ -772,17 +921,17 @@ static void test_font_metrics(void) gdip_get_font_metrics(font, &fm_gdip); trace("gdiplus:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent, fm_gdip.font_height, fm_gdip.font_size); stat = GdipGetLogFontW(font, graphics, &lf); expect(Ok, stat); - ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight); + ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %ld\n", lf.lfHeight); gdi_get_font_metrics(&lf, &fm_gdi); trace("gdi:\n"); - trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", + trace("%s,%ld: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n", wine_dbgstr_w(lf.lfFaceName), lf.lfHeight, fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent, fm_gdi.font_height, fm_gdi.font_size); @@ -806,52 +955,43 @@ static void test_font_metrics(void) static void test_font_substitution(void) { - WCHAR ms_shell_dlg[LF_FACESIZE]; char fallback_font[LF_FACESIZE]; HDC hdc; - HFONT hfont; LOGFONTA lf; GpStatus status; GpGraphics *graphics; GpFont *font; GpFontFamily *family; - int ret; hdc = CreateCompatibleDC(0); status = GdipCreateFromHDC(hdc, &graphics); expect(Ok, status); - hfont = GetStockObject(DEFAULT_GUI_FONT); - ok(hfont != 0, "GetStockObject(DEFAULT_GUI_FONT) failed\n"); - - memset(&lf, 0xfe, sizeof(lf)); - ret = GetObjectA(hfont, sizeof(lf), &lf); - ok(ret == sizeof(lf), "GetObject failed\n"); - ok(!lstrcmpA(lf.lfFaceName, "MS Shell Dlg"), "wrong face name %s\n", lf.lfFaceName); - MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, -1, ms_shell_dlg, LF_FACESIZE); + memset(&lf, 0, sizeof(lf)); + lstrcpyA(lf.lfFaceName, "MS Shell Dlg"); status = GdipCreateFontFromLogfontA(hdc, &lf, &font); expect(Ok, status); memset(&lf, 0xfe, sizeof(lf)); status = GdipGetLogFontA(font, graphics, &lf); expect(Ok, status); - ok(!lstrcmpA(lf.lfFaceName, "Microsoft Sans Serif") || - !lstrcmpA(lf.lfFaceName, "Tahoma"), "wrong face name %s\n", lf.lfFaceName); + ok(lstrcmpA(lf.lfFaceName, "MS Shell Dlg") != 0, "expected substitution of MS Shell Dlg\n"); GdipDeleteFont(font); - status = GdipCreateFontFamilyFromName(ms_shell_dlg, NULL, &family); + family = NULL; + status = GdipCreateFontFamilyFromName(L"MS Shell Dlg", NULL, &family); expect(Ok, status); + font = NULL; status = GdipCreateFont(family, 12, FontStyleRegular, UnitPoint, &font); expect(Ok, status); memset(&lf, 0xfe, sizeof(lf)); status = GdipGetLogFontA(font, graphics, &lf); expect(Ok, status); - ok(!lstrcmpA(lf.lfFaceName, "Microsoft Sans Serif") || - !lstrcmpA(lf.lfFaceName, "Tahoma"), "wrong face name %s\n", lf.lfFaceName); + ok(lstrcmpA(lf.lfFaceName, "MS Shell Dlg") != 0, "expected substitution of MS Shell Dlg\n"); GdipDeleteFont(font); GdipDeleteFontFamily(family); - status = GdipCreateFontFamilyFromName(nonexistent, NULL, &family); + status = GdipCreateFontFamilyFromName(L"ThisFontShouldNotExist", NULL, &family); ok(status == FontFamilyNotFound, "expected FontFamilyNotFound, got %d\n", status); /* nonexistent fonts fallback to Arial, or something else if it's missing */ @@ -889,7 +1029,7 @@ static void test_font_substitution(void) lstrcpyA(lf.lfFaceName, "ThisFontShouldNotExist"); font = NULL; status = GdipCreateFontFromLogfontA(hdc, &lf, &font); -todo_wine + todo_wine ok(status == NotTrueTypeFont || broken(status == FileNotFound), /* before XP */ "expected NotTrueTypeFont, got %d\n", status); /* FIXME: remove when wine is fixed */ @@ -899,7 +1039,7 @@ todo_wine lf.lfFaceName[0] = 0; font = NULL; status = GdipCreateFontFromLogfontA(hdc, &lf, &font); -todo_wine + todo_wine ok(status == NotTrueTypeFont || broken(status == FileNotFound), /* before XP */ "expected NotTrueTypeFont, got %d\n", status); /* FIXME: remove when wine is fixed */ @@ -911,7 +1051,7 @@ todo_wine static void test_font_transform(void) { - static const WCHAR string[] = { 'A',0 }; + static const WCHAR string[] = L"A"; GpStatus status; HDC hdc; LOGFONTA lf; @@ -964,7 +1104,7 @@ static void test_font_transform(void) expect(Ok, status); expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf(height + margin_y, bounds.Height); set_rect_empty(&rect); set_rect_empty(&bounds); @@ -1008,7 +1148,7 @@ todo_wine expect(Ok, status); expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf(height + margin_y, bounds.Height); set_rect_empty(&rect); set_rect_empty(&bounds); @@ -1029,9 +1169,9 @@ todo_wine DriverStringOptionsCmapLookup, matrix, &bounds); expect(Ok, status); expectf(0.0, bounds.X); -todo_wine + todo_wine expectf_(-300.0, bounds.Y, 0.15); -todo_wine + todo_wine expectf(height * 3.0, bounds.Height); /* scale + ratate matrix */ @@ -1054,7 +1194,7 @@ todo_wine expect(Ok, status); expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf(height + margin_y, bounds.Height); set_rect_empty(&rect); set_rect_empty(&bounds); @@ -1074,11 +1214,11 @@ todo_wine status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, DriverStringOptionsCmapLookup, matrix, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(-43.814377, bounds.X, 0.05); -todo_wine + todo_wine expectf_(-212.235611, bounds.Y, 0.05); -todo_wine + todo_wine expectf_(340.847534, bounds.Height, 0.05); /* scale + ratate + shear matrix */ @@ -1088,7 +1228,7 @@ todo_wine expect(Ok, status); status = GdipGetLogFontA(font, graphics, &lf); expect(Ok, status); -todo_wine + todo_wine expect(1032, lf.lfHeight); expect(0, lf.lfWidth); expect_(3099, lf.lfEscapement, 1); @@ -1102,7 +1242,7 @@ todo_wine expect(Ok, status); expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf(height + margin_y, bounds.Height); set_rect_empty(&rect); set_rect_empty(&bounds); @@ -1122,11 +1262,11 @@ todo_wine status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, DriverStringOptionsCmapLookup, matrix, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(-636.706848, bounds.X, 0.05); -todo_wine + todo_wine expectf_(-175.257523, bounds.Y, 0.05); -todo_wine + todo_wine expectf_(1532.984985, bounds.Height, 0.05); /* scale + ratate + shear + translate matrix */ @@ -1136,7 +1276,7 @@ todo_wine expect(Ok, status); status = GdipGetLogFontA(font, graphics, &lf); expect(Ok, status); -todo_wine + todo_wine expect(1032, lf.lfHeight); expect(0, lf.lfWidth); expect_(3099, lf.lfEscapement, 1); @@ -1150,7 +1290,7 @@ todo_wine expect(Ok, status); expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf(height + margin_y, bounds.Height); set_rect_empty(&rect); set_rect_empty(&bounds); @@ -1170,13 +1310,27 @@ todo_wine status = GdipMeasureDriverString(graphics, (const UINT16 *)string, -1, font, pos, DriverStringOptionsCmapLookup, matrix, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(-626.706848, bounds.X, 0.05); -todo_wine + todo_wine expectf_(-155.257523, bounds.Y, 0.05); -todo_wine + todo_wine expectf_(1532.984985, bounds.Height, 0.05); + GdipDeleteGraphics(graphics); + + SetMapMode( hdc, MM_ISOTROPIC); + SetWindowExtEx(hdc, 200, 200, NULL); + SetViewportExtEx(hdc, 100, 100, NULL); + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + status = GdipGetLogFontA(font, graphics, &lf); + expect(Ok, status); + expect(-50, lf.lfHeight); + expect(0, lf.lfWidth); + expect(0, lf.lfEscapement); + expect(0, lf.lfOrientation); + GdipDeleteMatrix(matrix); GdipDeleteFont(font); GdipDeleteGraphics(graphics); @@ -1187,8 +1341,9 @@ todo_wine static void test_GdipGetFontCollectionFamilyList(void) { - GpFontFamily *family, *family2; + GpFontFamily *family, *family2, **families; GpFontCollection *collection; + UINT i; INT found, count; GpStatus status; @@ -1228,15 +1383,31 @@ static void test_GdipGetFontCollectionFamilyList(void) ok(found == 1, "Unexpected list count %d.\n", found); ok(family != NULL, "Expected family instance.\n"); - family = NULL; + family2 = NULL; found = 0; status = GdipGetFontCollectionFamilyList(collection, 1, &family2, &found); ok(status == Ok, "Failed to get family list, status %d.\n", status); ok(found == 1, "Unexpected list count %d.\n", found); - ok(family2 != family, "Unexpected family instance.\n"); + ok(family2 == family, "Unexpected family instance.\n"); - GdipDeleteFontFamily(family); - GdipDeleteFontFamily(family2); + status = GdipDeleteFontFamily(family); + expect(Ok, status); + + status = GdipDeleteFontFamily(family2); + expect(Ok, status); + + families = GdipAlloc((count + 1) * sizeof(*families)); + found = 0; + status = GdipGetFontCollectionFamilyList(collection, count + 1, families, &found); + ok(status == Ok, "Failed to get family list, status %d.\n", status); + ok(found == count, "Unexpected list count %d, extected %d.\n", found, count); + + for (i = 0; i < found; i++) + { + status = GdipDeleteFontFamily(families[i]); + expect(Ok, status); + } + GdipFree(families); } static void test_GdipGetFontCollectionFamilyCount(void) @@ -1260,6 +1431,147 @@ static void test_GdipGetFontCollectionFamilyCount(void) ok(status == InvalidParameter, "Unexpected status %d.\n", status); } +static BOOL is_family_in_collection(GpFontCollection *collection, GpFontFamily *family) +{ + GpStatus status; + GpFontFamily **list; + int count, i; + BOOL found = FALSE; + + status = GdipGetFontCollectionFamilyCount(collection, &count); + expect(Ok, status); + + list = GdipAlloc(count * sizeof(GpFontFamily *)); + status = GdipGetFontCollectionFamilyList(collection, count, list, &count); + expect(Ok, status); + + for (i = 0; i < count; i++) + { + if (list[i] == family) + { + found = TRUE; + break; + } + } + + GdipFree(list); + + return found; +} + +static void test_CloneFont(void) +{ + GpStatus status; + GpFontCollection *collection, *collection2; + GpFont *font, *font2; + GpFontFamily *family, *family2; + REAL height; + Unit unit; + int style; + BOOL ret; + + status = GdipNewInstalledFontCollection(&collection); + expect(Ok, status); + + status = GdipNewInstalledFontCollection(&collection2); + expect(Ok, status); + ok(collection == collection2, "got %p\n", collection2); + + status = GdipCreateFontFamilyFromName(L"ThisFontShouldNotExist", NULL, &family); + expect(FontFamilyNotFound, status); + + status = GdipCreateFontFamilyFromName(L"ThisFontShouldNotExist", collection, &family); + expect(FontFamilyNotFound, status); + + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); + expect(Ok, status); + + ret = is_family_in_collection(collection, family); + ok(ret, "family is not in collection\n"); + + status = GdipCreateFont(family, 30.0f, FontStyleRegular, UnitPixel, &font); + expect(Ok, status); + + status = GdipGetFontUnit(font, &unit); + expect(Ok, status); + ok(unit == UnitPixel, "got %u\n", unit); + + status = GdipGetFontSize(font, &height); + expect(Ok, status); + ok(height == 30.0f, "got %f\n", height); + + status = GdipGetFontStyle(font, &style); + expect(Ok, status); + ok(style == FontStyleRegular, "got %d\n", style); + + status = GdipGetFamily(font, &family2); + expect(Ok, status); + ok(family == family2, "got %p\n", family2); + + status = GdipCloneFont(font, &font2); + expect(Ok, status); + + status = GdipGetFontUnit(font2, &unit); + expect(Ok, status); + ok(unit == UnitPixel, "got %u\n", unit); + + status = GdipGetFontSize(font2, &height); + expect(Ok, status); + ok(height == 30.0f, "got %f\n", height); + + status = GdipGetFontStyle(font2, &style); + expect(Ok, status); + ok(style == FontStyleRegular, "got %d\n", style); + + status = GdipGetFamily(font2, &family2); + expect(Ok, status); + ok(family == family2, "got %p\n", family2); + + GdipDeleteFont(font2); + GdipDeleteFont(font); + GdipDeleteFontFamily(family); +} + +static void test_GdipPrivateAddMemoryFont(void) +{ + static const WORD resource_ids[] = + { + 3, /* A font that has an invalid full name on Mac platform and a valid full name on Microsoft platform */ + 4, /* A font that has an invalid full name on Unicode platform and a valid full name on Mac platform */ + }; + GpFontCollection *fonts; + GpStatus stat; + int count, i; + void *buffer; + DWORD size; + + for (i = 0; i < ARRAY_SIZE(resource_ids); i++) + { + winetest_push_context("test %d", i); + + stat = GdipNewPrivateFontCollection(&fonts); + ok(stat == Ok, "GdipNewPrivateFontCollection failed, error %d\n", stat); + + load_resource(MAKEINTRESOURCEW(resource_ids[i]), (BYTE **)&buffer, &size); + stat = GdipPrivateAddMemoryFont(fonts, buffer, size); + if (stat == Ok) + { + stat = GdipGetFontCollectionFamilyCount(fonts, &count); + ok(stat == Ok, "GdipGetFontCollectionFamilyCount failed, error %d\n", stat); + ok(count == 1, "Expected count 1, got %d\n", count); + } + else if (i == 1 && stat == FileNotFound) + win_skip("Fonts without Microsoft platform names are unsupported on win7.\n"); + else + ok(0, "GdipPrivateAddMemoryFont failed, error %d\n", stat); + + stat = GdipDeletePrivateFontCollection(&fonts); + ok(stat == Ok, "GdipDeletePrivateFontCollection failed, error %d\n", stat); + + winetest_pop_context(); + } +} + START_TEST(font) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1279,11 +1591,13 @@ START_TEST(font) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + test_CloneFont(); test_long_name(); test_font_transform(); test_font_substitution(); test_font_metrics(); test_createfont(); + test_createfont_charset(); test_logfont(); test_fontfamily(); test_fontfamily_properties(); @@ -1292,6 +1606,7 @@ START_TEST(font) test_heightgivendpi(); test_GdipGetFontCollectionFamilyList(); test_GdipGetFontCollectionFamilyCount(); + test_GdipPrivateAddMemoryFont(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/graphics.c b/modules/rostests/winetests/gdiplus/graphics.c index f2ff589ab5b..dd25dd54fb2 100644 --- a/modules/rostests/winetests/gdiplus/graphics.c +++ b/modules/rostests/winetests/gdiplus/graphics.c @@ -23,6 +23,7 @@ #include "objbase.h" #include "gdiplus.h" +#include "winspool.h" #include "wine/test.h" #define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (INT)(expected), (INT)(got)) @@ -175,7 +176,7 @@ typedef struct node{ /* Linked list prepend function. */ static void log_state(GraphicsState data, node ** log) { - node * new_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(node)); + node * new_entry = malloc(sizeof(node)); new_entry->data = data; new_entry->next = *log; @@ -208,7 +209,7 @@ static void check_no_duplicates(node * log) temp = orig; do{ temp2 = temp->next; - HeapFree(GetProcessHeap(), 0, temp); + free(temp); temp = temp2; }while(temp); @@ -1306,27 +1307,78 @@ static void test_GdipDrawLineI(void) ReleaseDC(hwnd, hdc); } +static void test_GdipDrawImageFX(void) +{ + GpGraphics *graphics = NULL; + GpMatrix *transform; + GpBitmap *bm = NULL; + GpStatus status; + GpRectF source; + BYTE buff[400]; + HDC hdc; + + if (!(hdc = GetDC( hwnd ))) + return; + + memset(buff, 0, sizeof(buff)); + status = GdipCreateBitmapFromScan0(10, 10, 40, PixelFormat32bppRGB, buff, &bm); + expect(Ok, status); + ok(NULL != bm, "Expected bitmap to be initialized\n"); + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + status = GdipCreateMatrix2(2.0, 0.0, 0.0, 1.0, 10.0, 20.0, &transform); + expect(Ok, status); + + /* DrawImageFX with source rectangle */ + status = GdipDrawImageFX(graphics, NULL, &source, NULL, NULL, NULL, UnitPixel); + expect(InvalidParameter, status); + + /* DrawImageFX with source bitmap */ + status = GdipDrawImageFX(graphics, (GpImage*)bm, NULL, NULL, NULL, NULL, UnitPixel); + expect(Ok, status); + + /* DrawImageFX with source bitmap and transform */ + status = GdipDrawImageFX(graphics, (GpImage*)bm, NULL, transform, NULL, NULL, UnitPixel); + expect(Ok, status); + + /* DrawImageFX with source bitmap and source rectangle */ + source.X = source.Y = 0.0; + source.Height = source.Width = 10.0; + + status = GdipDrawImageFX(graphics, (GpImage*)bm, &source, NULL, NULL, NULL, UnitPixel); + expect(Ok, status); + + /* DrawImageFX with source bitmap, source rectangle, and transform */ + status = GdipDrawImageFX(graphics, (GpImage*)bm, &source, transform, NULL, NULL, UnitPixel); + expect(Ok, status); + + GdipDeleteMatrix(transform); + GdipDeleteGraphics(graphics); + GdipDisposeImage((GpImage*)bm); + ReleaseDC(hwnd, hdc); +} + static void test_GdipDrawImagePointsRect(void) { GpStatus status; GpGraphics *graphics = NULL; GpPointF ptf[4]; GpBitmap *bm = NULL; - BYTE rbmi[sizeof(BITMAPINFOHEADER)]; BYTE buff[400]; - BITMAPINFO *bmi = (BITMAPINFO*)rbmi; + BITMAPINFOHEADER bmihdr; HDC hdc = GetDC( hwnd ); if (!hdc) return; - memset(rbmi, 0, sizeof(rbmi)); - bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi->bmiHeader.biWidth = 10; - bmi->bmiHeader.biHeight = 10; - bmi->bmiHeader.biPlanes = 1; - bmi->bmiHeader.biBitCount = 32; - bmi->bmiHeader.biCompression = BI_RGB; - status = GdipCreateBitmapFromGdiDib(bmi, buff, &bm); + memset(&bmihdr, 0, sizeof(bmihdr)); + bmihdr.biSize = sizeof(BITMAPINFOHEADER); + bmihdr.biWidth = 10; + bmihdr.biHeight = 10; + bmihdr.biPlanes = 1; + bmihdr.biBitCount = 32; + bmihdr.biCompression = BI_RGB; + status = GdipCreateBitmapFromGdiDib((BITMAPINFO*)&bmihdr, buff, &bm); expect(Ok, status); ok(NULL != bm, "Expected bitmap to be initialized\n"); status = GdipCreateFromHDC(hdc, &graphics); @@ -1341,6 +1393,8 @@ static void test_GdipDrawImagePointsRect(void) ptf[3].Y = 10; status = GdipDrawImagePointsRect(graphics, (GpImage*)bm, ptf, 4, 0, 0, 10, 10, UnitPixel, NULL, NULL, NULL); expect(NotImplemented, status); + status = GdipDrawImagePointsRect(graphics, (GpImage*)bm, ptf, 5, 0, 0, 10, 10, UnitPixel, NULL, NULL, NULL); + expect(InvalidParameter, status); status = GdipDrawImagePointsRect(graphics, (GpImage*)bm, ptf, 2, 0, 0, 10, 10, UnitPixel, NULL, NULL, NULL); expect(InvalidParameter, status); status = GdipDrawImagePointsRect(graphics, (GpImage*)bm, ptf, 3, 0, 0, 10, 10, UnitPixel, NULL, NULL, NULL); @@ -1826,6 +1880,8 @@ static void test_Get_Release_DC(void) /* GdipMeasureString */ status = GdipResetClip(graphics); expect(ObjectBusy, status); + status = GdipResetPageTransform(graphics); + expect(ObjectBusy, status); status = GdipResetWorldTransform(graphics); expect(ObjectBusy, status); /* GdipRestoreGraphics */ @@ -1842,6 +1898,8 @@ static void test_Get_Release_DC(void) expect(ObjectBusy, status); status = GdipSetPageScale(graphics, 1.0); expect(ObjectBusy, status); + status = GdipSetPageScale(graphics, 0.0); + expect(ObjectBusy, status); status = GdipSetPageUnit(graphics, UnitWorld); expect(ObjectBusy, status); status = GdipSetPixelOffsetMode(graphics, PixelOffsetModeDefault); @@ -2278,8 +2336,8 @@ static void test_GdipDrawString(void) GpStringFormat *format; GpBrush *brush; LOGFONTA logfont; - HDC hdc = GetDC( hwnd ); - static const WCHAR string[] = {'T','e','s','t',0}; + HDC hdc = GetDC( hwnd ), temp_hdc; + static const WCHAR string[] = L"Test"; static const PointF positions[4] = {{0,0}, {1,1}, {2,2}, {3,3}}; GpMatrix *matrix; @@ -2299,10 +2357,29 @@ static void test_GdipDrawString(void) } expect(Ok, status); - status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); + status = GdipCreateStringFormat(0,0,&format); expect(Ok, status); - status = GdipCreateStringFormat(0,0,&format); + if (winetest_interactive) + { + status = GdipCreateSolidFill(0xFF000000, (GpSolidFill**)&brush); + expect(Ok, status); + rect.X = 0; + rect.Y = 0; + rect.Width = 0; + rect.Height = 14; + GdipRotateWorldTransform(graphics, 45, MatrixOrderPrepend); + GdipScaleWorldTransform(graphics, 2, 2, MatrixOrderPrepend); + GdipGraphicsClear(graphics, 0xFFFFFFFF); + status = GdipDrawString(graphics, L"\u8336Hola\u8336", 6, fnt, &rect, format, brush); + expect(Ok, status); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); /* FIXME: In Windows this test works without this line. */ + Sleep(4000); + GdipDeleteBrush(brush); + GdipResetWorldTransform(graphics); + } + + status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); expect(Ok, status); rect.X = 0; @@ -2313,6 +2390,16 @@ static void test_GdipDrawString(void) status = GdipDrawString(graphics, string, 4, fnt, &rect, format, brush); expect(Ok, status); + status = GdipGetDC(graphics, &temp_hdc); + expect(Ok, status); + ok(temp_hdc != NULL, "got NULL temp_hdc\n"); + + status = GdipDrawString(graphics, string, 4, fnt, &rect, format, brush); + expect(ObjectBusy, status); + + status = GdipReleaseDC(graphics, temp_hdc); + expect(Ok, status); + status = GdipCreateMatrix(&matrix); expect(Ok, status); @@ -2622,7 +2709,7 @@ static void test_fromMemoryBitmap(void) color = GetPixel(hdc, 0, 0); /* The HDC is write-only, and native fills with a solid color to figure out * which pixels have changed. */ - todo_wine expect(0x0c0b0d, color); + expect(0x0c0b0d, color); SetPixel(hdc, 0, 0, 0x797979); SetPixel(hdc, 1, 0, 0x0c0b0d); @@ -2633,7 +2720,7 @@ static void test_fromMemoryBitmap(void) GdipDeleteGraphics(graphics); expect(0x79, bits[0]); - todo_wine expect(0x68, bits[3]); + expect(0x68, bits[3]); GdipDisposeImage((GpImage*)bitmap); @@ -2649,7 +2736,7 @@ static void test_fromMemoryBitmap(void) ok(hdc != NULL, "got NULL hdc\n"); color = GetPixel(hdc, 0, 0); - todo_wine expect(0x0c0b0d, color); + expect(0x0c0b0d, color); status = GdipReleaseDC(graphics, hdc); expect(Ok, status); @@ -2670,7 +2757,7 @@ static void test_fromMemoryBitmap(void) ok(hdc != NULL, "got NULL hdc\n"); color = GetPixel(hdc, 0, 0); - todo_wine expect(0x0c0b0d, color); + expect(0x0c0b0d, color); status = GdipReleaseDC(graphics, hdc); expect(Ok, status); @@ -3178,7 +3265,7 @@ static void test_GdipGetNearestColor(void) todo_wine ok(color == 0xffa8b8e8 || broken(color == 0xffa0b8e0), /* Win98/WinMe */ - "Expected ffa8b8e8, got %.8x\n", color); + "Expected ffa8b8e8, got %.8lx\n", color); GdipDeleteGraphics(graphics); GdipDisposeImage((GpImage*)bitmap); @@ -3195,9 +3282,9 @@ static void test_string_functions(void) GpBrush *brush; ARGB color = 0xff000000; HDC hdc = GetDC( hwnd ); - const WCHAR fontname[] = {'T','a','h','o','m','a',0}; - const WCHAR teststring[] = {'M','M',' ','M','\n','M',0}; - const WCHAR teststring2[] = {'j',0}; + const WCHAR teststring[] = L"MM M\nM"; + const WCHAR teststring2[] = L"j"; + const WCHAR teststring3[] = L"MM M\r\nM\0"; REAL char_width, char_height; INT codepointsfitted, linesfilled; GpStringFormat *format; @@ -3213,7 +3300,7 @@ static void test_string_functions(void) expect(Ok, status); ok(graphics != NULL, "Expected graphics to be initialized\n"); - status = GdipCreateFontFamilyFromName(fontname, NULL, &family); + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, status); status = GdipCreateFont(family, 10.0, FontStyleRegular, UnitPixel, &font); @@ -3269,6 +3356,12 @@ static void test_string_functions(void) status = GdipMeasureString(graphics, teststring, 6, font, &rc, NULL, &bounds, &codepointsfitted, NULL); expect(Ok, status); + /* new line handling */ + status = GdipMeasureString(graphics, teststring3, -1, font, &rc, NULL, &bounds, &codepointsfitted, &linesfilled); + expect(Ok, status); + expect(7, codepointsfitted); + expect(2, linesfilled); + status = GdipMeasureString(graphics, teststring, 1, font, &rc, NULL, &char_bounds, &codepointsfitted, &linesfilled); expect(Ok, status); expectf(0.0, char_bounds.X); @@ -3744,8 +3837,7 @@ static void test_GdipMeasureString(void) { 200.0, 600.0, 1.0, UnitPixel }, { 200.0, 600.0, 2.0, UnitPixel }, }; - static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; - static const WCHAR string[] = { '1','2','3','4','5','6','7',0 }; + static const WCHAR string[] = L"1234567"; GpStatus status; GpGraphics *graphics; GpFontFamily *family; @@ -3761,7 +3853,7 @@ static void test_GdipMeasureString(void) status = GdipCreateStringFormat(0, LANG_NEUTRAL, &format); expect(Ok, status); - status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, status); /* font size in pixels */ @@ -3786,7 +3878,7 @@ static void test_GdipMeasureString(void) height = units_to_pixels(font_size, td[i].unit, td[i].res_y); if (td[i].unit != UnitDisplay) height *= td[i].page_scale; - ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %d (%f), got %d\n", + ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %ld (%f), got %ld\n", i, (LONG)(height + 0.5), height, lf.lfHeight); height = font_size + 2.0 * font_size / 6.0; @@ -3804,7 +3896,7 @@ static void test_GdipMeasureString(void) expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf_(height, bounds.Height, height / 100.0); expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); expect(7, chars); @@ -3821,7 +3913,7 @@ todo_wine expect(Ok, status); expectf(50.0, bounds.X); expectf(50.0, bounds.Y); -todo_wine + todo_wine expectf_(height, bounds.Height, height / 100.0); expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); expect(7, chars); @@ -3867,7 +3959,7 @@ todo_wine else height = units_to_pixels(font_size, font_unit, td[i].res_y); /*trace("%.1f font units = %f pixels with %.1f dpi, page_scale %.1f\n", font_size, height, td[i].res_y, td[i].page_scale);*/ - ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %d (%f), got %d\n", + ok(-lf.lfHeight == (LONG)(height + 0.5), "%u: expected %ld (%f), got %ld\n", i, (LONG)(height + 0.5), height, lf.lfHeight); if (td[i].unit == UnitDisplay || td[i].unit == UnitPixel) @@ -3893,7 +3985,7 @@ todo_wine expectf(0.0, bounds.X); expectf(0.0, bounds.Y); -todo_wine + todo_wine expectf_(height, bounds.Height, height / 85.0); expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); expect(7, chars); @@ -3910,7 +4002,7 @@ todo_wine expect(Ok, status); expectf(50.0, bounds.X); expectf(50.0, bounds.Y); -todo_wine + todo_wine expectf_(height, bounds.Height, height / 85.0); expectf_(bounds.Height / base_cy, bounds.Width / base_cx, 0.1); expect(7, chars); @@ -3922,7 +4014,7 @@ todo_wine height *= td[i].page_scale; /*trace("%u: unit %u, %.1fx%.1f dpi, scale %.1f, height %f, pixels %f\n", i, td[i].unit, td[i].res_x, td[i].res_y, td[i].page_scale, bounds.Height, height);*/ -todo_wine + todo_wine expectf_(100.0, height, 1.1); status = GdipDeleteGraphics(graphics); @@ -3957,7 +4049,7 @@ todo_wine lf.lfHeight = 0xdeadbeef; status = GdipGetLogFontW(font, graphics, &lf); expect(Ok, status); - ok(lf.lfHeight == -100, "%u: expected -100, got %d\n", i, lf.lfHeight); + ok(lf.lfHeight == -100, "%u: expected -100, got %ld\n", i, lf.lfHeight); set_rect_empty(&rc); set_rect_empty(&bounds); @@ -4051,6 +4143,91 @@ static void test_transform(void) } } +static void test_set_page_transform(void) +{ + static const struct + { + GpUnit unit; + BOOL isInvalid; + } td_unit[] = + { + {UnitWorld, TRUE}, + {UnitDisplay}, + {UnitPixel}, + {UnitPoint}, + {UnitInch}, + {UnitDocument}, + {UnitMillimeter}, + {UnitMillimeter + 1, TRUE}, + }; + static const struct { + REAL scale; + BOOL isInvalid; + } td_scale[] = + { + {-1.0, TRUE}, + {0.0, TRUE}, + {0.5}, + {1.0}, + {2.0}, + }; + GpStatus status; + GpGraphics *graphics; + HDC hdc = GetDC( hwnd ); + GpUnit unit; + REAL scale; + UINT i; + + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + for (i = 0; i < ARRAY_SIZE(td_unit); i++) + { + winetest_push_context("%u", i); + status = GdipSetPageUnit(graphics, td_unit[i].unit); + expect(td_unit[i].isInvalid ? InvalidParameter : Ok, status); + if (status == Ok) + { + status = GdipGetPageUnit(graphics, &unit); + expect(Ok, status); + expect(td_unit[i].unit, unit); + } + winetest_pop_context(); + } + + for (i = 0; i < ARRAY_SIZE(td_scale); i++) + { + winetest_push_context("%u", i); + status = GdipSetPageScale(graphics, td_scale[i].scale); + expect(td_scale[i].isInvalid ? InvalidParameter : Ok, status); + if (status == Ok) + { + status = GdipGetPageScale(graphics, &scale); + expect(Ok, status); + expectf_(td_scale[i].scale, scale, 0); + } + winetest_pop_context(); + } + + status = GdipGetPageUnit(graphics, &unit); + expect(Ok, status); + expect(UnitMillimeter, unit); + status = GdipGetPageScale(graphics, &scale); + expect(Ok, status); + expectf_(2.0, scale, 0); + status = GdipResetPageTransform(graphics); + expect(Ok, status); + status = GdipGetPageUnit(graphics, &unit); + expect(Ok, status); + expect(UnitDisplay, unit); + status = GdipGetPageScale(graphics, &scale); + expect(Ok, status); + expectf_(1.0, scale, 0); + + GdipDeleteGraphics(graphics); + ReleaseDC(hwnd, hdc); +} + static void test_pen_thickness(void) { static const struct test_data @@ -4141,7 +4318,7 @@ static void test_pen_thickness(void) size = max-min+1; - ok(size == td[i].cx, "%u: expected %d, got %d\n", i, td[i].cx, size); + ok(size == td[i].cx || broken (i == 1 && size == 1), "%u: expected %d, got %d\n", i, td[i].cx, size); min = -1; max = -2; @@ -4166,7 +4343,7 @@ static void test_pen_thickness(void) size = max-min+1; - ok(size == td[i].cy, "%u: expected %d, got %d\n", i, td[i].cy, size); + ok(size == td[i].cy || broken (i == 1 && size == 1), "%u: expected %d, got %d\n", i, td[i].cy, size); status = GdipBitmapUnlockBits(u.bitmap, &bd); expect(Ok, status); @@ -4259,8 +4436,7 @@ static void test_pen_thickness(void) */ static void test_font_height_scaling(void) { - static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; - static const WCHAR string[] = { '1','2','3','4','5','6','7',0 }; + static const WCHAR string[] = L"1234567"; HDC hdc; GpStringFormat *format; CharacterRange range = { 0, 7 }; @@ -4281,7 +4457,7 @@ static void test_font_height_scaling(void) status = GdipCreateRegion(®ion); expect(Ok, status); - status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, status); hdc = CreateCompatibleDC(0); @@ -4360,7 +4536,6 @@ static void test_font_height_scaling(void) /* UnitPixel = 2, UnitPoint = 3, UnitInch = 4, UnitDocument = 5, UnitMillimeter = 6 */ for (gfx_unit = 2; gfx_unit <= 6; gfx_unit++) { - static const WCHAR doubleW[2] = { 'W','W' }; RectF bounds_1, bounds_2; REAL margin, margin_y, font_height; int match; @@ -4379,7 +4554,7 @@ static void test_font_height_scaling(void) status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, NULL, NULL); expect(Ok, status); /*trace("bounds: %f,%f,%f,%f\n", bounds.X, bounds.Y, bounds.Width, bounds.Height);*/ -todo_wine + todo_wine expectf_(font_height + margin_y, bounds.Height, 0.005); ptf.X = 0; @@ -4387,25 +4562,25 @@ todo_wine status = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &ptf, 1); expect(Ok, status); match = fabs(100.0 - ptf.Y) <= 1.0; -todo_wine + todo_wine ok(match, "Expected 100.0, got %f\n", ptf.Y); /* verify the result */ ptf.Y = units_to_pixels(bounds.Height, gfx_unit, dpi); ptf.Y /= 100.0; match = fabs(100.0 - ptf.Y) <= 1.0; -todo_wine + todo_wine ok(match, "Expected 100.0, got %f\n", ptf.Y); /* bounds.width of 1 glyph: [margin]+[width]+[margin] */ set_rect_empty(&rect); set_rect_empty(&bounds_1); - status = GdipMeasureString(graphics, doubleW, 1, font, &rect, format, &bounds_1, NULL, NULL); + status = GdipMeasureString(graphics, L"W", 1, font, &rect, format, &bounds_1, NULL, NULL); expect(Ok, status); /* bounds.width of 2 identical glyphs: [margin]+[width]+[width]+[margin] */ set_rect_empty(&rect); set_rect_empty(&bounds_2); - status = GdipMeasureString(graphics, doubleW, 2, font, &rect, format, &bounds_2, NULL, NULL); + status = GdipMeasureString(graphics, L"WW", 2, font, &rect, format, &bounds_2, NULL, NULL); expect(Ok, status); /* margin = [bounds.width of 1] - [bounds.width of 2] / 2*/ @@ -4447,10 +4622,10 @@ cleanup: static void test_measure_string(void) { - static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; - static const WCHAR string[] = { 'A','0','1',0 }; + static const WCHAR string[] = L"A01"; + static const WCHAR string2[] = L"M MM"; HDC hdc; - GpStringFormat *format; + GpStringFormat *format, *format_no_wrap; CharacterRange range; GpRegion *region; GpGraphics *graphics; @@ -4458,7 +4633,7 @@ static void test_measure_string(void) GpFont *font; GpStatus status; RectF bounds, rect; - REAL width, height, width_1, width_2; + REAL width, height, width_1, width_2, width_MM, width_M_M; REAL margin_x, margin_y, width_rgn, height_rgn; int lines, glyphs; @@ -4469,7 +4644,7 @@ static void test_measure_string(void) status = GdipCreateRegion(®ion); expect(Ok, status); - status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, status); hdc = CreateCompatibleDC(0); @@ -4502,7 +4677,6 @@ static void test_measure_string(void) expectf(0.0, bounds.X); expectf(0.0, bounds.Y); expectf(width, bounds.Width); -todo_wine expectf(height / 2.0, bounds.Height); range.First = 0; @@ -4522,7 +4696,7 @@ todo_wine expectf_(5.0 + margin_x, bounds.X, 1.0); expectf(5.0, bounds.Y); expectf_(width - margin_x*2.0, bounds.Width, 1.0); -todo_wine + todo_wine expectf_(height - margin_y, bounds.Height, 1.0); width_rgn = bounds.Width; @@ -4578,7 +4752,7 @@ todo_wine expectf_(5.0 + margin_x, bounds.X, 1.0); expectf(5.0, bounds.Y); expectf_(width_1, bounds.Width, 1.0); -todo_wine + todo_wine expectf_(height - margin_y, bounds.Height, 1.0); status = GdipSetStringFormatFlags(format, StringFormatFlagsNoWrap | StringFormatFlagsNoClip); @@ -4621,7 +4795,7 @@ todo_wine expectf(0.0, bounds.X); expectf(0.0, bounds.Y); expectf_(width, bounds.Width, 0.01); -todo_wine + todo_wine expectf(height, bounds.Height); set_rect_empty(&rect); @@ -4676,7 +4850,7 @@ todo_wine set_rect_empty(&rect); rect.Height = height; - rect.Width = width_2 - 0.05; + rect.Width = width_2 - 0.006; set_rect_empty(&bounds); status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); expect(Ok, status); @@ -4687,6 +4861,19 @@ todo_wine expectf_(width_1, bounds.Width, 0.01); expectf(height, bounds.Height); + set_rect_empty(&rect); + rect.Height = height; + rect.Width = width_2 - 0.004; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(2, glyphs); + expect(1, lines); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); + expectf_(width_2, bounds.Width, 0.01); + expectf(height, bounds.Height); + /* Default (Near) alignment */ rect.X = 5.0; rect.Y = 5.0; @@ -4714,7 +4901,7 @@ todo_wine expectf_(5.0 + margin_x, bounds.X, 1.0); expectf(5.0, bounds.Y); expectf_(width - margin_x*2.0, bounds.Width, 1.0); -todo_wine + todo_wine expectf_(height - margin_y, bounds.Height, 1.0); width_rgn = bounds.Width; @@ -4733,9 +4920,8 @@ todo_wine expect(Ok, status); expect(3, glyphs); expect(1, lines); -todo_wine expectf_(5.0 + width/2.0, bounds.X, 0.01); -todo_wine + todo_wine expectf(5.0 + height/2.0, bounds.Y); expectf_(width, bounds.Width, 0.01); expectf(height, bounds.Height); @@ -4749,9 +4935,9 @@ todo_wine expect(Ok, status); expect(3, glyphs); expect(1, lines); -todo_wine + todo_wine expectf_(5.0 - width/2.0, bounds.X, 0.01); -todo_wine + todo_wine expectf(5.0 - height/2.0, bounds.Y); expectf_(width, bounds.Width, 0.01); expectf(height, bounds.Height); @@ -4765,9 +4951,9 @@ todo_wine set_rect_empty(&bounds); status = GdipGetRegionBounds(region, graphics, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(5.0 + width_rgn/2.0, bounds.X, 1.0); -todo_wine + todo_wine expectf_(5.0 + height_rgn/2.0, bounds.Y, 1.0); expectf_(width_rgn, bounds.Width, 1.0); expectf_(height_rgn, bounds.Height, 1.0); @@ -4781,9 +4967,9 @@ todo_wine set_rect_empty(&bounds); status = GdipGetRegionBounds(region, graphics, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(5.0 - width_rgn/2.0, bounds.X, 1.0); -todo_wine + todo_wine expectf_(5.0 - height_rgn/2.0, bounds.Y, 1.0); expectf_(width_rgn, bounds.Width, 1.0); expectf_(height_rgn, bounds.Height, 1.0); @@ -4801,9 +4987,8 @@ todo_wine expect(Ok, status); expect(3, glyphs); expect(1, lines); -todo_wine expectf_(5.0 + width, bounds.X, 0.01); -todo_wine + todo_wine expectf(5.0 + height, bounds.Y); expectf_(width, bounds.Width, 0.01); expectf(height, bounds.Height); @@ -4817,9 +5002,9 @@ todo_wine expect(Ok, status); expect(3, glyphs); expect(1, lines); -todo_wine + todo_wine expectf_(5.0 - width, bounds.X, 0.01); -todo_wine + todo_wine expectf(5.0 - height, bounds.Y); expectf_(width, bounds.Width, 0.01); expectf(height, bounds.Height); @@ -4833,9 +5018,9 @@ todo_wine set_rect_empty(&bounds); status = GdipGetRegionBounds(region, graphics, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(5.0 + width_rgn, bounds.X, 2.0); -todo_wine + todo_wine expectf_(5.0 + height_rgn, bounds.Y, 1.0); expectf_(width_rgn, bounds.Width, 1.0); expectf_(height_rgn, bounds.Height, 1.0); @@ -4849,13 +5034,60 @@ todo_wine set_rect_empty(&bounds); status = GdipGetRegionBounds(region, graphics, &bounds); expect(Ok, status); -todo_wine + todo_wine expectf_(5.0 - width_rgn, bounds.X, 2.0); -todo_wine + todo_wine expectf_(5.0 - height_rgn, bounds.Y, 1.0); expectf_(width_rgn, bounds.Width, 1.0); expectf_(height_rgn, bounds.Height, 1.0); + /* Measure "MM" */ + rect.X = 5.0; + rect.Y = 5.0; + rect.Width = 32000.0; + rect.Height = 32000.0; + status = GdipMeasureString(graphics, string2 + 2, 2, font, &rect, NULL, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(2, glyphs); + expect(1, lines); + width_MM = bounds.Width; + + /* Measure "M M" */ + rect.X = 5.0; + rect.Y = 5.0; + rect.Width = 32000.0; + rect.Height = 32000.0; + status = GdipMeasureString(graphics, string2, 3, font, &rect, NULL, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(3, glyphs); + expect(1, lines); + width_M_M = bounds.Width; + + /* With wrap */ + rect.X = 5.0; + rect.Y = 5.0; + rect.Width = width_M_M; + rect.Height = 32000.0; + status = GdipMeasureString(graphics, string2, -1, font, &rect, NULL, &bounds, &glyphs, &lines); + expect(Ok, status); + expectf_(width_MM, bounds.Width, 0.1); + expect(4, glyphs); + expect(2, lines); + + /* Without wrap */ + status = GdipCreateStringFormat(StringFormatFlagsNoWrap, LANG_NEUTRAL, &format_no_wrap); + expect(Ok, status); + + rect.X = 5.0; + rect.Y = 5.0; + rect.Width = width_M_M; + rect.Height = 32000.0; + status = GdipMeasureString(graphics, string2, -1, font, &rect, format_no_wrap, &bounds, &glyphs, &lines); + expect(Ok, status); + expectf_(width_M_M, bounds.Width, 0.1); + expect(3, glyphs); + expect(1, lines); + status = GdipDeleteFont(font); expect(Ok, status); @@ -4866,12 +5098,11 @@ todo_wine GdipDeleteFontFamily(family); GdipDeleteRegion(region); GdipDeleteStringFormat(format); + GdipDeleteStringFormat(format_no_wrap); } static void test_measured_extra_space(void) { - static const WCHAR tahomaW[] = { 'T','a','h','o','m','a',0 }; - static const WCHAR string[2] = { 'W','W' }; GpStringFormat *format; HDC hdc; GpGraphics *graphics; @@ -4885,7 +5116,7 @@ static void test_measured_extra_space(void) status = GdipCreateStringFormat(0, LANG_NEUTRAL, &format); expect(Ok, status); - status = GdipCreateFontFamilyFromName(tahomaW, NULL, &family); + status = GdipCreateFontFamilyFromName(L"Tahoma", NULL, &family); expect(Ok, status); hdc = CreateCompatibleDC(0); status = GdipCreateFromHDC(hdc, &graphics); @@ -4915,12 +5146,12 @@ static void test_measured_extra_space(void) /* bounds.width of 1 glyph: [margin]+[width]+[margin] */ set_rect_empty(&rect); set_rect_empty(&bounds_1); - status = GdipMeasureString(graphics, string, 1, font, &rect, format, &bounds_1, NULL, NULL); + status = GdipMeasureString(graphics, L"W", 1, font, &rect, format, &bounds_1, NULL, NULL); expect(Ok, status); /* bounds.width of 2 identical glyphs: [margin]+[width]+[width]+[margin] */ set_rect_empty(&rect); set_rect_empty(&bounds_2); - status = GdipMeasureString(graphics, string, 2, font, &rect, format, &bounds_2, NULL, NULL); + status = GdipMeasureString(graphics, L"WW", 2, font, &rect, format, &bounds_2, NULL, NULL); expect(Ok, status); /* margin = [bounds.width of 1] - [bounds.width of 2] / 2*/ @@ -5071,6 +5302,7 @@ static void test_clipping(void) GpRegion *region, *region100x100; GpMatrix *matrix; GpRectF rect; + GpRect recti; GpPointF ptf[4]; GpUnit unit; HRGN hrgn; @@ -5107,6 +5339,11 @@ static void test_clipping(void) ok(rect.X == 100.0 && rect.Y == 100.0 && rect.Width == 100.0 && rect.Height == 100.0, "expected 100.0,100.0-100.0,100.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height); + status = GdipGetClipBoundsI(graphics, &recti); + expect(Ok, status); + ok(recti.X == 100 && recti.Y == 100 && recti.Width == 100 && recti.Height == 100, + "expected 100,100-100,100, got %i,%i-%i,%i\n", recti.X, recti.Y, recti.Width, recti.Height); + /* Clip region does not account for changes to gdi32 transform */ SetViewportOrgEx(hdc, 10, 10, NULL); @@ -5149,6 +5386,11 @@ static void test_clipping(void) ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0, "expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height); + status = GdipGetClipBoundsI(graphics, &recti); + expect(Ok, status); + ok(recti.X == 45 && recti.Y == 20 && recti.Width == 50 && recti.Height == 25, + "expected 45,20-50,25, got %i,%i-%i,%i\n", recti.X, recti.Y, recti.Width, recti.Height); + status = GdipSetEmpty(region); expect(Ok, status); status = GdipGetClip(graphics, region); @@ -6368,7 +6610,7 @@ static DWORD* GetBitmapPixelBuffer(HDC hdc, HBITMAP hbmp, int width, int height) bi.biClrImportant = 0; lines = GetDIBits(hdc, hbmp, 0, height, buffer, (BITMAPINFO *)&bi, DIB_RGB_COLORS); - ok(lines == height, "Expected GetDIBits:%p,%d->%d,%d\n", buffer, height, lines, GetLastError()); + ok(lines == height, "Expected GetDIBits:%p,%d->%d,%ld\n", buffer, height, lines, GetLastError()); return buffer; } @@ -6792,6 +7034,455 @@ static void test_hdc_caching(void) DeleteObject(hbm); } +static void test_gdi_interop_bitmap(void) +{ + GpBitmap *bitmap; + GpGraphics *graphics; + GpMatrix *transform; + GpBrush *brush; + GpStatus stat; + HDC hdc; + HBRUSH hbrush, holdbrush; + ARGB color; + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + stat = GdipCreateMatrix(&transform); + expect(Ok, stat); + + stat = GdipSetMatrixElements(transform, 1.0, 0.0, 0.0, 1.0, 50.0, 50.0); + expect(Ok, stat); + + /* GDI+: Set world transform. Should not matter to GDI. */ + stat = GdipSetWorldTransform(graphics, transform); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &hdc); + expect(Ok, stat); + + hbrush = CreateSolidBrush(0xff0000); + + holdbrush = SelectObject(hdc, hbrush); + + /* GDI: Draw a rectangle at physical coords (5, 5) to (12, 10). */ + Rectangle(hdc, 5, 5, 12, 10); + + holdbrush = SelectObject(hdc, holdbrush); + + /* GDI: Set view port origin. Should not matter to GDI+. */ + SetViewportOrgEx(hdc, 20, 20, NULL); + + GdipReleaseDC(graphics, hdc); + + stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&brush); + expect(Ok, stat); + + /* GDI+: Draw a rectangle at physical coords (85, 85) to (88, 95). */ + stat = GdipFillRectangleI(graphics, brush, 35, 35, 3, 10); + expect(Ok, stat); + + stat = GdipDeleteBrush(brush); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &hdc); + expect(Ok, stat); + + holdbrush = SelectObject(hdc, hbrush); + + /* GDI: Draw a rectangle at physical coords (25, 25) to (30, 34). + Updated view port origin should still be in effect. */ + Rectangle(hdc, 5, 5, 10, 14); + + SelectObject(hdc, holdbrush); + + DeleteObject(hbrush); + stat = GdipReleaseDC(graphics, hdc); + expect(Ok, stat); + + stat = GdipDeleteMatrix(transform); + expect(Ok, stat); + + stat = GdipBitmapGetPixel(bitmap, 6, 6, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipBitmapGetPixel(bitmap, 26, 26, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipBitmapGetPixel(bitmap, 86, 86, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + stat = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, stat); +} + +static void test_gdi_interop_hdc(void) +{ + BITMAPINFO bmi; + GpBrush *brush; + GpGraphics *graphics; + GpMatrix *transform; + GpStatus stat; + HBITMAP hbm; + HBRUSH hbrush, holdbrush; + HDC gdi_hdc; + HDC src_hdc; + ULONG *bits; + XFORM xform = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }; + + src_hdc = CreateCompatibleDC(0); + ok(src_hdc != NULL, "CreateCompatibleDC failed\n"); + + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biHeight = -100; + bmi.bmiHeader.biWidth = 100; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + + hbm = CreateDIBSection(src_hdc, &bmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok(hbm != NULL, "CreateDIBSection failed\n"); + + SelectObject(src_hdc, hbm); + + SetGraphicsMode(src_hdc, GM_ADVANCED); + + xform.eDx = 10.0; + xform.eDy = 10.0; + SetWorldTransform(src_hdc, &xform); + + stat = GdipCreateFromHDC(src_hdc, &graphics); + expect(Ok, stat); + + stat = GdipCreateMatrix(&transform); + expect(Ok, stat); + + stat = GdipSetMatrixElements(transform, 1.0, 0.0, 0.0, 1.0, 40.0, 40.0); + expect(Ok, stat); + + /* GDI+: Set world transform. Should not matter to GDI. */ + stat = GdipSetWorldTransform(graphics, transform); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &gdi_hdc); + expect(Ok, stat); + ok( gdi_hdc == src_hdc, "wrong dc\n" ); + + /* GDI: Set GDI transform back to (0, 0). + Should not matter to GDI+. */ + xform.eDx = 0.0; + xform.eDy = 0.0; + SetWorldTransform(gdi_hdc, &xform); + + hbrush = CreateSolidBrush(0xff00aa); + + holdbrush = SelectObject(gdi_hdc, hbrush); + + /* GDI: Draw a rectangle at physical coords (5, 5) to (12, 10). */ + Rectangle(gdi_hdc, 5, 5, 12, 10); + + holdbrush = SelectObject(gdi_hdc, holdbrush); + + /* GDI: Set GDI transform to translate (+20, +20). + Should not matter to GDI+. */ + xform.eDx = 20.0; + xform.eDy = 20.0; + SetWorldTransform(gdi_hdc, &xform); + + GdipReleaseDC(graphics, gdi_hdc); + + /* GDI world transform should still be intact, even when back + in GDI+ mode. */ + stat = GetWorldTransform(src_hdc, &xform); + expect(TRUE, stat); + expect(20.0, xform.eDx); + expect(20.0, xform.eDy); + + stat = GdipCreateSolidFill((ARGB)0xffaa00ff, (GpSolidFill**)&brush); + expect(Ok, stat); + + /* GDI+: Draw a rectangle at physical coords (85, 85) to (88, 95). + The fact that the GDI world transform has been updated should + not influence the GDI+ world transform. GDI+ should still apply + the world transform from the when HDC backed graphics object was + instantiated. */ + stat = GdipFillRectangleI(graphics, brush, 35, 35, 3, 10); + expect(Ok, stat); + + stat = GdipDeleteBrush(brush); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &gdi_hdc); + expect(Ok, stat); + + holdbrush = SelectObject(gdi_hdc, hbrush); + + /* GDI: Draw a rectangle at physical coords (25, 25) to (30, 34). + Updated transform should still be in effect. */ + Rectangle(gdi_hdc, 5, 5, 10, 14); + + SelectObject(gdi_hdc, holdbrush); + + stat = GdipReleaseDC(graphics, gdi_hdc); + expect(Ok, stat); + + GdipDeleteGraphics(graphics); + stat = GdipDeleteMatrix(transform); + expect(Ok, stat); + + holdbrush = SelectObject(src_hdc, hbrush); + + /* GDI: Draw a rectangle at physical coords (35, 35) to (40, 38). + Updated transform should still be in effect on src_hdc. */ + Rectangle(gdi_hdc, 15, 15, 20, 18); + + SelectObject(gdi_hdc, holdbrush); + + DeleteObject(hbrush); + + expect(0x00aa00ff, bits[6 * 100 + 6]); + expect(0x00aa00ff, bits[26 * 100 + 26]); + expect(0x00aa00ff, bits[36 * 100 + 36]); + expect(0xffaa00ff, bits[86 * 100 + 86]); + + DeleteDC(src_hdc); + DeleteObject(hbm); +} + +static HDC create_printer_dc(void) +{ + char buffer[260]; + DWORD len; + PRINTER_INFO_2A *pbuf = NULL; + DRIVER_INFO_3A *dbuf = NULL; + HANDLE hprn = 0; + HDC hdc = 0; + HMODULE winspool = LoadLibraryA("winspool.drv"); + BOOL (WINAPI *pOpenPrinterA)(LPSTR, HANDLE *, LPPRINTER_DEFAULTSA); + BOOL (WINAPI *pGetDefaultPrinterA)(LPSTR, LPDWORD); + BOOL (WINAPI *pGetPrinterA)(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD); + BOOL (WINAPI *pGetPrinterDriverA)(HANDLE, LPSTR, DWORD, LPBYTE, DWORD, LPDWORD); + BOOL (WINAPI *pClosePrinter)(HANDLE); + + pGetDefaultPrinterA = (void *)GetProcAddress(winspool, "GetDefaultPrinterA"); + pOpenPrinterA = (void *)GetProcAddress(winspool, "OpenPrinterA"); + pGetPrinterA = (void *)GetProcAddress(winspool, "GetPrinterA"); + pGetPrinterDriverA = (void *)GetProcAddress(winspool, "GetPrinterDriverA"); + pClosePrinter = (void *)GetProcAddress(winspool, "ClosePrinter"); + + if (!pGetDefaultPrinterA || !pOpenPrinterA || !pGetPrinterA || !pGetPrinterDriverA || !pClosePrinter) + goto done; + + len = sizeof(buffer); + if (!pGetDefaultPrinterA(buffer, &len)) goto done; + if (!pOpenPrinterA(buffer, &hprn, NULL)) goto done; + + pGetPrinterA(hprn, 2, NULL, 0, &len); + pbuf = malloc(len); + if (!pGetPrinterA(hprn, 2, (LPBYTE)pbuf, len, &len)) goto done; + + pGetPrinterDriverA(hprn, NULL, 3, NULL, 0, &len); + dbuf = malloc(len); + if (!pGetPrinterDriverA(hprn, NULL, 3, (LPBYTE)dbuf, len, &len)) goto done; + + hdc = CreateDCA(dbuf->pDriverPath, pbuf->pPrinterName, pbuf->pPortName, pbuf->pDevMode); + trace("hdc %p for driver '%s' printer '%s' port '%s'\n", hdc, + dbuf->pDriverPath, pbuf->pPrinterName, pbuf->pPortName); +done: + free(dbuf); + free(pbuf); + if (hprn) pClosePrinter(hprn); + if (winspool) FreeLibrary(winspool); + return hdc; +} + +static BOOL check_rect_pixels(const DWORD *pixel, const RectF *rect, UINT width, DWORD expected, Point *failed) +{ + UINT x, y; + BOOL ret = TRUE; + + for (y = (UINT)rect->Y; y < (UINT)(rect->Y + rect->Height); y++) + { + for (x = (UINT)rect->X; x < (UINT)(rect->X + rect->Width); x++) + { + if (pixel[x + y * width] != expected) + { + ret = FALSE; + goto done; + } + } + } + +done: + if (!ret) + { + failed->X = x; + failed->Y = y; + } + else + { + failed->X = 0; + failed->Y = 0; + } + return ret; +} + +static void test_printer_dc(void) +{ + HDC hdc_printer, hdc; + Status status; + GpGraphics *graphics; + REAL dpi_x, dpi_y, pixel_per_unit_x, pixel_per_unit_y; + HBITMAP bitmap; + UINT width = 16, height = 16; + GpUnit unit; + GpSolidFill *brush; + DWORD *pixel; + BOOL match; + RectF rect; + Point pt; + + hdc_printer = create_printer_dc(); + if (!hdc_printer) + { + skip("could not create a DC for the default printer\n"); + return; + } + + hdc = CreateCompatibleDC(hdc_printer); + bitmap = CreateCompatibleBitmap(hdc, width, height); + SelectObject(hdc, bitmap); + + status = GdipCreateFromHDC(hdc, &graphics); + expect(Ok, status); + + GdipGetPageUnit(graphics, &unit); + expect(UnitDisplay, unit); + + GdipGetDpiX(graphics, &dpi_x); + GdipGetDpiY(graphics, &dpi_y); + expectf((REAL)GetDeviceCaps(hdc, LOGPIXELSX), dpi_x); + expectf((REAL)GetDeviceCaps(hdc, LOGPIXELSY), dpi_y); + + /* For graphics created from printer DC, UnitDisplay specifies that a unit is 1/100 inch */ + pixel_per_unit_x = dpi_x / 100.0; + pixel_per_unit_y = dpi_y / 100.0; + + status = GdipCreateSolidFill((ARGB)0xffffffff, &brush); + expect(Ok, status); + + status = GdipFillRectangleI(graphics, (GpBrush *)brush, 1, 1, 1, 1); + expect(Ok, status); + + pixel = GetBitmapPixelBuffer(hdc, bitmap, width, height); + + /* pixels at (0, 0) should all be 0 */ + rect.X = 0; + rect.Y = 0; + rect.Width = pixel_per_unit_x; + rect.Height = pixel_per_unit_y; + match = check_rect_pixels(pixel, &rect, width, 0, &pt); + ok(match, "Expected pixel (%u, %u) to be %08x, got %08lx\n", + pt.X, pt.Y, 0, pixel[pt.X + pt.Y * width]); + + /* pixels at (1, 1) should all be 0x00ffffff */ + rect.X = pixel_per_unit_x; + rect.Y = pixel_per_unit_y; + rect.Width = pixel_per_unit_x; + rect.Height = pixel_per_unit_y; + match = check_rect_pixels(pixel, &rect, width, 0x00ffffff, &pt); + ok(match, "Expected pixel (%u, %u) to be %08x, got %08lx\n", + pt.X, pt.Y, 0x00ffffff, pixel[pt.X + pt.Y * width]); + + GdipFree(pixel); + GdipDeleteBrush((GpBrush *)brush); + GdipDeleteGraphics(graphics); + DeleteObject(bitmap); + DeleteDC(hdc); + DeleteDC(hdc_printer); +} + +void test_bitmap_stride(void) +{ + GpStatus status; + GpBitmap *bitmap = NULL; + BitmapData locked_data; + GpRect bounds = {0, 0, 10, 10}; + BYTE buffer[400]; + BYTE *scan0; + int i; + struct { + INT stride; + BOOL use_scan0; + INT expected_stride; + GpStatus status; + } test_data[] = { + { 12, FALSE, 20 }, + { 40, FALSE, 20 }, + { 0, FALSE, 20 }, + { 20, FALSE, 20 }, + { -12, FALSE, 20 }, + { -32, FALSE, 20 }, + { 30, TRUE, 0, InvalidParameter }, + { 12, TRUE, 12 }, + { 40, TRUE, 40 }, + { 0, TRUE, 0, InvalidParameter }, + { 32, TRUE, 32 }, + { -12, TRUE, -12 }, + { -32, TRUE, -32 }, + { -13, TRUE, 0, InvalidParameter }, + }; + + for (i=0; i < ARRAY_SIZE(test_data); i++) + { + if (test_data[i].use_scan0) + { + if (test_data[i].stride >= 0) + scan0 = buffer; + else + scan0 = buffer + sizeof(buffer) + test_data[i].stride; + } + else + scan0 = NULL; + + winetest_push_context("%i: %i %i", i, test_data[i].stride, test_data[i].use_scan0); + + status = GdipCreateBitmapFromScan0(10, 10, test_data[i].stride, PixelFormat16bppGrayScale, scan0, &bitmap); + expect(test_data[i].status, status); + + if (status == Ok) + { + status = GdipBitmapLockBits(bitmap, &bounds, ImageLockModeRead, PixelFormat16bppGrayScale, &locked_data); + expect(Ok, status); + + expect(10, locked_data.Width); + expect(10, locked_data.Height); + expect(test_data[i].expected_stride, locked_data.Stride); + expect(PixelFormat16bppGrayScale, locked_data.PixelFormat); + if (test_data[i].use_scan0) + ok(locked_data.Scan0 == scan0, "got %p, expected %p\n", locked_data.Scan0, scan0); + + status = GdipBitmapUnlockBits(bitmap, &locked_data); + expect(Ok, status); + + GdipDisposeImage((GpImage*)bitmap); + } + + winetest_pop_context(); + } +} + START_TEST(graphics) { struct GdiplusStartupInput gdiplusStartupInput; @@ -6834,6 +7525,7 @@ START_TEST(graphics) test_measure_string(); test_font_height_scaling(); test_transform(); + test_set_page_transform(); test_pen_thickness(); test_GdipMeasureString(); test_constructor_destructor(); @@ -6851,6 +7543,7 @@ START_TEST(graphics) test_GdipDrawCurve3I(); test_GdipDrawLineI(); test_GdipDrawLinesI(); + test_GdipDrawImageFX(); test_GdipDrawImagePointsRect(); test_GdipFillClosedCurve(); test_GdipFillClosedCurveI(); @@ -6885,6 +7578,10 @@ START_TEST(graphics) test_GdipGraphicsSetAbort(); test_cliphrgn_transform(); test_hdc_caching(); + test_gdi_interop_bitmap(); + test_gdi_interop_hdc(); + test_printer_dc(); + test_bitmap_stride(); GdiplusShutdown(gdiplusToken); DestroyWindow( hwnd ); diff --git a/modules/rostests/winetests/gdiplus/graphicspath.c b/modules/rostests/winetests/gdiplus/graphicspath.c index 096fbc600ab..89a7dd786de 100644 --- a/modules/rostests/winetests/gdiplus/graphicspath.c +++ b/modules/rostests/winetests/gdiplus/graphicspath.c @@ -75,7 +75,10 @@ typedef struct int todo; } path_test_t; -static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size, BOOL todo_size) +#define ok_path(a,b,c,d) _ok_path_fudge(a,b,c,d,1.0,__LINE__) +#define ok_path_fudge(a,b,c,d,e) _ok_path_fudge(a,b,c,d,e,__LINE__) +static void _ok_path_fudge(GpPath* path, const path_test_t *expected, INT expected_size, + BOOL todo_size, REAL fudge, int line) { BYTE * types; INT size, idx = 0, eidx = 0, numskip; @@ -88,11 +91,11 @@ static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size } todo_wine_if (todo_size) - ok(size == expected_size, "Path size %d does not match expected size %d\n", + ok_(__FILE__,line)(size == expected_size, "Path size %d does not match expected size %d\n", size, expected_size); - points = HeapAlloc(GetProcessHeap(), 0, size * sizeof(GpPointF)); - types = HeapAlloc(GetProcessHeap(), 0, size); + points = malloc(size * sizeof(GpPointF)); + types = malloc(size); if(GdipGetPathPoints(path, points, size) != Ok || GdipGetPathTypes(path, types, size) != Ok){ skip("Cannot perform path comparisons due to failure to retrieve path.\n"); @@ -104,14 +107,14 @@ static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in * floating point to integer conversion */ BOOL match = (types[idx] == expected[eidx].type) && - fabs(points[idx].X - expected[eidx].X) <= 2.0 && - fabs(points[idx].Y - expected[eidx].Y) <= 2.0; + fabs(points[idx].X - expected[eidx].X) <= fudge && + fabs(points[idx].Y - expected[eidx].Y) <= fudge; stringify_point_type(expected[eidx].type, ename); stringify_point_type(types[idx], name); todo_wine_if (expected[eidx].todo || numskip) - ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx, + ok_(__FILE__,line)(match, "Expected #%d: %s (%.6f,%.6f) but got %s (%.6f,%.6f)\n", eidx, ename, expected[eidx].X, expected[eidx].Y, name, points[idx].X, points[idx].Y); @@ -122,8 +125,8 @@ static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size } end: - HeapFree(GetProcessHeap(), 0, types); - HeapFree(GetProcessHeap(), 0, points); + free(types); + free(points); } static void test_constructor_destructor(void) @@ -173,6 +176,115 @@ static void test_getpathdata(void) GdipDeletePath(path); } +static void test_createpath2(void) +{ + GpStatus status; + GpPath* path = NULL; + GpPathData data; + INT i, count, expect_count; + + PointF test_line_points[] = {{1.0,1.0}, {2.0,1.0}, {2.0,2.0}}; + BYTE test_line_types[] = {PathPointTypeStart, PathPointTypeLine, PathPointTypeStart}; + + PointF test_bez_points[] = {{1.0,1.0}, {2.0,1.0}, {3.0,1.0}, {4.0,1.0}, + {5.0,1.0}, {6.0,1.0}, {7.0,1.0}}; + BYTE test_bez_types[] = {PathPointTypeStart, PathPointTypeBezier, + PathPointTypeBezier, PathPointTypeBezier, PathPointTypeBezier, + PathPointTypeBezier, PathPointTypeBezier}; + + status = GdipCreatePath2(test_line_points, test_line_types, 2, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(2, count); + GdipDeletePath(path); + + status = GdipCreatePath2(test_line_points, test_line_types, 1, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(1, count); + GdipDeletePath(path); + + path = (void *)0xdeadbeef; + status = GdipCreatePath2(test_line_points, test_line_types, 0, FillModeAlternate, &path); + expect(OutOfMemory, status); + ok(!path, "Expected NULL, got %p\n", path); + if(path && path != (void *)0xdeadbeef) + GdipDeletePath(path); + + path = (void *)0xdeadbeef; + status = GdipCreatePath2(test_line_points, test_line_types, -1, FillModeAlternate, &path); + expect(OutOfMemory, status); + ok(!path, "Expected NULL, got %p\n", path); + if(path && path != (void *)0xdeadbeef) + GdipDeletePath(path); + + path = (void *)0xdeadbeef; + status = GdipCreatePath2(NULL, test_line_types, 2, FillModeAlternate, &path); + expect(InvalidParameter, status); + ok(path == (void *)0xdeadbeef, "Expected %p, got %p\n", (void *)0xdeadbeef, path); + if(path && path != (void *)0xdeadbeef) + GdipDeletePath(path); + + path = (void *)0xdeadbeef; + status = GdipCreatePath2(test_line_points, NULL, 2, FillModeAlternate, &path); + expect(InvalidParameter, status); + ok(path == (void *)0xdeadbeef, "Expected %p, got %p\n", (void *)0xdeadbeef, path); + if(path && path != (void *)0xdeadbeef) + GdipDeletePath(path); + + status = GdipCreatePath2(test_line_points, test_line_types, 2, FillModeAlternate, NULL); + expect(InvalidParameter, status); + + /* Multi-point paths should not end with Start */ + status = GdipCreatePath2(test_line_points, test_line_types, 3, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(0, count); + GdipDeletePath(path); + + /* Zero-length line points do not get altered */ + test_line_points[1].X = test_line_points[0].X; + test_line_points[1].Y = test_line_points[0].Y; + status = GdipCreatePath2(test_line_points, test_line_types, 2, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(2, count); + GdipDeletePath(path); + + /* The type of the first point is always converted to PathPointTypeStart */ + test_line_types[0] = PathPointTypeLine; + status = GdipCreatePath2(test_line_points, test_line_types, 1, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(1, count); + data.Count = count; + data.Types = GdipAlloc(sizeof(BYTE) * count); + data.Points = GdipAlloc(sizeof(PointF) * count); + status = GdipGetPathData(path, &data); + expect(Ok, status); + expect((data.Points[0].X == 1.0) && (data.Points[0].Y == 1.0), TRUE); + expect(data.Types[0], PathPointTypeStart); + GdipFree(data.Points); + GdipFree(data.Types); + GdipDeletePath(path); + + /* Bezier points must come in groups of three */ + for(i = 2; i <= 7; i++) { + expect_count = (i % 3 == 1) ? i : 0; + status = GdipCreatePath2(test_bez_points, test_bez_types, i, FillModeAlternate, &path); + expect(Ok, status); + status = GdipGetPointCount(path, &count); + expect(Ok, status); + expect(expect_count, count); + GdipDeletePath(path); + } +} + static path_test_t line2_path[] = { {0.0, 50.0, PathPointTypeStart, 0, 0}, /*0*/ {5.0, 45.0, PathPointTypeLine, 0, 0}, /*1*/ @@ -198,6 +310,16 @@ static void test_line2(void) } GdipCreatePath(FillModeAlternate, &path); + + status = GdipAddPathLine2(NULL, line2_points, 2); + expect(InvalidParameter, status); + status = GdipAddPathLine2(path, NULL, 2); + expect(InvalidParameter, status); + status = GdipAddPathLine2(path, line2_points, 0); + expect(InvalidParameter, status); + status = GdipAddPathLine2(path, line2_points, -1); + expect(InvalidParameter, status); + status = GdipAddPathLine2(path, line2_points, 3); expect(Ok, status); status = GdipAddPathLine2(path, &(line2_points[3]), 3); @@ -209,6 +331,69 @@ static void test_line2(void) ok_path(path, line2_path, ARRAY_SIZE(line2_path), FALSE); + GdipResetPath(path); + status = GdipAddPathLine2(path, line2_points, 3); + expect(Ok, status); + status = GdipAddPathLine2(path, &(line2_points[2]), 3); + expect(Ok, status); + + ok_path(path, line2_path, 5, FALSE); + + GdipDeletePath(path); +} + +static path_test_t bezier_path[] = { + {10.0, 10.0, PathPointTypeStart, 0, 0}, /*0*/ + {20.0, 10.0, PathPointTypeBezier, 0, 0}, /*1*/ + {20.0, 20.0, PathPointTypeBezier, 0, 0}, /*2*/ + {30.0, 20.0, PathPointTypeBezier, 0, 0}, /*3*/ + {40.0, 20.0, PathPointTypeBezier, 0, 0}, /*4*/ + {40.0, 30.0, PathPointTypeBezier, 0, 0}, /*5*/ + {50.0, 30.0, PathPointTypeBezier, 0, 0}, /*6*/ + {50.0, 10.0, PathPointTypeLine, 0, 0}, /*7*/ + {60.0, 10.0, PathPointTypeBezier, 0, 0}, /*8*/ + {60.0, 20.0, PathPointTypeBezier, 0, 0}, /*9*/ + {70.0, 20.0, PathPointTypeBezier, 0, 0} /*10*/ + }; + +static void test_bezier(void) +{ + GpStatus status; + GpPath* path; + + GdipCreatePath(FillModeAlternate, &path); + + status = GdipAddPathBezier(path, 10.0, 10.0, 20.0, 10.0, 20.0, 20.0, 30.0, 20.0); + expect(Ok, status); + status = GdipAddPathBezier(path, 30.0, 20.0, 40.0, 20.0, 40.0, 30.0, 50.0, 30.0); + expect(Ok, status); + status = GdipAddPathBezier(path, 50.0, 10.0, 60.0, 10.0, 60.0, 20.0, 70.0, 20.0); + expect(Ok, status); + + ok_path(path, bezier_path, ARRAY_SIZE(bezier_path), FALSE); + + GdipDeletePath(path); +} + +static void test_beziers(void) +{ + GpStatus status; + GpPath* path; + PointF bezier_points1[] = {{10.0,10.0}, {20.0,10.0}, {20.0,20.0}, {30.0,20.0}}; + PointF bezier_points2[] = {{30.0,20.0}, {40.0,20.0}, {40.0,30.0}, {50.0,30.0}}; + PointF bezier_points3[] = {{50.0,10.0}, {60.0,10.0}, {60.0,20.0}, {70.0,20.0}}; + + GdipCreatePath(FillModeAlternate, &path); + + status = GdipAddPathBeziers(path, bezier_points1, 4); + expect(Ok, status); + status = GdipAddPathBeziers(path, bezier_points2, 4); + expect(Ok, status); + status = GdipAddPathBeziers(path, bezier_points3, 4); + expect(Ok, status); + + ok_path(path, bezier_path, ARRAY_SIZE(bezier_path), FALSE); + GdipDeletePath(path); } @@ -252,6 +437,13 @@ static path_test_t arc_path[] = { {450.9, 824.1, PathPointTypeBezier, 0, 0}, /*36*/ {540.4, 676.9, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 1} /*37*/ }; +static path_test_t arc_path2[] = { + {1.0, 0.0, PathPointTypeStart, 0, 0}, /*0*/ + {1.0, 0.5, PathPointTypeLine, 0, 0}, /*1*/ + {1.0, 0.776142, PathPointTypeBezier, 0, 0}, /*2*/ + {0.776142, 1.0, PathPointTypeBezier, 0, 0}, /*3*/ + {0.5, 1.0, PathPointTypeBezier, 0, 0} /*4*/ + }; static void test_arc(void) { @@ -259,6 +451,20 @@ static void test_arc(void) GpPath* path; GdipCreatePath(FillModeAlternate, &path); + + status = GdipAddPathArc(path, 100.0, 100.0, 1.0, 0.0, 0.0, 90.0); + expect(InvalidParameter, status); + + status = GdipAddPathArc(path, 100.0, 100.0, 0.0, 1.0, 0.0, 90.0); + expect(InvalidParameter, status); + + status = GdipAddPathArc(path, 100.0, 100.0, -40, 1.0, 0.0, 90.0); + expect(InvalidParameter, status); + + status = GdipAddPathArc(path, 100.0, 100.0, 1.0, -50.0, 0.0, 90.0); + expect(InvalidParameter, status); + + GdipResetPath(path); /* Exactly 90 degrees */ status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0); expect(Ok, status); @@ -280,6 +486,13 @@ static void test_arc(void) ok_path(path, arc_path, ARRAY_SIZE(arc_path), FALSE); + GdipResetPath(path); + GdipAddPathLine(path, 1.0, 0.0, 1.0, 0.5); + status = GdipAddPathArc(path, 0.0, 0.0, 1.0, 1.0, 0.0, 90.0); + expect(Ok, status); + + ok_path_fudge(path, arc_path2, ARRAY_SIZE(arc_path2), FALSE, 0.000005); + GdipDeletePath(path); } @@ -547,7 +760,8 @@ static path_test_t linei_path[] = { {15.00, 15.00, PathPointTypeLine, 0, 0}, /*9*/ {26.00, 28.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*10*/ {35.00, 35.00, PathPointTypeStart, 0, 0}, /*11*/ - {36.00, 38.00, PathPointTypeLine, 0, 0} /*12*/ + {36.00, 38.00, PathPointTypeLine, 0, 0}, /*12*/ + {39.00, 40.00, PathPointTypeLine, 0, 0} /*13*/ }; static void test_linei(void) @@ -564,6 +778,8 @@ static void test_linei(void) GdipClosePathFigure(path); status = GdipAddPathLineI(path, 35.0, 35.0, 36.0, 38.0); expect(Ok, status); + status = GdipAddPathLineI(path, 36, 38, 39, 40); + expect(Ok, status); ok_path(path, linei_path, ARRAY_SIZE(linei_path), FALSE); @@ -703,6 +919,18 @@ static path_test_t addcurve_path[] = { {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*8*/ {30.0, 10.0, PathPointTypeBezier, 0, 0} /*9*/ }; +static path_test_t addcurve_path_default_tension[] = { + {0.0, 0.0, PathPointTypeStart, 0, 0}, /*0*/ + {1.66, 1.66, PathPointTypeBezier, 0, 0}, /*1*/ + {8.33, 6.66, PathPointTypeBezier, 0, 0}, /*2*/ + {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/ + {11.6, 13.3, PathPointTypeBezier, 0, 0}, /*4*/ + {6.66, 20.0, PathPointTypeBezier, 0, 0}, /*5*/ + {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*6*/ + {13.3, 20.0, PathPointTypeBezier, 0, 0}, /*7*/ + {26.6, 11.6, PathPointTypeBezier, 0, 0}, /*8*/ + {30.0, 10.0, PathPointTypeBezier, 0, 0} /*9*/ + }; static path_test_t addcurve_path2[] = { {100.0,120.0,PathPointTypeStart, 0, 0}, /*0*/ {123.0,10.0, PathPointTypeLine, 0, 0}, /*1*/ @@ -719,13 +947,26 @@ static path_test_t addcurve_path2[] = { }; static path_test_t addcurve_path3[] = { {10.0, 10.0, PathPointTypeStart, 0, 0}, /*0*/ - {13.3, 16.7, PathPointTypeBezier, 0, 1}, /*1*/ + {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*1*/ {3.3, 20.0, PathPointTypeBezier, 0, 0}, /*2*/ {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*3*/ {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*4*/ {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*5*/ {30.0, 10.0, PathPointTypeBezier, 0, 0} /*6*/ }; +static path_test_t addcurve_path4[] = { + {0.0, 0.0, PathPointTypeStart, 0, 0}, /*0*/ + {3.33, 3.33, PathPointTypeBezier, 0, 0}, /*1*/ + {6.66, 3.33, PathPointTypeBezier, 0, 0}, /*2*/ + {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/ + }; +static path_test_t addcurve_path5[] = { + {10.0, 10.0, PathPointTypeStart, 0, 0}, /*0*/ + {13.3, 16.6, PathPointTypeBezier, 0, 0}, /*1*/ + {3.33, 20.0, PathPointTypeBezier, 0, 0}, /*2*/ + {10.0, 20.0, PathPointTypeBezier, 0, 0} /*3*/ + }; + static void test_addcurve(void) { GpStatus status; @@ -759,6 +1000,13 @@ static void test_addcurve(void) ok_path(path, addcurve_path, ARRAY_SIZE(addcurve_path), FALSE); GdipDeletePath(path); + /* add to empty path with default tension */ + GdipCreatePath(FillModeAlternate, &path); + status = GdipAddPathCurve(path, points, 4); + expect(Ok, status); + ok_path(path, addcurve_path_default_tension, ARRAY_SIZE(addcurve_path_default_tension), FALSE); + GdipDeletePath(path); + /* add to notempty path and opened figure */ GdipCreatePath(FillModeAlternate, &path); GdipAddPathLine(path, 100.0, 120.0, 123.0, 10.0); @@ -792,9 +1040,23 @@ static void test_addcurve(void) ok_path(path, addcurve_path, ARRAY_SIZE(addcurve_path), FALSE); GdipResetPath(path); + /* Skip first point */ status = GdipAddPathCurve3(path, points, 4, 1, 2, 1.0); expect(Ok, status); ok_path(path, addcurve_path3, ARRAY_SIZE(addcurve_path3), FALSE); + GdipResetPath(path); + + /* Skip two last points */ + status = GdipAddPathCurve3(path, points, 4, 0, 1, 1.0); + expect(Ok, status); + ok_path(path, addcurve_path4, ARRAY_SIZE(addcurve_path4), FALSE); + GdipResetPath(path); + + /* Skip first and last points */ + status = GdipAddPathCurve3(path, points, 4, 1, 1, 1.0); + expect(Ok, status); + ok_path(path, addcurve_path5, ARRAY_SIZE(addcurve_path5), FALSE); + GdipResetPath(path); GdipDeletePath(path); } @@ -814,6 +1076,21 @@ static path_test_t addclosedcurve_path[] = { {6.7, 0.0, PathPointTypeBezier, 0, 0}, /*11*/ {0.0, 0.0, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0} /*12*/ }; +static path_test_t addclosedcurve_path_default_tension[] = { + {0.0, 0.0, PathPointTypeStart, 0, 0}, /*0*/ + {-3.33, 0.0, PathPointTypeBezier, 0, 0}, /*1*/ + {8.33, 6.66, PathPointTypeBezier, 0, 0}, /*2*/ + {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/ + {11.6, 13.3, PathPointTypeBezier, 0, 0}, /*4*/ + {6.66, 20.0, PathPointTypeBezier, 0, 0}, /*5*/ + {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*6*/ + {13.3, 20.0, PathPointTypeBezier, 0, 0}, /*7*/ + {31.6, 13.3, PathPointTypeBezier, 0, 0}, /*8*/ + {30.0, 10.0, PathPointTypeBezier, 0, 0}, /*9*/ + {28.3, 6.66, PathPointTypeBezier, 0, 0}, /*10*/ + {3.33, 0.0, PathPointTypeBezier, 0, 0}, /*11*/ + {0.0, 0.0, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0} /*12*/ + }; static void test_addclosedcurve(void) { GpStatus status; @@ -846,6 +1123,13 @@ static void test_addclosedcurve(void) expect(Ok, status); ok_path(path, addclosedcurve_path, ARRAY_SIZE(addclosedcurve_path), FALSE); GdipDeletePath(path); + + /* add to empty path with default tension */ + GdipCreatePath(FillModeAlternate, &path); + status = GdipAddPathClosedCurve(path, points, 4); + expect(Ok, status); + ok_path(path, addclosedcurve_path_default_tension, ARRAY_SIZE(addclosedcurve_path_default_tension), FALSE); + GdipDeletePath(path); } static path_test_t reverse_path[] = { @@ -1047,6 +1331,22 @@ static void test_flatten(void) expect(Ok, status); ok_path(path, flattenquater_path, ARRAY_SIZE(flattenquater_path), FALSE); + status = GdipResetPath(path); + expect(Ok, status); + status = GdipStartPathFigure(path); + expect(Ok, status); + + /* path seen in the wild that caused a stack overflow */ + /* same path but redo with the manual points that caused a crash */ + status = GdipAddPathBezier(path, 154.950806, 33.391144, 221.586075, 15.536285, 291.747314, 15.536285, 358.382568, 33.391144); + expect(Ok, status); + status = GdipAddPathBezier(path, 256.666809, 412.999512, 256.666718, 412.999481, 256.666656, 412.999481, 256.666565, 412.999512); + expect(Ok, status); + status = GdipClosePathFigure(path); + expect(Ok, status); + status = GdipFlattenPath(path, NULL, 1.0); + expect(Ok, status); + GdipDeleteMatrix(m); GdipDeletePath(path); } @@ -1076,6 +1376,24 @@ static path_test_t widenline_dash_path[] = { {45.0, 10.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ }; +static path_test_t widenline_thin_dash_path[] = { + {5.0, 4.75, PathPointTypeStart, 0, 0}, /*0*/ + {8.0, 4.75, PathPointTypeLine, 0, 0}, /*1*/ + {8.0, 5.25, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 5.25, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {9.0, 4.75, PathPointTypeStart, 0, 0}, /*4*/ + {9.5, 4.75, PathPointTypeLine, 0, 0}, /*5*/ + {9.5, 5.25, PathPointTypeLine, 0, 0}, /*6*/ + {9.0, 5.25, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + }; + +static path_test_t widenline_unit_path[] = { + {5.0, 9.5, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 9.5, PathPointTypeLine, 0, 0}, /*1*/ + {50.0, 10.5, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 10.5, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0} /*3*/ + }; + static void test_widen(void) { GpStatus status; @@ -1166,6 +1484,18 @@ static void test_widen(void) status = GdipSetPenDashStyle(pen, DashStyleSolid); expect(Ok, status); + /* dashed line less than 1 pixel wide */ + GdipDeletePen(pen); + GdipCreatePen1(0xffffffff, 0.5, UnitPixel, &pen); + GdipSetPenDashStyle(pen, DashStyleDash); + + GdipResetPath(path); + GdipAddPathLine(path, 5.0, 5.0, 9.5, 5.0); + + status = GdipWidenPath(path, pen, m, 1.0); + expect(Ok, status); + ok_path_fudge(path, widenline_thin_dash_path, ARRAY_SIZE(widenline_thin_dash_path), FALSE, 0.000005); + /* pen width in UnitWorld */ GdipDeletePen(pen); status = GdipCreatePen1(0xffffffff, 10.0, UnitWorld, &pen); @@ -1238,13 +1568,366 @@ static void test_widen(void) status = GdipGetPointCount(path, &count); expect(Ok, status); - todo_wine expect(0, count); + ok(count == 0 || broken(count == 4), "expected 0, got %i\n", count); + + /* pen width = 0 pixels, UnitWorld - result is a path 1 unit wide */ + GdipDeletePen(pen); + status = GdipCreatePen1(0xffffffff, 0.0, UnitWorld, &pen); + expect(Ok, status); + + status = GdipResetPath(path); + expect(Ok, status); + status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0); + expect(Ok, status); + + status = GdipWidenPath(path, pen, m, 1.0); + expect(Ok, status); + + status = GdipGetPointCount(path, &count); + expect(Ok, status); + ok_path_fudge(path, widenline_unit_path, ARRAY_SIZE(widenline_unit_path), FALSE, 0.000005); GdipDeleteMatrix(m); GdipDeletePen(pen); GdipDeletePath(path); } +static path_test_t widenline_capflat_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0} /*3*/ + }; + +static path_test_t widenline_capsquare_path[] = { + {0.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {55.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {55.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {0.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0} /*3*/ + }; + +static path_test_t widenline_capround_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {52.761421, 5.0, PathPointTypeBezier, 0, 0}, /*2*/ + {55.0, 7.238576, PathPointTypeBezier, 0, 0}, /*3*/ + {55.0, 10.0, PathPointTypeBezier, 0, 0}, /*4*/ + {55.0, 12.761423, PathPointTypeBezier, 0, 0}, /*5*/ + {52.761421, 15.0, PathPointTypeBezier, 0, 0}, /*6*/ + {50.0, 15.0, PathPointTypeBezier, 0, 0}, /*7*/ + {5.0, 15.0, PathPointTypeLine, 0, 0}, /*8*/ + {2.238576, 15.0, PathPointTypeBezier, 0, 0}, /*9*/ + {0.0, 12.761423, PathPointTypeBezier, 0, 0}, /*10*/ + {0.0, 10.0, PathPointTypeBezier, 0, 0}, /*11*/ + {0.0, 7.238576, PathPointTypeBezier, 0, 0}, /*12*/ + {2.238576, 5.0, PathPointTypeBezier, 0, 0}, /*13*/ + {5.0, 5.0, PathPointTypeBezier|PathPointTypeCloseSubpath, 0, 0}, /*14*/ + }; + +static path_test_t widenline_captriangle_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {55.0, 10.0, PathPointTypeLine, 0, 0}, /*2*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*3*/ + {5.0, 15.0, PathPointTypeLine, 0, 0}, /*4*/ + {0.0, 10.0, PathPointTypeLine, 0, 0}, /*5*/ + {5.0, 5.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0} /*6*/ + }; + +static path_test_t widenline_capsquareanchor_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {12.071068, 2.928932, PathPointTypeStart, 0, 0}, /*4*/ + {12.071068, 17.071066, PathPointTypeLine, 0, 0}, /*5*/ + {-2.071068, 17.071066, PathPointTypeLine, 0, 0}, /*6*/ + {-2.071068, 2.928932, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + {42.928928, 17.071068, PathPointTypeStart, 0, 0}, /*8*/ + {42.928928, 2.928932, PathPointTypeLine, 0, 0}, /*9*/ + {57.071068, 2.928932, PathPointTypeLine, 0, 0}, /*10*/ + {57.071068, 17.071068, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*11*/ + }; + +static path_test_t widenline_caproundanchor_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {5.0, 20.0, PathPointTypeStart, 0, 0}, /*4*/ + {-0.522847, 20.0, PathPointTypeBezier, 0, 0}, /*5*/ + {-5.0, 15.522846, PathPointTypeBezier, 0, 0}, /*6*/ + {-5.0, 10.0, PathPointTypeBezier, 0, 0}, /*7*/ + {-5.0, 4.477152, PathPointTypeBezier, 0, 0}, /*8*/ + {-0.522847, 0.0, PathPointTypeBezier, 0, 0}, /*9*/ + {5.0, 0.0, PathPointTypeBezier, 0, 0}, /*10*/ + {10.522847, 0.0, PathPointTypeBezier, 0, 0}, /*11*/ + {15.0, 4.477152, PathPointTypeBezier, 0, 0}, /*12*/ + {15.0, 10.0, PathPointTypeBezier, 0, 0}, /*13*/ + {15.0, 15.522846, PathPointTypeBezier, 0, 0}, /*14*/ + {10.522847, 20.0, PathPointTypeBezier, 0, 0}, /*15*/ + {5.0, 20.0, PathPointTypeBezier|PathPointTypeCloseSubpath, 0, 0}, /*16*/ + {50.0, 0.0, PathPointTypeStart, 0, 0}, /*17*/ + {55.522846, 0.0, PathPointTypeBezier, 0, 0}, /*18*/ + {60.0, 4.477153, PathPointTypeBezier, 0, 0}, /*19*/ + {60.0, 10.0, PathPointTypeBezier, 0, 0}, /*20*/ + {60.0, 15.522847, PathPointTypeBezier, 0, 0}, /*21*/ + {55.522846, 20.0, PathPointTypeBezier, 0, 0}, /*22*/ + {50.0, 20.0, PathPointTypeBezier, 0, 0}, /*23*/ + {44.477150, 20.0, PathPointTypeBezier, 0, 0}, /*24*/ + {40.0, 15.522847, PathPointTypeBezier, 0, 0}, /*25*/ + {40.0, 10.0, PathPointTypeBezier, 0, 0}, /*26*/ + {40.0, 4.477153, PathPointTypeBezier, 0, 0}, /*27*/ + {44.477150, 0.0, PathPointTypeBezier, 0, 0}, /*28*/ + {50.0, 0.0, PathPointTypeBezier|PathPointTypeCloseSubpath, 0, 0}, /*29*/ + }; + +static path_test_t widenline_capdiamondanchor_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {-5.0, 10.0, PathPointTypeStart, 0, 0}, /*4*/ + {5.0, 0.0, PathPointTypeLine, 0, 0}, /*5*/ + {15.0, 10.0, PathPointTypeLine, 0, 0}, /*6*/ + {5.0, 20.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + {60.0, 10.0, PathPointTypeStart, 0, 0}, /*8*/ + {50.0, 20.0, PathPointTypeLine, 0, 0}, /*9*/ + {40.0, 10.0, PathPointTypeLine, 0, 0}, /*10*/ + {50.0, 0.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*11*/ + }; + +static path_test_t widenline_caparrowanchor_path[] = { + {15.0, 5.0, PathPointTypeStart, 0, 1}, /*0*/ + {40.0, 5.0, PathPointTypeLine, 0, 1}, /*1*/ + {40.0, 15.0, PathPointTypeLine, 0, 1}, /*2*/ + {15.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 1}, /*3*/ + {5.0, 10.0, PathPointTypeStart, 0, 0}, /*4*/ + {22.320507, 0.0, PathPointTypeLine, 0, 0}, /*5*/ + {22.320507, 20.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*6*/ + {50.0, 10.0, PathPointTypeStart, 0, 0}, /*7*/ + {32.679489, 20.0, PathPointTypeLine, 0, 0}, /*8*/ + {32.679489, 0.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*9*/ + }; + +static path_test_t widenline_capsquareanchor_thin_path[] = { + {6.414213, 8.585786, PathPointTypeStart, 0, 0}, /*0*/ + {6.414213, 11.414213, PathPointTypeLine, 0, 0}, /*1*/ + {3.585786, 11.414213, PathPointTypeLine, 0, 0}, /*2*/ + {3.585786, 8.585786, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {48.585785, 11.414213, PathPointTypeStart, 0, 0}, /*4*/ + {48.585785, 8.585786, PathPointTypeLine, 0, 0}, /*5*/ + {51.414211, 8.585786, PathPointTypeLine, 0, 0}, /*6*/ + {51.414211, 11.414213, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + }; + +static path_test_t widenline_capsquareanchor_dashed_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {35.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {35.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {45.0, 5.0, PathPointTypeStart, 0, 0}, /*4*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*5*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*6*/ + {45.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + {12.071068, 2.928932, PathPointTypeStart, 0, 0}, /*8*/ + {12.071068, 17.071066, PathPointTypeLine, 0, 0}, /*9*/ + {-2.071068, 17.071066, PathPointTypeLine, 0, 0}, /*10*/ + {-2.071068, 2.928932, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*11*/ + {42.928928, 17.071068, PathPointTypeStart, 0, 0}, /*12*/ + {42.928928, 2.928932, PathPointTypeLine, 0, 0}, /*13*/ + {57.071068, 2.928932, PathPointTypeLine, 0, 0}, /*14*/ + {57.071068, 17.071068, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*15*/ + }; + +static path_test_t widenline_capsquareanchor_multifigure_path[] = { + {5.0, 5.0, PathPointTypeStart, 0, 0}, /*0*/ + {25.0, 5.0, PathPointTypeLine, 0, 0}, /*1*/ + {25.0, 15.0, PathPointTypeLine, 0, 0}, /*2*/ + {5.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {30.0, 5.0, PathPointTypeStart, 0, 0}, /*4*/ + {50.0, 5.0, PathPointTypeLine, 0, 0}, /*5*/ + {50.0, 15.0, PathPointTypeLine, 0, 0}, /*6*/ + {30.0, 15.0, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*7*/ + {12.071068, 2.928932, PathPointTypeStart, 0, 0}, /*8*/ + {12.071068, 17.071066, PathPointTypeLine, 0, 0}, /*9*/ + {-2.071068, 17.071066, PathPointTypeLine, 0, 0}, /*10*/ + {-2.071068, 2.928932, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*11*/ + {17.928930, 17.071068, PathPointTypeStart, 0, 0}, /*12*/ + {17.928930, 2.928932, PathPointTypeLine, 0, 0}, /*13*/ + {32.071068, 2.928932, PathPointTypeLine, 0, 0}, /*14*/ + {32.071068, 17.071068, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*15*/ + {37.071068, 2.928932, PathPointTypeStart, 0, 0}, /*16*/ + {37.071068, 17.071066, PathPointTypeLine, 0, 0}, /*17*/ + {22.928930, 17.071066, PathPointTypeLine, 0, 0}, /*18*/ + {22.928930, 2.928932, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*19*/ + {42.928928, 17.071068, PathPointTypeStart, 0, 0}, /*20*/ + {42.928928, 2.928932, PathPointTypeLine, 0, 0}, /*21*/ + {57.071068, 2.928932, PathPointTypeLine, 0, 0}, /*22*/ + {57.071068, 17.071068, PathPointTypeLine|PathPointTypeCloseSubpath, 0, 0}, /*23*/ + }; + +static path_test_t widenline_customarrow_multifigure_path[] = { + {6.0, 9.5, PathPointTypeStart}, /*0*/ + {24.0, 9.5, PathPointTypeLine}, /*1*/ + {24.0, 10.5, PathPointTypeLine}, /*2*/ + {6.0, 10.5, PathPointTypeLine|PathPointTypeCloseSubpath}, /*3*/ + {30.5, 11.0, PathPointTypeStart}, /*4*/ + {30.5, 29.0, PathPointTypeLine}, /*5*/ + {29.5, 29.0, PathPointTypeLine}, /*6*/ + {29.5, 11.0, PathPointTypeLine|PathPointTypeCloseSubpath}, /*7*/ + {13.0, 14.0, PathPointTypeStart}, /*8*/ + {5.0, 10.0, PathPointTypeLine}, /*9*/ + {13.0, 6.0, PathPointTypeLine}, /*10*/ + {11.0, 10.0, PathPointTypeLine|PathPointTypeCloseSubpath}, /*11*/ + {17.0, 6.0, PathPointTypeStart}, /*12*/ + {25.0, 10.0, PathPointTypeLine}, /*13*/ + {17.0, 14.0, PathPointTypeLine}, /*14*/ + {19.0, 10.0, PathPointTypeLine|PathPointTypeCloseSubpath}, /*15*/ + {26.0, 18.0, PathPointTypeStart}, /*16*/ + {30.0, 10.0, PathPointTypeLine}, /*17*/ + {34.0, 18.0, PathPointTypeLine}, /*18*/ + {30.0, 16.0, PathPointTypeLine|PathPointTypeCloseSubpath}, /*19*/ + {34.0, 22.0, PathPointTypeStart}, /*20*/ + {30.0, 30.0, PathPointTypeLine}, /*21*/ + {26.0, 22.0, PathPointTypeLine}, /*22*/ + {30.0, 24.0, PathPointTypeLine|PathPointTypeCloseSubpath}, /*23*/ + }; + +static void test_widen_cap(void) +{ + struct + { + LineCap type; + REAL line_width; + const path_test_t *expected; + INT expected_size; + BOOL dashed; + } + caps[] = + { + { LineCapFlat, 10.0, widenline_capflat_path, + ARRAY_SIZE(widenline_capflat_path) }, + { LineCapSquare, 10.0, widenline_capsquare_path, + ARRAY_SIZE(widenline_capsquare_path) }, + { LineCapRound, 10.0, widenline_capround_path, + ARRAY_SIZE(widenline_capround_path) }, + { LineCapTriangle, 10.0, widenline_captriangle_path, + ARRAY_SIZE(widenline_captriangle_path) }, + { LineCapNoAnchor, 10.0, widenline_capflat_path, + ARRAY_SIZE(widenline_capflat_path) }, + { LineCapSquareAnchor, 10.0, widenline_capsquareanchor_path, + ARRAY_SIZE(widenline_capsquareanchor_path) }, + { LineCapRoundAnchor, 10.0, widenline_caproundanchor_path, + ARRAY_SIZE(widenline_caproundanchor_path) }, + { LineCapDiamondAnchor, 10.0, widenline_capdiamondanchor_path, + ARRAY_SIZE(widenline_capdiamondanchor_path) }, + { LineCapArrowAnchor, 10.0, widenline_caparrowanchor_path, + ARRAY_SIZE(widenline_caparrowanchor_path) }, + { LineCapSquareAnchor, 0.0, widenline_capsquareanchor_thin_path, + ARRAY_SIZE(widenline_capsquareanchor_thin_path) }, + { LineCapSquareAnchor, 10.0, widenline_capsquareanchor_dashed_path, + ARRAY_SIZE(widenline_capsquareanchor_dashed_path), TRUE }, + }; + + GpAdjustableArrowCap *arrowcap; + GpStatus status; + GpPath *path; + GpPen *pen; + + int i; + + status = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, status); + + for (i = 0; i < ARRAY_SIZE(caps); i++) + { + status = GdipCreatePen1(0xffffffff, caps[i].line_width, UnitPixel, &pen); + expect(Ok, status); + if (caps[i].dashed) + { + status = GdipSetPenDashStyle(pen, DashStyleDash); + expect(Ok, status); + } + + status = GdipResetPath(path); + expect(Ok, status); + status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0); + expect(Ok, status); + status = GdipSetPenStartCap(pen, caps[i].type); + expect(Ok, status); + status = GdipSetPenEndCap(pen, caps[i].type); + expect(Ok, status); + + status = GdipWidenPath(path, pen, NULL, FlatnessDefault); + expect(Ok, status); + + if (i == 9) + { + INT size; + status = GdipGetPointCount(path, &size); + expect(Ok, status); + ok(size == caps[i].expected_size || broken(size == 12), "unexpected path size %i\n", size); + + if (size == 12) + { + GdipDeletePen(pen); + continue; + } + } + + ok_path_fudge(path, caps[i].expected, caps[i].expected_size, FALSE, 0.000005); + + GdipDeletePen(pen); + } + + status = GdipCreatePen1(0xffffffff, 10.0, UnitPixel, &pen); + expect(Ok, status); + status = GdipResetPath(path); + expect(Ok, status); + status = GdipAddPathLine(path, 5.0, 10.0, 25.0, 10.0); + expect(Ok, status); + status = GdipStartPathFigure(path); + expect(Ok, status); + status = GdipAddPathLine(path, 30.0, 10.0, 50.0, 10.0); + expect(Ok, status); + status = GdipSetPenStartCap(pen, LineCapSquareAnchor); + expect(Ok, status); + status = GdipSetPenEndCap(pen, LineCapSquareAnchor); + expect(Ok, status); + status = GdipWidenPath(path, pen, NULL, FlatnessDefault); + expect(Ok, status); + ok_path_fudge(path, widenline_capsquareanchor_multifigure_path, + ARRAY_SIZE(widenline_capsquareanchor_multifigure_path), FALSE, 0.000005); + + status = GdipResetPath(path); + expect(Ok, status); + status = GdipAddPathLine(path, 5.0, 10.0, 25.0, 10.0); + expect(Ok, status); + status = GdipStartPathFigure(path); + expect(Ok, status); + status = GdipAddPathLine(path, 30.0, 10.0, 30.0, 30.0); + expect(Ok, status); + status = GdipCreateAdjustableArrowCap(4.0, 4.0, TRUE, &arrowcap); + ok(status == Ok, "Failed to create adjustable cap, %d\n", status); + status = GdipSetAdjustableArrowCapMiddleInset(arrowcap, 1.0); + ok(status == Ok, "Failed to set middle inset inadjustable cap, %d\n", status); + status = GdipSetPenCustomStartCap(pen, (GpCustomLineCap*)arrowcap); + ok(status == Ok, "Failed to create custom end cap, %d\n", status); + status = GdipSetPenCustomEndCap(pen, (GpCustomLineCap*)arrowcap); + ok(status == Ok, "Failed to create custom end cap, %d\n", status); + status = GdipSetPenWidth(pen, 1.0); + expect(Ok, status); + status = GdipWidenPath(path, pen, NULL, FlatnessDefault); + expect(Ok, status); + ok_path_fudge(path, widenline_customarrow_multifigure_path, + ARRAY_SIZE(widenline_customarrow_multifigure_path), FALSE, 0.000005); + + GdipDeletePen(pen); + + GdipDeletePath(path); +} + static void test_isvisible(void) { GpPath *path; @@ -1291,12 +1974,165 @@ static void test_isvisible(void) status = GdipIsVisiblePathPoint(path, 0.0, 0.0, graphics, &result); expect(Ok, status); expect(TRUE, result); + /* not affected by world transform */ + status = GdipScaleWorldTransform(graphics, 2.0, 2.0, MatrixOrderPrepend); + expect(Ok, status); + result = FALSE; + status = GdipIsVisiblePathPoint(path, 9.0, 9.0, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsVisiblePathPoint(path, 11.0, 11.0, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + GdipResetWorldTransform(graphics); GdipDeletePath(path); GdipDeleteGraphics(graphics); ReleaseDC(0, hdc); } +static void test_is_outline_visible_path_point(void) +{ + BOOL result; + GpBitmap *bitmap; + GpGraphics *graphics = NULL; + GpPath *path; + GpPen *pen = NULL; + GpStatus status; + static const int width = 20, height = 20; + + /* Graphics associated with an Image object.*/ + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics); + expect(Ok, status); + ok(graphics != NULL, "Expected the graphics context to be initialized.\n"); + + status = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, status); + + status = GdipAddPathRectangle(path, 2.0, 0.0, 13.0, 15.0); + expect(Ok, status); + + status = GdipCreatePen1((ARGB)0xffff00ff, 3.0f, UnitPixel, &pen); + expect(Ok, status); + ok(pen != NULL, "Expected pen to be initialized\n"); + + /* With NULL pen */ + result = 9; + status = GdipIsOutlineVisiblePathPoint(path, 0.0, 1.0, NULL, graphics, &result); + expect(InvalidParameter, status); + expect(9, result); + + /* Without transformation */ + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 0.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 1.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 10.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 16.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 17.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + + /* Translating */ + status = GdipTranslateWorldTransform(graphics, 50.0, 50.0, MatrixOrderPrepend); + expect(Ok, status); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 10.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 15.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 16.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + + /* Scaling */ + status = GdipScaleWorldTransform(graphics, 2.0, 2.0, MatrixOrderPrepend); + expect(Ok, status); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 0.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 1.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 2.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 3.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 14.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 15.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 16.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + + /* Page Unit */ + GdipResetWorldTransform(graphics); + status = GdipSetPageUnit(graphics, UnitMillimeter); + expect(Ok, status); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 0.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 1.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 2.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 3.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 14.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + result = FALSE; + status = GdipIsOutlineVisiblePathPoint(path, 15.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(TRUE, result); + result = TRUE; + status = GdipIsOutlineVisiblePathPoint(path, 16.0, 1.0, pen, graphics, &result); + expect(Ok, status); + expect(FALSE, result); + + GdipResetWorldTransform(graphics); + GdipDeletePath(path); + GdipDeleteGraphics(graphics); +} + static void test_empty_rect(void) { GpPath *path; @@ -1349,6 +2185,33 @@ static void test_empty_rect(void) GdipDeletePath(path); } +static path_test_t rect_line_path[] = { + {1.0, 4.0, PathPointTypeStart, 0, 0}, /*0*/ + {17.0, 4.0, PathPointTypeLine, 0, 0}, /*1*/ + {17.0, 68.0, PathPointTypeLine, 0, 0}, /*2*/ + {1.0, 68.0, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*3*/ + {1.0, 8.0, PathPointTypeStart, 0, 0}, /*4*/ + {17.0, 8.0, PathPointTypeLine, 0, 0} /*5*/ + }; + +static void test_rect_line(void) +{ + GpStatus status; + GpPath* path; + + GdipCreatePath(FillModeAlternate, &path); + + status = GdipAddPathRectangleI(path, 1, 4, 16, 64); + expect(Ok, status); + + status = GdipAddPathLineI(path, 1, 8, 17, 8); + expect(Ok, status); + + ok_path(path, rect_line_path, ARRAY_SIZE(rect_line_path), FALSE); + + GdipDeletePath(path); +} + START_TEST(graphicspath) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1370,7 +2233,10 @@ START_TEST(graphicspath) test_constructor_destructor(); test_getpathdata(); + test_createpath2(); test_line2(); + test_bezier(); + test_beziers(); test_arc(); test_worldbounds(); test_pathpath(); @@ -1385,8 +2251,11 @@ START_TEST(graphicspath) test_addpie(); test_flatten(); test_widen(); + test_widen_cap(); test_isvisible(); + test_is_outline_visible_path_point(); test_empty_rect(); + test_rect_line(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/guid.c b/modules/rostests/winetests/gdiplus/guid.c index d7bbe34d19e..c30d002fbf3 100644 --- a/modules/rostests/winetests/gdiplus/guid.c +++ b/modules/rostests/winetests/gdiplus/guid.c @@ -11,17 +11,4 @@ #include #include -/* FIXME: They belong to gdipluseffects.h */ -DEFINE_GUID(BlurEffectGuid, 0x633c80a4, 0x1843, 0x482b, 0x9e, 0xf2, 0xbe, 0x28, 0x34, 0xc5, 0xfd, 0xd4); -DEFINE_GUID(SharpenEffectGuid, 0x63cbf3ee, 0xc526, 0x402c, 0x8f, 0x71, 0x62, 0xc5, 0x40, 0xbf, 0x51, 0x42); -DEFINE_GUID(ColorMatrixEffectGuid, 0x718f2615, 0x7933, 0x40e3, 0xa5, 0x11, 0x5f, 0x68, 0xfe, 0x14, 0xdd, 0x74); -DEFINE_GUID(ColorLUTEffectGuid, 0xa7ce72a9, 0x0f7f, 0x40d7, 0xb3, 0xcc, 0xd0, 0xc0, 0x2d, 0x5c, 0x32, 0x12); -DEFINE_GUID(BrightnessContrastEffectGuid, 0xd3a1dbe1, 0x8ec4, 0x4c17, 0x9f, 0x4c, 0xea, 0x97, 0xad, 0x1c, 0x34, 0x3d); -DEFINE_GUID(HueSaturationLightnessEffectGuid, 0x8b2dd6c3, 0xeb07, 0x4d87, 0xa5, 0xf0, 0x71, 0x08, 0xe2, 0x6a, 0x9c, 0x5f); -DEFINE_GUID(LevelsEffectGuid, 0x99c354ec, 0x2a31, 0x4f3a, 0x8c, 0x34, 0x17, 0xa8, 0x03, 0xb3, 0x3a, 0x25); -DEFINE_GUID(TintEffectGuid, 0x1077af00, 0x2848, 0x4441, 0x94, 0x89, 0x44, 0xad, 0x4c, 0x2d, 0x7a, 0x2c); -DEFINE_GUID(ColorBalanceEffectGuid, 0x537e597d, 0x251e, 0x48da, 0x96, 0x64, 0x29, 0xca, 0x49, 0x6b, 0x70, 0xf8); -DEFINE_GUID(RedEyeCorrectionEffectGuid, 0x74d29d05, 0x69a4, 0x4266, 0x95, 0x49, 0x3c, 0xc5, 0x28, 0x36, 0xb6, 0x32); -DEFINE_GUID(ColorCurveEffectGuid, 0xdd6a0022, 0x58e4, 0x4a67, 0x9d, 0x9b, 0xd4, 0x8e, 0xb8, 0x81, 0xa5, 0x3d); - /* NO CODE HERE, THIS IS JUST REQUIRED FOR THE GUID DEFINITIONS */ diff --git a/modules/rostests/winetests/gdiplus/image.c b/modules/rostests/winetests/gdiplus/image.c index c5802f1dc56..df26f699e86 100644 --- a/modules/rostests/winetests/gdiplus/image.c +++ b/modules/rostests/winetests/gdiplus/image.c @@ -30,23 +30,6 @@ #include "gdiplus.h" #include "wine/test.h" -#ifdef __REACTOS__ -#include -#endif - -/* FIXME: They belong to gdipluseffects.h */ -DEFINE_GUID(BlurEffectGuid, 0x633c80a4, 0x1843, 0x482b, 0x9e, 0xf2, 0xbe, 0x28, 0x34, 0xc5, 0xfd, 0xd4); -DEFINE_GUID(SharpenEffectGuid, 0x63cbf3ee, 0xc526, 0x402c, 0x8f, 0x71, 0x62, 0xc5, 0x40, 0xbf, 0x51, 0x42); -DEFINE_GUID(ColorMatrixEffectGuid, 0x718f2615, 0x7933, 0x40e3, 0xa5, 0x11, 0x5f, 0x68, 0xfe, 0x14, 0xdd, 0x74); -DEFINE_GUID(ColorLUTEffectGuid, 0xa7ce72a9, 0x0f7f, 0x40d7, 0xb3, 0xcc, 0xd0, 0xc0, 0x2d, 0x5c, 0x32, 0x12); -DEFINE_GUID(BrightnessContrastEffectGuid, 0xd3a1dbe1, 0x8ec4, 0x4c17, 0x9f, 0x4c, 0xea, 0x97, 0xad, 0x1c, 0x34, 0x3d); -DEFINE_GUID(HueSaturationLightnessEffectGuid, 0x8b2dd6c3, 0xeb07, 0x4d87, 0xa5, 0xf0, 0x71, 0x08, 0xe2, 0x6a, 0x9c, 0x5f); -DEFINE_GUID(LevelsEffectGuid, 0x99c354ec, 0x2a31, 0x4f3a, 0x8c, 0x34, 0x17, 0xa8, 0x03, 0xb3, 0x3a, 0x25); -DEFINE_GUID(TintEffectGuid, 0x1077af00, 0x2848, 0x4441, 0x94, 0x89, 0x44, 0xad, 0x4c, 0x2d, 0x7a, 0x2c); -DEFINE_GUID(ColorBalanceEffectGuid, 0x537e597d, 0x251e, 0x48da, 0x96, 0x64, 0x29, 0xca, 0x49, 0x6b, 0x70, 0xf8); -DEFINE_GUID(RedEyeCorrectionEffectGuid, 0x74d29d05, 0x69a4, 0x4266, 0x95, 0x49, 0x3c, 0xc5, 0x28, 0x36, 0xb6, 0x32); -DEFINE_GUID(ColorCurveEffectGuid, 0xdd6a0022, 0x58e4, 0x4a67, 0x9d, 0x9b, 0xd4, 0x8e, 0xb8, 0x81, 0xa5, 0x3d); - static GpStatus (WINAPI *pGdipBitmapGetHistogramSize)(HistogramFormat,UINT*); static GpStatus (WINAPI *pGdipBitmapGetHistogram)(GpBitmap*,HistogramFormat,UINT,UINT*,UINT*,UINT*,UINT*); static GpStatus (WINAPI *pGdipImageSetAbort)(GpImage*,GdiplusAbort*); @@ -56,15 +39,21 @@ static GpStatus (WINGDIPAPI *pGdipInitializePalette)(ColorPalette*,PaletteType,I #define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (UINT)(expected), (UINT)(got)) #define expectf(expected, got) ok(fabs((expected) - (got)) < 0.0001, "Expected %f, got %f\n", (expected), (got)) -static BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff) +static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) { - if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE; + unsigned int diff = x > y ? x - y : y - x; + return diff <= max_diff; +} + +BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff) +{ + if (!compare_uint(c1 & 0xff, c2 & 0xff, max_diff)) return FALSE; c1 >>= 8; c2 >>= 8; - if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE; + if (!compare_uint(c1 & 0xff, c2 & 0xff, max_diff)) return FALSE; c1 >>= 8; c2 >>= 8; - if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE; + if (!compare_uint(c1 & 0xff, c2 & 0xff, max_diff)) return FALSE; c1 >>= 8; c2 >>= 8; - if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE; + if (!compare_uint(c1 & 0xff, c2 & 0xff, max_diff)) return FALSE; return TRUE; } @@ -93,6 +82,103 @@ static void expect_rawformat(REFGUID expected, GpImage *img, int line, BOOL todo expect_guid(expected, &raw, line, todo); } +static void expect_image_properties(GpImage *image, UINT width, UINT height, int line) +{ + GpStatus stat; + UINT dim; + ImageType type; + PixelFormat format; + + stat = GdipGetImageWidth(image, &dim); + ok_(__FILE__, line)(stat == Ok, "Expected %d, got %d\n", Ok, stat); + ok_(__FILE__, line)(dim == width, "Expected %d, got %d\n", width, dim); + + stat = GdipGetImageHeight(image, &dim); + ok_(__FILE__, line)(stat == Ok, "Expected %d, got %d\n", Ok, stat); + ok_(__FILE__, line)(dim == height, "Expected %d, got %d\n", height, dim); + + stat = GdipGetImageType(image, &type); + ok_(__FILE__, line)(stat == Ok, "Expected %d, got %d\n", Ok, stat); + ok_(__FILE__, line)(type == ImageTypeBitmap, "Expected %d, got %d\n", ImageTypeBitmap, type); + + stat = GdipGetImagePixelFormat(image, &format); + ok_(__FILE__, line)(stat == Ok, "Expected %d, got %d\n", Ok, stat); + ok_(__FILE__, line)(format == PixelFormat32bppARGB, "Expected %d, got %d\n", PixelFormat32bppARGB, format); +} + +static const char * dbgstr_hexdata(const BYTE *data, UINT len) +{ + UINT i, offset = 0; + char buffer[770]; + const UINT max_len = 256; + const UINT output_len = (len <= max_len) ? len : max_len - 1; + + if (!len) return ""; + + for (i = 0; i < output_len; i++) + offset += sprintf(buffer + offset, " %02x", data[i]); + + if (len > output_len) + offset += sprintf(buffer + offset, " ..."); + + return __wine_dbg_strdup( buffer ); +} + +static void expect_bitmap_locked_data(GpBitmap *bitmap, const BYTE *expect_bits, + UINT width, UINT height, UINT stride, int line) +{ + GpStatus stat; + BitmapData lockeddata; + int match; + + memset(&lockeddata, 0x55, sizeof(lockeddata)); + stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppARGB, &lockeddata); + ok_(__FILE__, line)(stat == Ok, "Expected %d, got %d\n", Ok, stat); + ok_(__FILE__, line)(lockeddata.Width == width, "Expected %d, got %d\n", width, lockeddata.Width); + ok_(__FILE__, line)(lockeddata.Height == height, "Expected %d, got %d\n", height, lockeddata.Height); + ok_(__FILE__, line)(lockeddata.Stride == stride, "Expected %d, got %d\n", stride, lockeddata.Stride); + ok_(__FILE__, line)(lockeddata.PixelFormat == PixelFormat32bppARGB, + "Expected %d, got %d\n", PixelFormat32bppARGB, lockeddata.PixelFormat); + match = !memcmp(expect_bits, lockeddata.Scan0, lockeddata.Height * lockeddata.Stride); + ok_(__FILE__, line)(match, "data mismatch\n"); + if (!match) + { + trace("Expected: %s\n", dbgstr_hexdata(expect_bits, lockeddata.Height * lockeddata.Stride)); + trace("Got: %s\n", dbgstr_hexdata(lockeddata.Scan0, lockeddata.Height * lockeddata.Stride)); + } + GdipBitmapUnlockBits(bitmap, &lockeddata); +} + +static BOOL get_encoder_clsid(LPCWSTR mime, GUID *format, CLSID *clsid) +{ + GpStatus status; + UINT n_codecs, info_size, i; + ImageCodecInfo *info; + BOOL ret = FALSE; + + status = GdipGetImageEncodersSize(&n_codecs, &info_size); + expect(Ok, status); + + info = GdipAlloc(info_size); + + status = GdipGetImageEncoders(n_codecs, info_size, info); + expect(Ok, status); + + for (i = 0; i < n_codecs; i++) + { + if (!lstrcmpW(info[i].MimeType, mime)) + { + *format = info[i].FormatID; + *clsid = info[i].Clsid; + ret = TRUE; + break; + } + } + + GdipFree(info); + return ret; +} + static void test_bufferrawformat(void* buff, int size, REFGUID expected, int line, BOOL todo) { LPSTREAM stream; @@ -100,7 +186,7 @@ static void test_bufferrawformat(void* buff, int size, REFGUID expected, int lin LPBYTE data; HRESULT hres; GpStatus stat; - GpImage *img; + GpImage *img, *copy; hglob = GlobalAlloc (0, size); data = GlobalLock (hglob); @@ -119,8 +205,12 @@ static void test_bufferrawformat(void* buff, int size, REFGUID expected, int lin } expect_rawformat(expected, img, line, todo); + stat = GdipCloneImage(img, ©); + expect(Ok, stat); + expect_rawformat(expected, copy, line, todo); GdipDisposeImage(img); + GdipDisposeImage(copy); IStream_Release(stream); } @@ -390,6 +480,9 @@ static void test_GdipImageGetFrameDimensionsCount(void) expect(Ok, stat); expect(0xffffffff, color); + stat = GdipImageSelectActiveFrame((GpImage*)bm, &dimension, 1); + expect(Ok, stat); + GdipDisposeImage((GpImage*)bm); } @@ -398,7 +491,6 @@ static void test_LoadingImages(void) GpStatus stat; GpBitmap *bm; GpImage *img; - static const WCHAR nonexistentW[] = {'n','o','n','e','x','i','s','t','e','n','t',0}; stat = GdipCreateBitmapFromFile(0, 0); expect(InvalidParameter, stat); @@ -409,7 +501,7 @@ static void test_LoadingImages(void) ok(bm == (GpBitmap *)0xdeadbeef, "returned %p\n", bm); bm = (GpBitmap *)0xdeadbeef; - stat = GdipCreateBitmapFromFile(nonexistentW, &bm); + stat = GdipCreateBitmapFromFile(L"nonexistent", &bm); todo_wine expect(InvalidParameter, stat); ok(!bm, "returned %p\n", bm); @@ -422,7 +514,7 @@ static void test_LoadingImages(void) ok(img == (GpImage *)0xdeadbeef, "returned %p\n", img); img = (GpImage *)0xdeadbeef; - stat = GdipLoadImageFromFile(nonexistentW, &img); + stat = GdipLoadImageFromFile(L"nonexistent", &img); todo_wine expect(OutOfMemory, stat); ok(!img, "returned %p\n", img); @@ -435,7 +527,7 @@ static void test_LoadingImages(void) ok(img == (GpImage *)0xdeadbeef, "returned %p\n", img); img = (GpImage *)0xdeadbeef; - stat = GdipLoadImageFromFileICM(nonexistentW, &img); + stat = GdipLoadImageFromFileICM(L"nonexistent", &img); todo_wine expect(OutOfMemory, stat); ok(!img, "returned %p\n", img); } @@ -450,7 +542,7 @@ static void test_SavingImages(void) REAL w, h; ImageCodecInfo *codecs; static const CHAR filenameA[] = "a.bmp"; - static const WCHAR filename[] = { 'a','.','b','m','p',0 }; + static const WCHAR filename[] = L"a.bmp"; codecs = NULL; @@ -504,6 +596,188 @@ static void test_SavingImages(void) ok(DeleteFileA(filenameA), "Delete failed.\n"); } +static void test_SavingMultiPageTiff(void) +{ + GpStatus stat; + BOOL result; + GpBitmap *bm1 = NULL, *bm2 = NULL, *check_bm = NULL; + const REAL WIDTH = 10.0, HEIGHT = 20.0; + REAL w, h; + GUID format, tiff_clsid; + EncoderParameters params; + ULONG32 paramValue = EncoderValueFrameDimensionPage; + UINT frame_count; + static const CHAR filename1A[] = "1.tif"; + static const CHAR filename2A[] = "2.tif"; + static const WCHAR filename1[] = L"1.tif"; + static const WCHAR filename2[] = L"2.tif"; + + params.Count = 1; + params.Parameter[0].Guid = EncoderSaveFlag; + params.Parameter[0].Type = EncoderParameterValueTypeLong; + params.Parameter[0].NumberOfValues = 1; + params.Parameter[0].Value = ¶mValue; + + stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm1); + expect(Ok, stat); + stat = GdipCreateBitmapFromScan0(2 * WIDTH, 2 * HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm2); + expect(Ok, stat); + result = get_encoder_clsid(L"image/tiff", &format, &tiff_clsid); + ok(result, "getting TIFF encoding clsid failed"); + + if (!bm1 || !bm2 || !result) + return; + + /* invalid params: NULL */ + stat = GdipSaveAdd(0, ¶ms); + expect(InvalidParameter, stat); + stat = GdipSaveAdd((GpImage*)bm1, 0); + expect(InvalidParameter, stat); + + stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, 0); + expect(InvalidParameter, stat); + stat = GdipSaveAddImage((GpImage*)bm1, 0, ¶ms); + expect(InvalidParameter, stat); + stat = GdipSaveAddImage(0, (GpImage*)bm2, ¶ms); + expect(InvalidParameter, stat); + + /* win32 error: SaveAdd() can only be called after Save() with the MultiFrame param */ + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Win32Error, stat); + stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); + expect(Win32Error, stat); + + stat = GdipSaveImageToFile((GpImage*)bm1, filename1, &tiff_clsid, 0); /* param not set! */ + expect(Ok, stat); + if (stat != Ok) goto cleanup; + + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Win32Error, stat); + stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); + expect(Win32Error, stat); + + /* win32 error: can't flush before starting the encoding process */ + paramValue = EncoderValueFlush; + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Win32Error, stat); + + /* win32 error: can't start encoding process through SaveAdd(), only Save() */ + paramValue = EncoderValueMultiFrame; + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Win32Error, stat); + + /* start encoding process: add first frame (bm1) */ + paramValue = EncoderValueMultiFrame; + stat = GdipSaveImageToFile((GpImage*)bm1, filename1, &tiff_clsid, ¶ms); + expect(Ok, stat); + + /* re-start encoding process: add first frame (bm1), should re-create the file */ + stat = GdipSaveImageToFile((GpImage*)bm1, filename1, &tiff_clsid, ¶ms); + expect(Ok, stat); + + /* add second frame (bm2) */ + paramValue = EncoderValueFrameDimensionPage; + stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); + expect(Ok, stat); + if (stat != Ok) goto cleanup; + + /* finish encoding process */ + paramValue = EncoderValueFlush; + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Ok, stat); + + /* bm1 should be unchanged, only the saved file on disk has multiple frames */ + stat = GdipImageGetFrameCount((GpImage*)bm1, &FrameDimensionPage, &frame_count); + expect(Ok, stat); + expect(1, frame_count); + + /* win32 error: encoding process already finished */ + paramValue = EncoderValueFrameDimensionPage; + stat = GdipSaveAddImage((GpImage*)bm1, (GpImage*)bm2, ¶ms); + expect(Win32Error, stat); + + stat = GdipSaveAdd((GpImage*)bm1, ¶ms); + expect(Win32Error, stat); + + GdipDisposeImage((GpImage*)bm1); + bm1 = 0; + GdipDisposeImage((GpImage*)bm2); + bm2 = 0; + + /* re-load and check image stats */ + stat = GdipLoadImageFromFile(filename1, (GpImage**)&check_bm); + expect(Ok, stat); + + stat = GdipImageGetFrameCount((GpImage*)check_bm, &FrameDimensionPage, &frame_count); + expect(Ok, stat); + expect(2, frame_count); + if (stat != Ok || frame_count != 2) goto cleanup; + + stat = GdipGetImageDimension((GpImage*)check_bm, &w, &h); + expect(Ok, stat); + expectf(WIDTH, w); /* frame index 0: bm1 stats */ + expectf(HEIGHT, h); + + stat = GdipImageSelectActiveFrame((GpImage*)check_bm, &FrameDimensionPage, 1); + expect(Ok, stat); + + stat = GdipGetImageDimension((GpImage*)check_bm, &w, &h); + expectf(2 * WIDTH, w); /* frame index 1: bm2 stats */ + expectf(2 * HEIGHT, h); + + /* now proper API use for SaveAdd() to swap the frames in check_bm */ + paramValue = EncoderValueMultiFrame; + stat = GdipSaveImageToFile((GpImage*)check_bm, filename2, &tiff_clsid, ¶ms); + expect(Ok, stat); /* second frame is active: bm2 */ + + stat = GdipImageSelectActiveFrame((GpImage*)check_bm, &FrameDimensionPage, 0); + expect(Ok, stat); + + paramValue = EncoderValueFrameDimensionPage; + stat = GdipSaveAdd((GpImage*)check_bm, ¶ms); + expect(Ok, stat); /* first frame is active: bm1 */ + + paramValue = EncoderValueFlush; + stat = GdipSaveAdd((GpImage*)check_bm, ¶ms); + expect(Ok, stat); /* flushed encoder (finished encoding process) */ + + GdipDisposeImage((GpImage*)check_bm); + check_bm = 0; + + /* re-load and check image stats */ + stat = GdipLoadImageFromFile(filename2, (GpImage**)&check_bm); + expect(Ok, stat); + + stat = GdipImageGetFrameCount((GpImage*)check_bm, &FrameDimensionPage, &frame_count); + expect(Ok, stat); + expect(2, frame_count); + + stat = GdipGetImageDimension((GpImage*)check_bm, &w, &h); + expect(Ok, stat); + expectf(2 * WIDTH, w); /* frame index 0: bm2 stats */ + expectf(2 * HEIGHT, h); + + stat = GdipImageSelectActiveFrame((GpImage*)check_bm, &FrameDimensionPage, 1); + expect(Ok, stat); + + stat = GdipGetImageDimension((GpImage*)check_bm, &w, &h); + expectf(WIDTH, w); /* frame index 1: bm1 stats */ + expectf(HEIGHT, h); + + cleanup: + if (bm1) + GdipDisposeImage((GpImage*)bm1); + if (bm2) + GdipDisposeImage((GpImage*)bm2); + ok(DeleteFileA(filename1A), "Delete 1.tif failed.\n"); + + if (check_bm) + { + GdipDisposeImage((GpImage*)check_bm); + ok(DeleteFileA(filename2A), "Delete 2.tif failed.\n"); + } +} + static void test_encoders(void) { GpStatus stat; @@ -883,7 +1157,7 @@ static void test_LockBits_UserBuf(void) struct BITMAPINFOWITHBITFIELDS { BITMAPINFOHEADER bmiHeader; - DWORD masks[3]; + DWORD masks[255]; }; union BITMAPINFOUNION @@ -899,7 +1173,10 @@ static void test_GdipCreateBitmapFromHBITMAP(void) HPALETTE hpal = NULL; GpStatus stat; BYTE buff[1000]; - LOGPALETTE* LogPal = NULL; + char logpalette_buf[sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 255]; + LOGPALETTE *LogPal = (LOGPALETTE *)logpalette_buf; + char colorpalette_buf[sizeof(ColorPalette) + sizeof(ARGB) * 255]; + ColorPalette *palette = (ColorPalette *)colorpalette_buf; REAL width, height; const REAL WIDTH1 = 5; const REAL HEIGHT1 = 15; @@ -909,6 +1186,7 @@ static void test_GdipCreateBitmapFromHBITMAP(void) union BITMAPINFOUNION bmi; BYTE *bits; PixelFormat format; + int i; stat = GdipCreateBitmapFromHBITMAP(NULL, NULL, NULL); expect(InvalidParameter, stat); @@ -922,6 +1200,9 @@ static void test_GdipCreateBitmapFromHBITMAP(void) expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height)); expectf(WIDTH1, width); expectf(HEIGHT1, height); + stat = GdipGetImagePixelFormat((GpImage*)gpbm, &format); + expect(Ok, stat); + expect(PixelFormat1bppIndexed, format); if (stat == Ok) GdipDisposeImage((GpImage*)gpbm); DeleteObject(hbm); @@ -933,6 +1214,10 @@ static void test_GdipCreateBitmapFromHBITMAP(void) /* raw format */ expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)gpbm, __LINE__, FALSE); + stat = GdipGetImagePixelFormat((GpImage*)gpbm, &format); + expect(Ok, stat); + expect(PixelFormat1bppIndexed, format); + expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height)); expectf(WIDTH2, width); expectf(HEIGHT2, height); @@ -960,6 +1245,9 @@ static void test_GdipCreateBitmapFromHBITMAP(void) expect(Ok, GdipGetImageDimension((GpImage*) gpbm, &width, &height)); expectf(WIDTH1, width); expectf(HEIGHT1, height); + stat = GdipGetImagePixelFormat((GpImage*)gpbm, &format); + expect(Ok, stat); + expect(PixelFormat24bppRGB, format); if (stat == Ok) { /* test whether writing to the bitmap affects the original */ @@ -971,21 +1259,77 @@ static void test_GdipCreateBitmapFromHBITMAP(void) GdipDisposeImage((GpImage*)gpbm); } - LogPal = GdipAlloc(sizeof(LOGPALETTE)); - ok(LogPal != NULL, "unable to allocate LOGPALETTE\n"); LogPal->palVersion = 0x300; - LogPal->palNumEntries = 1; + LogPal->palNumEntries = 8; + for (i = 0; i < 8; i++) + { + LogPal->palPalEntry[i].peRed = i; + LogPal->palPalEntry[i].peGreen = i; + LogPal->palPalEntry[i].peBlue = i; + LogPal->palPalEntry[i].peFlags = 0; + } + hpal = CreatePalette(LogPal); ok(hpal != NULL, "CreatePalette failed\n"); - GdipFree(LogPal); stat = GdipCreateBitmapFromHBITMAP(hbm, hpal, &gpbm); expect(Ok, stat); + stat = GdipGetImagePalette((GpImage *)gpbm, palette, sizeof(colorpalette_buf)); + expect(Ok, stat); + expect(0, palette->Count); + GdipDisposeImage((GpImage*)gpbm); + DeleteObject(hbm); - if (stat == Ok) - GdipDisposeImage((GpImage*)gpbm); + for (i = 0; i < 16; i++) + { + RGBQUAD *colors = bmi.bi.bmiColors; + BYTE clr = 255 - i; + colors[i].rgbBlue = clr; + colors[i].rgbGreen = clr; + colors[i].rgbRed = clr; + colors[i].rgbReserved = 0; + } + + bmi.bi.bmiHeader.biBitCount = 8; + bmi.bi.bmiHeader.biClrUsed = 16; + bmi.bi.bmiHeader.biClrImportant = 16; + hbm = CreateDIBSection(hdc, &bmi.bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok(hbm != NULL, "CreateDIBSection failed\n"); + + stat = GdipCreateBitmapFromHBITMAP(hbm, hpal, &gpbm); + expect(Ok, stat); + stat = GdipGetImagePixelFormat((GpImage*)gpbm, &format); + expect(Ok, stat); + expect(PixelFormat8bppIndexed, format); + stat = GdipGetImagePalette((GpImage *)gpbm, palette, sizeof(colorpalette_buf)); + expect(Ok, stat); + expect(256, palette->Count); + for (i = 0; i < 16; i++) + { + BYTE clr = 255 - i; + ARGB argb = 0xff000000 | (clr << 16) | (clr << 8) | clr; + ok(palette->Entries[i] == argb, "got %08lx, expected %08lx\n", palette->Entries[i], argb); + } + GdipDisposeImage((GpImage*)gpbm); DeleteObject(hpal); + + stat = GdipCreateBitmapFromHBITMAP(hbm, 0, &gpbm); + expect(Ok, stat); + stat = GdipGetImagePixelFormat((GpImage*)gpbm, &format); + expect(Ok, stat); + expect(PixelFormat8bppIndexed, format); + stat = GdipGetImagePalette((GpImage *)gpbm, palette, sizeof(colorpalette_buf)); + expect(Ok, stat); + expect(256, palette->Count); + for (i = 0; i < 16; i++) + { + BYTE clr = 255 - i; + ARGB argb = 0xff000000 | (clr << 16) | (clr << 8) | clr; + ok(palette->Entries[i] == argb, "got %08lx, expected %08lx\n", palette->Entries[i], argb); + } + GdipDisposeImage((GpImage*)gpbm); + DeleteObject(hbm); /* 16-bit 555 dib, rgb */ @@ -1016,7 +1360,7 @@ static void test_GdipCreateBitmapFromHBITMAP(void) DeleteObject(hbm); /* 16-bit 555 dib, with bitfields */ - bmi.bi.bmiHeader.biSize = sizeof(bmi); + bmi.bi.bmiHeader.biSize = sizeof(bmi.bi.bmiHeader); bmi.bi.bmiHeader.biCompression = BI_BITFIELDS; bmi.bf.masks[0] = 0x7c00; bmi.bf.masks[1] = 0x3e0; @@ -1243,20 +1587,21 @@ static void test_testcontrol(void) param = 0; stat = GdipTestControl(TestControlGetBuildNumber, ¶m); expect(Ok, stat); - ok(param != 0, "Build number expected, got %u\n", param); + ok(param != 0, "Build number expected, got %lu\n", param); } static void test_fromhicon(void) { - static const BYTE bmp_bits[1024]; + BYTE bmp_bits[1024], bmp_bits_masked[1024]; HBITMAP hbmMask, hbmColor; - ICONINFO info; + ICONINFO info, iconinfo_base = {TRUE, 0, 0, 0, 0}; HICON hIcon; GpStatus stat; GpBitmap *bitmap = NULL; - UINT dim; - ImageType type; - PixelFormat format; + UINT i; + + for (i = 0; i < sizeof(bmp_bits); ++i) + bmp_bits[i] = 111 * i; /* NULL */ stat = GdipCreateBitmapFromHICON(NULL, NULL); @@ -1264,53 +1609,64 @@ static void test_fromhicon(void) stat = GdipCreateBitmapFromHICON(NULL, &bitmap); expect(InvalidParameter, stat); - /* color icon 1 bit */ + /* monochrome icon */ hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); hbmColor = CreateBitmap(16, 16, 1, 1, bmp_bits); ok(hbmColor != 0, "CreateBitmap failed\n"); - info.fIcon = TRUE; - info.xHotspot = 8; - info.yHotspot = 8; + + info = iconinfo_base; info.hbmMask = hbmMask; info.hbmColor = hbmColor; hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); - DeleteObject(hbmMask); - DeleteObject(hbmColor); stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); ok(stat == Ok || broken(stat == InvalidParameter), /* Win98 */ "Expected Ok, got %.8x\n", stat); if(stat == Ok){ - /* check attributes */ - stat = GdipGetImageHeight((GpImage*)bitmap, &dim); - expect(Ok, stat); - expect(16, dim); - stat = GdipGetImageWidth((GpImage*)bitmap, &dim); - expect(Ok, stat); - expect(16, dim); - stat = GdipGetImageType((GpImage*)bitmap, &type); - expect(Ok, stat); - expect(ImageTypeBitmap, type); - stat = GdipGetImagePixelFormat((GpImage*)bitmap, &format); - expect(Ok, stat); - expect(PixelFormat32bppARGB, format); - /* raw format */ + expect_image_properties((GpImage*)bitmap, 16, 16, __LINE__); expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)bitmap, __LINE__, FALSE); GdipDisposeImage((GpImage*)bitmap); } DestroyIcon(hIcon); - /* color icon 8 bpp */ - hbmMask = CreateBitmap(16, 16, 1, 8, bmp_bits); + /* monochrome cursor */ + info.fIcon = FALSE; + info.xHotspot = 8; + info.yHotspot = 8; + info.hbmMask = hbmMask; + info.hbmColor = hbmColor; + hIcon = CreateIconIndirect(&info); + ok(hIcon != 0, "CreateIconIndirect failed\n"); + + stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); + expect(InvalidParameter, stat); + if (stat == Ok) + GdipDisposeImage((GpImage*)bitmap); + DestroyIcon(hIcon); + + /* mask-only icon */ + info = iconinfo_base; + info.hbmMask = hbmMask; + hIcon = CreateIconIndirect(&info); + ok(hIcon != 0, "CreateIconIndirect failed\n"); + + stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); + expect(InvalidParameter, stat); + DestroyIcon(hIcon); + + DeleteObject(hbmMask); + DeleteObject(hbmColor); + + /* 8 bpp icon */ + hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); hbmColor = CreateBitmap(16, 16, 1, 8, bmp_bits); ok(hbmColor != 0, "CreateBitmap failed\n"); - info.fIcon = TRUE; - info.xHotspot = 8; - info.yHotspot = 8; + + info = iconinfo_base; info.hbmMask = hbmMask; info.hbmColor = hbmColor; hIcon = CreateIconIndirect(&info); @@ -1321,24 +1677,63 @@ static void test_fromhicon(void) stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); expect(Ok, stat); if(stat == Ok){ - /* check attributes */ - stat = GdipGetImageHeight((GpImage*)bitmap, &dim); - expect(Ok, stat); - expect(16, dim); - stat = GdipGetImageWidth((GpImage*)bitmap, &dim); - expect(Ok, stat); - expect(16, dim); - stat = GdipGetImageType((GpImage*)bitmap, &type); - expect(Ok, stat); - expect(ImageTypeBitmap, type); - stat = GdipGetImagePixelFormat((GpImage*)bitmap, &format); - expect(Ok, stat); - expect(PixelFormat32bppARGB, format); - /* raw format */ + expect_image_properties((GpImage*)bitmap, 16, 16, __LINE__); expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)bitmap, __LINE__, FALSE); GdipDisposeImage((GpImage*)bitmap); } DestroyIcon(hIcon); + + /* 32 bpp icon */ + hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); + ok(hbmMask != 0, "CreateBitmap failed\n"); + hbmColor = CreateBitmap(16, 16, 1, 32, bmp_bits); + ok(hbmColor != 0, "CreateBitmap failed\n"); + info = iconinfo_base; + info.hbmMask = hbmMask; + info.hbmColor = hbmColor; + hIcon = CreateIconIndirect(&info); + ok(hIcon != 0, "CreateIconIndirect failed\n"); + DeleteObject(hbmMask); + DeleteObject(hbmColor); + + for (i = 0; i < sizeof(bmp_bits_masked)/sizeof(ARGB); i++) + { + BYTE mask = bmp_bits[i / 8] & (1 << (7 - (i % 8))); + ((ARGB *)bmp_bits_masked)[i] = mask ? 0 : ((ARGB *)bmp_bits)[i] | 0xff000000; + } + + stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); + expect(Ok, stat); + if(stat == Ok){ + expect_image_properties((GpImage*)bitmap, 16, 16, __LINE__); + expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)bitmap, __LINE__, FALSE); + expect_bitmap_locked_data(bitmap, bmp_bits_masked, 16, 16, 64, __LINE__); + GdipDisposeImage((GpImage*)bitmap); + } + DestroyIcon(hIcon); + + /* non-square 32 bpp icon */ + hbmMask = CreateBitmap(16, 8, 1, 1, bmp_bits); + ok(hbmMask != 0, "CreateBitmap failed\n"); + hbmColor = CreateBitmap(16, 8, 1, 32, bmp_bits); + ok(hbmColor != 0, "CreateBitmap failed\n"); + info = iconinfo_base; + info.hbmMask = hbmMask; + info.hbmColor = hbmColor; + hIcon = CreateIconIndirect(&info); + ok(hIcon != 0, "CreateIconIndirect failed\n"); + DeleteObject(hbmMask); + DeleteObject(hbmColor); + + stat = GdipCreateBitmapFromHICON(hIcon, &bitmap); + expect(Ok, stat); + if(stat == Ok){ + expect_image_properties((GpImage*)bitmap, 16, 8, __LINE__); + expect_rawformat(&ImageFormatMemoryBMP, (GpImage*)bitmap, __LINE__, FALSE); + expect_bitmap_locked_data(bitmap, bmp_bits_masked, 16, 8, 64, __LINE__); + GdipDisposeImage((GpImage*)bitmap); + } + DestroyIcon(hIcon); } /* 1x1 pixel png */ @@ -1499,7 +1894,7 @@ static void test_loadwmf(void) expect(0, header.Y); expect(320, header.Width); expect(320, header.Height); - expect(1, U(header).WmfHeader.mtType); + expect(1, header.WmfHeader.mtType); expect(0, header.EmfPlusHeaderSize); expect(0, header.LogicalDpiX); expect(0, header.LogicalDpiY); @@ -1557,7 +1952,7 @@ static void test_createfromwmf(void) expect(0, header.Y); expect(320, header.Width); expect(320, header.Height); - expect(1, U(header).WmfHeader.mtType); + expect(1, header.WmfHeader.mtType); expect(0, header.EmfPlusHeaderSize); expect(0, header.LogicalDpiX); expect(0, header.LogicalDpiY); @@ -1721,7 +2116,7 @@ static void test_createhbitmap(void) if (bm.bmBits) { DWORD val = *(DWORD*)bm.bmBits; - ok(val == 0xff686868, "got %x, expected 0xff686868\n", val); + ok(val == 0xff686868, "got %lx, expected 0xff686868\n", val); } hdc = CreateCompatibleDC(NULL); @@ -1768,9 +2163,9 @@ static void test_createhbitmap(void) if (bm.bmBits) { DWORD val = *(DWORD*)bm.bmBits; - ok(val == 0x682a2a2a, "got %x, expected 0x682a2a2a\n", val); + ok(val == 0x682a2a2a, "got %lx, expected 0x682a2a2a\n", val); val = *((DWORD*)bm.bmBits + (bm.bmHeight-1) * bm.bmWidthBytes/4 + 1); - ok(val == 0x0, "got %x, expected 0x682a2a2a\n", val); + ok(val == 0x0, "got %lx, expected 0x682a2a2a\n", val); } hdc = CreateCompatibleDC(NULL); @@ -1810,20 +2205,20 @@ static void test_createhbitmap(void) if (bm.bmBits) { DWORD val = *(DWORD*)bm.bmBits; - ok(val == 0x68c12ac1 || broken(val == 0x682a2ac1), "got %x, expected 0x68c12ac1\n", val); + ok(val == 0x68c12ac1 || broken(val == 0x682a2ac1), "got %lx, expected 0x68c12ac1\n", val); val = *((DWORD*)bm.bmBits + (bm.bmHeight-1) * bm.bmWidthBytes/4 + 1); - ok(val == 0xff00ff || broken(val == 0xff), "got %x, expected 0xff00ff\n", val); + ok(val == 0xff00ff || broken(val == 0xff), "got %lx, expected 0xff00ff\n", val); } hdc = CreateCompatibleDC(NULL); oldhbitmap = SelectObject(hdc, hbitmap); pixel = GetPixel(hdc, 5, 5); - ok(pixel == 0xc12ac1 || broken(pixel == 0xc12a2a), "got %x, expected 0xc12ac1\n", pixel); + ok(pixel == 0xc12ac1 || broken(pixel == 0xc12a2a), "got %lx, expected 0xc12ac1\n", pixel); pixel = GetPixel(hdc, 1, 0); - ok(pixel == 0xff00ff || broken(pixel == 0xff0000), "got %x, expected 0xff00ff\n", pixel); + ok(pixel == 0xff00ff || broken(pixel == 0xff0000), "got %lx, expected 0xff00ff\n", pixel); pixel = GetPixel(hdc, 2, 0); - ok(pixel == 0xb12ac1 || broken(pixel == 0xb12a2a), "got %x, expected 0xb12ac1\n", pixel); + ok(pixel == 0xb12ac1 || broken(pixel == 0xb12a2a), "got %lx, expected 0xb12ac1\n", pixel); SelectObject(hdc, oldhbitmap); DeleteDC(hdc); @@ -1850,20 +2245,20 @@ static void test_createhbitmap(void) if (bm.bmBits) { DWORD val = *(DWORD*)bm.bmBits; - ok(val == 0x68c12ac1 || broken(val == 0x682a2ac1), "got %x, expected 0x68c12ac1\n", val); + ok(val == 0x68c12ac1 || broken(val == 0x682a2ac1), "got %lx, expected 0x68c12ac1\n", val); val = *((DWORD*)bm.bmBits + (bm.bmHeight-1) * bm.bmWidthBytes/4 + 1); - ok(val == 0xff00ff || broken(val == 0xff), "got %x, expected 0xff00ff\n", val); + ok(val == 0xff00ff || broken(val == 0xff), "got %lx, expected 0xff00ff\n", val); } hdc = CreateCompatibleDC(NULL); oldhbitmap = SelectObject(hdc, hbitmap); pixel = GetPixel(hdc, 5, 5); - ok(pixel == 0xc12ac1 || broken(pixel == 0xc12a2a), "got %x, expected 0xc12ac1\n", pixel); + ok(pixel == 0xc12ac1 || broken(pixel == 0xc12a2a), "got %lx, expected 0xc12ac1\n", pixel); pixel = GetPixel(hdc, 1, 0); - ok(pixel == 0xff00ff || broken(pixel == 0xff0000), "got %x, expected 0xff00ff\n", pixel); + ok(pixel == 0xff00ff || broken(pixel == 0xff0000), "got %lx, expected 0xff00ff\n", pixel); pixel = GetPixel(hdc, 2, 0); - ok(pixel == 0xb12ac1 || broken(pixel == 0xb12a2a), "got %x, expected 0xb12ac1\n", pixel); + ok(pixel == 0xb12ac1 || broken(pixel == 0xb12a2a), "got %lx, expected 0xb12ac1\n", pixel); SelectObject(hdc, oldhbitmap); DeleteDC(hdc); @@ -2084,7 +2479,7 @@ static void check_halftone_palette(ColorPalette *palette) expected |= halftone_values[((i-40)/6)%6] << 8; expected |= halftone_values[((i-40)/36)%6] << 16; } - ok(expected == palette->Entries[i], "Expected %.8x, got %.8x, i=%u/%u\n", + ok(expected == palette->Entries[i], "Expected %.8lx, got %.8lx, i=%u/%u\n", expected, palette->Entries[i], i, palette->Count); } } @@ -2392,7 +2787,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xeeff40cc, color, 3), "expected 0xeeff40cc, got 0x%08x\n", color); + ok(color_match(0xeeff40cc, color, 3), "expected 0xeeff40cc, got 0x%08lx\n", color); /* Toggle NoOp */ stat = GdipSetImageAttributesNoOp(imageattr, ColorAdjustTypeDefault, FALSE); @@ -2404,7 +2799,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xfefe40cc, color, 3), "expected 0xfefe40cc, got 0x%08x\n", color); + ok(color_match(0xfefe40cc, color, 3), "expected 0xfefe40cc, got 0x%08lx\n", color); stat = GdipSetImageAttributesNoOp(imageattr, ColorAdjustTypeDefault, TRUE); expect(Ok, stat); @@ -2415,7 +2810,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); expect(Ok, stat); @@ -2429,7 +2824,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); stat = GdipDrawImageRectRectI(graphics, (GpImage *)bitmap1, 0, 0, 1, 1, 0, 0, 1, 1, UnitPixel, imageattr, NULL, NULL); @@ -2437,7 +2832,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 1), "Expected ff40ccee, got %.8x\n", color); + ok(color_match(0xff40ccee, color, 1), "Expected ff40ccee, got %.8lx\n", color); /* Disable adjustment, toggle NoOp */ stat = GdipSetImageAttributesColorMatrix(imageattr, ColorAdjustTypeDefault, @@ -2453,7 +2848,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); stat = GdipSetImageAttributesNoOp(imageattr, ColorAdjustTypeDefault, TRUE); expect(Ok, stat); @@ -2464,7 +2859,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); /* Reset with NoOp on, enable adjustment. */ stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); @@ -2480,7 +2875,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xfff24ace, color, 3), "expected 0xfff24ace, got 0x%08x\n", color); + ok(color_match(0xfff24ace, color, 3), "expected 0xfff24ace, got 0x%08lx\n", color); /* Now inhibit specific category. */ stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); @@ -2496,7 +2891,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xfffe41cc, color, 3), "expected 0xfffe41cc, got 0x%08x\n", color); + ok(color_match(0xfffe41cc, color, 3), "expected 0xfffe41cc, got 0x%08lx\n", color); stat = GdipSetImageAttributesNoOp(imageattr, ColorAdjustTypeBitmap, TRUE); expect(Ok, stat); @@ -2507,7 +2902,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); stat = GdipSetImageAttributesNoOp(imageattr, ColorAdjustTypeBitmap, FALSE); expect(Ok, stat); @@ -2521,7 +2916,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xfff24ace, color, 3), "expected 0xfff24ace, got 0x%08x\n", color); + ok(color_match(0xfff24ace, color, 3), "expected 0xfff24ace, got 0x%08lx\n", color); stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeBitmap); expect(Ok, stat); @@ -2532,7 +2927,7 @@ static void test_colormatrix(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08x\n", color); + ok(color_match(0xff40ccee, color, 3), "expected 0xff40ccee, got 0x%08lx\n", color); GdipDeleteGraphics(graphics); GdipDisposeImage((GpImage*)bitmap1); @@ -2594,7 +2989,7 @@ static void test_gamma(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff20ffff, color, 1), "Expected ff20ffff, got %.8x\n", color); + ok(color_match(0xff20ffff, color, 1), "Expected ff20ffff, got %.8lx\n", color); stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); expect(Ok, stat); @@ -2605,7 +3000,7 @@ static void test_gamma(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff80ffff, color, 1), "Expected ff80ffff, got %.8x\n", color); + ok(color_match(0xff80ffff, color, 1), "Expected ff80ffff, got %.8lx\n", color); GdipDeleteGraphics(graphics); GdipDisposeImage((GpImage*)bitmap1); @@ -2629,10 +3024,11 @@ static const unsigned char gifanimation[72] = { * -dispose previous -page +4+0 -size 2x2 xc:green \ * -dispose undefined -page +6+0 -size 2x2 xc:gray \ * test.gif + * Background color index changed to 1. */ static const unsigned char gifanimation2[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x08, 0x00, - 0x02, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x64, 0x00, 0x00, 0x00, 0x21, 0xff, 0x0b, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, @@ -2667,6 +3063,43 @@ static ARGB gifanimation2_pixels[5][4] = { {0xffff0000, 0, 0, 0xff7e7e7e} }; +static const unsigned char gifanimation3[] = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x08, 0x00, + 0x02, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x64, + 0x00, 0x00, 0x00, 0x21, 0xff, 0x0b, 0x4e, 0x45, + 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, + 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x04, 0x84, 0x8f, 0x09, 0x05, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x64, 0x00, 0x10, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x81, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x02, 0x03, 0x44, + 0x34, 0x05, 0x00, 0x21, 0xf9, 0x04, 0x09, 0x64, + 0x00, 0x10, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x02, 0x03, 0x44, 0x34, 0x05, 0x00, 0x21, + 0xf9, 0x04, 0x0d, 0x64, 0x00, 0x10, 0x00, 0x2c, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x81, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x02, 0x03, 0x44, + 0x34, 0x05, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x64, + 0x00, 0x10, 0x00, 0x2c, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x80, 0x7e, 0x7e, 0x7e, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x84, 0x51, 0x00, + 0x3b +}; + +static ARGB gifanimation3_pixels[5][4] = { + {0xff000000, 0xff000000, 0xff000000, 0xff000000}, + {0xffff0000, 0xff000000, 0xff000000, 0xff000000}, + {0xffff0000, 0xff0000ff, 0xff000000, 0xff000000}, + {0xffff0000, 0xff000000, 0xff008000, 0xff000000}, + {0xffff0000, 0xff000000, 0xff000000, 0xff7e7e7e} +}; + static void test_multiframegif(void) { LPSTREAM stream; @@ -2896,7 +3329,7 @@ static void test_multiframegif(void) expect(Ok, stat); stat = GdipBitmapGetPixel(bmp, 2, 0, &color); expect(Ok, stat); - ok(color==0 || broken(color==0xff0000ff), "color = %x\n", color); + ok(color==0 || broken(color==0xff0000ff), "color = %lx\n", color); if(color != 0) { win_skip("broken animated gif support\n"); GdipDisposeImage((GpImage*)bmp); @@ -2910,7 +3343,32 @@ static void test_multiframegif(void) for(j=0; j<4; j++) { stat = GdipBitmapGetPixel(bmp, j*2, 0, &color); expect(Ok, stat); - ok(gifanimation2_pixels[i%5][j] == color, "at %d,%d got %x, expected %x\n", i, j, color, gifanimation2_pixels[i%5][j]); + ok(gifanimation2_pixels[i%5][j] == color, "at %d,%d got %lx, expected %lx\n", i, j, color, gifanimation2_pixels[i%5][j]); + } + } + + GdipDisposeImage((GpImage*)bmp); + + hglob = GlobalAlloc (0, sizeof(gifanimation3)); + data = GlobalLock (hglob); + memcpy(data, gifanimation3, sizeof(gifanimation3)); + GlobalUnlock(hglob); + + hres = CreateStreamOnHGlobal(hglob, TRUE, &stream); + ok(hres == S_OK, "Failed to create a stream\n"); + + stat = GdipCreateBitmapFromStream(stream, &bmp); + ok(stat == Ok, "Failed to create a Bitmap\n"); + IStream_Release(stream); + + for(i=0; i<6; i++) { + stat = GdipImageSelectActiveFrame((GpImage*)bmp, &dimension, i%5); + expect(Ok, stat); + + for(j=0; j<4; j++) { + stat = GdipBitmapGetPixel(bmp, j*2, 0, &color); + expect(Ok, stat); + ok(gifanimation3_pixels[i%5][j] == color, "at %d,%d got %lx, expected %lx\n", i, j, color, gifanimation3_pixels[i%5][j]); } } @@ -3093,7 +3551,7 @@ static void test_remaptable(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xffff00ff, color, 1), "Expected ffff00ff, got %.8x\n", color); + ok(color_match(0xffff00ff, color, 1), "Expected ffff00ff, got %.8lx\n", color); stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); expect(Ok, stat); @@ -3104,7 +3562,7 @@ static void test_remaptable(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0xff00ff00, color, 1), "Expected ff00ff00, got %.8x\n", color); + ok(color_match(0xff00ff00, color, 1), "Expected ff00ff00, got %.8lx\n", color); GdipDeleteGraphics(graphics); GdipDisposeImage((GpImage*)bitmap1); @@ -3163,19 +3621,19 @@ static void test_colorkey(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8x\n", color); + ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 0, 1, &color); expect(Ok, stat); - ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8x\n", color); + ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 1, 0, &color); expect(Ok, stat); - ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8x\n", color); + ok(color_match(0x00000000, color, 1), "Expected 00000000, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 1, 1, &color); expect(Ok, stat); - ok(color_match(0xffffffff, color, 1), "Expected ffffffff, got %.8x\n", color); + ok(color_match(0xffffffff, color, 1), "Expected ffffffff, got %.8lx\n", color); stat = GdipResetImageAttributes(imageattr, ColorAdjustTypeDefault); expect(Ok, stat); @@ -3186,19 +3644,19 @@ static void test_colorkey(void) stat = GdipBitmapGetPixel(bitmap2, 0, 0, &color); expect(Ok, stat); - ok(color_match(0x20405060, color, 1), "Expected 20405060, got %.8x\n", color); + ok(color_match(0x20405060, color, 1), "Expected 20405060, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 0, 1, &color); expect(Ok, stat); - ok(color_match(0x40506070, color, 1), "Expected 40506070, got %.8x\n", color); + ok(color_match(0x40506070, color, 1), "Expected 40506070, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 1, 0, &color); expect(Ok, stat); - ok(color_match(0x60708090, color, 1), "Expected 60708090, got %.8x\n", color); + ok(color_match(0x60708090, color, 1), "Expected 60708090, got %.8lx\n", color); stat = GdipBitmapGetPixel(bitmap2, 1, 1, &color); expect(Ok, stat); - ok(color_match(0xffffffff, color, 1), "Expected ffffffff, got %.8x\n", color); + ok(color_match(0xffffffff, color, 1), "Expected ffffffff, got %.8lx\n", color); GdipDeleteGraphics(graphics); @@ -3222,8 +3680,11 @@ static void test_dispose(void) stat = GdipDisposeImage(image); expect(Ok, stat); + if (0) { + /* Can crash with page heap or if the heap region is decommitted. */ stat = GdipDisposeImage(image); expect(ObjectBusy, stat); + } memset(invalid_image, 0, 256); stat = GdipDisposeImage((GpImage*)invalid_image); @@ -3253,11 +3714,11 @@ static GpImage *load_image(const BYTE *image_data, UINT image_size, BOOL valid_d GlobalUnlock(hmem); hr = CreateStreamOnHGlobal(hmem, TRUE, &stream); - ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr); + ok(hr == S_OK, "CreateStreamOnHGlobal error %#lx\n", hr); if (hr != S_OK) return NULL; refcount = obj_refcount(stream); - ok(refcount == 1, "expected stream refcount 1, got %d\n", refcount); + ok(refcount == 1, "expected stream refcount 1, got %ld\n", refcount); status = GdipLoadImageFromStream(stream, &image); todo_wine_if(todo_load) @@ -3277,29 +3738,183 @@ static GpImage *load_image(const BYTE *image_data, UINT image_size, BOOL valid_d refcount = obj_refcount(stream); if (image_type == ImageTypeBitmap) - ok(refcount > 1, "expected stream refcount > 1, got %d\n", refcount); + ok(refcount > 1, "expected stream refcount > 1, got %ld\n", refcount); else - ok(refcount == 1, "expected stream refcount 1, got %d\n", refcount); + ok(refcount == 1, "expected stream refcount 1, got %ld\n", refcount); old_refcount = refcount; status = GdipCloneImage(image, &clone); ok(status == Ok, "GdipCloneImage error %d\n", status); refcount = obj_refcount(stream); - ok(refcount == old_refcount, "expected stream refcount %d, got %d\n", old_refcount, refcount); + ok(refcount == old_refcount, "expected stream refcount %ld, got %ld\n", old_refcount, refcount); status = GdipDisposeImage(clone); ok(status == Ok, "GdipDisposeImage error %d\n", status); refcount = obj_refcount(stream); - ok(refcount == old_refcount, "expected stream refcount %d, got %d\n", old_refcount, refcount); + ok(refcount == old_refcount, "expected stream refcount %ld, got %ld\n", old_refcount, refcount); refcount = IStream_Release(stream); if (image_type == ImageTypeBitmap) ok(refcount >= 1, "expected stream refcount != 0\n"); else - ok(refcount == 0, "expected stream refcount 0, got %d\n", refcount); + ok(refcount == 0, "expected stream refcount 0, got %ld\n", refcount); return image; } +struct property_test_data +{ + ULONG type, id, length; + const BYTE value[32]; + BOOL broken_length; + BOOL broken_data; +}; + +#ifndef PropertyTagTypeSByte +#define PropertyTagTypeSByte 6 +#define PropertyTagTypeSShort 8 +#define PropertyTagTypeFloat 11 +#define PropertyTagTypeDouble 12 +#endif + +static UINT documented_type(UINT type) +{ + /* Win7 stopped using proper but not documented types, and it + looks broken since TypeFloat and TypeDouble now reported as + TypeUndefined, and signed types reported as unsigned. */ + switch (type) + { + case PropertyTagTypeSByte: return PropertyTagTypeByte; + case PropertyTagTypeSShort: return PropertyTagTypeShort; + case PropertyTagTypeFloat: return PropertyTagTypeUndefined; + case PropertyTagTypeDouble: return PropertyTagTypeUndefined; + default: return type; + } +} + +static void check_properties_id_list(GpImage *image, const struct property_test_data *td, UINT count, + const struct property_test_data *td_broken, UINT count_broken, UINT *prop_size) +{ + GpStatus status; + UINT prop_count, size, i; + PROPID *prop_id; + PropertyItem *prop_item; + + prop_count = 0xdeadbeef; + status = GdipGetPropertyCount(image, &prop_count); + expect(Ok, status); + ok(count == prop_count || broken(count_broken != ~0 && count_broken == prop_count), + "expected property count %u, got %u\n", count, prop_count); + if (count_broken != ~0 && count_broken == prop_count) + td = td_broken; + + prop_id = malloc(prop_count * sizeof(*prop_id)); + + status = GdipGetPropertyIdList(image, prop_count, prop_id); + expect(Ok, status); + + if (prop_size) + *prop_size = 0; + + for (i = 0; i < prop_count; i++) + { + winetest_push_context("prop %u", i); + + status = GdipGetPropertyItemSize(image, prop_id[i], &size); + expect(Ok, status); + if (status != Ok) + { + winetest_pop_context(); + break; + } + ok(size > sizeof(*prop_item), "too small item length %u\n", size); + + if (prop_size) + *prop_size += size; + + prop_item = calloc(1, size); + status = GdipGetPropertyItem(image, prop_id[i], size, prop_item); + size -= sizeof(*prop_item); + expect(Ok, status); + + ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", + prop_item + 1, prop_item->value); + ok(td[i].type == prop_item->type || + broken(documented_type(td[i].type) == prop_item->type), + "expected type %lu, got %u\n", td[i].type, prop_item->type); + ok(td[i].id == prop_item->id, "expected id %#lx, got %#lx\n", td[i].id, prop_item->id); + ok(prop_item->length == size, "expected length %u, got %lu\n", size, prop_item->length); + ok(td[i].length == prop_item->length || broken(td[i].broken_length), + "expected length %lu, got %lu\n", td[i].length, prop_item->length); + ok(td[i].length == size || broken(td[i].broken_length), + "expected length %lu, got %u\n", td[i].length, size); + if (td[i].length == prop_item->length) + { + int match = !memcmp(td[i].value, prop_item->value, td[i].length); + ok(match || broken(td[i].broken_data), "data mismatch\n"); + if (!match) + trace("(id %#lx) %s\n", prop_item->id, dbgstr_hexdata(prop_item->value, prop_item->length)); + } + + free(prop_item); + + winetest_pop_context(); + } + + free(prop_id); +} + +static void check_properties_get_all(GpImage *image, const struct property_test_data *td, UINT count, + const struct property_test_data *td_broken, UINT count_broken, UINT prop_size) +{ + GpStatus status; + UINT total_count, total_size, i; + PropertyItem *prop_item; + const BYTE *item_data; + + total_size = 0xdeadbeef; + total_count = 0xdeadbeef; + status = GdipGetPropertySize(image, &total_size, &total_count); + expect(Ok, status); + ok(prop_size == total_size || prop_size == ~0, + "expected total property size %u, got %u\n", prop_size, total_size); + ok(count == total_count || broken(count_broken != ~0 && count_broken == total_count), + "expected total property count %u, got %u\n", count, total_count); + if (count_broken != ~0 && count_broken == total_count) + td = td_broken; + + prop_item = malloc(total_size); + status = GdipGetAllPropertyItems(image, total_size, total_count, prop_item); + expect(Ok, status); + + item_data = (const BYTE *)(prop_item + total_count); + for (i = 0; i < total_count && i < count; i++) + { + winetest_push_context("prop %u", i); + + ok(prop_item[i].value == item_data, + "expected value %p, got %p\n", item_data, prop_item[i].value); + ok(td[i].type == prop_item[i].type || + broken(documented_type(td[i].type) == prop_item[i].type), + "expected type %lu, got %u\n", td[i].type, prop_item[i].type); + ok(td[i].id == prop_item[i].id, + "expected id %#lx, got %#lx\n", td[i].id, prop_item[i].id); + ok(td[i].length == prop_item[i].length || broken(td[i].broken_length), + "expected length %lu, got %lu\n", td[i].length, prop_item[i].length); + if (td[i].length == prop_item[i].length) + { + int match = !memcmp(td[i].value, prop_item[i].value, td[i].length); + ok(match || broken(td[i].broken_data), "data mismatch\n"); + if (!match) + trace("(id %#lx) %s\n", prop_item[i].id, dbgstr_hexdata(prop_item[i].value, prop_item[i].length)); + } + item_data += prop_item[i].length; + + winetest_pop_context(); + } + + free(prop_item); +} + static void test_image_properties(void) { static const struct test_data @@ -3308,26 +3923,30 @@ static void test_image_properties(void) UINT image_size; ImageType image_type; UINT prop_count; - UINT prop_count2; /* if win7 behaves differently */ + UINT prop_count2; /* if win7+ behaves differently, else ~0 */ /* 1st property attributes */ UINT prop_size; - UINT prop_size2; /* if win7 behaves differently */ + UINT prop_size2; /* if win7+ behaves differently, else ~0 */ UINT prop_id; - UINT prop_id2; /* if win7 behaves differently */ + UINT prop_id2; /* if win7+ behaves differently, else ~0 */ + INT palette_size; } td[] = { - { pngimage, sizeof(pngimage), ImageTypeBitmap, 4, ~0, 1, 20, 0x5110, 0x132 }, - { jpgimage, sizeof(jpgimage), ImageTypeBitmap, 2, ~0, 128, 0, 0x5090, 0x5091 }, - { tiffimage, sizeof(tiffimage), ImageTypeBitmap, 16, 0, 4, 0, 0xfe, 0 }, - { bmpimage, sizeof(bmpimage), ImageTypeBitmap, 0, 0, 0, 0, 0, 0 }, - { wmfimage, sizeof(wmfimage), ImageTypeMetafile, 0, 0, 0, 0, 0, 0 } + { pngimage, sizeof(pngimage), ImageTypeBitmap, 4, ~0, 1, 20, 0x5110, 0x132, 12 }, + { jpgimage, sizeof(jpgimage), ImageTypeBitmap, 2, ~0, 128, ~0, 0x5090, 0x5091, 12 }, + { tiffimage, sizeof(tiffimage), ImageTypeBitmap, 16, ~0, 4, ~0, 0xfe, ~0, 12 }, + { bmpimage, sizeof(bmpimage), ImageTypeBitmap, 0, ~0, 0, ~0, 0, ~0, 16 }, + { gifimage, sizeof(gifimage), ImageTypeBitmap, 1, 4, 4, ~0, 0x5100, ~0, 16 }, + { wmfimage, sizeof(wmfimage), ImageTypeMetafile, 0, ~0, 0, ~0, 0, ~0, -GenericError } }; - GpStatus status; + GpStatus status, expected; GpImage *image; + PropertyItem *prop_item; UINT prop_count, prop_size, i; PROPID prop_id[16] = { 0 }; ImageType image_type; + INT palette_size; union { PropertyItem data; @@ -3336,24 +3955,42 @@ static void test_image_properties(void) for (i = 0; i < ARRAY_SIZE(td); i++) { + winetest_push_context("%u", i); + image = load_image(td[i].image_data, td[i].image_size, TRUE, FALSE); if (!image) { - trace("%u: failed to load image data\n", i); + trace("failed to load image data\n"); + winetest_pop_context(); continue; } status = GdipGetImageType(image, &image_type); - ok(status == Ok, "%u: GdipGetImageType error %d\n", i, status); - ok(td[i].image_type == image_type, "%u: expected image_type %d, got %d\n", - i, td[i].image_type, image_type); + ok(status == Ok, "GdipGetImageType error %d\n", status); + ok(td[i].image_type == image_type, "expected image_type %d, got %d\n", + td[i].image_type, image_type); + + palette_size = -1; + status = GdipGetImagePaletteSize(image, &palette_size); + if (td[i].palette_size >= 0) + { + ok(status == Ok, "GdipGetImagePaletteSize error %d\n", status); + ok(td[i].palette_size == palette_size, "expected palette_size %d, got %d\n", + td[i].palette_size, palette_size); + } + else + { + ok(status == -td[i].palette_size, "GdipGetImagePaletteSize returned %d\n", status); + ok(palette_size == 0, "expected palette_size 0, got %d\n", + palette_size); + } status = GdipGetPropertyCount(image, &prop_count); - ok(status == Ok, "%u: GdipGetPropertyCount error %d\n", i, status); - todo_wine_if(td[i].image_data == pngimage || td[i].image_data == jpgimage) - ok(td[i].prop_count == prop_count || td[i].prop_count2 == prop_count, - " %u: expected property count %u or %u, got %u\n", - i, td[i].prop_count, td[i].prop_count2, prop_count); + ok(status == Ok, "GdipGetPropertyCount error %d\n", status); + todo_wine_if(td[i].image_data == jpgimage) + ok(td[i].prop_count == prop_count || (td[i].prop_count2 != ~0 && td[i].prop_count2 == prop_count), + "expected property count %u or %u, got %u\n", + td[i].prop_count, td[i].prop_count2, prop_count); status = GdipGetPropertyItemSize(NULL, 0, &prop_size); expect(InvalidParameter, status); @@ -3375,80 +4012,100 @@ static void test_image_properties(void) else expect(PropertyNotFound, status); - /* FIXME: remove once Wine is fixed */ - if (td[i].prop_count != prop_count) - { - GdipDisposeImage(image); - continue; - } - status = GdipGetPropertyIdList(NULL, prop_count, prop_id); expect(InvalidParameter, status); status = GdipGetPropertyIdList(image, prop_count, NULL); expect(InvalidParameter, status); + expected = (image_type == ImageTypeMetafile) ? NotImplemented : InvalidParameter; + status = GdipGetPropertyIdList(image, prop_count - 1, prop_id); + expect(expected, status); + status = GdipGetPropertyIdList(image, prop_count + 1, prop_id); + expect(expected, status); + if (image_type != ImageTypeMetafile && prop_count == 0) + expected = Ok; status = GdipGetPropertyIdList(image, 0, prop_id); + expect(expected, status); + expected = (image_type == ImageTypeMetafile) ? NotImplemented : Ok; + status = GdipGetPropertyIdList(image, prop_count, prop_id); + expect(expected, status); + + status = GdipGetPropertyItemSize(image, prop_id[0], &prop_size); if (image_type == ImageTypeMetafile) expect(NotImplemented, status); else if (prop_count == 0) - expect(Ok, status); - else - expect(InvalidParameter, status); - status = GdipGetPropertyIdList(image, prop_count - 1, prop_id); - if (image_type == ImageTypeMetafile) - expect(NotImplemented, status); - else - expect(InvalidParameter, status); - status = GdipGetPropertyIdList(image, prop_count + 1, prop_id); - if (image_type == ImageTypeMetafile) - expect(NotImplemented, status); - else - expect(InvalidParameter, status); - status = GdipGetPropertyIdList(image, prop_count, prop_id); - if (image_type == ImageTypeMetafile) - expect(NotImplemented, status); - else + expect(PropertyNotFound, status); + /* FIXME: remove condition once Wine is fixed, i.e. this should just be an else */ + else if (td[i].prop_count == prop_count || (td[i].prop_count2 != ~0 && td[i].prop_count2 == prop_count)) { + ok(td[i].prop_id == prop_id[0] || (td[i].prop_id2 != ~0 && td[i].prop_id2 == prop_id[0]), + "expected property id %#x or %#x, got %#lx\n", + td[i].prop_id, td[i].prop_id2, prop_id[0]); + expect(Ok, status); - if (prop_count != 0) - ok(td[i].prop_id == prop_id[0] || td[i].prop_id2 == prop_id[0], - " %u: expected property id %#x or %#x, got %#x\n", - i, td[i].prop_id, td[i].prop_id2, prop_id[0]); + + assert(sizeof(item) >= prop_size); + ok(prop_size > sizeof(PropertyItem), "got too small prop_size %u\n", + prop_size); + ok(td[i].prop_size + sizeof(PropertyItem) == prop_size || + (td[i].prop_size2 != ~0 && td[i].prop_size2 + sizeof(PropertyItem) == prop_size), + "expected property size (%u or %u)+%u, got %u\n", + td[i].prop_size, td[i].prop_size2, (UINT) sizeof(PropertyItem), prop_size); + + status = GdipGetPropertyItem(image, prop_id[0], 0, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "expected InvalidParameter, got %d\n", status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size - 1, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "expected InvalidParameter, got %d\n", status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size + 1, &item.data); + ok(status == InvalidParameter || status == GenericError /* Win7 */, + "expected InvalidParameter, got %d\n", status); + status = GdipGetPropertyItem(image, prop_id[0], prop_size, &item.data); + expect(Ok, status); + ok(prop_id[0] == item.data.id, + "expected property id %#lx, got %#lx\n", prop_id[0], item.data.id); } - if (status == Ok) + status = GdipGetPropertySize(NULL, &prop_size, &prop_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, &prop_size, NULL); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, &prop_count); + expect(InvalidParameter, status); + status = GdipGetPropertySize(image, NULL, NULL); + expect(InvalidParameter, status); + expected = (image_type == ImageTypeMetafile) ? NotImplemented : Ok; + status = GdipGetPropertySize(image, &prop_size, &prop_count); + expect(expected, status); + + status = GdipGetAllPropertyItems(image, 0, 0, NULL); + expect(InvalidParameter, status); + status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); + expect(InvalidParameter, status); + prop_item = malloc(prop_size); + expected = (image_type == ImageTypeMetafile) ? NotImplemented : InvalidParameter; + if (prop_count != 1) { - status = GdipGetPropertyItemSize(image, prop_id[0], &prop_size); - if (prop_count == 0) - expect(PropertyNotFound, status); - else - { - expect(Ok, status); - - assert(sizeof(item) >= prop_size); - ok(prop_size > sizeof(PropertyItem), "%u: got too small prop_size %u\n", - i, prop_size); - ok(td[i].prop_size + sizeof(PropertyItem) == prop_size || - td[i].prop_size2 + sizeof(PropertyItem) == prop_size, - " %u: expected property size %u or %u, got %u\n", - i, td[i].prop_size, td[i].prop_size2, prop_size); - - status = GdipGetPropertyItem(image, prop_id[0], 0, &item.data); - ok(status == InvalidParameter || status == GenericError /* Win7 */, - "%u: expected InvalidParameter, got %d\n", i, status); - status = GdipGetPropertyItem(image, prop_id[0], prop_size - 1, &item.data); - ok(status == InvalidParameter || status == GenericError /* Win7 */, - "%u: expected InvalidParameter, got %d\n", i, status); - status = GdipGetPropertyItem(image, prop_id[0], prop_size + 1, &item.data); - ok(status == InvalidParameter || status == GenericError /* Win7 */, - "%u: expected InvalidParameter, got %d\n", i, status); - status = GdipGetPropertyItem(image, prop_id[0], prop_size, &item.data); - expect(Ok, status); - ok(prop_id[0] == item.data.id, - "%u: expected property id %#x, got %#x\n", i, prop_id[0], item.data.id); - } + status = GdipGetAllPropertyItems(image, prop_size, 1, prop_item); + expect(expected, status); } + if (prop_size != 0) + { + status = GdipGetAllPropertyItems(image, 0, prop_count, prop_item); + expect(expected, status); + } + status = GdipGetAllPropertyItems(image, prop_size + 1, prop_count, prop_item); + expect(expected, status); + if (image_type != ImageTypeMetafile) + expected = (prop_count == 0) ? GenericError : Ok; + status = GdipGetAllPropertyItems(image, prop_size, prop_count, prop_item); + ok(status == expected || broken(status == Ok && prop_count == 0), /* XP */ + "Expected %d, got %d\n", expected, status); + free(prop_item); GdipDisposeImage(image); + + winetest_pop_context(); } } @@ -3465,25 +4122,6 @@ static void test_image_properties(void) #define IFD_FLOAT 11 #define IFD_DOUBLE 12 -#ifndef PropertyTagTypeSByte -#define PropertyTagTypeSByte 6 -#define PropertyTagTypeSShort 8 -#define PropertyTagTypeFloat 11 -#define PropertyTagTypeDouble 12 -#endif - -static UINT documented_type(UINT type) -{ - switch (type) - { - case PropertyTagTypeSByte: return PropertyTagTypeByte; - case PropertyTagTypeSShort: return PropertyTagTypeShort; - case PropertyTagTypeFloat: return PropertyTagTypeUndefined; - case PropertyTagTypeDouble: return PropertyTagTypeUndefined; - default: return type; - } -} - #include "pshpack2.h" struct IFD_entry { @@ -3580,11 +4218,7 @@ static const struct tiff_data static void test_tiff_properties(void) { - static const struct test_data - { - ULONG type, id, length; - const BYTE value[24]; - } td[31] = + static const struct property_test_data td[31] = { { PropertyTagTypeShort, 0xff, 2, { 0 } }, { PropertyTagTypeLong, 0x100, 4, { 1 } }, @@ -3601,20 +4235,20 @@ static void test_tiff_properties(void) { PropertyTagTypeShort, 0x128, 2, { 2 } }, { PropertyTagTypeByte, 0xf001, 1, { 0x44 } }, { PropertyTagTypeByte, 0xf002, 4, { 0x44,0x33,0x22,0x11 } }, - { PropertyTagTypeSByte, 0xf003, 1, { 0x44 } }, - { PropertyTagTypeSShort, 0xf004, 2, { 0x44,0x33 } }, - { PropertyTagTypeSShort, 0xf005, 4, { 0x44,0x33,0x22,0x11 } }, - { PropertyTagTypeSLONG, 0xf006, 4, { 0x44,0x33,0x22,0x11 } }, - { PropertyTagTypeFloat, 0xf007, 4, { 0x44,0x33,0x22,0x11 } }, + { PropertyTagTypeSByte, 0xf003, 1, { 0x44 }, FALSE, TRUE }, + { PropertyTagTypeSShort, 0xf004, 2, { 0x44,0x33 }, FALSE, TRUE }, + { PropertyTagTypeSShort, 0xf005, 4, { 0x44,0x33,0x22,0x11 }, FALSE, TRUE }, + { PropertyTagTypeSLONG, 0xf006, 4, { 0x44,0x33,0x22,0x11 }, FALSE, TRUE }, + { PropertyTagTypeFloat, 0xf007, 4, { 0x44,0x33,0x22,0x11 }, FALSE, TRUE }, { PropertyTagTypeDouble, 0xf008, 8, { 0x2c,0x52,0x86,0xb4,0x80,0x65,0xd2,0x41 } }, { PropertyTagTypeSRational, 0xf009, 8, { 0x4d, 0x3c, 0x2b, 0x1a, 0x8d, 0x7c, 0x6b, 0x5a } }, - { PropertyTagTypeByte, 0xf00a, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeByte, 0xf00a, 13, "Hello World!" }, { PropertyTagTypeSShort, 0xf00b, 8, { 0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04 } }, { PropertyTagTypeSLONG, 0xf00c, 8, { 0x44,0x33,0x22,0x11,0x88,0x77,0x66,0x55 } }, - { PropertyTagTypeASCII, 0xf00e, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, - { PropertyTagTypeASCII, 0xf00f, 5, { 'a','b','c','d' } }, - { PropertyTagTypeUndefined, 0xf010, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, - { PropertyTagTypeUndefined, 0xf011, 4, { 'a','b','c','d' } }, + { PropertyTagTypeASCII, 0xf00e, 13, "Hello World!" }, + { PropertyTagTypeASCII, 0xf00f, 5, "abcd", TRUE }, + { PropertyTagTypeUndefined, 0xf010, 13, "Hello World!" }, + { PropertyTagTypeUndefined, 0xf011, 4, { 'a','b','c','d' }, FALSE, TRUE }, { PropertyTagTypeSRational, 0xf016, 24, { 0x04,0x03,0x02,0x01,0x08,0x07,0x06,0x05, 0x40,0x30,0x20,0x10,0x80,0x70,0x60,0x50, @@ -3625,9 +4259,7 @@ static void test_tiff_properties(void) GpStatus status; GpImage *image; GUID guid; - UINT dim_count, frame_count, prop_count, prop_size, i; - PROPID *prop_id; - PropertyItem *prop_item; + UINT dim_count, frame_count; image = load_image((const BYTE *)&TIFF_data, sizeof(TIFF_data), TRUE, FALSE); if (!image) @@ -3649,71 +4281,16 @@ static void test_tiff_properties(void) expect(Ok, status); expect(1, frame_count); - prop_count = 0xdeadbeef; - status = GdipGetPropertyCount(image, &prop_count); - expect(Ok, status); - ok(prop_count == ARRAY_SIZE(td) || - broken(prop_count == ARRAY_SIZE(td) - 1) /* Win7 SP0 */, - "expected property count %u, got %u\n", (UINT) ARRAY_SIZE(td), prop_count); - - prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); - - status = GdipGetPropertyIdList(image, prop_count, prop_id); - expect(Ok, status); - - for (i = 0; i < prop_count; i++) - { - status = GdipGetPropertyItemSize(image, prop_id[i], &prop_size); - expect(Ok, status); - if (status != Ok) break; - ok(prop_size > sizeof(*prop_item), "%u: too small item length %u\n", i, prop_size); - - prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, prop_size); - status = GdipGetPropertyItem(image, prop_id[i], prop_size, prop_item); - expect(Ok, status); - ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); - ok(td[i].type == prop_item->type || - /* Win7 stopped using proper but not documented types, and it - looks broken since TypeFloat and TypeDouble now reported as - TypeUndefined, and signed types reported as unsigned. */ - broken(prop_item->type == documented_type(td[i].type)), - "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); - ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); - prop_size -= sizeof(*prop_item); - ok(prop_item->length == prop_size, "%u: expected length %u, got %u\n", i, prop_size, prop_item->length); - ok(td[i].length == prop_item->length || broken(td[i].id == 0xf00f && td[i].length == prop_item->length+1) /* XP */, - "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); - ok(td[i].length == prop_size || broken(td[i].id == 0xf00f && td[i].length == prop_size+1) /* XP */, - "%u: expected length %u, got %u\n", i, td[i].length, prop_size); - if (td[i].length == prop_item->length) - { - int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; - ok(match || broken(td[i].length <= 4 && !match), "%u: data mismatch\n", i); - if (!match) - { - UINT j; - BYTE *data = prop_item->value; - trace("id %#x:", prop_item->id); - for (j = 0; j < prop_item->length; j++) - trace(" %02x", data[j]); - trace("\n"); - } - } - HeapFree(GetProcessHeap(), 0, prop_item); - } - - HeapFree(GetProcessHeap(), 0, prop_id); + winetest_push_context("%s", __FUNCTION__); + check_properties_id_list(image, td, ARRAY_SIZE(td), td, ARRAY_SIZE(td) - 1 /* Win7 SP0 */, NULL); + winetest_pop_context(); GdipDisposeImage(image); } static void test_GdipGetAllPropertyItems(void) { - static const struct test_data - { - ULONG type, id, length; - BYTE value[32]; - } td[16] = + static const struct property_test_data td[16] = { { PropertyTagTypeLong, 0xfe, 4, { 0 } }, { PropertyTagTypeShort, 0x100, 2, { 1 } }, @@ -3735,11 +4312,7 @@ static void test_GdipGetAllPropertyItems(void) GpStatus status; GpImage *image; GUID guid; - UINT dim_count, frame_count, prop_count, prop_size, i; - UINT total_size, total_count; - PROPID *prop_id; - PropertyItem *prop_item; - const char *item_data; + UINT dim_count, frame_count, prop_size; image = load_image(tiffimage, sizeof(tiffimage), TRUE, FALSE); ok(image != 0, "Failed to load TIFF image data\n"); @@ -3759,118 +4332,10 @@ static void test_GdipGetAllPropertyItems(void) expect(Ok, status); expect(1, frame_count); - prop_count = 0xdeadbeef; - status = GdipGetPropertyCount(image, &prop_count); - expect(Ok, status); - ok(prop_count == ARRAY_SIZE(td), - "expected property count %u, got %u\n", (UINT) ARRAY_SIZE(td), prop_count); - - prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); - - status = GdipGetPropertyIdList(image, prop_count, prop_id); - expect(Ok, status); - - prop_size = 0; - for (i = 0; i < prop_count; i++) - { - UINT size; - status = GdipGetPropertyItemSize(image, prop_id[i], &size); - expect(Ok, status); - if (status != Ok) break; - ok(size > sizeof(*prop_item), "%u: too small item length %u\n", i, size); - - prop_size += size; - - prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); - status = GdipGetPropertyItem(image, prop_id[i], size, prop_item); - expect(Ok, status); - ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); - ok(td[i].type == prop_item->type, - "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); - ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); - size -= sizeof(*prop_item); - ok(prop_item->length == size, "%u: expected length %u, got %u\n", i, size, prop_item->length); - ok(td[i].length == prop_item->length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); - if (td[i].length == prop_item->length) - { - int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; - ok(match, "%u: data mismatch\n", i); - if (!match) - { - UINT j; - BYTE *data = prop_item->value; - trace("id %#x:", prop_item->id); - for (j = 0; j < prop_item->length; j++) - trace(" %02x", data[j]); - trace("\n"); - } - } - HeapFree(GetProcessHeap(), 0, prop_item); - } - - HeapFree(GetProcessHeap(), 0, prop_id); - - status = GdipGetPropertySize(NULL, &total_size, &total_count); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, &total_size, NULL); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, NULL, &total_count); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, NULL, NULL); - expect(InvalidParameter, status); - total_size = 0xdeadbeef; - total_count = 0xdeadbeef; - status = GdipGetPropertySize(image, &total_size, &total_count); - expect(Ok, status); - ok(prop_count == total_count, - "expected total property count %u, got %u\n", prop_count, total_count); - ok(prop_size == total_size, - "expected total property size %u, got %u\n", prop_size, total_size); - - prop_item = HeapAlloc(GetProcessHeap(), 0, prop_size); - - status = GdipGetAllPropertyItems(image, 0, prop_count, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, 1, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, 0, 0, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size + 1, prop_count, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, prop_item); - expect(Ok, status); - - item_data = (const char *)(prop_item + prop_count); - for (i = 0; i < prop_count; i++) - { - ok(prop_item[i].value == item_data, "%u: expected value %p, got %p\n", - i, item_data, prop_item[i].value); - ok(td[i].type == prop_item[i].type, - "%u: expected type %u, got %u\n", i, td[i].type, prop_item[i].type); - ok(td[i].id == prop_item[i].id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item[i].id); - ok(td[i].length == prop_item[i].length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item[i].length); - if (td[i].length == prop_item[i].length) - { - int match = memcmp(td[i].value, prop_item[i].value, td[i].length) == 0; - ok(match, "%u: data mismatch\n", i); - if (!match) - { - UINT j; - BYTE *data = prop_item[i].value; - trace("id %#x:", prop_item[i].id); - for (j = 0; j < prop_item[i].length; j++) - trace(" %02x", data[j]); - trace("\n"); - } - } - item_data += prop_item[i].length; - } - - HeapFree(GetProcessHeap(), 0, prop_item); + winetest_push_context("%s", __FUNCTION__); + check_properties_id_list(image, td, ARRAY_SIZE(td), NULL, ~0, &prop_size); + check_properties_get_all(image, td, ARRAY_SIZE(td), NULL, ~0, prop_size); + winetest_pop_context(); GdipDisposeImage(image); } @@ -3916,8 +4381,8 @@ static void test_tiff_palette(void) expect(2, palette.pal.Count); if (palette.pal.Count == 2) { - ok(entries[0] == 0xff000000, "expected 0xff000000, got %#x\n", entries[0]); - ok(entries[1] == 0xffffffff, "expected 0xffffffff, got %#x\n", entries[1]); + ok(entries[0] == 0xff000000, "expected 0xff000000, got %#lx\n", entries[0]); + ok(entries[1] == 0xffffffff, "expected 0xffffffff, got %#lx\n", entries[1]); } GdipDisposeImage(image); @@ -4091,13 +4556,8 @@ static void test_bitmapbits(void) ok(match, "%u: data should match\n", i); if (!match) - { - BYTE *bits = data.Scan0; - trace("%u: data mismatch for format %#x:", i, td[i].format); - for (j = 0; j < td[i].size; j++) - trace(" %02x", bits[j]); - trace("\n"); - } + trace("%u: data mismatch for format %#x:%s\n", i, td[i].format, + dbgstr_hexdata(data.Scan0, td[i].size)); } else ok(!match, "%u: data shouldn't match\n", i); @@ -4121,14 +4581,7 @@ static void test_bitmapbits(void) int match = memcmp(data.Scan0, td[i].pixels_unlocked, 48) == 0; ok(match, "%u: data should match\n", i); if (!match) - { - UINT j; - BYTE *bits = data.Scan0; - trace("%u: data mismatch for format %#x:", i, td[i].format); - for (j = 0; j < 48; j++) - trace(" %02x", bits[j]); - trace("\n"); - } + trace("%u: data mismatch for format %#x:%s\n", i, td[i].format, dbgstr_hexdata(data.Scan0, 48)); } status = GdipBitmapUnlockBits(bitmap, &data); @@ -4175,13 +4628,7 @@ static void test_DrawImage(void) match = memcmp(white_2x2, black_2x2, sizeof(black_2x2)) == 0; ok(match, "data should match\n"); if (!match) - { - UINT i, size = sizeof(white_2x2); - BYTE *bits = white_2x2; - for (i = 0; i < size; i++) - trace(" %02x", bits[i]); - trace("\n"); - } + trace("%s\n", dbgstr_hexdata(white_2x2, sizeof(white_2x2))); status = GdipDeleteGraphics(graphics); expect(Ok, status); @@ -4222,10 +4669,10 @@ static void test_DrawImage_SourceCopy(void) status = GdipDrawImageI(graphics, u2.image, 0, 0); expect(Ok, status); - todo_wine expect(0, dst_pixels[0]); + expect(0, dst_pixels[0]); expect(0xffff0000, dst_pixels[1]); - todo_wine expect(0, dst_pixels[2]); - todo_wine expect(0, dst_pixels[3]); + expect(0, dst_pixels[2]); + expect(0, dst_pixels[3]); status = GdipDeleteGraphics(graphics); expect(Ok, status); @@ -4271,13 +4718,7 @@ static void test_GdipDrawImagePointRect(void) match = memcmp(white_2x2, black_2x2, sizeof(black_2x2)) == 0; ok(match, "data should match\n"); if (!match) - { - UINT i, size = sizeof(white_2x2); - BYTE *bits = white_2x2; - for (i = 0; i < size; i++) - trace(" %02x", bits[i]); - trace("\n"); - } + trace("%s\n", dbgstr_hexdata(white_2x2, sizeof(white_2x2))); status = GdipDeleteGraphics(graphics); expect(Ok, status); @@ -4366,73 +4807,118 @@ static void test_image_format(void) } } +INT compare_with_precision(const BYTE *ptr1, const BYTE *ptr2, size_t num, INT precision) +{ + if (ptr1 == NULL || ptr2 == NULL) + return ptr1 < ptr2 ? -1 : 1; + + for (size_t i = 0; i < num; i++) + { + INT byte1 = ptr1[i]; + INT byte2 = ptr2[i]; + + if ((byte1 < byte2 - precision) || (byte1 > byte2 + precision)) + return byte1 < byte2 ? -1 : 1; + } + + return 0; +} + static void test_DrawImage_scale(void) { - static const BYTE back_8x1[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_080[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_100[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_120[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_150[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_180[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, - 0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_200[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, - 0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_250[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80, - 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40 }; - static const BYTE image_120_half[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_150_half[24] = { 0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_200_half[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, - 0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40,0x40,0x40,0x40 }; - static const BYTE image_250_half[24] = { 0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x80,0x80,0x80,0x80,0x80, - 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x40,0x40,0x40 }; + static const BYTE back_8x1[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_080[24] = { 0x40,0x40,0x40, 0x80,0x80,0x80, 0x40,0x40,0x40, 0x40,0x40,0x40, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_100[24] = { 0x40,0x40,0x40, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, 0x40,0x40,0x40, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_120[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0xcc,0xcc,0xcc, 0x40,0x40,0x40, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_150[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_180[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, + 0xcc,0xcc,0xcc, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_200[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, + 0xcc,0xcc,0xcc, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_250[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, + 0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc, 0x40,0x40,0x40 }; + static const BYTE image_120_half[24] = { 0x40,0x40,0x40, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_150_half[24] = { 0x40,0x40,0x40, 0x80,0x80,0x80, 0x80,0x80,0x80, 0xcc,0xcc,0xcc, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_180_half[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0x80,0x80,0x80, + 0xcc,0xcc,0xcc, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_200_half[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0x80,0x80,0x80, + 0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_250_half[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0x80,0x80,0x80, + 0x80,0x80,0x80, 0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc, 0x40,0x40,0x40 }; + + static const BYTE image_bil_080[24] = { 0x40,0x40,0x40, 0x93,0x93,0x93, 0x86,0x86,0x86, 0x40,0x40,0x40, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_bil_120[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0xb2,0xb2,0xb2, 0x87,0x87,0x87, + 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_bil_150[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x99,0x99,0x99, 0xcc,0xcc,0xcc, + 0x6f,0x6f,0x6f, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_bil_180[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x88,0x88,0x88, 0xb2,0xb2,0xb2, + 0xad,0xad,0xad, 0x5f,0x5f,0x5f, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_bil_200[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x80,0x80,0x80, 0xa6,0xa6,0xa6, + 0xcc,0xcc,0xcc, 0x86,0x86,0x86, 0x40,0x40,0x40, 0x40,0x40,0x40 }; + static const BYTE image_bil_250[24] = { 0x40,0x40,0x40, 0x40,0x40,0x40, 0x40,0x40,0x40, 0x8f,0x8f,0x8f, + 0xad,0xad,0xad, 0xcc,0xcc,0xcc, 0x95,0x95,0x95, 0x5c,0x5c,0x5c }; static const struct test_data { REAL scale_x; + InterpolationMode interpolation_mode; PixelOffsetMode pixel_offset_mode; const BYTE *image; + INT precision; BOOL todo; } td[] = { - { 0.8, PixelOffsetModeNone, image_080 }, /* 0 */ - { 1.0, PixelOffsetModeNone, image_100 }, - { 1.2, PixelOffsetModeNone, image_120 }, - { 1.5, PixelOffsetModeNone, image_150 }, - { 1.8, PixelOffsetModeNone, image_180 }, - { 2.0, PixelOffsetModeNone, image_200 }, - { 2.5, PixelOffsetModeNone, image_250 }, + { 0.8, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_080 }, /* 0 */ + { 1.0, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_100 }, + { 1.2, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_120 }, + { 1.5, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_150 }, + { 1.8, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_180 }, + { 2.0, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_200 }, + { 2.5, InterpolationModeNearestNeighbor, PixelOffsetModeNone, image_250 }, - { 0.8, PixelOffsetModeHighSpeed, image_080 }, /* 7 */ - { 1.0, PixelOffsetModeHighSpeed, image_100 }, - { 1.2, PixelOffsetModeHighSpeed, image_120 }, - { 1.5, PixelOffsetModeHighSpeed, image_150 }, - { 1.8, PixelOffsetModeHighSpeed, image_180 }, - { 2.0, PixelOffsetModeHighSpeed, image_200 }, - { 2.5, PixelOffsetModeHighSpeed, image_250 }, + { 0.8, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_080 }, /* 7 */ + { 1.0, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_100 }, + { 1.2, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_120 }, + { 1.5, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_150 }, + { 1.8, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_180 }, + { 2.0, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_200 }, + { 2.5, InterpolationModeNearestNeighbor, PixelOffsetModeHighSpeed, image_250 }, - { 0.8, PixelOffsetModeHalf, image_080 }, /* 14 */ - { 1.0, PixelOffsetModeHalf, image_100 }, - { 1.2, PixelOffsetModeHalf, image_120_half, TRUE }, - { 1.5, PixelOffsetModeHalf, image_150_half, TRUE }, - { 1.8, PixelOffsetModeHalf, image_180 }, - { 2.0, PixelOffsetModeHalf, image_200_half, TRUE }, - { 2.5, PixelOffsetModeHalf, image_250_half, TRUE }, + /* TODO There are missing left pixel column of image*/ + { 0.8, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_080 }, /* 14 */ + { 1.0, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_100 }, + { 1.2, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_120_half, 0, TRUE }, + { 1.5, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_150_half, 0, TRUE }, + { 1.8, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_180_half, 0, TRUE }, + { 2.0, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_200_half, 0, TRUE }, + { 2.5, InterpolationModeNearestNeighbor, PixelOffsetModeHalf, image_250_half, 0, TRUE }, - { 0.8, PixelOffsetModeHighQuality, image_080 }, /* 21 */ - { 1.0, PixelOffsetModeHighQuality, image_100 }, - { 1.2, PixelOffsetModeHighQuality, image_120_half, TRUE }, - { 1.5, PixelOffsetModeHighQuality, image_150_half, TRUE }, - { 1.8, PixelOffsetModeHighQuality, image_180 }, - { 2.0, PixelOffsetModeHighQuality, image_200_half, TRUE }, - { 2.5, PixelOffsetModeHighQuality, image_250_half, TRUE }, + { 0.8, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_080 }, /* 21 */ + { 1.0, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_100 }, + { 1.2, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_120_half, 0, TRUE }, + { 1.5, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_150_half, 0, TRUE }, + { 1.8, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_180_half, 0, TRUE }, + { 2.0, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_200_half, 0, TRUE }, + { 2.5, InterpolationModeNearestNeighbor, PixelOffsetModeHighQuality, image_250_half, 0, TRUE }, + + /* The bilinear interpolation results are little bit different than on Windows */ + /* TODO In two cases, there are missing right pixel column of image */ + { 0.8, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_080, 1, TRUE }, /* 28 */ + { 1.0, InterpolationModeBilinear, PixelOffsetModeNone, image_100 }, + { 1.2, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_120, 2 }, + { 1.5, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_150, 1 }, + { 1.8, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_180, 1, TRUE }, + { 2.0, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_200, 1 }, + { 2.5, InterpolationModeBilinear, PixelOffsetModeNone, image_bil_250, 1 }, }; - BYTE src_2x1[6] = { 0x80,0x80,0x80,0x80,0x80,0x80 }; + BYTE src_2x1[6] = { 0x80,0x80,0x80, 0xcc,0xcc,0xcc }; BYTE dst_8x1[24]; GpStatus status; union @@ -4455,11 +4941,12 @@ static void test_DrawImage_scale(void) expect(Ok, status); status = GdipGetImageGraphicsContext(u2.image, &graphics); expect(Ok, status); - status = GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); - expect(Ok, status); for (i = 0; i < ARRAY_SIZE(td); i++) { + status = GdipSetInterpolationMode(graphics, td[i].interpolation_mode); + expect(Ok, status); + status = GdipSetPixelOffsetMode(graphics, td[i].pixel_offset_mode); expect(Ok, status); @@ -4473,16 +4960,13 @@ static void test_DrawImage_scale(void) status = GdipDrawImageI(graphics, u1.image, 1, 0); expect(Ok, status); - match = memcmp(dst_8x1, td[i].image, sizeof(dst_8x1)) == 0; + match = compare_with_precision(dst_8x1, td[i].image, sizeof(dst_8x1), td[i].precision) == 0; todo_wine_if (!match && td[i].todo) ok(match, "%d: data should match\n", i); if (!match) { - UINT i, size = sizeof(dst_8x1); - const BYTE *bits = dst_8x1; - for (i = 0; i < size; i++) - trace(" %02x", bits[i]); - trace("\n"); + trace("Expected: %s\n", dbgstr_hexdata(td[i].image, sizeof(dst_8x1))); + trace("Got: %s\n", dbgstr_hexdata(dst_8x1, sizeof(dst_8x1))); } } @@ -4516,171 +5000,149 @@ static const BYTE animatedgif[] = { 0x21,0x01,0x0C,'p','l','a','i','n','t','e','x','t',' ','#','2',0x00,0x3B }; +static const BYTE gif_2frame_global_pal[] = { +'G','I','F','8','7','a', 0x01,0x00, 0x01,0x00, 0xa1, 0x02, 0x00, +0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c, +0x21,0xF9,0x04, 0x00,0x0A,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, +0x21,0xF9,0x04, 0x00,0x14,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, 0x3b +}; + +static const BYTE gif_2frame_no_pal[] = { +'G','I','F','8','7','a', 0x01,0x00, 0x01,0x00, 0x21, 0x02, 0x00, +0x21,0xF9,0x04, 0x00,0x0A,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, +0x21,0xF9,0x04, 0x00,0x14,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, 0x3b +}; + +static const BYTE gif_2frame_missing_gce1[] = { +'G','I','F','8','7','a', 0x01,0x00, 0x01,0x00, 0x21, 0x02, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, +0x21,0xF9,0x04, 0x00,0x14,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, 0x3b +}; + +static const BYTE gif_2frame_missing_gce2[] = { +'G','I','F','8','7','a', 0x01,0x00, 0x01,0x00, 0x21, 0x02, 0x00, +0x21,0xF9,0x04, 0x00,0x0A,0x00,0x08, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, 0x3b +}; + +static const BYTE gif_no_pal[] = { +'G','I','F','8','7','a', 0x01,0x00, 0x01,0x00, 0x27, 0x02, 0x00, +0x2c, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01, +0x02,0x02,0x44,0x01,0x00, 0x3b +}; + static void test_gif_properties(void) { - static const struct test_data - { - ULONG type, id, length; - const BYTE value[13]; - } td[] = + static const struct property_test_data animatedgif_props[] = { { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 10,0,0,0,20,0,0,0 } }, - { PropertyTagTypeASCII, PropertyTagExifUserComment, 13, { 'H','e','l','l','o',' ','W','o','r','l','d','!',0 } }, + { PropertyTagTypeASCII, PropertyTagExifUserComment, 13, "Hello World!" }, { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 5,0 } }, { PropertyTagTypeByte, PropertyTagGlobalPalette, 12, { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c } }, { PropertyTagTypeByte, PropertyTagIndexBackground, 1, { 2 } }, { PropertyTagTypeByte, PropertyTagIndexTransparent, 1, { 8 } } }; + static const struct property_test_data gif_2frame_global_pal_props[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 10,0,0,0,20,0,0,0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 1,0 } }, + { PropertyTagTypeByte, PropertyTagGlobalPalette, 12, { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c } }, + { PropertyTagTypeByte, PropertyTagIndexBackground, 1, { 2 } }, + }; + static const struct property_test_data gif_2frame_no_pal_props[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 10,0,0,0,20,0,0,0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 1,0 } }, + }; + static const struct property_test_data gif_2frame_missing_gce1_props[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 0,0,0,0,20,0,0,0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 1,0 } }, + }; + static const struct property_test_data gif_2frame_missing_gce2_props[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 8, { 10,0,0,0,10,0,0,0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 1,0 } }, + }; + static const struct property_test_data gif_no_pal_props[] = + { + { PropertyTagTypeLong, PropertyTagFrameDelay, 4, { 0,0,0,0 } }, + { PropertyTagTypeShort, PropertyTagLoopCount, 2, { 1,0 } }, + }; + + static const struct test_data { + const BYTE *image_data; + size_t image_size; + const struct property_test_data *prop_item; + size_t prop_count; + UINT frame_count; + } td[] = + { +#define giftest(img, prop, frames) { img, sizeof(img), prop, ARRAY_SIZE(prop), frames } + giftest(animatedgif, animatedgif_props, 2), + giftest(gif_2frame_global_pal, gif_2frame_global_pal_props, 2), + giftest(gif_2frame_no_pal, gif_2frame_no_pal_props, 2), + giftest(gif_2frame_missing_gce1, gif_2frame_missing_gce1_props, 2), + giftest(gif_2frame_missing_gce2, gif_2frame_missing_gce2_props, 2), + giftest(gif_no_pal, gif_no_pal_props, 1), +#undef giftest + }; + GpStatus status; GpImage *image; GUID guid; - UINT dim_count, frame_count, prop_count, prop_size, i; - UINT total_size, total_count; - PROPID *prop_id; - PropertyItem *prop_item; - const char *item_data; + UINT dim_count, frame_count, prop_size, i; - image = load_image(animatedgif, sizeof(animatedgif), TRUE, FALSE); - if (!image) /* XP fails to load this GIF image */ + for (i = 0; i < ARRAY_SIZE(td); i++) { - trace("Failed to load GIF image data\n"); - return; - } + winetest_push_context("test %u", i); - status = GdipImageGetFrameDimensionsCount(image, &dim_count); - expect(Ok, status); - expect(1, dim_count); + image = load_image(td[i].image_data, td[i].image_size, TRUE, FALSE); + if (!image) /* XP fails to load most of these GIF images */ + { + trace("Failed to load GIF image data\n"); + winetest_pop_context(); + continue; + } - status = GdipImageGetFrameDimensionsList(image, &guid, 1); - expect(Ok, status); - expect_guid(&FrameDimensionTime, &guid, __LINE__, FALSE); + status = GdipImageGetFrameDimensionsCount(image, &dim_count); + expect(Ok, status); + expect(1, dim_count); - status = GdipImageGetFrameCount(image, &guid, &frame_count); - expect(Ok, status); - expect(2, frame_count); + status = GdipImageGetFrameDimensionsList(image, &guid, 1); + expect(Ok, status); + expect_guid(&FrameDimensionTime, &guid, __LINE__, FALSE); - status = GdipImageSelectActiveFrame(image, &guid, 1); - expect(Ok, status); + status = GdipImageGetFrameCount(image, &guid, &frame_count); + expect(Ok, status); + expect(td[i].frame_count, frame_count); - status = GdipGetPropertyCount(image, &prop_count); - expect(Ok, status); - ok(prop_count == ARRAY_SIZE(td) || broken(prop_count == 1) /* before win7 */, - "expected property count %u, got %u\n", (UINT) ARRAY_SIZE(td), prop_count); + status = GdipImageSelectActiveFrame(image, &guid, td[i].frame_count - 1); + expect(Ok, status); + + winetest_pop_context(); + + winetest_push_context("%s test %u", __FUNCTION__, i); + check_properties_id_list(image, td[i].prop_item, td[i].prop_count, td[i].prop_item, 1, &prop_size); + check_properties_get_all(image, td[i].prop_item, td[i].prop_count, td[i].prop_item, 1, prop_size); + winetest_pop_context(); - if (prop_count != ARRAY_SIZE(td)) - { GdipDisposeImage(image); - return; } - - prop_id = HeapAlloc(GetProcessHeap(), 0, prop_count * sizeof(*prop_id)); - - status = GdipGetPropertyIdList(image, prop_count, prop_id); - expect(Ok, status); - - prop_size = 0; - for (i = 0; i < prop_count; i++) - { - UINT size; - status = GdipGetPropertyItemSize(image, prop_id[i], &size); - expect(Ok, status); - if (status != Ok) break; - ok(size > sizeof(*prop_item), "%u: too small item length %u\n", i, size); - - prop_size += size; - - prop_item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); - status = GdipGetPropertyItem(image, prop_id[i], size, prop_item); - expect(Ok, status); - ok(prop_item->value == prop_item + 1, "expected item->value %p, got %p\n", prop_item + 1, prop_item->value); - ok(td[i].type == prop_item->type, - "%u: expected type %u, got %u\n", i, td[i].type, prop_item->type); - ok(td[i].id == prop_item->id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item->id); - size -= sizeof(*prop_item); - ok(prop_item->length == size, "%u: expected length %u, got %u\n", i, size, prop_item->length); - ok(td[i].length == prop_item->length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item->length); - if (td[i].length == prop_item->length) - { - int match = memcmp(td[i].value, prop_item->value, td[i].length) == 0; - ok(match, "%u: data mismatch\n", i); - if (!match) - { - UINT j; - BYTE *data = prop_item->value; - trace("id %#x:", prop_item->id); - for (j = 0; j < prop_item->length; j++) - trace(" %02x", data[j]); - trace("\n"); - } - } - HeapFree(GetProcessHeap(), 0, prop_item); - } - - HeapFree(GetProcessHeap(), 0, prop_id); - - status = GdipGetPropertySize(NULL, &total_size, &total_count); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, &total_size, NULL); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, NULL, &total_count); - expect(InvalidParameter, status); - status = GdipGetPropertySize(image, NULL, NULL); - expect(InvalidParameter, status); - total_size = 0xdeadbeef; - total_count = 0xdeadbeef; - status = GdipGetPropertySize(image, &total_size, &total_count); - expect(Ok, status); - ok(prop_count == total_count, - "expected total property count %u, got %u\n", prop_count, total_count); - ok(prop_size == total_size, - "expected total property size %u, got %u\n", prop_size, total_size); - - prop_item = HeapAlloc(GetProcessHeap(), 0, prop_size); - - status = GdipGetAllPropertyItems(image, 0, prop_count, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, 1, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, 0, 0, NULL); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size + 1, prop_count, prop_item); - expect(InvalidParameter, status); - status = GdipGetAllPropertyItems(image, prop_size, prop_count, prop_item); - expect(Ok, status); - - item_data = (const char *)(prop_item + prop_count); - for (i = 0; i < prop_count; i++) - { - ok(prop_item[i].value == item_data, "%u: expected value %p, got %p\n", - i, item_data, prop_item[i].value); - ok(td[i].type == prop_item[i].type, - "%u: expected type %u, got %u\n", i, td[i].type, prop_item[i].type); - ok(td[i].id == prop_item[i].id, "%u: expected id %#x, got %#x\n", i, td[i].id, prop_item[i].id); - ok(td[i].length == prop_item[i].length, "%u: expected length %u, got %u\n", i, td[i].length, prop_item[i].length); - if (td[i].length == prop_item[i].length) - { - int match = memcmp(td[i].value, prop_item[i].value, td[i].length) == 0; - ok(match, "%u: data mismatch\n", i); - if (!match) - { - UINT j; - BYTE *data = prop_item[i].value; - trace("id %#x:", prop_item[i].id); - for (j = 0; j < prop_item[i].length; j++) - trace(" %02x", data[j]); - trace("\n"); - } - } - item_data += prop_item[i].length; - } - - HeapFree(GetProcessHeap(), 0, prop_item); - - GdipDisposeImage(image); } static void test_ARGB_conversion(void) @@ -4753,12 +5215,103 @@ static void test_ARGB_conversion(void) GdipDisposeImage((GpImage *)bitmap); } +static void test_PARGB_conversion(void) +{ + BYTE pargb[8] = { 0x62,0x77,0x99,0x77, 0x62,0x77,0x99,0 }; + BYTE argb[8] = { 0xd1,0xfe,0xff,0x77, 0x62,0x77,0x99,0 }; + BYTE pargb2[8] = { 0x01,0x01,0x00,0x01, 0xfe,0x7f,0x7f,0xfe }; + BYTE *bits; + GpBitmap *bitmap; + BitmapData data; + GpStatus status; + int match; + + status = GdipCreateBitmapFromScan0(2, 1, 8, PixelFormat32bppPARGB, pargb, &bitmap); + expect(Ok, status); + + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppARGB, &data); + expect(Ok, status); + ok(data.Width == 2, "expected 2, got %d\n", data.Width); + ok(data.Height == 1, "expected 1, got %d\n", data.Height); + ok(data.Stride == 8, "expected 8, got %d\n", data.Stride); + ok(data.PixelFormat == PixelFormat32bppARGB, "expected PixelFormat32bppARGB, got %d\n", data.PixelFormat); + match = !memcmp(data.Scan0, argb, sizeof(argb)); + ok(match, "bits don't match\n"); + if (!match) + { + bits = data.Scan0; + trace("format %#x, bits %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x\n", PixelFormat32bppARGB, + bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7]); + } + status = GdipBitmapUnlockBits(bitmap, &data); + expect(Ok, status); + + /* Testing SetPixel 32-bit ARGB to PARGB */ + status = GdipBitmapSetPixel(bitmap, 0, 0, 0x017f80ff); + expect(Ok, status); + status = GdipBitmapSetPixel(bitmap, 1, 0, 0xfe7f80ff); + expect(Ok, status); + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppPARGB, &data); + expect(Ok, status); + ok(data.Width == 2, "expected 2, got %d\n", data.Width); + ok(data.Height == 1, "expected 1, got %d\n", data.Height); + ok(data.Stride == 8, "expected 8, got %d\n", data.Stride); + ok(data.PixelFormat == PixelFormat32bppPARGB, "expected PixelFormat32bppPARGB, got %d\n", data.PixelFormat); + match = !memcmp(data.Scan0, pargb2, sizeof(pargb2)); + ok(match, "bits don't match\n"); + if (!match) + { + bits = data.Scan0; + trace("format %#x, bits %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x\n", PixelFormat32bppPARGB, + bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7]); + } + status = GdipBitmapUnlockBits(bitmap, &data); + expect(Ok, status); + + GdipDisposeImage((GpImage *)bitmap); +} + static void test_CloneBitmapArea(void) { + /* 3x3 pixeldata in various formats: red, green, blue, yellow, turquoise, pink, black, gray, white */ + static BYTE bmp_3x3_data_32bpp_argb[] = { + 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0x00,0xff,0xff, + 0xff,0xff,0x00,0xff, 0x00,0xff,0xff,0xff, 0xff,0x00,0xff,0xff, + 0xff,0xff,0xff,0xff, 0x80,0x80,0x80,0xff, 0x00,0x00,0x00,0xff + }; + static BYTE bmp_3x3_data_32bpp_rgb[] = { + 0xff,0x00,0x00,0x00, 0x00,0xff,0x00,0x00, 0x00,0x00,0xff,0x00, + 0xff,0xff,0x00,0x00, 0x00,0xff,0xff,0x00, 0xff,0x00,0xff,0x00, + 0xff,0xff,0xff,0x00, 0x80,0x80,0x80,0x00, 0x00,0x00,0x00,0x00 + }; + static BYTE bmp_3x3_data_24bpp_rgb[] = { + 0xff,0x00,0x00, 0x00,0xff,0x00, 0x00,0x00,0xff, + 0xff,0xff,0x00, 0x00,0xff,0xff, 0xff,0x00,0xff, + 0xff,0xff,0xff, 0x80,0x80,0x80, 0x00,0x00,0x00 + }; + + static const struct test_data { + BYTE *src_pixeldata; + PixelFormat src_format; + PixelFormat dst_format; + } td[] = + { + { bmp_3x3_data_32bpp_argb, PixelFormat32bppARGB, PixelFormat8bppIndexed }, + { bmp_3x3_data_32bpp_argb, PixelFormat32bppARGB, PixelFormat4bppIndexed }, + { bmp_3x3_data_32bpp_argb, PixelFormat32bppARGB, PixelFormat1bppIndexed }, + { bmp_3x3_data_32bpp_rgb, PixelFormat32bppRGB, PixelFormat8bppIndexed }, + { bmp_3x3_data_32bpp_rgb, PixelFormat32bppRGB, PixelFormat4bppIndexed }, + { bmp_3x3_data_32bpp_rgb, PixelFormat32bppRGB, PixelFormat1bppIndexed }, + { bmp_3x3_data_24bpp_rgb, PixelFormat24bppRGB, PixelFormat8bppIndexed }, + { bmp_3x3_data_24bpp_rgb, PixelFormat24bppRGB, PixelFormat4bppIndexed }, + { bmp_3x3_data_24bpp_rgb, PixelFormat24bppRGB, PixelFormat1bppIndexed }, + }; + GpStatus status; GpBitmap *bitmap, *copy; BitmapData data, data2; + INT x, y, i; status = GdipCreateBitmapFromScan0(1, 1, 0, PixelFormat24bppRGB, NULL, &bitmap); expect(Ok, status); @@ -4777,56 +5330,55 @@ static void test_CloneBitmapArea(void) GdipDisposeImage((GpImage *)copy); GdipDisposeImage((GpImage *)bitmap); -} -static BOOL get_encoder_clsid(LPCWSTR mime, GUID *format, CLSID *clsid) -{ - GpStatus status; - UINT n_codecs, info_size, i; - ImageCodecInfo *info; - BOOL ret = FALSE; - - status = GdipGetImageEncodersSize(&n_codecs, &info_size); - expect(Ok, status); - - info = GdipAlloc(info_size); - - status = GdipGetImageEncoders(n_codecs, info_size, info); - expect(Ok, status); - - for (i = 0; i < n_codecs; i++) + for(i=0; i> 16 & 0xff) + (color_orig >> 8 & 0xff) + (color_orig & 0xff) + > 0x17d ? 0xffffffff : 0xff000000; + + match = color_match(color_orig, color_copy, 0x00); + ok(match == TRUE, "Colors 0x%08lx and 0x%08lx do not match! (Conversion from %x to %x)\n", + color_orig, color_copy, td[i].src_format, td[i].dst_format); + } + + GdipDisposeImage((GpImage *)copy); + GdipDisposeImage((GpImage *)bitmap); + } } static void test_supported_encoders(void) { - static const WCHAR bmp_mimetype[] = { 'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p',0 }; - static const WCHAR jpeg_mimetype[] = { 'i','m','a','g','e','/','j','p','e','g',0 }; - static const WCHAR gif_mimetype[] = { 'i','m','a','g','e','/','g','i','f',0 }; - static const WCHAR tiff_mimetype[] = { 'i','m','a','g','e','/','t','i','f','f',0 }; - static const WCHAR png_mimetype[] = { 'i','m','a','g','e','/','p','n','g',0 }; static const struct test_data { LPCWSTR mime; const GUID *format; } td[] = { - { bmp_mimetype, &ImageFormatBMP }, - { jpeg_mimetype, &ImageFormatJPEG }, - { gif_mimetype, &ImageFormatGIF }, - { tiff_mimetype, &ImageFormatTIFF }, - { png_mimetype, &ImageFormatPNG } + { L"image/bmp", &ImageFormatBMP }, + { L"image/jpeg", &ImageFormatJPEG }, + { L"image/gif", &ImageFormatGIF }, + { L"image/tiff", &ImageFormatTIFF }, + { L"image/png", &ImageFormatPNG } }; GUID format, clsid; BOOL ret; @@ -4849,10 +5401,10 @@ static void test_supported_encoders(void) hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, 16); hr = CreateStreamOnHGlobal(hmem, TRUE, &stream); - ok(hr == S_OK, "CreateStreamOnHGlobal error %#x\n", hr); + ok(hr == S_OK, "CreateStreamOnHGlobal error %#lx\n", hr); status = GdipSaveImageToStream((GpImage *)bm, stream, &clsid, NULL); - ok(status == Ok, "GdipSaveImageToStream error %d\n", status); + ok(status == Ok, "%s encoder, GdipSaveImageToStream error %d\n", wine_dbgstr_w(td[i].mime), status); IStream_Release(stream); } @@ -4865,20 +5417,44 @@ static void test_createeffect(void) static const GUID noneffect = { 0xcd0c3d4b, 0xe15e, 0x4cf2, { 0x9e, 0xa8, 0x6e, 0x1d, 0x65, 0x48, 0xc5, 0xa5 } }; GpStatus (WINAPI *pGdipCreateEffect)( const GUID guid, CGpEffect **effect); GpStatus (WINAPI *pGdipDeleteEffect)( CGpEffect *effect); + GpStatus (WINAPI *pGdipGetEffectParameterSize)(CGpEffect *effect, UINT *size); + GpStatus (WINAPI *pGdipGetEffectParameters)(CGpEffect *effect, const VOID *params, const UINT size); GpStatus stat; - CGpEffect *effect; + CGpEffect *effect = NULL; HMODULE mod = GetModuleHandleA("gdiplus.dll"); int i; - const GUID * const effectlist[] = - {&BlurEffectGuid, &SharpenEffectGuid, &ColorMatrixEffectGuid, &ColorLUTEffectGuid, - &BrightnessContrastEffectGuid, &HueSaturationLightnessEffectGuid, &LevelsEffectGuid, - &TintEffectGuid, &ColorBalanceEffectGuid, &RedEyeCorrectionEffectGuid, &ColorCurveEffectGuid}; + UINT param_size; + + static const struct test_data { + const GUID *effect; + const UINT parameters_number; + } td[] = + { + { &BlurEffectGuid, 8 }, + { &BrightnessContrastEffectGuid, 8 }, + { &ColorBalanceEffectGuid, 12 }, + { &ColorCurveEffectGuid, 12 }, + { &ColorLUTEffectGuid, 1024 }, + { &ColorMatrixEffectGuid, 100 }, + { &HueSaturationLightnessEffectGuid, 12 }, + { &LevelsEffectGuid, 12 }, + /* Parameter Size for Red Eye Correction effect is different for 64 bits build */ +#ifdef _WIN64 + { &RedEyeCorrectionEffectGuid, 16 }, +#else + { &RedEyeCorrectionEffectGuid, 8 }, +#endif + { &SharpenEffectGuid, 8 }, + { &TintEffectGuid, 8 }, + }; pGdipCreateEffect = (void*)GetProcAddress( mod, "GdipCreateEffect"); pGdipDeleteEffect = (void*)GetProcAddress( mod, "GdipDeleteEffect"); - if(!pGdipCreateEffect || !pGdipDeleteEffect) + pGdipGetEffectParameterSize = (void*)GetProcAddress( mod, "GdipGetEffectParameterSize"); + pGdipGetEffectParameters = (void*)GetProcAddress( mod, "GdipGetEffectParameters"); + if (!pGdipCreateEffect || !pGdipDeleteEffect || !pGdipGetEffectParameterSize || !pGdipGetEffectParameters) { - /* GdipCreateEffect/GdipDeleteEffect was introduced in Windows Vista. */ + /* GdipCreateEffect/GdipDeleteEffect/GdipGetEffectParameterSize/GdipGetEffectParameters were introduced in Windows Vista. */ win_skip("GDIPlus version 1.1 not available\n"); return; } @@ -4887,18 +5463,31 @@ static void test_createeffect(void) expect(InvalidParameter, stat); stat = pGdipCreateEffect(noneffect, &effect); - todo_wine expect(Win32Error, stat); + expect(Win32Error, stat); + ok( !effect, "expected null effect\n"); - for(i=0; i < ARRAY_SIZE(effectlist); i++) + param_size = 0; + stat = pGdipGetEffectParameterSize(NULL, ¶m_size); + expect(InvalidParameter, stat); + expect(0, param_size); + + for (i = 0; i < ARRAY_SIZE(td); i++) { - stat = pGdipCreateEffect(*effectlist[i], &effect); - todo_wine expect(Ok, stat); - if(stat == Ok) + stat = pGdipCreateEffect(*(td[i].effect), &effect); + expect(Ok, stat); + if (stat == Ok) { + param_size = 0; + stat = pGdipGetEffectParameterSize(effect, ¶m_size); + expect(Ok, stat); + expect(td[i].parameters_number, param_size); stat = pGdipDeleteEffect(effect); expect(Ok, stat); } } + + stat = pGdipDeleteEffect(NULL); + expect(InvalidParameter, stat); } static void test_getadjustedpalette(void) @@ -5243,7 +5832,7 @@ static void test_png_color_formats(void) GpImage *image; ImageType type; PixelFormat format; - ImageFlags flags; + UINT flags; BOOL valid; int i, j, PLTE_off = 0, tRNS_off = 0; const ImageFlags color_space_mask = ImageFlagsColorSpaceRGB | ImageFlagsColorSpaceCMYK | ImageFlagsColorSpaceGRAY | ImageFlagsColorSpaceYCBCR | ImageFlagsColorSpaceYCCK; @@ -5325,6 +5914,230 @@ static void test_png_color_formats(void) #undef PNG_COLOR_TYPE_GRAY_ALPHA #undef PNG_COLOR_TYPE_RGB_ALPHA +static void test_png_save_palette(void) +{ + GpStatus status; + GpBitmap *bitmap; + HGLOBAL hglob; + BOOL result; + IStream *stream; + GUID enc_format, clsid; + LARGE_INTEGER seek; + ULARGE_INTEGER pos; + UINT i, ptr; + BYTE *data; + + PixelFormat formats[] = { + PixelFormat1bppIndexed, + PixelFormat4bppIndexed, + PixelFormat8bppIndexed, + PixelFormat16bppGrayScale, + PixelFormat16bppRGB555, + PixelFormat16bppRGB565, + PixelFormat16bppARGB1555, + PixelFormat24bppRGB, + PixelFormat32bppRGB, + PixelFormat32bppARGB, + PixelFormat32bppPARGB, + }; + + result = get_encoder_clsid(L"image/png", &enc_format, &clsid); + ok(result, "getting PNG encoding clsid failed"); + + hglob = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD | GMEM_ZEROINIT, 1024); + + for (i = 0; i < ARRAY_SIZE(formats); i++) + { + status = GdipCreateBitmapFromScan0(8, 8, 0, formats[i], NULL, &bitmap); + ok(status == Ok, "Unexpected return value %d creating bitmap for PixelFormat %#x\n", status, formats[i]); + + CreateStreamOnHGlobal(hglob, FALSE, &stream); + status = GdipSaveImageToStream((GpImage *)bitmap, stream, &clsid, NULL); + GdipDisposeImage((GpImage*)bitmap); + + todo_wine_if(formats[i] == PixelFormat16bppGrayScale) + ok(formats[i] == PixelFormat16bppGrayScale ? + (status == GenericError || status == Win32Error) : status == Ok, + "Unexpected return value %d saving image for PixelFormat %#x\n", status, formats[i]); + + if (status == Ok) + { + data = GlobalLock(hglob); + seek.QuadPart = 0; + IStream_Seek(stream, seek, STREAM_SEEK_CUR, &pos); + for (ptr = 0; ptr < pos.QuadPart - 4; ptr++) + if (!memcmp(data + ptr, "PLTE", 4)) + break; + memset(data, 0, 1024); + GlobalUnlock(hglob); + + if (IsIndexedPixelFormat(formats[i])) + ok(ptr < pos.QuadPart - 4, "Expected palette not found for PixelFormat %#x\n", formats[i]); + else + ok(ptr >= pos.QuadPart - 4, "Unexpected palette found for PixelFormat %#x\n", formats[i]); + } + + IStream_Release(stream); + } + + GlobalFree(hglob); +} + +static const BYTE png_minimal[] = { + 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a, + 0x00,0x00,0x00,0x0d,'I','H','D','R',0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x0c,'I','D','A','T',0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,0xe7, + 0x00,0x00,0x00,0x00,'I','E','N','D',0xae,0x42,0x60,0x82 +}; + +static const BYTE png_phys[] = { + 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a, + 0x00,0x00,0x00,0x0d,'I','H','D','R',0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,0xde, + 0x00,0x00,0x00,0x09,'p','H','Y','s',0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x0c,'I','D','A','T',0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,0xe7, + 0x00,0x00,0x00,0x00,'I','E','N','D',0xae,0x42,0x60,0x82 +}; + +static const BYTE png_time[] = { + 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a, + 0x00,0x00,0x00,0x0d,'I','H','D','R',0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,0xde, + 0x00,0x00,0x00,0x07,'t','I','M','E',0x07,0xb2,0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x0c,'I','D','A','T',0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,0xe7, + 0x00,0x00,0x00,0x00,'I','E','N','D',0xae,0x42,0x60,0x82 +}; + +static const BYTE png_hist[] = { + 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a, + 0x00,0x00,0x00,0x0d,'I','H','D','R',0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,0xde, + 0x00,0x00,0x00,0x08,'h','I','S','T',0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x0c,'I','D','A','T',0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,0xe7, + 0x00,0x00,0x00,0x00,'I','E','N','D',0xae,0x42,0x60,0x82 +}; + +static void test_png_unit_properties(void) +{ + GpImage *image; + UINT pHYs_off = 0, i; + static const struct { + BYTE unit; + ULONG unitX; + ULONG unitY; + } td[] = + { + {0, 0, 0}, + {1, 0, 0}, + {0, 1000, 1000}, + {1, 1000, 1000}, + {1, 3780, 3780}, + }; + struct property_test_data prop_td[][3] = + { + {{ PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0,0,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0,0,0,0 } }}, + {{ PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0,0,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0,0,0,0 } }}, + {{ PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 }, FALSE, TRUE }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0,0,0,0 }, FALSE, TRUE }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0,0,0,0 }, FALSE, TRUE }}, + {{ PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0xe8,0x03,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0xe8,0x03,0,0 } }}, + {{ PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0xc4,0x0e,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0xc4,0x0e,0,0 } }}, + }; + BYTE buf[sizeof(png_phys)]; + + + for (i = 0; i < sizeof(png_phys) - 4; i++) + { + if (!memcmp(png_phys + i, "pHYs", 4)) + pHYs_off = i; + } + + ok(pHYs_off, "pHYs offset %d\n", pHYs_off); + if (!pHYs_off) + return; + + for (i = 0; i < ARRAY_SIZE(td); i++) + { + if (i == 0) + image = load_image(png_minimal, sizeof(png_minimal), TRUE, FALSE); + else + { + memcpy(buf, png_phys, sizeof(png_phys)); + buf[pHYs_off + 4] = (td[i].unitX >> 24) & 0xff; + buf[pHYs_off + 5] = (td[i].unitX >> 16) & 0xff; + buf[pHYs_off + 6] = (td[i].unitX >> 8) & 0xff; + buf[pHYs_off + 7] = td[i].unitX & 0xff; + buf[pHYs_off + 8] = (td[i].unitY >> 24) & 0xff; + buf[pHYs_off + 9] = (td[i].unitY >> 16) & 0xff; + buf[pHYs_off + 10] = (td[i].unitY >> 8) & 0xff; + buf[pHYs_off + 11] = td[i].unitY & 0xff; + buf[pHYs_off + 12] = td[i].unit; + image = load_image(buf, sizeof(buf), TRUE, FALSE); + } + + ok(image != NULL, "%u: Failed to load PNG image data\n", i); + if (!image) + continue; + + winetest_push_context("%s test %u", __FUNCTION__, i); + check_properties_get_all(image, prop_td[i], 3, NULL, 0, ~0); + winetest_pop_context(); + + GdipDisposeImage(image); + } +} + +static void test_png_datetime_property(void) +{ + struct property_test_data td[] = + { + { PropertyTagTypeASCII, PropertyTagDateTime, 20, { "1970:01:01 00:00:00" } }, + { PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0,0,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0,0,0,0 } }, + }; + + GpImage *image = load_image(png_time, sizeof(png_time), TRUE, FALSE); + ok(image != NULL, "Failed to load PNG image data\n"); + if (!image) + return; + + winetest_push_context("%s", __FUNCTION__); + check_properties_get_all(image, td, ARRAY_SIZE(td), td, 1, ~0); + winetest_pop_context(); + + GdipDisposeImage(image); +} + +static void test_png_histogram_property(void) +{ + struct property_test_data td[] = + { + { PropertyTagTypeShort, PropertyTagPaletteHistogram, 8, { 1,0,2,0,3,0,4,0 } }, + { PropertyTagTypeByte, PropertyTagPixelUnit, 1, { 1 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitX, 4, { 0,0,0,0 } }, + { PropertyTagTypeLong, PropertyTagPixelPerUnitY, 4, { 0,0,0,0 } }, + }; + + GpImage *image = load_image(png_hist, sizeof(png_hist), TRUE, FALSE); + if (!image) + { + win_skip("broken PNG histogram support\n"); + return; + } + + winetest_push_context("%s", __FUNCTION__); + check_properties_get_all(image, td, ARRAY_SIZE(td), NULL, 0, ~0); + winetest_pop_context(); + + GdipDisposeImage(image); +} + static void test_GdipLoadImageFromStream(void) { IStream *stream; @@ -5366,7 +6179,7 @@ static BYTE *init_bitmap(UINT *width, UINT *height, UINT *stride) *stride = (*width * 3 + 3) & ~3; trace("width %d, height %d, stride %d\n", *width, *height, *stride); - src = HeapAlloc(GetProcessHeap(), 0, *stride * *height); + src = malloc(*stride * *height); scale = 256 / *width; if (!scale) scale = 1; @@ -5432,7 +6245,7 @@ static void test_GdipInitializePalette(void) palette->Count = 256; status = pGdipInitializePalette(palette, PaletteTypeFixedBW, 0, FALSE, bitmap); expect(Ok, status); -todo_wine + todo_wine expect(0x200, palette->Flags); expect(2, palette->Count); expect(0xff000000, palette->Entries[0]); @@ -5443,7 +6256,7 @@ todo_wine palette->Count = 256; status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone8, 1, FALSE, NULL); expect(Ok, status); -todo_wine + todo_wine expect(0x300, palette->Flags); expect(16, palette->Count); expect(0xff000000, palette->Entries[0]); @@ -5455,7 +6268,7 @@ todo_wine palette->Count = 256; status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone8, 1, FALSE, bitmap); expect(Ok, status); -todo_wine + todo_wine expect(0x300, palette->Flags); expect(16, palette->Count); expect(0xff000000, palette->Entries[0]); @@ -5467,7 +6280,7 @@ todo_wine palette->Count = 256; status = pGdipInitializePalette(palette, PaletteTypeFixedHalftone252, 1, FALSE, bitmap); expect(Ok, status); -todo_wine + todo_wine expect(0x800, palette->Flags); expect(252, palette->Count); expect(0xff000000, palette->Entries[0]); @@ -5497,204 +6310,49 @@ todo_wine GdipFree(palette); GdipDisposeImage((GpImage *)bitmap); + free(data); } -#include "pshpack2.h" -static const struct tiff_1x1_data +static void test_graphics_clear(void) { - USHORT byte_order; - USHORT version; - ULONG dir_offset; - USHORT number_of_entries; - struct IFD_entry entry[12]; - ULONG next_IFD; - struct IFD_rational res; - short palette_data[3][256]; - short bps_data[4]; - BYTE pixel_data[32]; -} tiff_1x1_data = -{ -#ifdef WORDS_BIGENDIAN - 'M' | 'M' << 8, -#else - 'I' | 'I' << 8, -#endif - 42, - FIELD_OFFSET(struct tiff_1x1_data, number_of_entries), - 12, - { - { 0xff, IFD_SHORT, 1, 0 }, /* SUBFILETYPE */ - { 0x100, IFD_LONG, 1, 1 }, /* IMAGEWIDTH */ - { 0x101, IFD_LONG, 1, 1 }, /* IMAGELENGTH */ - { 0x102, IFD_SHORT, 3, FIELD_OFFSET(struct tiff_1x1_data, bps_data) }, /* BITSPERSAMPLE */ - { 0x103, IFD_SHORT, 1, 1 }, /* COMPRESSION: XP doesn't accept IFD_LONG here */ - { 0x106, IFD_SHORT, 1, 2 }, /* PHOTOMETRIC */ - { 0x111, IFD_LONG, 1, FIELD_OFFSET(struct tiff_1x1_data, pixel_data) }, /* STRIPOFFSETS */ - { 0x115, IFD_SHORT, 1, 3 }, /* SAMPLESPERPIXEL */ - { 0x11a, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_1x1_data, res) }, - { 0x11b, IFD_RATIONAL, 1, FIELD_OFFSET(struct tiff_1x1_data, res) }, - { 0x128, IFD_SHORT, 1, 2 }, /* RESOLUTIONUNIT */ - { 0x140, IFD_SHORT, 256*3, FIELD_OFFSET(struct tiff_1x1_data, palette_data) } /* COLORMAP */ - }, - 0, - { 96, 1 }, - { { 0 } }, - { 8,8,8,0 }, - { 1,0,2,3,4,5,6,7,8,9,0,1,2,3,4,5 } -}; -#include "poppack.h" - -static void test_tiff_color_formats(void) -{ - static const struct - { - int photometric; /* PhotometricInterpretation */ - int samples; /* SamplesPerPixel */ - int bps; /* BitsPerSample */ - PixelFormat format; - } td[] = - { - /* 2 - RGB */ - { 2, 3, 1, PixelFormat24bppRGB }, - { 2, 3, 4, PixelFormat24bppRGB }, - { 2, 3, 8, PixelFormat24bppRGB }, - { 2, 3, 16, PixelFormat48bppRGB }, - { 2, 3, 24, 0 }, -#if 0 /* FIXME */ - { 2, 3, 32, 0 }, -#endif - { 2, 4, 1, PixelFormat32bppARGB }, - { 2, 4, 4, PixelFormat32bppARGB }, - { 2, 4, 8, PixelFormat32bppARGB }, - { 2, 4, 16, PixelFormat48bppRGB }, - { 2, 4, 24, 0 }, - { 2, 4, 32, 0 }, - /* 1 - BlackIsZero (Bilevel) */ - { 1, 1, 1, PixelFormat1bppIndexed }, -#if 0 /* FIXME: PNG vs TIFF mismatch */ - { 1, 1, 4, PixelFormat8bppIndexed }, -#endif - { 1, 1, 8, PixelFormat8bppIndexed }, - { 1, 1, 16, PixelFormat32bppARGB }, - { 1, 1, 24, 0 }, - { 1, 1, 32, PixelFormat32bppARGB }, - /* 3 - Palette Color */ - { 3, 1, 1, PixelFormat1bppIndexed }, - { 3, 1, 4, PixelFormat4bppIndexed }, - { 3, 1, 8, PixelFormat8bppIndexed }, -#if 0 /* FIXME: for some reason libtiff replaces photometric 3 by 1 for bps > 8 */ - { 3, 1, 16, 0 }, - { 3, 1, 24, 0 }, - { 3, 1, 32, 0 }, -#endif - /* 5 - Separated */ - { 5, 4, 1, 0 }, - { 5, 4, 4, 0 }, - { 5, 4, 8, PixelFormat32bppCMYK }, - { 5, 4, 16, PixelFormat48bppRGB }, - { 5, 4, 24, 0 }, - { 5, 4, 32, 0 }, - }; - BYTE buf[sizeof(tiff_1x1_data)]; + BYTE argb[8] = { 0x11,0x22,0x33,0x80, 0xff,0xff,0xff,0 }; + BYTE cleared[8] = { 0,0,0,0, 0,0,0,0 }; + BYTE *bits; + GpBitmap *bitmap; + GpGraphics *graphics; + BitmapData data; GpStatus status; - GpImage *image; - UINT count, i; - struct IFD_entry *tag, *tag_photo = NULL, *tag_bps = NULL, *tag_samples = NULL, *tag_colormap = NULL; - short *bps; - ImageType type; - PixelFormat format; + int match; - memcpy(buf, &tiff_1x1_data, sizeof(tiff_1x1_data)); + status = GdipCreateBitmapFromScan0(2, 1, 8, PixelFormat32bppARGB, argb, &bitmap); + expect(Ok, status); - count = *(short *)(buf + tiff_1x1_data.dir_offset); - tag = (struct IFD_entry *)(buf + tiff_1x1_data.dir_offset + sizeof(short)); + status = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, status); - /* verify the TIFF structure */ - for (i = 0; i < count; i++) + status = GdipGraphicsClear(graphics, 0x00000000); + expect(Ok, status); + + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppARGB, &data); + expect(Ok, status); + ok(data.Width == 2, "expected 2, got %d\n", data.Width); + ok(data.Height == 1, "expected 1, got %d\n", data.Height); + ok(data.Stride == 8, "expected 8, got %d\n", data.Stride); + ok(data.PixelFormat == PixelFormat32bppARGB, "expected PixelFormat32bppARGB, got %d\n", data.PixelFormat); + match = !memcmp(data.Scan0, cleared, sizeof(cleared)); + ok(match, "bits don't match\n"); + if (!match) { - if (tag[i].id == 0x102) /* BitsPerSample */ - tag_bps = &tag[i]; - else if (tag[i].id == 0x106) /* PhotometricInterpretation */ - tag_photo = &tag[i]; - else if (tag[i].id == 0x115) /* SamplesPerPixel */ - tag_samples = &tag[i]; - else if (tag[i].id == 0x140) /* ColorMap */ - tag_colormap = &tag[i]; + bits = data.Scan0; + trace("format %#x, bits %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x\n", PixelFormat32bppARGB, + bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7]); } + status = GdipBitmapUnlockBits(bitmap, &data); + expect(Ok, status); - ok(tag_bps && tag_photo && tag_samples && tag_colormap, "tag 0x102,0x106,0x115 or 0x140 is missing\n"); - if (!tag_bps || !tag_photo || !tag_samples || !tag_colormap) return; - - ok(tag_bps->type == IFD_SHORT, "tag 0x102 should have type IFD_SHORT\n"); - bps = (short *)(buf + tag_bps->value); - ok(bps[0] == 8 && bps[1] == 8 && bps[2] == 8 && bps[3] == 0, - "expected bps 8,8,8,0 got %d,%d,%d,%d\n", bps[0], bps[1], bps[2], bps[3]); - - for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) - { - tag_colormap->count = (1 << td[i].bps) * 3; - tag_photo->value = td[i].photometric; - tag_bps->count = td[i].samples; - tag_samples->value = td[i].samples; - - if (td[i].samples == 1) - tag_bps->value = td[i].bps; - else if (td[i].samples == 2) - tag_bps->value = MAKELONG(td[i].bps, td[i].bps); - else if (td[i].samples == 3) - { - tag_bps->value = (BYTE *)bps - buf; - bps[0] = bps[1] = bps[2] = td[i].bps; - } - else if (td[i].samples == 4) - { - tag_bps->value = (BYTE *)bps - buf; - bps[0] = bps[1] = bps[2] = bps[3] = td[i].bps; - } - else - { - ok(0, "%u: unsupported samples count %d\n", i, td[i].samples); - continue; - } - - image = load_image(buf, sizeof(buf), TRUE, FALSE); - if (!td[i].format) -#ifdef __REACTOS__ - ok(!image || (((GetVersion() & 0xFF) < 6) && - (i == 19 || i == 20)), -#else - ok(!image, -#endif - "%u: (%d,%d,%d) TIFF image loading should have failed\n", i, td[i].photometric, td[i].samples, td[i].bps); - else - ok(image != NULL || broken(!image) /* XP */, "%u: failed to load TIFF image data (%d,%d,%d)\n", - i, td[i].photometric, td[i].samples, td[i].bps); - if (!image) continue; - - status = GdipGetImageType(image, &type); - ok(status == Ok, "%u: GdipGetImageType error %d\n", i, status); - ok(type == ImageTypeBitmap, "%u: wrong image type %d\n", i, type); - - status = GdipGetImagePixelFormat(image, &format); - expect(Ok, status); -#ifdef __REACTOS__ - ok(format == td[i].format || (((GetVersion() & 0xFF) < 6) && - ((i == 0 && format == 0) || - (i == 1 && format == 0) || - (i == 5 && format == 0) || - (i == 6 && format == 0) || - (i == 13 && format == 0) || - (i == 19 && format == PixelFormat32bppARGB) || - (i == 20 && format == PixelFormat32bppARGB) || - (i == 21 && format == PixelFormat32bppARGB) || - (i == 22 && format == PixelFormat32bppARGB))), -#else - ok(format == td[i].format, -#endif - "%u: expected %#x, got %#x\n", i, td[i].format, format); - - GdipDisposeImage(image); - } + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + GdipDisposeImage((GpImage *)bitmap); } START_TEST(image) @@ -5721,12 +6379,16 @@ START_TEST(image) pGdipBitmapGetHistogram = (void*)GetProcAddress(mod, "GdipBitmapGetHistogram"); pGdipImageSetAbort = (void*)GetProcAddress(mod, "GdipImageSetAbort"); - test_tiff_color_formats(); test_GdipInitializePalette(); test_png_color_formats(); + test_png_save_palette(); + test_png_unit_properties(); + test_png_datetime_property(); + test_png_histogram_property(); test_supported_encoders(); test_CloneBitmapArea(); test_ARGB_conversion(); + test_PARGB_conversion(); test_DrawImage_scale(); test_image_format(); test_DrawImage(); @@ -5744,6 +6406,7 @@ START_TEST(image) test_GdipImageGetFrameDimensionsCount(); test_LoadingImages(); test_SavingImages(); + test_SavingMultiPageTiff(); test_encoders(); test_LockBits(); test_LockBits_UserBuf(); @@ -5773,6 +6436,7 @@ START_TEST(image) test_histogram(); test_imageabort(); test_GdipLoadImageFromStream(); + test_graphics_clear(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/matrix.c b/modules/rostests/winetests/gdiplus/matrix.c index 5ca0209f6a9..7f515d0a60c 100644 --- a/modules/rostests/winetests/gdiplus/matrix.c +++ b/modules/rostests/winetests/gdiplus/matrix.c @@ -28,6 +28,13 @@ #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) #define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %.2f, got %.2f\n", expected, got) +static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) +{ + unsigned int diff = x > y ? x - y : y - x; + + return diff <= max_diff; +} + static BOOL compare_float(float f, float g, unsigned int ulps) { int x = *(int *)&f; @@ -38,10 +45,7 @@ static BOOL compare_float(float f, float g, unsigned int ulps) if (y < 0) y = INT_MIN - y; - if (abs(x - y) > ulps) - return FALSE; - - return TRUE; + return compare_uint(x, y, ulps); } static void test_constructor_destructor(void) @@ -110,6 +114,85 @@ static void test_transform(void) GdipDeleteMatrix(matrix); } +static void test_translate(void) +{ + GpStatus status; + GpMatrix *matrix = NULL; + REAL elems[6]; + + static const REAL expected_elem_append[] = {1.0, -2.0, 30.0, 40.0, -470.0, 620.0}; + static const REAL expected_elem_prepend[] = {1.0, -2.0, 30.0, 40.0, 130.0, 1340.0}; + + GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix); + + status = GdipTranslateMatrix(NULL, 30.0, 20.0, MatrixOrderAppend); + expect(InvalidParameter, status); + status = GdipTranslateMatrix(matrix, 30.0, 20.0, MatrixOrderAppend); + expect(Ok, status); + + status = GdipGetMatrixElements(matrix, elems); + expect(Ok, status); + GdipDeleteMatrix(matrix); + + for(INT i = 0; i < 6; i++) + ok(expected_elem_append[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i, + expected_elem_append[i], elems[i]); + + GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix); + + status = GdipTranslateMatrix(matrix, 30.0, 20.0, MatrixOrderPrepend); + expect(Ok, status); + + status = GdipGetMatrixElements(matrix, elems); + expect(Ok, status); + GdipDeleteMatrix(matrix); + + for(INT i = 0; i < 6; i++) + ok(expected_elem_prepend[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i, + expected_elem_prepend[i], elems[i]); + +} + +static void test_scale(void) +{ + GpStatus status; + GpMatrix *matrix = NULL; + REAL elems[6]; + int i; + + static const REAL expected_elem[] = {3.0, -4.0, 90.0, 80.0, -1500.0, 1200.0}; + static const REAL expected_elem2[] = {3.0, -6.0, 60.0, 80.0, -500.0, 600.0}; + + GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix); + + status = GdipScaleMatrix(NULL, 3, 2, MatrixOrderAppend); + expect(InvalidParameter, status); + status = GdipScaleMatrix(matrix, 3, 2, MatrixOrderAppend); + expect(Ok, status); + + status = GdipGetMatrixElements(matrix, elems); + expect(Ok, status); + GdipDeleteMatrix(matrix); + + for(i = 0; i < 6; i++) + ok(expected_elem[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i, + expected_elem[i], elems[i]); + + GdipCreateMatrix2(1.0, -2.0, 30.0, 40.0, -500.0, 600.0, &matrix); + + status = GdipScaleMatrix(matrix, 3, 2, MatrixOrderPrepend); + expect(Ok, status); + + status = GdipGetMatrixElements(matrix, elems); + expect(Ok, status); + GdipDeleteMatrix(matrix); + + for(i = 0; i < 6; i++) + ok(expected_elem2[i] == elems[i], "Expected #%d to be (%.1f) but got (%.1f)\n", i, + expected_elem2[i], elems[i]); + +} + static void test_isinvertible(void) { GpStatus status; @@ -390,6 +473,8 @@ START_TEST(matrix) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); test_constructor_destructor(); + test_translate(); + test_scale(); test_transform(); test_isinvertible(); test_invert(); diff --git a/modules/rostests/winetests/gdiplus/metafile.c b/modules/rostests/winetests/gdiplus/metafile.c index bd4d5264eda..eda5760c0fa 100644 --- a/modules/rostests/winetests/gdiplus/metafile.c +++ b/modules/rostests/winetests/gdiplus/metafile.c @@ -22,15 +22,22 @@ #include "objbase.h" #include "gdiplus.h" +#include "winspool.h" #include "wine/test.h" -#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) +#define expect(expected,got) expect_(__LINE__, expected, got) +static inline void expect_(unsigned line, DWORD expected, DWORD got) +{ + ok_(__FILE__, line)(expected == got, "Expected %.8ld, got %.8ld\n", expected, got); +} #define expectf_(expected, got, precision) ok(fabs((expected) - (got)) <= (precision), "Expected %f, got %f\n", (expected), (got)) #define expectf(expected, got) expectf_((expected), (got), 0.001) static BOOL save_metafiles; static BOOL load_metafiles; +static const WCHAR description[] = L"winetest"; + typedef struct emfplus_record { DWORD record_type; @@ -57,14 +64,14 @@ static void check_record(int count, const char *desc, const struct emfplus_recor todo_wine_if (expected->todo) ok(expected->record_type == actual->record_type && (expected->flags == actual->flags || broken(expected->broken_flags == actual->flags)), - "%s.%i: Expected record type 0x%x, got 0x%x. Expected flags %#x, got %#x.\n", desc, count, + "%s.%i: Expected record type 0x%lx, got 0x%lx. Expected flags %#lx, got %#lx.\n", desc, count, expected->record_type, actual->record_type, expected->flags, actual->flags); } else { todo_wine_if (expected->todo) ok(expected->record_type == actual->record_type, - "%s.%i: Expected record type 0x%x, got 0x%x.\n", desc, count, + "%s.%i: Expected record type 0x%lx, got 0x%lx.\n", desc, count, expected->record_type, actual->record_type); } } @@ -129,7 +136,7 @@ static int CALLBACK enum_emf_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETAR const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset]; ok(record->Size == record->DataSize + sizeof(EmfPlusRecordHeader), - "%s: EMF+ record datasize %u and size %u mismatch\n", state->desc, record->DataSize, record->Size); + "%s: EMF+ record datasize %lu and size %lu mismatch\n", state->desc, record->DataSize, record->Size); ok(offset + record->DataSize <= comment->cbData, "%s: EMF+ record truncated\n", state->desc); @@ -189,7 +196,7 @@ static int CALLBACK enum_emf_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETAR } else { - ok(0, "%s: Unexpected EMF 0x%x record\n", state->desc, lpEMFR->iType); + ok(0, "%s: Unexpected EMF 0x%lx record\n", state->desc, lpEMFR->iType); } return 1; @@ -287,7 +294,7 @@ static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned todo_wine_if (state->expected[state->count].todo) ok(state->expected[state->count].record_type == record_type, - "%s.%i: expected record type 0x%x, got 0x%x\n", state->desc, state->count, + "%s.%i: expected record type 0x%lx, got 0x%x\n", state->desc, state->count, state->expected[state->count].record_type, record_type); state->count++; } @@ -378,23 +385,23 @@ static void test_empty(void) MetafileHeader header; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; + UINT limit_dpi; hdc = CreateCompatibleDC(0); stat = GdipRecordMetafile(NULL, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); expect(InvalidParameter, stat); - stat = GdipRecordMetafile(hdc, MetafileTypeInvalid, &frame, MetafileFrameUnitPixel, description, &metafile); + stat = GdipRecordMetafile(hdc, (EmfType)MetafileTypeInvalid, &frame, MetafileFrameUnitPixel, description, &metafile); expect(InvalidParameter, stat); - stat = GdipRecordMetafile(hdc, MetafileTypeWmf, &frame, MetafileFrameUnitPixel, description, &metafile); + stat = GdipRecordMetafile(hdc, (EmfType)MetafileTypeWmf, &frame, MetafileFrameUnitPixel, description, &metafile); expect(InvalidParameter, stat); - stat = GdipRecordMetafile(hdc, MetafileTypeWmfPlaceable, &frame, MetafileFrameUnitPixel, description, &metafile); + stat = GdipRecordMetafile(hdc, (EmfType)MetafileTypeWmfPlaceable, &frame, MetafileFrameUnitPixel, description, &metafile); expect(InvalidParameter, stat); - stat = GdipRecordMetafile(hdc, MetafileTypeEmfPlusDual+1, &frame, MetafileFrameUnitPixel, description, &metafile); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusDual+1, &frame, MetafileFrameUnitPixel, description, &metafile); expect(InvalidParameter, stat); stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, NULL); @@ -408,6 +415,42 @@ static void test_empty(void) if (stat != Ok) return; + stat = GdipGetMetafileDownLevelRasterizationLimit(metafile, NULL); + expect(InvalidParameter, stat); + + stat = GdipGetMetafileDownLevelRasterizationLimit(NULL, &limit_dpi); + expect(InvalidParameter, stat); + + limit_dpi = 0xdeadbeef; + stat = GdipGetMetafileDownLevelRasterizationLimit(metafile, &limit_dpi); + expect(Ok, stat); + ok(limit_dpi == 96, "limit_dpi was %d\n", limit_dpi); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 255); + expect(Ok, stat); + + limit_dpi = 0xdeadbeef; + stat = GdipGetMetafileDownLevelRasterizationLimit(metafile, &limit_dpi); + expect(Ok, stat); + ok(limit_dpi == 255, "limit_dpi was %d\n", limit_dpi); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 0); + expect(Ok, stat); + + limit_dpi = 0xdeadbeef; + stat = GdipGetMetafileDownLevelRasterizationLimit(metafile, &limit_dpi); + expect(Ok, stat); + ok(limit_dpi == 96, "limit_dpi was %d\n", limit_dpi); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 1); + expect(InvalidParameter, stat); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 9); + expect(InvalidParameter, stat); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 10); + expect(Ok, stat); + stat = GdipGetHemfFromMetafile(metafile, &hemf); expect(InvalidParameter, stat); @@ -420,6 +463,14 @@ static void test_empty(void) stat = GdipDeleteGraphics(graphics); expect(Ok, stat); + limit_dpi = 0xdeadbeef; + stat = GdipGetMetafileDownLevelRasterizationLimit(metafile, &limit_dpi); + expect(WrongState, stat); + expect(0xdeadbeef, limit_dpi); + + stat = GdipSetMetafileDownLevelRasterizationLimit(metafile, 200); + expect(WrongState, stat); + check_metafile(metafile, empty_records, "empty metafile", dst_points, &frame, UnitPixel); sync_metafile(&metafile, "empty.emf"); @@ -442,29 +493,29 @@ static void test_empty(void) stat = GdipGetMetafileHeaderFromMetafile(metafile, &header); expect(Ok, stat); expect(MetafileTypeEmfPlusOnly, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version); expect(1, header.EmfPlusFlags); /* reference device was display, not printer */ expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(28, header.EmfPlusHeaderSize); expect(96, header.LogicalDpiX); - expect(96, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(0, U(header).EmfHeader.rclBounds.left); - expect(0, U(header).EmfHeader.rclBounds.top); - expect(-1, U(header).EmfHeader.rclBounds.right); - expect(-1, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(96, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(0, header.EmfHeader.rclBounds.left); + expect(0, header.EmfHeader.rclBounds.top); + expect(-1, header.EmfHeader.rclBounds.right); + expect(-1, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipGetHemfFromMetafile(metafile, &hemf); expect(Ok, stat); @@ -481,29 +532,29 @@ static void test_empty(void) stat = GdipGetMetafileHeaderFromEmf(hemf, &header); expect(Ok, stat); expect(MetafileTypeEmfPlusOnly, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version); expect(1, header.EmfPlusFlags); /* reference device was display, not printer */ expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(28, header.EmfPlusHeaderSize); expect(96, header.LogicalDpiX); - expect(96, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(0, U(header).EmfHeader.rclBounds.left); - expect(0, U(header).EmfHeader.rclBounds.top); - expect(-1, U(header).EmfHeader.rclBounds.right); - expect(-1, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(96, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(0, header.EmfHeader.rclBounds.left); + expect(0, header.EmfHeader.rclBounds.top); + expect(-1, header.EmfHeader.rclBounds.right); + expect(-1, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipCreateMetafileFromEmf(hemf, TRUE, &metafile); expect(Ok, stat); @@ -528,29 +579,29 @@ static void test_empty(void) stat = GdipGetMetafileHeaderFromMetafile(metafile, &header); expect(Ok, stat); expect(MetafileTypeEmfPlusOnly, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version); expect(1, header.EmfPlusFlags); /* reference device was display, not printer */ expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(28, header.EmfPlusHeaderSize); expect(96, header.LogicalDpiX); - expect(96, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(0, U(header).EmfHeader.rclBounds.left); - expect(0, U(header).EmfHeader.rclBounds.top); - expect(-1, U(header).EmfHeader.rclBounds.right); - expect(-1, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(96, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(0, header.EmfHeader.rclBounds.left); + expect(0, header.EmfHeader.rclBounds.top); + expect(-1, header.EmfHeader.rclBounds.right); + expect(-1, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipDisposeImage((GpImage*)metafile); expect(Ok, stat); @@ -581,7 +632,6 @@ static void test_getdc(void) static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; static const GpPointF dst_points_half[3] = {{0.0,0.0},{50.0,0.0},{0.0,50.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; HBRUSH hbrush, holdbrush; GpBitmap *bitmap; ARGB color; @@ -739,7 +789,6 @@ static void test_emfonly(void) MetafileHeader header; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; HBRUSH hbrush, holdbrush; GpBitmap *bitmap; ARGB color; @@ -816,30 +865,30 @@ static void test_emfonly(void) stat = GdipGetMetafileHeaderFromMetafile(metafile, &header); expect(Ok, stat); expect(MetafileTypeEmf, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); /* For some reason a recoreded EMF Metafile has an EMF+ version. */ todo_wine ok(header.Version == 0xdbc01001 || header.Version == 0xdbc01002, "Unexpected version %x\n", header.Version); expect(0, header.EmfPlusFlags); expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(0, header.EmfPlusHeaderSize); expect(0, header.LogicalDpiX); - expect(0, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(25, U(header).EmfHeader.rclBounds.left); - expect(25, U(header).EmfHeader.rclBounds.top); - expect(74, U(header).EmfHeader.rclBounds.right); - expect(74, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(0, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(25, header.EmfHeader.rclBounds.left); + expect(25, header.EmfHeader.rclBounds.top); + expect(74, header.EmfHeader.rclBounds.right); + expect(74, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); expect(Ok, stat); @@ -913,29 +962,29 @@ static void test_emfonly(void) stat = GdipGetMetafileHeaderFromEmf(hemf, &header); expect(Ok, stat); expect(MetafileTypeEmf, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); expect(0x10000, header.Version); expect(0, header.EmfPlusFlags); expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(0, header.EmfPlusHeaderSize); expect(0, header.LogicalDpiX); - expect(0, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(25, U(header).EmfHeader.rclBounds.left); - expect(25, U(header).EmfHeader.rclBounds.top); - expect(74, U(header).EmfHeader.rclBounds.right); - expect(74, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(0, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(25, header.EmfHeader.rclBounds.left); + expect(25, header.EmfHeader.rclBounds.top); + expect(74, header.EmfHeader.rclBounds.right); + expect(74, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipCreateMetafileFromEmf(hemf, TRUE, &metafile); expect(Ok, stat); @@ -960,29 +1009,29 @@ static void test_emfonly(void) stat = GdipGetMetafileHeaderFromMetafile(metafile, &header); expect(Ok, stat); expect(MetafileTypeEmf, header.Type); - expect(U(header).EmfHeader.nBytes, header.Size); + expect(header.EmfHeader.nBytes, header.Size); expect(0x10000, header.Version); expect(0, header.EmfPlusFlags); expectf(xres, header.DpiX); - expectf(xres, U(header).EmfHeader.szlDevice.cx / (REAL)U(header).EmfHeader.szlMillimeters.cx * 25.4); + expectf(xres, header.EmfHeader.szlDevice.cx / (REAL)header.EmfHeader.szlMillimeters.cx * 25.4); expectf(yres, header.DpiY); - expectf(yres, U(header).EmfHeader.szlDevice.cy / (REAL)U(header).EmfHeader.szlMillimeters.cy * 25.4); + expectf(yres, header.EmfHeader.szlDevice.cy / (REAL)header.EmfHeader.szlMillimeters.cy * 25.4); expect(0, header.X); expect(0, header.Y); expect(100, header.Width); expect(100, header.Height); expect(0, header.EmfPlusHeaderSize); expect(0, header.LogicalDpiX); - expect(0, header.LogicalDpiX); - expect(EMR_HEADER, U(header).EmfHeader.iType); - expect(25, U(header).EmfHeader.rclBounds.left); - expect(25, U(header).EmfHeader.rclBounds.top); - expect(74, U(header).EmfHeader.rclBounds.right); - expect(74, U(header).EmfHeader.rclBounds.bottom); - expect(0, U(header).EmfHeader.rclFrame.left); - expect(0, U(header).EmfHeader.rclFrame.top); - expectf_(100.0, U(header).EmfHeader.rclFrame.right * xres / 2540.0, 2.0); - expectf_(100.0, U(header).EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); + expect(0, header.LogicalDpiY); + expect(EMR_HEADER, header.EmfHeader.iType); + expect(25, header.EmfHeader.rclBounds.left); + expect(25, header.EmfHeader.rclBounds.top); + expect(74, header.EmfHeader.rclBounds.right); + expect(74, header.EmfHeader.rclBounds.bottom); + expect(0, header.EmfHeader.rclFrame.left); + expect(0, header.EmfHeader.rclFrame.top); + expectf_(100.0, header.EmfHeader.rclFrame.right * xres / 2540.0, 2.0); + expectf_(100.0, header.EmfHeader.rclFrame.bottom * yres / 2540.0, 2.0); stat = GdipDisposeImage((GpImage*)metafile); expect(Ok, stat); @@ -1036,7 +1085,6 @@ static void test_fillrect(void) static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; static const GpPointF dst_points_half[3] = {{0.0,0.0},{50.0,0.0},{0.0,50.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpBitmap *bitmap; ARGB color; GpBrush *brush; @@ -1149,7 +1197,6 @@ static void test_clear(void) HENHMETAFILE hemf; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{10.0,10.0},{20.0,10.0},{10.0,20.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpBitmap *bitmap; ARGB color; @@ -1221,7 +1268,6 @@ static void test_nullframerect(void) { GpMetafile *metafile; GpGraphics *graphics; HDC hdc, metafile_dc; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpBrush *brush; HBRUSH hbrush, holdbrush; GpRectF bounds; @@ -1329,6 +1375,64 @@ static void test_nullframerect(void) { stat = GdipDisposeImage((GpImage*)metafile); expect(Ok, stat); + + hdc = CreateCompatibleDC(0); + + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, NULL, MetafileFrameUnitMillimeter, + description, &metafile); + expect(Ok, stat); + + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &metafile_dc); + expect(Ok, stat); + + if (stat != Ok) + { + GdipDeleteGraphics(graphics); + GdipDisposeImage((GpImage*)metafile); + return; + } + + hbrush = CreateSolidBrush(0xff0000); + + holdbrush = SelectObject(metafile_dc, hbrush); + + Rectangle(metafile_dc, 25, 25, 75, 75); + + SelectObject(metafile_dc, holdbrush); + + DeleteObject(hbrush); + + stat = GdipReleaseDC(graphics, metafile_dc); + expect(Ok, stat); + + stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&brush); + expect(Ok, stat); + + stat = GdipFillRectangleI(graphics, brush, 20, 40, 10, 70); + expect(Ok, stat); + + stat = GdipDeleteBrush(brush); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + stat = GdipGetImageBounds((GpImage*)metafile, &bounds, &unit); + expect(Ok, stat); + expect(UnitPixel, unit); + expectf_(20.0, bounds.X, 0.05); + expectf_(25.0, bounds.Y, 0.05); + expectf_(55.0, bounds.Width, 1.00); + todo_wine expectf_(55.0, bounds.Width, 0.05); + expectf_(85.0, bounds.Height, 0.05); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); } static const emfplus_record pagetransform_records[] = { @@ -1356,7 +1460,6 @@ static void test_pagetransform(void) HDC hdc; static const GpRectF frame = {0.0, 0.0, 5.0, 5.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpBitmap *bitmap; ARGB color; GpBrush *brush; @@ -1561,7 +1664,6 @@ static void test_worldtransform(void) HDC hdc; static const GpRectF frame = {0.0, 0.0, 5.0, 5.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpBitmap *bitmap; ARGB color; GpBrush *brush; @@ -1818,7 +1920,6 @@ static void test_converttoemfplus(void) GpStatus (WINAPI *pGdipConvertToEmfPlus)( const GpGraphics *graphics, GpMetafile *metafile, BOOL *succ, EmfType emfType, const WCHAR *description, GpMetafile **outmetafile); static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpStatus stat; GpMetafile *metafile, *metafile2 = NULL, *emhmeta; GpGraphics *graphics; @@ -1836,7 +1937,7 @@ static void test_converttoemfplus(void) hdc = CreateCompatibleDC(0); - stat = GdipRecordMetafile(hdc, MetafileTypeEmf, &frame, MetafileFrameUnitPixel, description, &metafile); + stat = GdipRecordMetafile(hdc, EmfTypeEmfOnly, &frame, MetafileFrameUnitPixel, description, &metafile); expect(Ok, stat); stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &emhmeta); @@ -1860,10 +1961,10 @@ static void test_converttoemfplus(void) stat = pGdipConvertToEmfPlus(graphics, metafile, &succ, EmfTypeEmfPlusOnly, description, NULL); expect(InvalidParameter, stat); - stat = pGdipConvertToEmfPlus(graphics, metafile, NULL, MetafileTypeInvalid, NULL, &metafile2); + stat = pGdipConvertToEmfPlus(graphics, metafile, NULL, 0, NULL, &metafile2); expect(InvalidParameter, stat); - stat = pGdipConvertToEmfPlus(graphics, metafile, NULL, MetafileTypeEmfPlusDual+1, NULL, &metafile2); + stat = pGdipConvertToEmfPlus(graphics, metafile, NULL, EmfTypeEmfPlusDual+1, NULL, &metafile2); expect(InvalidParameter, stat); /* If we are already an Enhanced Metafile then the conversion fails. */ @@ -1898,7 +1999,6 @@ static void test_frameunit(void) GpGraphics *graphics; HDC hdc; static const GpRectF frame = {0.0, 0.0, 5.0, 5.0}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GpUnit unit; REAL dpix, dpiy; GpRectF bounds; @@ -1994,7 +2094,6 @@ static void test_containers(void) HDC hdc; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GraphicsContainer state1, state2; GpRectF srcrect, dstrect; REAL dpix, dpiy; @@ -2190,7 +2289,6 @@ static void test_clipping(void) HDC hdc; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; GraphicsState state; hdc = CreateCompatibleDC(0); @@ -2331,7 +2429,6 @@ static void test_gditransform(void) MetafileHeader header; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; static const GpPointF dst_points[3] = {{0.0,0.0},{40.0,0.0},{0.0,40.0}}; - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; HBRUSH hbrush, holdbrush; GpBitmap *bitmap; ARGB color; @@ -2458,7 +2555,6 @@ static const emfplus_record draw_image_metafile_records[] = { static void test_drawimage(void) { - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; static const GpPointF dst_points[3] = {{10.0,10.0},{85.0,15.0},{10.0,80.0}}; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; const ColorMatrix double_red = {{ @@ -2519,6 +2615,9 @@ static void test_drawimage(void) check_emfplus(hemf, draw_image_bitmap_records, "draw image bitmap"); + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); + /* test drawing metafile */ stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); expect(Ok, stat); @@ -2566,6 +2665,8 @@ static const emfplus_record properties_records[] = { { EmfPlusRecordTypeSetCompositingMode, CompositingModeSourceCopy }, { EmfPlusRecordTypeSetCompositingQuality, CompositingQualityHighQuality }, { EmfPlusRecordTypeSetInterpolationMode, InterpolationModeHighQualityBicubic }, + { EmfPlusRecordTypeSetRenderingOrigin }, + { EmfPlusRecordTypeSetRenderingOrigin }, { EmfPlusRecordTypeEndOfFile }, { EMR_EOF }, { 0 } @@ -2573,7 +2674,6 @@ static const emfplus_record properties_records[] = { static void test_properties(void) { - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; GpMetafile *metafile; @@ -2620,6 +2720,15 @@ static void test_properties(void) stat = GdipSetInterpolationMode(graphics, InterpolationModeHighQuality); expect(Ok, stat); + stat = GdipSetRenderingOrigin(graphics, 1, 2); + expect(Ok, stat); + + stat = GdipSetRenderingOrigin(graphics, 1, 2); + expect(Ok, stat); + + stat = GdipSetRenderingOrigin(graphics, 2, 1); + expect(Ok, stat); + stat = GdipDeleteGraphics(graphics); expect(Ok, stat); sync_metafile(&metafile, "properties.emf"); @@ -2651,7 +2760,6 @@ static const emfplus_record draw_path_records[] = { static void test_drawpath(void) { - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; GpMetafile *metafile; @@ -2716,9 +2824,7 @@ static const emfplus_record fill_path_records[] = { static void test_fillpath(void) { - static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0}; static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; - static const WCHAR winetestemfW[] = {'w','i','n','e','t','e','s','t','.','e','m','f',0}; GpMetafile *metafile; GpGraphics *graphics; @@ -2764,7 +2870,7 @@ static void test_fillpath(void) check_emfplus(hemf, fill_path_records, "fill path"); /* write to disk */ - DeleteEnhMetaFile(CopyEnhMetaFileW(hemf, winetestemfW)); + DeleteEnhMetaFile(CopyEnhMetaFileW(hemf, L"winetest.emf")); DeleteEnhMetaFile(hemf); @@ -2772,18 +2878,1125 @@ static void test_fillpath(void) expect(Ok, stat); /* should succeed when given path to an EMF */ - stat = GdipCreateMetafileFromWmfFile(winetestemfW, NULL, &metafile); + stat = GdipCreateMetafileFromWmfFile(L"winetest.emf", NULL, &metafile); expect(Ok, stat); stat = GdipDisposeImage((GpImage*)metafile); expect(Ok, stat); - DeleteFileW(winetestemfW); + DeleteFileW(L"winetest.emf"); - stat = GdipCreateMetafileFromWmfFile(winetestemfW, NULL, &metafile); + stat = GdipCreateMetafileFromWmfFile(L"winetest.emf", NULL, &metafile); expect(GenericError, stat); } +static const emfplus_record restoredc_records[] = { + { EMR_HEADER }, + { EMR_CREATEBRUSHINDIRECT }, + { EMR_SELECTOBJECT }, + + { EMR_SAVEDC }, + { EMR_SETVIEWPORTORGEX }, + { EMR_SAVEDC }, + { EMR_SETVIEWPORTORGEX }, + { EMR_SAVEDC }, + { EMR_SETVIEWPORTORGEX }, + + { EMR_RECTANGLE }, + + { EMR_RESTOREDC }, + { EMR_RECTANGLE }, + + { EMR_RESTOREDC }, + { EMR_RECTANGLE }, + + { EMR_SELECTOBJECT }, + { EMR_DELETEOBJECT }, + { EMR_EOF }, + { 0 } +}; + +static void test_restoredc(void) +{ + static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + + GpBitmap *bitmap; + GpGraphics *graphics; + GpMetafile *metafile; + GpStatus stat; + HDC hdc, metafile_dc; + HBRUSH hbrush, holdbrush; + ARGB color; + + hdc = CreateCompatibleDC(0); + + stat = GdipRecordMetafile(hdc, EmfTypeEmfOnly, &frame, MetafileFrameUnitPixel, + L"winetest", &metafile); + expect(Ok, stat); + + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipGetDC(graphics, &metafile_dc); + expect(Ok, stat); + + hbrush = CreateSolidBrush(0xff0000); + holdbrush = SelectObject(metafile_dc, hbrush); + + SaveDC(metafile_dc); + SetViewportOrgEx(metafile_dc, 20, 20, NULL); + + SaveDC(metafile_dc); + SetViewportOrgEx(metafile_dc, 40, 40, NULL); + + SaveDC(metafile_dc); + SetViewportOrgEx(metafile_dc, 60, 60, NULL); + + Rectangle(metafile_dc, 0, 0, 3, 3); + RestoreDC(metafile_dc, -2); + + Rectangle(metafile_dc, 0, 0, 3, 3); + RestoreDC(metafile_dc, -1); + + Rectangle(metafile_dc, 0, 0, 3, 3); + + SelectObject(metafile_dc, holdbrush); + DeleteObject(hbrush); + + stat = GdipReleaseDC(graphics, metafile_dc); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + check_metafile(metafile, restoredc_records, "restoredc metafile", dst_points, + &frame, UnitPixel); + sync_metafile(&metafile, "restoredc.emf"); + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(metafile, graphics, restoredc_records, "restoredc playback", dst_points, + &frame, UnitPixel); + + stat = GdipBitmapGetPixel(bitmap, 1, 1, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipBitmapGetPixel(bitmap, 21, 21, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipBitmapGetPixel(bitmap, 41, 41, &color); + expect(Ok, stat); + expect(0, color); + + stat = GdipBitmapGetPixel(bitmap, 61, 61, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + stat = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, stat); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static const emfplus_record drawdriverstring_records[] = { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypeFont << 8 }, + { EmfPlusRecordTypeDrawDriverString, 0x8000 }, + { EmfPlusRecordTypeObject, (ObjectTypeFont << 8) | 1 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2 }, + { EmfPlusRecordTypeDrawDriverString, 0x1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_drawdriverstring(void) +{ + static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + static const PointF solidpos[4] = {{10.0,10.0}, {20.0,10.0}, {30.0,10.0}, {40.0,10.0}}; + static const PointF hatchpos = {10.0,30.0}; + + GpBitmap *bitmap; + GpStatus stat; + GpGraphics *graphics; + GpFont *solidfont, *hatchfont; + GpBrush *solidbrush, *hatchbrush; + HDC hdc; + GpMatrix *matrix; + GpMetafile *metafile; + LOGFONTA logfont = { 0 }; + + hdc = CreateCompatibleDC(0); + + strcpy(logfont.lfFaceName, "Times New Roman"); + logfont.lfHeight = 12; + logfont.lfCharSet = DEFAULT_CHARSET; + + stat = GdipCreateFontFromLogfontA(hdc, &logfont, &solidfont); + if (stat == NotTrueTypeFont || stat == FileNotFound) + { + DeleteDC(hdc); + skip("Times New Roman not installed.\n"); + return; + } + + stat = GdipCloneFont(solidfont, &hatchfont); + expect(Ok, stat); + + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, + L"winetest", &metafile); + expect(Ok, stat); + + DeleteDC(hdc); + hdc = NULL; + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&solidbrush); + expect(Ok, stat); + + stat = GdipCreateHatchBrush(HatchStyleHorizontal, (ARGB)0xff00ff00, (ARGB)0xffff0000, + (GpHatch**)&hatchbrush); + expect(Ok, stat); + + stat = GdipCreateMatrix(&matrix); + expect(Ok, stat); + + stat = GdipDrawDriverString(graphics, L"Test", 4, solidfont, solidbrush, solidpos, + DriverStringOptionsCmapLookup, matrix); + expect(Ok, stat); + + stat = GdipSetMatrixElements(matrix, 1.5, 0.0, 0.0, 1.5, 0.0, 0.0); + expect(Ok, stat); + + stat = GdipDrawDriverString(graphics, L"Test ", 5, hatchfont, hatchbrush, &hatchpos, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, matrix); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + graphics = NULL; + + check_metafile(metafile, drawdriverstring_records, "drawdriverstring metafile", dst_points, + &frame, UnitPixel); + sync_metafile(&metafile, "drawdriverstring.emf"); + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(metafile, graphics, drawdriverstring_records, "drawdriverstring playback", + dst_points, &frame, UnitPixel); + + GdipDeleteMatrix(matrix); + GdipDeleteGraphics(graphics); + GdipDeleteBrush(solidbrush); + GdipDeleteBrush(hatchbrush); + GdipDeleteFont(solidfont); + GdipDeleteFont(hatchfont); + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)metafile); +} + +static const emfplus_record unknownfontdecode_records[] = { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypeFont << 8 }, + { EmfPlusRecordTypeDrawDriverString, 0x8000 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_unknownfontdecode(void) +{ + static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + static const PointF pos = {10.0,30.0}; + static const INT testfont0_resnum = 2; + + BOOL rval; + DWORD written, ressize; + GpBitmap *bitmap; + GpBrush *brush; + GpFont *font; + GpFontCollection *fonts; + GpFontFamily *family; + GpGraphics *graphics; + GpMetafile *metafile; + GpStatus stat; + HANDLE file; + HDC hdc; + HRSRC res; + INT fontscount; + WCHAR path[MAX_PATH]; + void *buf; + + /* Create a custom font from a resource. */ + GetTempPathW(MAX_PATH, path); + lstrcatW(path, L"wine_testfont0.ttf"); + + file = CreateFileW(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %ld\n", + wine_dbgstr_w(path), GetLastError()); + + res = FindResourceA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(testfont0_resnum), + (LPCSTR)RT_RCDATA); + ok(res != 0, "couldn't find resource\n"); + + buf = LockResource(LoadResource(GetModuleHandleA(NULL), res)); + ressize = SizeofResource(GetModuleHandleA(NULL), res); + + WriteFile(file, buf, ressize, &written, NULL); + expect(ressize, written); + + CloseHandle(file); + + stat = GdipNewPrivateFontCollection(&fonts); + expect(Ok, stat); + + stat = GdipPrivateAddFontFile(fonts, path); + expect(Ok, stat); + + stat = GdipGetFontCollectionFamilyCount(fonts, &fontscount); + expect(Ok, stat); + expect(1, fontscount); + + stat = GdipGetFontCollectionFamilyList(fonts, fontscount, &family, &fontscount); + expect(Ok, stat); + + stat = GdipCreateFont(family, 16.0, FontStyleRegular, UnitPixel, &font); + expect(Ok, stat); + + /* Start metafile recording. */ + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, + L"winetest", &metafile); + expect(Ok, stat); + DeleteDC(hdc); + hdc = NULL; + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreateSolidFill((ARGB)0xff0000ff, (GpSolidFill**)&brush); + expect(Ok, stat); + + /* Write something with the custom font so that it is encoded. */ + stat = GdipDrawDriverString(graphics, L"Test", 4, font, brush, &pos, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + expect(Ok, stat); + + /* Delete the custom font so that it is not present during playback. */ + GdipDeleteFont(font); + GdipDeletePrivateFontCollection(&fonts); + rval = DeleteFileW(path); + expect(TRUE, rval); + + GdipDeleteGraphics(graphics); + graphics = NULL; + + check_metafile(metafile, unknownfontdecode_records, "unknownfontdecode metafile", dst_points, + &frame, UnitPixel); + sync_metafile(&metafile, "unknownfontdecode.emf"); + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(metafile, graphics, unknownfontdecode_records, "unknownfontdecode playback", + dst_points, &frame, UnitPixel); + + GdipDeleteGraphics(graphics); + GdipDeleteBrush(brush); + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)metafile); +} + +static const emfplus_record fillregion_records[] = { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypeRegion << 8 }, + { EmfPlusRecordTypeFillRegion, 0x8000 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 1 }, + { EmfPlusRecordTypeObject, (ObjectTypeRegion << 8) | 2 }, + { EmfPlusRecordTypeFillRegion, 2 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_fillregion(void) +{ + static const GpPointF dst_points[3] = {{0.0, 0.0}, {100.0, 0.0}, {0.0, 100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + static const GpRectF solidrect = {20.0, 20.0, 20.0, 20.0}; + static const GpRectF hatchrect = {50.0, 50.0, 20.0, 20.0}; + + GpStatus stat; + GpMetafile *metafile; + GpGraphics *graphics; + GpBitmap *bitmap; + GpBrush *solidbrush, *hatchbrush; + GpRegion *solidregion, *hatchregion; + ARGB color; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, + L"winetest", &metafile); + expect(Ok, stat); + DeleteDC(hdc); + hdc = NULL; + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreateRegionRect(&solidrect, &solidregion); + expect(Ok, stat); + + stat = GdipCreateSolidFill(0xffaabbcc, (GpSolidFill**)&solidbrush); + expect(Ok, stat); + + stat = GdipFillRegion(graphics, solidbrush, solidregion); + expect(Ok, stat); + + stat = GdipCreateRegionRect(&hatchrect, &hatchregion); + expect(Ok, stat); + + stat = GdipCreateHatchBrush(HatchStyleHorizontal, 0xffff0000, 0xff0000ff, + (GpHatch**)&hatchbrush); + expect(Ok, stat); + + stat = GdipFillRegion(graphics, hatchbrush, hatchregion); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + graphics = NULL; + expect(Ok, stat); + + check_metafile(metafile, fillregion_records, "regionfill metafile", dst_points, + &frame, UnitPixel); + sync_metafile(&metafile, "regionfill.emf"); + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(metafile, graphics, fillregion_records, "regionfill playback", + dst_points, &frame, UnitPixel); + + stat = GdipBitmapGetPixel(bitmap, 25, 25, &color); + expect(Ok, stat); + expect(0xffaabbcc, color); + + stat = GdipBitmapGetPixel(bitmap, 56, 56, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 57, 57, &color); + expect(Ok, stat); + expect(0xff0000ff, color); + + GdipDeleteRegion(solidregion); + GdipDeleteRegion(hatchregion); + GdipDeleteBrush(solidbrush); + GdipDeleteBrush(hatchbrush); + GdipDeleteGraphics(graphics); + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)metafile); +} + +static const emfplus_record lineargradient_records[] = { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypeBrush << 8 }, + { EmfPlusRecordTypeFillRects, 0x4000 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 1 }, + { EmfPlusRecordTypeFillRects, 0x4000 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2 }, + { EmfPlusRecordTypeFillRects, 0x4000 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 3 }, + { EmfPlusRecordTypeFillRects, 0x4000 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_lineargradient(void) +{ + static const GpPointF dst_points[3] = {{0.0, 0.0}, {100.0, 0.0}, {0.0, 100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + static const GpRectF horizrect = {10.0, 10.0, 20.0, 20.0}; + static const GpRectF vertrect = {50.0, 10.0, 20.0, 20.0}; + static const GpRectF blendrect = {10.0, 50.0, 20.0, 20.0}; + static const GpRectF presetrect = {50.0, 50.0, 20.0, 20.0}; + static const REAL blendfac[3] = {0.0, 0.9, 1.0}; + static const REAL blendpos[3] = {0.0, 0.5, 1.0}; + static const ARGB pblendcolor[3] = {0xffff0000, 0xff00ff00, 0xff0000ff}; + static const REAL pblendpos[3] = {0.0, 0.5, 1.0}; + + ARGB color; + GpBitmap *bitmap; + GpBrush *horizbrush, *vertbrush, *blendbrush, *presetbrush; + GpGraphics *graphics; + GpMetafile *metafile; + GpStatus stat; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, + L"winetest", &metafile); + expect(Ok, stat); + DeleteDC(hdc); + hdc = NULL; + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + /* Test various brush types to cover all valid combinations + of optional serialized data. */ + stat = GdipCreateLineBrushFromRect(&horizrect, 0xffff0000, 0xff0000ff, + LinearGradientModeHorizontal, WrapModeTile, (GpLineGradient**)&horizbrush); + expect(Ok, stat); + + stat = GdipCreateLineBrushFromRect(&vertrect, 0xffff0000, 0xff0000ff, + LinearGradientModeVertical, WrapModeTile, (GpLineGradient**)&vertbrush); + expect(Ok, stat); + + stat = GdipCreateLineBrushFromRect(&blendrect, 0xffff0000, 0xff0000ff, + LinearGradientModeHorizontal, WrapModeTile, (GpLineGradient**)&blendbrush); + expect(Ok, stat); + + stat = GdipSetLineBlend((GpLineGradient*)blendbrush, blendfac, blendpos, 3); + expect(Ok, stat); + + stat = GdipCreateLineBrushFromRect(&presetrect, 0xffff0000, 0xff0000ff, + LinearGradientModeVertical, WrapModeTile, (GpLineGradient**)&presetbrush); + expect(Ok, stat); + + stat = GdipSetLinePresetBlend((GpLineGradient*)presetbrush, pblendcolor, pblendpos, 3); + expect(Ok, stat); + + stat = GdipFillRectangles(graphics, vertbrush, &vertrect, 1); + expect(Ok, stat); + + stat = GdipFillRectangles(graphics, horizbrush, &horizrect, 1); + expect(Ok, stat); + + stat = GdipFillRectangles(graphics, blendbrush, &blendrect, 1); + expect(Ok, stat); + + stat = GdipFillRectangles(graphics, presetbrush, &presetrect, 1); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + graphics = NULL; + expect(Ok, stat); + + check_metafile(metafile, lineargradient_records, "lineargradient metafile", dst_points, + &frame, UnitPixel); + sync_metafile(&metafile, "lineargradient.emf"); + + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(metafile, graphics, lineargradient_records, "lineargradient playback", + dst_points, &frame, UnitPixel); + + /* Verify horizontal gradient fill. */ + stat = GdipBitmapGetPixel(bitmap, 10, 10, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 18, 10, &color); + expect(Ok, stat); + expect(0xff990066, color); + + /* Verify vertical gradient fill. */ + stat = GdipBitmapGetPixel(bitmap, 50, 10, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 50, 18, &color); + expect(Ok, stat); + expect(0xff990066, color); + + /* Verify custom blend gradient fill. */ + stat = GdipBitmapGetPixel(bitmap, 10, 50, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 18, 50, &color); + expect(Ok, stat); + expect(0xff4700b8, color); + + /* Verify preset color gradient fill. */ + stat = GdipBitmapGetPixel(bitmap, 50, 50, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 50, 60, &color); + expect(Ok, stat); + expect(0xff00ff00, color); + + GdipDeleteBrush(vertbrush); + GdipDeleteBrush(horizbrush); + GdipDeleteBrush(blendbrush); + GdipDeleteBrush(presetbrush); + GdipDeleteGraphics(graphics); + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)metafile); +} + +static HDC create_printer_dc(void) +{ + char buffer[260]; + DWORD len; + PRINTER_INFO_2A *pbuf = NULL; + DRIVER_INFO_3A *dbuf = NULL; + HANDLE hprn = 0; + HDC hdc = 0; + HMODULE winspool = LoadLibraryA("winspool.drv"); + BOOL (WINAPI *pOpenPrinterA)(LPSTR, HANDLE *, LPPRINTER_DEFAULTSA); + BOOL (WINAPI *pGetDefaultPrinterA)(LPSTR, LPDWORD); + BOOL (WINAPI *pGetPrinterA)(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD); + BOOL (WINAPI *pGetPrinterDriverA)(HANDLE, LPSTR, DWORD, LPBYTE, DWORD, LPDWORD); + BOOL (WINAPI *pClosePrinter)(HANDLE); + + pGetDefaultPrinterA = (void *)GetProcAddress(winspool, "GetDefaultPrinterA"); + pOpenPrinterA = (void *)GetProcAddress(winspool, "OpenPrinterA"); + pGetPrinterA = (void *)GetProcAddress(winspool, "GetPrinterA"); + pGetPrinterDriverA = (void *)GetProcAddress(winspool, "GetPrinterDriverA"); + pClosePrinter = (void *)GetProcAddress(winspool, "ClosePrinter"); + + if (!pGetDefaultPrinterA || !pOpenPrinterA || !pGetPrinterA || !pGetPrinterDriverA || !pClosePrinter) + goto done; + + len = sizeof(buffer); + if (!pGetDefaultPrinterA(buffer, &len)) goto done; + if (!pOpenPrinterA(buffer, &hprn, NULL)) goto done; + + pGetPrinterA(hprn, 2, NULL, 0, &len); + pbuf = malloc(len); + if (!pGetPrinterA(hprn, 2, (LPBYTE)pbuf, len, &len)) goto done; + + pGetPrinterDriverA(hprn, NULL, 3, NULL, 0, &len); + dbuf = malloc(len); + if (!pGetPrinterDriverA(hprn, NULL, 3, (LPBYTE)dbuf, len, &len)) goto done; + + hdc = CreateDCA(dbuf->pDriverPath, pbuf->pPrinterName, pbuf->pPortName, pbuf->pDevMode); + trace("hdc %p for driver '%s' printer '%s' port '%s'\n", hdc, + dbuf->pDriverPath, pbuf->pPrinterName, pbuf->pPortName); +done: + free(dbuf); + free(pbuf); + if (hprn) pClosePrinter(hprn); + if (winspool) FreeLibrary(winspool); + return hdc; +} + +static void test_printer_dc(void) +{ + HDC hdc; + Status status; + RectF frame = { 0.0, 0.0, 1.0, 1.0 }; + GpMetafile *metafile; + GpGraphics *graphics; + REAL dpix, dpiy; + + hdc = create_printer_dc(); + if (!hdc) + { + skip("could not create a DC for the default printer\n"); + return; + } + + status = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitInch, L"winetest", &metafile); + expect(Ok, status); + + status = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, status); + + GdipGetDpiX(graphics, &dpix); + GdipGetDpiX(graphics, &dpiy); + expectf((REAL)(GetDeviceCaps(hdc, LOGPIXELSX)), dpix); + expectf((REAL)(GetDeviceCaps(hdc, LOGPIXELSY)), dpiy); + + GdipDeleteGraphics(graphics); + GdipDisposeImage((GpImage *)metafile); +} + +static const emfplus_record draw_ellipse_records[] = +{ + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypePen << 8 }, + { EmfPlusRecordTypeDrawEllipse, 0x4000 }, + { EMR_SAVEDC, 0, 1 }, + { EMR_SETICMMODE, 0, 1 }, + { EMR_BITBLT, 0, 1 }, + { EMR_RESTOREDC, 0, 1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_drawellipse(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + + GpMetafile *metafile; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpStatus stat; + GpPen *pen; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen); + expect(Ok, stat); + + stat = GdipDrawEllipse(graphics, pen, 1.0f, 1.0f, 16.0f, 32.0f); + expect(Ok, stat); + + stat = GdipDeletePen(pen); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "draw_ellipse.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, draw_ellipse_records, "draw ellipse"); + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static const emfplus_record fill_ellipse_records[] = +{ + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeFillEllipse, 0xc000 }, + { EMR_SAVEDC, 0, 1 }, + { EMR_SETICMMODE, 0, 1 }, + { EMR_BITBLT, 0, 1 }, + { EMR_RESTOREDC, 0, 1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_fillellipse(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + + GpMetafile *metafile; + GpGraphics *graphics; + GpSolidFill *brush; + HENHMETAFILE hemf; + GpStatus stat; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreateSolidFill(0xffaabbcc, &brush); + expect(Ok, stat); + + stat = GdipFillEllipse(graphics, (GpBrush *)brush, 0.0f, 0.0f, 10.0f, 20.0f); + expect(Ok, stat); + + stat = GdipDeleteBrush((GpBrush*)brush); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "fill_ellipse.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, fill_ellipse_records, "fill ellipse"); + + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static const emfplus_record draw_rectangle_records[] = +{ + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypePen << 8 }, + { EmfPlusRecordTypeDrawRects, 0x4000 }, + { EMR_SAVEDC, 0, 1 }, + { EMR_SETICMMODE, 0, 1 }, + { EMR_BITBLT, 0, 1 }, + { EMR_RESTOREDC, 0, 1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_drawrectangle(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + + GpMetafile *metafile; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpStatus stat; + GpPen *pen; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen); + expect(Ok, stat); + + stat = GdipDrawRectangle(graphics, pen, 1.0f, 1.0f, 16.0f, 32.0f); + expect(Ok, stat); + + stat = GdipDeletePen(pen); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "draw_rectangle.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, draw_rectangle_records, "draw rectangle"); + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static void test_offsetclip(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + static const emfplus_record offset_clip_records[] = + { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeOffsetClip }, + { EmfPlusRecordTypeOffsetClip }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 }, + }; + + GpMetafile *metafile; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpStatus stat; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipTranslateClip(graphics, 1.0f, -1.0f); + expect(Ok, stat); + + stat = GdipTranslateClipI(graphics, 2, 3); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "offset_clip.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, offset_clip_records, "offset clip"); + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static void test_resetclip(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + static const emfplus_record reset_clip_records[] = + { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeResetClip }, + { EmfPlusRecordTypeResetClip }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 }, + }; + + GpMetafile *metafile; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpStatus stat; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipResetClip(graphics); + expect(Ok, stat); + + stat = GdipResetClip(graphics); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "reset_clip.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, reset_clip_records, "reset clip"); + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static void test_setclippath(void) +{ + static const GpRectF frame = { 0.0f, 0.0f, 100.0f, 100.0f }; + static const emfplus_record set_clip_path_records[] = + { + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypePath << 8 }, + { EmfPlusRecordTypeSetClipPath, 0x100 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 }, + }; + + GpMetafile *metafile; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpStatus stat; + GpPath *path; + HDC hdc; + + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, stat); + stat = GdipAddPathLine(path, 5, 5, 30, 30); + expect(Ok, stat); + stat = GdipAddPathLine(path, 30, 30, 5, 30); + expect(Ok, stat); + + stat = GdipSetClipPath(graphics, path, CombineModeIntersect); + expect(Ok, stat); + + stat = GdipDeletePath(path); + expect(Ok, stat); + + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + sync_metafile(&metafile, "set_clip_path.emf"); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, set_clip_path_records, "set clip path"); + DeleteEnhMetaFile(hemf); + + stat = GdipDisposeImage((GpImage*)metafile); + expect(Ok, stat); +} + +static const emfplus_record pen_dc_records[] = +{ + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypePen << 8 }, + { EmfPlusRecordTypeObject, (ObjectTypePath << 8) | 1 }, + { EmfPlusRecordTypeDrawPath, 1 }, + { EMR_SAVEDC, 0, 1 }, + { EMR_SETICMMODE, 0, 1 }, + { EMR_BITBLT, 0, 1 }, + { EMR_RESTOREDC, 0, 1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static const emfplus_record pen_bitmap_records[] = +{ + { EMR_HEADER }, + { EmfPlusRecordTypeHeader }, + { EmfPlusRecordTypeObject, ObjectTypePen << 8 }, + { EmfPlusRecordTypeObject, (ObjectTypePath << 8) | 1 }, + { EmfPlusRecordTypeDrawPath, 1 }, + { EmfPlusRecordTypeEndOfFile }, + { EMR_EOF }, + { 0 } +}; + +static void test_pen(void) +{ + static const GpPointF dst_points[3] = {{0.0, 0.0}, {100.0, 0.0}, {0.0, 100.0}}; + static const GpRectF frame = {0.0, 0.0, 100.0, 100.0}; + GpMetafile *metafile, *clone_metafile; + GpPath *draw_path, *line_cap_path; + GpCustomLineCap *custom_line_cap; + GpGraphics *graphics; + HENHMETAFILE hemf; + GpBitmap *bitmap; + GpStatus stat; + ARGB color; + GpPen *pen; + BOOL ret; + HDC hdc; + + /* Record */ + hdc = CreateCompatibleDC(0); + stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile); + expect(Ok, stat); + DeleteDC(hdc); + + stat = GdipGetImageGraphicsContext((GpImage *)metafile, &graphics); + expect(Ok, stat); + + stat = GdipCreatePath(FillModeAlternate, &draw_path); + expect(Ok, stat); + stat = GdipAddPathLine(draw_path, 25, 25, 25, 75); + expect(Ok, stat); + + stat = GdipCreatePen1((ARGB)0xffff0000, 1.0f, UnitPixel, &pen); + expect(Ok, stat); + stat = GdipCreatePath(FillModeAlternate, &line_cap_path); + expect(Ok, stat); + stat = GdipAddPathRectangle(line_cap_path, 5.0, 5.0, 10.0, 10.0); + expect(Ok, stat); + stat = GdipCreateCustomLineCap(NULL, line_cap_path, LineCapCustom, 0.0, &custom_line_cap); + expect(Ok, stat); + stat = GdipSetPenCustomStartCap(pen, custom_line_cap); + expect(Ok, stat); + stat = GdipSetPenCustomEndCap(pen, custom_line_cap); + expect(Ok, stat); + stat = GdipDeleteCustomLineCap(custom_line_cap); + expect(Ok, stat); + stat = GdipDeletePath(line_cap_path); + expect(Ok, stat); + + stat = GdipDrawPath(graphics, pen, draw_path); + expect(Ok, stat); + + stat = GdipDeletePen(pen); + expect(Ok, stat); + stat = GdipDeletePath(draw_path); + expect(Ok, stat); + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + + sync_metafile(&metafile, "pen.emf"); + GdipCloneImage((GpImage *)metafile, (GpImage **)&clone_metafile); + + stat = GdipGetHemfFromMetafile(metafile, &hemf); + expect(Ok, stat); + + check_emfplus(hemf, pen_dc_records, "pen record"); + + ret = DeleteEnhMetaFile(hemf); + ok(ret != 0, "Failed to delete enhmetafile.\n"); + stat = GdipDisposeImage((GpImage *)metafile); + expect(Ok, stat); + + /* Play back */ + stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat24bppRGB, NULL, &bitmap); + expect(Ok, stat); + + stat = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics); + expect(Ok, stat); + + play_metafile(clone_metafile, graphics, pen_bitmap_records, "pen playback", dst_points, &frame, UnitPixel); + + stat = GdipBitmapGetPixel(bitmap, 10, 10, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipBitmapGetPixel(bitmap, 40, 90, &color); + expect(Ok, stat); + expect(0xffff0000, color); + + stat = GdipDisposeImage((GpImage *)clone_metafile); + expect(Ok, stat); + stat = GdipDeleteGraphics(graphics); + expect(Ok, stat); + stat = GdipDisposeImage((GpImage *)bitmap); + expect(Ok, stat); +} + START_TEST(metafile) { struct GdiplusStartupInput gdiplusStartupInput; @@ -2832,6 +4045,19 @@ START_TEST(metafile) test_properties(); test_drawpath(); test_fillpath(); + test_restoredc(); + test_drawdriverstring(); + test_unknownfontdecode(); + test_fillregion(); + test_lineargradient(); + test_printer_dc(); + test_drawellipse(); + test_fillellipse(); + test_drawrectangle(); + test_offsetclip(); + test_resetclip(); + test_setclippath(); + test_pen(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/pathiterator.c b/modules/rostests/winetests/gdiplus/pathiterator.c index 9bd3ac5452e..1c0d8b391b6 100644 --- a/modules/rostests/winetests/gdiplus/pathiterator.c +++ b/modules/rostests/winetests/gdiplus/pathiterator.c @@ -464,18 +464,38 @@ static void test_nextsubpath(void) GpPath *path; GpPathIterator *iter; GpStatus stat; - INT start, end, result; + INT result, start, end; BOOL closed; /* empty path */ GdipCreatePath(FillModeAlternate, &path); GdipCreatePathIter(&iter, path); - result = -2; - closed = TRUE; + result = start = end = closed = (INT)0xdeadbeef; stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); expect(Ok, stat); expect(0, result); + expect((INT)0xdeadbeef, start); + expect((INT)0xdeadbeef, end); + expect((INT)0xdeadbeef, closed); + + /* four points figure */ + GdipAddPathLine(path, 0.0, 0.0, 10.0, 30.0); + GdipAddPathLine(path, -10.0, 5.0, 15.0, 8.0); + GdipCreatePathIter(&iter, path); + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(4, result); + expect(0, start); + expect(3, end); + expect(FALSE, closed); + + /* No more subpaths*/ + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(0, result); + expect(0, start); + expect(0, end); expect(TRUE, closed); GdipDeletePathIter(iter); @@ -487,8 +507,10 @@ static void test_nextpathtype(void) GpPath *path; GpPathIterator *iter; GpStatus stat; - INT start, end, result; + INT count, result, start, end; BYTE type; + BOOL closed; + GpPointF point; GdipCreatePath(FillModeAlternate, &path); GdipCreatePathIter(&iter, path); @@ -510,53 +532,257 @@ static void test_nextpathtype(void) expect(InvalidParameter, stat); /* empty path */ - start = end = result = (INT)0xdeadbeef; - stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); - todo_wine expect(Ok, stat); - expect((INT)0xdeadbeef, start); - expect((INT)0xdeadbeef, end); - todo_wine expect(0, result); - GdipDeletePathIter(iter); - - /* single figure */ - GdipAddPathLine(path, 0.0, 0.0, 10.0, 30.0); - GdipCreatePathIter(&iter, path); - start = end = result = (INT)0xdeadbeef; + result = start = end = (INT)0xdeadbeef; type = 255; /* out of range */ stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); - todo_wine expect(Ok, stat); + expect(Ok, stat); + expect(0, result); + expect(255, type); expect((INT)0xdeadbeef, start); expect((INT)0xdeadbeef, end); - expect(255, type); - todo_wine expect(0, result); GdipDeletePathIter(iter); + + /* Single PathPointTypeStart point */ + point.X = 1.0; + point.Y = 2.0; + stat = GdipAddPathLine2(path, &point, 1); + expect(Ok, stat); + stat = GdipCreatePathIter(&iter, path); + expect(Ok, stat); + + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(1, result); + expect(0, start); + expect(0, end); + expect(FALSE, closed); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(1, result); + expect(255, type); + expect(0, start); + expect(0, end); + + GdipDeletePathIter(iter); + GdipDeletePath(path); + + + /* Figure with three subpaths */ + GdipCreatePath(FillModeAlternate, &path); + stat = GdipAddPathLine(path, 0.0, 0.0, 10.0, 30.0); + expect(Ok, stat); + stat = GdipAddPathRectangle(path, 1.0, 2.0, 3.0, 4.0); + expect(Ok, stat); stat = GdipAddPathEllipse(path, 0.0, 0.0, 35.0, 70.0); expect(Ok, stat); - GdipCreatePathIter(&iter, path); + stat = GdipCreatePathIter(&iter, path); + expect(Ok, stat); + + /* When subPath is not set, + it is not possible to get Path Type */ + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(0, result); + expect(255, type); + expect((INT)0xdeadbeef, start); + expect((INT)0xdeadbeef, end); + + /* When subPath is not set, + it is possible to get number of path points */ + stat = GdipPathIterGetCount(iter, &count); + expect(Ok, stat); + expect(19, count); + + /* Set subPath (Line) */ + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(2, result); + expect(0, start); + expect(1, end); + expect(FALSE, closed); + + /* When subPath is set (Line), return position of points with the same type */ + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(2, result); + expect(PathPointTypeLine, type); + expect(0, start); + expect(1, end); + + /* When NextSubpath is running twice, without invocation NextPathType + (skipping Rectangle figure), make sure that PathType index is updated */ + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(4, result); + expect(2, start); + expect(5, end); + expect(TRUE, closed); + + /* Increment subPath (to Ellipse figure) */ + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(13, result); + expect(6, start); + expect(18, end); + expect(TRUE, closed); + + /* When subPath is set to Ellipse figure, the type is PathPointTypeBezier */ + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(13, result); + expect(PathPointTypeBezier, type); + expect(6, start); + expect(18, end); + + /* As there is no more subpaths, when subPath is incremented, + it returns zeros */ + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(0, result); + expect(0, start); + expect(0, end); + expect(TRUE, closed); + + /* When no more elements, after invoking NextPathType + the output numbers are not changed */ start = end = result = (INT)0xdeadbeef; type = 255; /* out of range */ stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); - todo_wine expect(Ok, stat); + expect(Ok, stat); + expect(0, result); + expect(255, type); expect((INT)0xdeadbeef, start); expect((INT)0xdeadbeef, end); - expect(255, type); - todo_wine expect(0, result); - GdipDeletePathIter(iter); - /* closed */ - GdipClosePathFigure(path); - GdipCreatePathIter(&iter, path); - start = end = result = (INT)0xdeadbeef; + GdipDeletePathIter(iter); + GdipDeletePath(path); + + + /* Single PathPointTypeStart point */ + GdipCreatePath(FillModeAlternate, &path); + point.X = 1.0; + point.Y = 2.0; + stat = GdipAddPathLine2(path, &point, 1); + expect(Ok, stat); + stat = GdipCreatePathIter(&iter, path); + expect(Ok, stat); + + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(1, result); + expect(0, start); + expect(0, end); + expect(FALSE, closed); + + result = start = end = (INT)0xdeadbeef; type = 255; /* out of range */ stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); - todo_wine expect(Ok, stat); + expect(Ok, stat); + expect(1, result); + expect(255, type); + expect(0, start); + expect(0, end); + + GdipDeletePathIter(iter); + GdipDeletePath(path); + + + /* Mixed PathPointTypeLine and PathPointTypeBezier points */ + GdipCreatePath(FillModeAlternate, &path); + stat = GdipAddPathLine(path, -1.0, 1.0, 10.0, 15.0); + expect(Ok, stat); + /* Starting point of Bezier figure, is the same + as ending point of previous figure (10.0, 15.0) */ + stat = GdipAddPathBezier(path, 10.0, 15.0, 9.0, 9.0, 6.0, 7.0, 9.0, 0.0); + expect(Ok, stat); + /* Starting point of figure, is different that ending point of previous figure */ + stat = GdipAddPathLine(path, -1.0, -1.0, 7.5, 12.0); + expect(Ok, stat); + stat = GdipStartPathFigure(path); + expect(Ok, stat); + stat = GdipAddPathLine(path, 50.0, 50.0, 110.0, 40.0); + expect(Ok, stat); + stat = GdipCreatePathIter(&iter, path); + expect(Ok, stat); + + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(7, result); + expect(0, start); + expect(6, end); + expect(FALSE, closed); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(2, result); + expect(PathPointTypeLine, type); + expect(0, start); + expect(1, end); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(4, result); + expect(PathPointTypeBezier, type); + expect(1, start); + expect(4, end); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(3, result); + expect(PathPointTypeLine, type); + expect(4, start); + expect(6, end); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(0, result); + expect(255, type); expect((INT)0xdeadbeef, start); expect((INT)0xdeadbeef, end); - expect(255, type); - todo_wine expect(0, result); - GdipDeletePathIter(iter); + result = start = end = closed = (INT)0xdeadbeef; + stat = GdipPathIterNextSubpath(iter, &result, &start, &end, &closed); + expect(Ok, stat); + expect(2, result); + expect(7, start); + expect(8, end); + expect(FALSE, closed); + + result = start = end = (INT)0xdeadbeef; + type = 255; /* out of range */ + stat = GdipPathIterNextPathType(iter, &result, &type, &start, &end); + expect(Ok, stat); + expect(2, result); + expect(PathPointTypeLine, type); + expect(7, start); + expect(8, end); + + GdipDeletePathIter(iter); GdipDeletePath(path); } diff --git a/modules/rostests/winetests/gdiplus/pen.c b/modules/rostests/winetests/gdiplus/pen.c index ceab92acffd..445f2dbb5b8 100644 --- a/modules/rostests/winetests/gdiplus/pen.c +++ b/modules/rostests/winetests/gdiplus/pen.c @@ -24,7 +24,11 @@ #include "gdiplus.h" #include "wine/test.h" -#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got) +#define expect(expected,got) expect_(__LINE__, expected, got) +static inline void expect_(unsigned line, DWORD expected, DWORD got) +{ + ok_(__FILE__, line)(expected == got, "Expected %.8ld, got %.8ld\n", expected, got); +} #define expectf(expected, got) ok(fabs(got - expected) < 0.1, "Expected %.2f, got %.2f\n", expected, got) static void test_startup(void) @@ -175,7 +179,7 @@ static void test_dasharray(void) dashes[4] = 14.0; dashes[5] = -100.0; dashes[6] = -100.0; - dashes[7] = dashes[8] = dashes[9] = dashes[10] = dashes[11] = 0.0; + dashes[7] = dashes[8] = dashes[9] = dashes[10] = dashes[11] = 1.0; /* setting the array sets the type to custom */ GdipGetPenDashStyle(pen, &style); @@ -218,10 +222,13 @@ static void test_dasharray(void) /* Some invalid array values. */ status = GdipSetPenDashArray(pen, &dashes[7], 5); - expect(InvalidParameter, status); + expect(Ok, status); dashes[9] = -1.0; status = GdipSetPenDashArray(pen, &dashes[7], 5); expect(InvalidParameter, status); + dashes[9] = 0.0; + status = GdipSetPenDashArray(pen, &dashes[7], 5); + expect(InvalidParameter, status); /* Try to set with count = 0. */ GdipSetPenDashStyle(pen, DashStyleDot); @@ -343,7 +350,11 @@ static void test_compoundarray(void) { GpStatus status; GpPen *pen; + REAL *returnvalues; static const REAL testvalues[] = {0.2, 0.4, 0.6, 0.8}; + static const REAL notSortedValues[] = {0.2, 0.6, 0.4, 0.8}; + static const REAL negativeValues[] = {-1.2, 0.4, 0.6, 0.8}; + static const REAL tooLargeValues[] = {0.2, 0.4, 0.6, 2.8}; INT count; status = GdipSetPenCompoundArray(NULL, testvalues, 4); @@ -360,10 +371,9 @@ static void test_compoundarray(void) count = 10; status = GdipGetPenCompoundCount(pen, &count); -todo_wine { expect(Ok, status); ok(count == 0, "Unexpected compound count %d\n", count); -} + status = GdipSetPenCompoundArray(pen, NULL, 0); expect(InvalidParameter, status); status = GdipSetPenCompoundArray(pen, NULL, 4); @@ -375,17 +385,43 @@ todo_wine { status = GdipSetPenCompoundArray(pen, testvalues, -2); expect(InvalidParameter, status); + status = GdipSetPenCompoundArray(pen, notSortedValues, 4); + expect(InvalidParameter, status); + status = GdipSetPenCompoundArray(pen, negativeValues, 4); + expect(InvalidParameter, status); + status = GdipSetPenCompoundArray(pen, tooLargeValues, 4); + expect(InvalidParameter, status); + status = GdipSetPenCompoundArray(pen, testvalues, 4); - todo_wine expect(Ok, status); + expect(Ok, status); status = GdipSetPenCompoundArray(pen, NULL, 0); expect(InvalidParameter, status); count = 0; status = GdipGetPenCompoundCount(pen, &count); -todo_wine { expect(Ok, status); ok(count == 4, "Unexpected compound count %d\n", count); -} + + returnvalues = calloc(5, sizeof(REAL)); + /* When count larger than stored array return error */ + status = GdipGetPenCompoundArray(pen, returnvalues, 40); + expect(InvalidParameter, status); + status = GdipGetPenCompoundArray(NULL, returnvalues, 4); + expect(InvalidParameter, status); + /* When count is zero, it should do nothing */ + status = GdipGetPenCompoundArray(pen, returnvalues, 0); + expect(Ok, status); + ok(returnvalues[0] == 0.0, "Unexpected compound array %f\n", returnvalues[0]); + + status = GdipGetPenCompoundArray(pen, returnvalues, 4); + expect(Ok, status); + ok(memcmp(returnvalues, testvalues, 4 * sizeof(REAL)) == 0, "Unexpected compound array\n"); + + status = GdipGetPenCompoundArray(pen, returnvalues, -10); + expect(Ok, status); + ok(memcmp(returnvalues, testvalues, 4 * sizeof(REAL)) == 0, "Unexpected compound array\n"); + + free(returnvalues); GdipDeletePen(pen); } @@ -409,9 +445,10 @@ static void test_transform(void) { GpStatus status; GpPen *pen; - GpMatrix *matrix, *matrix2; + GpMatrix *matrix, *matrix2, *not_invertible_matrix; REAL values[6]; + /* Check default Pen Transformation */ status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen); expect(Ok, status); @@ -431,6 +468,26 @@ static void test_transform(void) expectf(0.0, values[4]); expectf(0.0, values[5]); + /* Setting Pen Tranformation to Not invertible matrix, should return InvalidParameter */ + GdipCreateMatrix2(3.0, 3.0, 2.0, 2.0, 6.0, 3.0, ¬_invertible_matrix); + + status = GdipSetPenTransform(pen, not_invertible_matrix); + expect(InvalidParameter, status); + GdipDeleteMatrix(not_invertible_matrix); + + status = GdipGetPenTransform(pen, matrix); + expect(Ok, status); + status = GdipGetMatrixElements(matrix, values); + expect(Ok, status); + + expectf(1.0, values[0]); + expectf(0.0, values[1]); + expectf(0.0, values[2]); + expectf(1.0, values[3]); + expectf(0.0, values[4]); + expectf(0.0, values[5]); + + /* Setting Pen Tranformation to invertible matrix, should be successfull */ GdipCreateMatrix2(3.0, -2.0, 5.0, 2.0, 6.0, 3.0, &matrix2); status = GdipSetPenTransform(pen, matrix2); expect(Ok, status); diff --git a/modules/rostests/winetests/gdiplus/region.c b/modules/rostests/winetests/gdiplus/region.c index d69f09db602..93c6c53ebc1 100644 --- a/modules/rostests/winetests/gdiplus/region.c +++ b/modules/rostests/winetests/gdiplus/region.c @@ -33,11 +33,15 @@ #define RGNDATA_MAGIC 0xdbc01001 #define RGNDATA_MAGIC2 0xdbc01002 -#define expect(expected, got) ok((got) == (expected), "Expected %.8x, got %.8x\n", (expected), (got)) +#define expect(expected,got) expect_(__LINE__, expected, got) +static inline void expect_(unsigned line, DWORD expected, DWORD got) +{ + ok_(__FILE__, line)(expected == got, "Expected %ld, got %ld\n", expected, got); +} #define expectf_(expected, got, precision) ok(fabs((expected) - (got)) < (precision), "Expected %f, got %f\n", (expected), (got)) #define expectf(expected, got) expectf_((expected), (got), 0.001) -#define expect_magic(value) ok(broken(*(value) == RGNDATA_MAGIC) || *(value) == RGNDATA_MAGIC2, "Expected a known magic value, got %8x\n", *(value)) +#define expect_magic(value) ok(broken(*(value) == RGNDATA_MAGIC) || *(value) == RGNDATA_MAGIC2, "Expected a known magic value, got %8lx\n", *(value)) #define expect_dword(value, expected) expect((expected), *(value)) #define expect_float(value, expected) expectf((expected), *(FLOAT *)(value)) @@ -59,19 +63,19 @@ static void verify_region(HRGN hrgn, const RECT *rc) ret = GetRegionData(hrgn, 0, NULL); if (IsRectEmpty(rc)) - ok(ret == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %u\n", ret); + ok(ret == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %lu\n", ret); else - ok(ret == sizeof(rgn.data.rdh) + sizeof(RECT), "expected sizeof(rgn), got %u\n", ret); + ok(ret == sizeof(rgn.data.rdh) + sizeof(RECT), "expected sizeof(rgn), got %lu\n", ret); if (!ret) return; ret = GetRegionData(hrgn, sizeof(rgn), &rgn.data); if (IsRectEmpty(rc)) - ok(ret == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %u\n", ret); + ok(ret == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %lu\n", ret); else - ok(ret == sizeof(rgn.data.rdh) + sizeof(RECT), "expected sizeof(rgn), got %u\n", ret); + ok(ret == sizeof(rgn.data.rdh) + sizeof(RECT), "expected sizeof(rgn), got %lu\n", ret); - trace("size %u, type %u, count %u, rgn size %u, bound %s\n", + trace("size %lu, type %lu, count %lu, rgn size %lu, bound %s\n", rgn.data.rdh.dwSize, rgn.data.rdh.iType, rgn.data.rdh.nCount, rgn.data.rdh.nRgnSize, wine_dbgstr_rect(&rgn.data.rdh.rcBound)); @@ -83,17 +87,17 @@ static void verify_region(HRGN hrgn, const RECT *rc) wine_dbgstr_rect(rc), wine_dbgstr_rect(rect)); } - ok(rgn.data.rdh.dwSize == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %u\n", rgn.data.rdh.dwSize); - ok(rgn.data.rdh.iType == RDH_RECTANGLES, "expected RDH_RECTANGLES, got %u\n", rgn.data.rdh.iType); + ok(rgn.data.rdh.dwSize == sizeof(rgn.data.rdh), "expected sizeof(rdh), got %lu\n", rgn.data.rdh.dwSize); + ok(rgn.data.rdh.iType == RDH_RECTANGLES, "expected RDH_RECTANGLES, got %lu\n", rgn.data.rdh.iType); if (IsRectEmpty(rc)) { - ok(rgn.data.rdh.nCount == 0, "expected 0, got %u\n", rgn.data.rdh.nCount); - ok(rgn.data.rdh.nRgnSize == 0, "expected 0, got %u\n", rgn.data.rdh.nRgnSize); + ok(rgn.data.rdh.nCount == 0, "expected 0, got %lu\n", rgn.data.rdh.nCount); + ok(rgn.data.rdh.nRgnSize == 0, "expected 0, got %lu\n", rgn.data.rdh.nRgnSize); } else { - ok(rgn.data.rdh.nCount == 1, "expected 1, got %u\n", rgn.data.rdh.nCount); - ok(rgn.data.rdh.nRgnSize == sizeof(RECT), "expected sizeof(RECT), got %u\n", rgn.data.rdh.nRgnSize); + ok(rgn.data.rdh.nCount == 1, "expected 1, got %lu\n", rgn.data.rdh.nCount); + ok(rgn.data.rdh.nRgnSize == sizeof(RECT), "expected sizeof(RECT), got %lu\n", rgn.data.rdh.nRgnSize); } ok(EqualRect(&rgn.data.rdh.rcBound, rc), "expected %s, got %s\n", wine_dbgstr_rect(rc), wine_dbgstr_rect(&rgn.data.rdh.rcBound)); @@ -142,11 +146,11 @@ static void test_region_data(DWORD *data, UINT size, INT line) for (i = 0; i < size - 1; i++) { if (i == 1) continue; /* data[1] never matches */ - ok_(__FILE__, line)(data[i] == buf[i], "off %u: %#x != %#x\n", i, data[i], buf[i]); + ok_(__FILE__, line)(data[i] == buf[i], "off %u: %#lx != %#lx\n", i, data[i], buf[i]); } /* some Windows versions fail to properly clear the aligned DWORD */ ok_(__FILE__, line)(data[size - 1] == buf[size - 1] || broken(data[size - 1] != buf[size - 1]), - "off %u: %#x != %#x\n", size - 1, data[size - 1], buf[size - 1]); + "off %u: %#lx != %#lx\n", size - 1, data[size - 1], buf[size - 1]); GdipDeleteRegion(region); } @@ -187,7 +191,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(20, needed); expect_dword(buf, 12); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_INFINITE_RECT); @@ -205,7 +209,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(20, needed); expect_dword(buf, 12); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_EMPTY_RECT); @@ -223,7 +227,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(20, needed); expect_dword(buf, 12); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_INFINITE_RECT); @@ -248,7 +252,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(36, needed); expect_dword(buf, 28); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_RECT); @@ -304,7 +308,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(156, needed); expect_dword(buf, 148); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 10); expect_dword(buf + 4, CombineModeExclude); @@ -367,7 +371,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(72, needed); expect_dword(buf, 64); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -402,7 +406,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(96, needed); expect_dword(buf, 88); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 2); expect_dword(buf + 4, CombineModeIntersect); @@ -447,7 +451,7 @@ static void test_getregiondata(void) expect(Ok, status); expect(36, needed); expect_dword(buf, 28); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -457,7 +461,7 @@ static void test_getregiondata(void) expect_dword(buf + 7, 0); /* flags 0 means that a path is an array of FLOATs */ ok(*(buf + 8) == 0x4000 /* before win7 */ || *(buf + 8) == 0, - "expected 0x4000 or 0, got %08x\n", *(buf + 8)); + "expected 0x4000 or 0, got %08lx\n", *(buf + 8)); expect_dword(buf + 10, 0xeeeeeeee); test_region_data(buf, needed, __LINE__); @@ -489,7 +493,7 @@ static void test_getregiondata(void) expect(Ok, status); expect(56, needed); expect_dword(buf, 48); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3 , 0); expect_dword(buf + 4 , RGNDATA_PATH); @@ -563,7 +567,7 @@ static void test_getregiondata(void) expect(Ok, status); expect(72, needed); expect_dword(buf, 64); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -614,7 +618,7 @@ static void test_getregiondata(void) expect(Ok, status); expect(116, needed); expect_dword(buf, 108); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 2); expect_dword(buf + 4, CombineModeUnion); @@ -641,8 +645,8 @@ static void test_getregiondata(void) expect_float(buf + 25, 50.0); expect_float(buf + 26, 70.2); expect_dword(buf + 27, 0x01010100); - ok(*(buf + 28) == 0x00000101 || *(buf + 28) == 0x43050101 /* Win 7 */, - "expected 00000101 or 43050101 got %08x\n", *(buf + 28)); + ok((*(buf + 28) & 0xffff) == 0x0101, + "expected ????0101 got %08lx\n", *(buf + 28)); expect_dword(buf + 29, 0xeeeeeeee); test_region_data(buf, needed, __LINE__); @@ -668,7 +672,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(56, needed); expect_dword(buf, 48); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -712,7 +716,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(72, needed); expect_dword(buf, 64); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -758,7 +762,7 @@ static void test_getregiondata(void) ok(status == Ok, "status %08x\n", status); expect(136, needed); expect_dword(buf, 128); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -791,9 +795,8 @@ static void test_getregiondata(void) expect_float(buf + 30, 789.799561); expect_dword(buf + 31, 0x03030300); expect_dword(buf + 32, 0x03030301); - ok(*(buf + 33) == 0x00030303 /* before win7 */ || - *(buf + 33) == 0x43030303 /* 32-bit win7 */ || *(buf + 33) == 0x4c030303 /* 64-bit win7 */, - "expected 0x00030303 or 0x43030303 or 0x4c030303 got %08x\n", *(buf + 33)); + ok((*(buf + 33) & 0xffffff) == 0x030303, + "expected 0x??030303 got %08lx\n", *(buf + 33)); expect_dword(buf + 34, 0xeeeeeeee); test_region_data(buf, needed, __LINE__); @@ -921,7 +924,7 @@ static void test_combinereplace(void) expect(Ok, status); expect(36, needed); expect_dword(buf, 28); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_RECT); @@ -941,7 +944,7 @@ static void test_combinereplace(void) expect(Ok, status); expect(156, needed); expect_dword(buf, 148); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_PATH); @@ -960,7 +963,7 @@ static void test_combinereplace(void) expect(Ok, status); expect(20, needed); expect_dword(buf, 12); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 0); expect_dword(buf + 4, RGNDATA_INFINITE_RECT); @@ -987,7 +990,7 @@ static void test_combinereplace(void) expect(Ok, status); expect(180, needed); expect_dword(buf, 172); - trace("buf[1] = %08x\n", buf[1]); + trace("buf[1] = %08lx\n", buf[1]); expect_magic(buf + 2); expect_dword(buf + 3, 2); expect_dword(buf + 4, CombineModeUnion); @@ -1446,14 +1449,14 @@ static void test_translate(void) static DWORD get_region_type(GpRegion *region) { DWORD *data; - DWORD size; + UINT size; DWORD result; DWORD status; status = GdipGetRegionDataSize(region, &size); expect(Ok, status); data = GdipAlloc(size); status = GdipGetRegionData(region, (BYTE*)data, size, NULL); - ok(status == Ok || status == InsufficientBuffer, "unexpected status 0x%x\n", status); + ok(status == Ok || status == InsufficientBuffer, "unexpected status 0x%lx\n", status); result = data[4]; GdipFree(data); return result; @@ -1594,7 +1597,7 @@ static void test_scans(void) GpMatrix *matrix; GpRectF rectf; GpStatus status; - ULONG count=80085; + UINT count=80085; INT icount; GpRectF scans[2]; GpRect scansi[2]; @@ -1729,6 +1732,7 @@ static void test_scans(void) static void test_getbounds(void) { GpRegion *region; + GpPath *path; GpGraphics *graphics; GpStatus status; GpRectF rectf; @@ -1804,6 +1808,44 @@ static void test_getbounds(void) ok(rectf.Width == 100.0, "Expected width = 0.0, got %.2f\n", rectf.Width); ok(rectf.Height == 100.0, "Expected height = 0.0, got %.2f\n", rectf.Height); + /* coordinates are not rounded */ + status = GdipResetWorldTransform(graphics); + ok(status == Ok, "status %08x\n", status); + status = GdipResetPageTransform(graphics); + ok(status == Ok, "status %08x\n", status); + rectf.X = 0.125; + rectf.Y = 1.125; + rectf.Width = 2.125; + rectf.Height = 3.125; + status = GdipCombineRegionRect(region, &rectf, CombineModeReplace); + ok(status == Ok, "status %08x\n", status); + rectf.X = rectf.Y = 0.0; + rectf.Height = rectf.Width = 0.0; + status = GdipGetRegionBounds(region, graphics, &rectf); + ok(status == Ok, "status %08x\n", status); + ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + + /* test path */ + status = GdipCreatePath(FillModeAlternate, &path); + ok(status == Ok, "status %08x\n", status); + status = GdipAddPathRectangle(path, 0.125, 1.125, 2.125, 3.125); + ok(status == Ok, "status %08x\n", status); + status = GdipCombineRegionPath(region, path, CombineModeReplace); + ok(status == Ok, "status %08x\n", status); + status = GdipDeletePath(path); + ok(status == Ok, "status %08x\n", status); + rectf.X = rectf.Y = 0.0; + rectf.Height = rectf.Width = 0.0; + status = GdipGetRegionBounds(region, graphics, &rectf); + ok(status == Ok, "status %08x\n", status); + ok(rectf.X == 0.125, "Expected X = 0.0, got %.2f\n", rectf.X); + ok(rectf.Y == 1.125, "Expected Y = 0.0, got %.2f\n", rectf.Y); + ok(rectf.Width == 2.125, "Expected width = 0.0, got %.2f\n", rectf.Width); + ok(rectf.Height == 3.125, "Expected height = 0.0, got %.2f\n", rectf.Height); + status = GdipDeleteRegion(region); ok(status == Ok, "status %08x\n", status); status = GdipDeleteGraphics(graphics); @@ -2330,6 +2372,204 @@ static void test_GdipCreateRegionRgnData(void) ReleaseDC(0, hdc); } +static void test_incombinedregion(void) +{ + struct testrgn + { + const char* desc; + BOOL origin_in_region; + GpRegion *region; + }; + + struct testrgn test_regions[] = { + { "infinite region", TRUE }, + { "infinite region inverted", FALSE }, + { "empty region", FALSE }, + { "empty region inverted", TRUE }, + { "inside rectangle", TRUE }, + { "inside rectangle inverted", FALSE }, + { "outside rectangle", FALSE }, + { "outside rectangle inverted", TRUE }, + { "inside path", TRUE }, + { "inside path inverted", FALSE }, + { "outside path but in bounding rect", FALSE }, + { "outside path but in bounding rect inverted", TRUE }, + { "outside path", FALSE }, + { "outside path inverted", TRUE }, + }; + + GpStatus stat; + GpRectF rect; + const GpPointF inside_path_points[] = { { -1, -2 }, { 2, 1 }, { -1, 1 } }; + const GpPointF outside_path_bounding_points[] = { { -2, -1 }, { 1, 2 }, { -2, 2 } }; + const GpPointF outside_path_points[] = { { 5, 5 }, { 5, 10 }, { 10, 10 } }; + GpPath *path; + int i, j; + BOOL in_region; + + /* Prepare test regions: */ + + /* infinite */ + stat = GdipCreateRegion(&test_regions[0].region); + expect(Ok, stat); + + /* empty */ + stat = GdipCreateRegion(&test_regions[2].region); + expect(Ok, stat); + stat = GdipSetEmpty(test_regions[2].region); + expect(Ok, stat); + + /* inside rectangle */ + rect.X = -5; + rect.Y = -2; + rect.Width = 10; + rect.Height = 4; + stat = GdipCreateRegionRect(&rect, &test_regions[4].region); + expect(Ok, stat); + + /* outside rectangle */ + rect.X = -10; + rect.Y = -10; + rect.Width = 7; + rect.Height = 7; + stat = GdipCreateRegionRect(&rect, &test_regions[6].region); + expect(Ok, stat); + + /* inside path */ + stat = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, stat); + stat = GdipAddPathPolygon(path, inside_path_points, ARRAY_SIZE(inside_path_points)); + expect(Ok, stat); + stat = GdipCreateRegion(&test_regions[8].region); + expect(Ok, stat); + stat = GdipCombineRegionPath(test_regions[8].region, path, CombineModeReplace); + expect(Ok, stat); + stat = GdipDeletePath(path); + expect(Ok, stat); + + /* outside path but in bounding rect */ + stat = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, stat); + stat = GdipAddPathPolygon(path, outside_path_bounding_points, ARRAY_SIZE(outside_path_bounding_points)); + expect(Ok, stat); + stat = GdipCreateRegion(&test_regions[10].region); + expect(Ok, stat); + stat = GdipCombineRegionPath(test_regions[10].region, path, CombineModeReplace); + expect(Ok, stat); + stat = GdipDeletePath(path); + expect(Ok, stat); + + /* outside path */ + stat = GdipCreatePath(FillModeAlternate, &path); + expect(Ok, stat); + stat = GdipAddPathPolygon(path, outside_path_points, ARRAY_SIZE(outside_path_points)); + expect(Ok, stat); + stat = GdipCreateRegion(&test_regions[12].region); + expect(Ok, stat); + stat = GdipCombineRegionPath(test_regions[12].region, path, CombineModeReplace); + expect(Ok, stat); + stat = GdipDeletePath(path); + expect(Ok, stat); + + for (i = 1; i < ARRAY_SIZE(test_regions); i += 2) + { + winetest_push_context("%s", test_regions[i].desc); + stat = GdipCreateRegion(&test_regions[i].region); + expect(Ok, stat); + stat = GdipCombineRegionRegion(test_regions[i].region, test_regions[i-1].region, CombineModeExclude); + expect(Ok, stat); + winetest_pop_context(); + } + + /* Check regions individually */ + for (i = 0; i < ARRAY_SIZE(test_regions); i++) + { + winetest_push_context("%s", test_regions[i].desc); + stat = GdipIsVisibleRegionPoint(test_regions[i].region, 0, 0, NULL, &in_region); + expect(Ok, stat); + expect(test_regions[i].origin_in_region, in_region); + winetest_pop_context(); + } + + /* Check combined regions */ + for (i = 0; i < ARRAY_SIZE(test_regions); i++) + { + for (j = 0; j < ARRAY_SIZE(test_regions); j++) + { + GpRegion *region; + BOOL expected_result; + + winetest_push_context("%s + %s", test_regions[i].desc, test_regions[j].desc); + + stat = GdipCreateRegion(®ion); + expect(Ok, stat); + + /* CombineModeIntersect */ + stat = GdipCombineRegionRegion(region, test_regions[i].region, CombineModeReplace); + expect(Ok, stat); + stat = GdipCombineRegionRegion(region, test_regions[j].region, CombineModeIntersect); + expect(Ok, stat); + + expected_result = test_regions[i].origin_in_region & test_regions[j].origin_in_region; + stat = GdipIsVisibleRegionPoint(region, 0, 0, NULL, &in_region); + expect(Ok, stat); + ok(expected_result == in_region, "CombineModeIntersect: expected %i, got %i\n", expected_result, in_region); + + /* CombineModeUnion */ + stat = GdipCombineRegionRegion(region, test_regions[i].region, CombineModeReplace); + expect(Ok, stat); + stat = GdipCombineRegionRegion(region, test_regions[j].region, CombineModeUnion); + expect(Ok, stat); + + expected_result = test_regions[i].origin_in_region | test_regions[j].origin_in_region; + stat = GdipIsVisibleRegionPoint(region, 0, 0, NULL, &in_region); + expect(Ok, stat); + ok(expected_result == in_region, "CombineModeUnion: expected %i, got %i\n", expected_result, in_region); + + /* CombineModeXor */ + stat = GdipCombineRegionRegion(region, test_regions[i].region, CombineModeReplace); + expect(Ok, stat); + stat = GdipCombineRegionRegion(region, test_regions[j].region, CombineModeXor); + expect(Ok, stat); + + expected_result = test_regions[i].origin_in_region ^ test_regions[j].origin_in_region; + stat = GdipIsVisibleRegionPoint(region, 0, 0, NULL, &in_region); + expect(Ok, stat); + ok(expected_result == in_region, "CombineModeXor: expected %i, got %i\n", expected_result, in_region); + + /* CombineModeExclude */ + stat = GdipCombineRegionRegion(region, test_regions[i].region, CombineModeReplace); + expect(Ok, stat); + stat = GdipCombineRegionRegion(region, test_regions[j].region, CombineModeExclude); + expect(Ok, stat); + + expected_result = test_regions[i].origin_in_region & !test_regions[j].origin_in_region; + stat = GdipIsVisibleRegionPoint(region, 0, 0, NULL, &in_region); + expect(Ok, stat); + ok(expected_result == in_region, "CombineModeExclude: expected %i, got %i\n", expected_result, in_region); + + /* CombineModeComplement */ + stat = GdipCombineRegionRegion(region, test_regions[i].region, CombineModeReplace); + expect(Ok, stat); + stat = GdipCombineRegionRegion(region, test_regions[j].region, CombineModeComplement); + expect(Ok, stat); + + expected_result = (!test_regions[i].origin_in_region) & test_regions[j].origin_in_region; + stat = GdipIsVisibleRegionPoint(region, 0, 0, NULL, &in_region); + expect(Ok, stat); + ok(expected_result == in_region, "CombineModeComplement: expected %i, got %i\n", expected_result, in_region); + + winetest_pop_context(); + } + } + + for (i = 0; i < ARRAY_SIZE(test_regions); i++) + { + stat = GdipDeleteRegion(test_regions[i].region); + expect(Ok, stat); + } +} + START_TEST(region) { struct GdiplusStartupInput gdiplusStartupInput; @@ -2364,6 +2604,7 @@ START_TEST(region) test_isvisiblerect(); test_excludeinfinite(); test_GdipCreateRegionRgnData(); + test_incombinedregion(); GdiplusShutdown(gdiplusToken); } diff --git a/modules/rostests/winetests/gdiplus/resource.rc b/modules/rostests/winetests/gdiplus/resource.rc index d7d915ce001..4af61fef65d 100644 --- a/modules/rostests/winetests/gdiplus/resource.rc +++ b/modules/rostests/winetests/gdiplus/resource.rc @@ -20,3 +20,14 @@ /* @makedep: wine_longname.ttf */ 1 RCDATA wine_longname.ttf + +/* @makedep: wine_testfont0.ttf */ +2 RCDATA wine_testfont0.ttf + +/* Generated with: fonttools ttx wine_mac_win.ttx */ +/* @makedep: wine_mac_win.ttf */ +3 RCDATA wine_mac_win.ttf + +/* Generated with: fonttools ttx wine_unicode_mac.ttx */ +/* @makedep: wine_unicode_mac.ttf */ +4 RCDATA wine_unicode_mac.ttf diff --git a/modules/rostests/winetests/gdiplus/wine_mac_win.ttf b/modules/rostests/winetests/gdiplus/wine_mac_win.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0a0493134deea4cf31e17182df526d9ececd96d2 GIT binary patch literal 1140 zcmds0O=}ZT6g@MOOjB#4%|Z$WnT28tQj=J^kb>1z7j6m>8d{X`F_|xzk1$Qr1O)Rp z6n8EKaak8GM3*j91aT$0bgkmbcwS~~$G_km?!D)ockZ1x?Sok6wel&7v0Jh@gw@0M?(KH^NRQb{j3-EhR1fW zN$HU5QV?5$nWnyGD1~C!#dXw)f6T0WK6=2yVKl3l~xpYyIwI8u5j=^(Ob8?|kRnbIv_;XCMI7 zV-7mbCzGSY)X0R-Qa1GW_9d|ejnos=8;_0bkDs`d3(+M@{d{WD_OMxhMExb_(bQB) z!qjE~eHZOU#x8p6>x_)>0qV7xiP`k$XMF>-@22i$9eaFi>cMT|70!cM5}{9#hm57{ zyR(y}nWlGwvBV4NnC+!&z{WwfJV?w(2r1RS9V(t1(K=ZtOv2?fT z&@1LQXpgCrsrdv$&(Cd1d_+WFCW9+23v*Rza_5Rxrd|t8u^imvGh>l^O4U~UND6KY z-DmXb9<@{W+!5wg!-(7yH^AO$*R-e9=POdYMTelbhESdF_seoT-8YWsiq_?N7smWb z{F^AKVyI6zRqQL${xyHcz_+mKpCQ;TkLM-Zv4k@}^6Bb_*Ww!5q zyfbP>_jE>^O;NYJw4~ixOPkth)x}i|_&1HuYMU7~SH;J#5v{y-OEt9~kE?xd#z24t zw&Hq#HOl(|*3p6w0XDE*^aMDBt>SWk!-$Fnbqfr2&yt)KEO;L#0<7_*cLS_r2oD2n zpiS%ya0pVI32+!qVjexn!^12J$RUF)N{rZw6!ucaVfk|(@d#};rr{C~^R1BFsThy> zZJ;Ob%@%T*Y)Q7J_R6>wxAw`AtUc|@;h5~n+TH}6rkTTq!#v|e9&2PF&K?l66vMOc*GoTq}ok{^^_45FRfXb@boU6t@sMN2Yl zGGE9zG9I&Jw>-s^vcGq*U5+xTRAx!He~9>p2q&4pz~klo>?*f~4!>rFByiFx6mxl3 zT2@EQvMe%x>ksStxkIFa@7(%Tw&Zv3H|;{Jx#;GYgzn0R-wkyGE2ADe2-G)LMN;gB KWkZtsaeM>1U)dr6 literal 0 HcmV?d00001 diff --git a/modules/rostests/winetests/gdiplus/wine_unicode_mac.ttf b/modules/rostests/winetests/gdiplus/wine_unicode_mac.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4c0af94ade4dd5f9a89246cf5f63ae9320d659c8 GIT binary patch literal 1168 zcmds0O=}ZT6g_W}OroW=bRjN+45HYgq)9BUx+tb@+z1gGT7>a2nU4pVnJ`V-1PW&D zKTyO!poq(E#LuM*7lIo>^cT2LaAiC%GfCrL@P_;Dx#ymH-8R zQU4Yo&XK+6gtCjp+*9Tw`VGG`^vKhU4_rjx%J$3d%3b2e^y>i$^IP#9@hAGlAnXlJ z?_!(M0rOHPb|fRsc+aI2%0U-5Q6v6Mw;~z3_LW!cH$JlGHT2o&Prmp~`^@GqER;w9 zN0(lVbo_p`{8h)yN-#3S9`o|}4USViSv_gq#DvMsDBYQITc}J}qXh92h5O^ewZcOd z&!;i>8J@Ai6T&>{{%3kiMJ~4V4AcAL;n?)VBPR123xX%xHg>g&H=lYr(`=H_>N4`M z81wv1y2oQ?;F_M4SePe+h5m-=4QQdZ`CU`y|6iBzeD;z0znPlgT~0FtOE}8-+#H|B z1w6?30?y)DhRum)dY2Wv&F)oT89y>?ph!Wg;ai&V`8iy`f+%JhbHXBX&w=inDCa)|C!XL^h;$C!4D{C*rO(zLaVs?hX^>2R&=mS+nZZ`i8X^ vsG;I2Bj57k#A?KCx8=$MH`zbr+pznQa!A>sbegW;@5p3_B^5_jty299