/* * Unit test suite for cursors and icons. * * Copyright 2006 Michael Kaufmann * Copyright 2007 Dmitry Timoshkov * Copyright 2007-2008 Andrew Riedi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include "wine/test.h" #include "windef.h" #include "winbase.h" #include "winreg.h" #include "wingdi.h" #include "winuser.h" #include "pshpack1.h" typedef struct { BYTE bWidth; BYTE bHeight; BYTE bColorCount; BYTE bReserved; WORD xHotspot; WORD yHotspot; DWORD dwDIBSize; DWORD dwDIBOffset; } CURSORICONFILEDIRENTRY; typedef struct { WORD idReserved; WORD idType; WORD idCount; CURSORICONFILEDIRENTRY idEntries[1]; } CURSORICONFILEDIR; #include "poppack.h" static char **test_argv; static int test_argc; static HWND child = 0; static HWND parent = 0; static HANDLE child_process; #define PROC_INIT (WM_USER+1) static LRESULT CALLBACK callback_child(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL ret; DWORD error; switch (msg) { /* Destroy the cursor. */ case WM_USER+1: SetLastError(0xdeadbeef); ret = DestroyCursor((HCURSOR) lParam); error = GetLastError(); todo_wine ok(!ret || broken(ret) /* win9x */, "DestroyCursor on the active cursor succeeded.\n"); ok(error == ERROR_DESTROY_OBJECT_OF_OTHER_THREAD || error == 0xdeadbeef, /* vista */ "Last error: %u\n", error); return TRUE; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } static LRESULT CALLBACK callback_parent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == PROC_INIT) { child = (HWND) wParam; return TRUE; } return DefWindowProc(hwnd, msg, wParam, lParam); } static void do_child(void) { WNDCLASS class; MSG msg; BOOL ret; /* Register a new class. */ class.style = CS_GLOBALCLASS; class.lpfnWndProc = callback_child; class.cbClsExtra = 0; class.cbWndExtra = 0; class.hInstance = GetModuleHandle(NULL); class.hIcon = NULL; class.hCursor = NULL; class.hbrBackground = NULL; class.lpszMenuName = NULL; class.lpszClassName = "cursor_child"; SetLastError(0xdeadbeef); ret = RegisterClass(&class); ok(ret, "Failed to register window class. Error: %u\n", GetLastError()); /* Create a window. */ child = CreateWindowA("cursor_child", "cursor_child", WS_POPUP | WS_VISIBLE, 0, 0, 200, 200, 0, 0, 0, NULL); ok(child != 0, "CreateWindowA failed. Error: %u\n", GetLastError()); /* Let the parent know our HWND. */ PostMessage(parent, PROC_INIT, (WPARAM) child, 0); /* Receive messages. */ while ((ret = GetMessage(&msg, 0, 0, 0))) { ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); TranslateMessage(&msg); DispatchMessage(&msg); } } static void do_parent(void) { char path_name[MAX_PATH]; PROCESS_INFORMATION info; STARTUPINFOA startup; WNDCLASS class; MSG msg; BOOL ret; /* Register a new class. */ class.style = CS_GLOBALCLASS; class.lpfnWndProc = callback_parent; class.cbClsExtra = 0; class.cbWndExtra = 0; class.hInstance = GetModuleHandle(NULL); class.hIcon = NULL; class.hCursor = NULL; class.hbrBackground = NULL; class.lpszMenuName = NULL; class.lpszClassName = "cursor_parent"; SetLastError(0xdeadbeef); ret = RegisterClass(&class); ok(ret, "Failed to register window class. Error: %u\n", GetLastError()); /* Create a window. */ parent = CreateWindowA("cursor_parent", "cursor_parent", WS_POPUP | WS_VISIBLE, 0, 0, 200, 200, 0, 0, 0, NULL); ok(parent != 0, "CreateWindowA failed. Error: %u\n", GetLastError()); /* Start child process. */ memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = SW_SHOWNORMAL; sprintf(path_name, "%s cursoricon %lx", test_argv[0], (INT_PTR)parent); ok(CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess failed.\n"); child_process = info.hProcess; /* Wait for child window handle. */ while ((child == 0) && (ret = GetMessage(&msg, parent, 0, 0))) { ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); TranslateMessage(&msg); DispatchMessage(&msg); } } static void finish_child_process(void) { SendMessage(child, WM_CLOSE, 0, 0); winetest_wait_child_process( child_process ); CloseHandle(child_process); } static void test_child_process(void) { static const BYTE bmp_bits[4096]; HCURSOR cursor; ICONINFO cursorInfo; UINT display_bpp; HDC hdc; /* Create and set a dummy cursor. */ hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(0, hdc); cursorInfo.fIcon = FALSE; cursorInfo.xHotspot = 0; cursorInfo.yHotspot = 0; cursorInfo.hbmMask = CreateBitmap(32, 32, 1, 1, bmp_bits); cursorInfo.hbmColor = CreateBitmap(32, 32, 1, display_bpp, bmp_bits); cursor = CreateIconIndirect(&cursorInfo); ok(cursor != NULL, "CreateIconIndirect returned %p.\n", cursor); SetCursor(cursor); /* Destroy the cursor. */ SendMessage(child, WM_USER+1, 0, (LPARAM) cursor); } static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, INT copyWidth, INT copyHeight, INT expectedWidth, INT expectedHeight, WORD expectedDepth, BOOL dibExpected) { HBITMAP copy; BITMAP origBitmap; BITMAP copyBitmap; BOOL orig_is_dib; BOOL copy_is_dib; copy = CopyImage(bitmap, IMAGE_BITMAP, copyWidth, copyHeight, flags); ok(copy != NULL, "CopyImage() failed\n"); if (copy != NULL) { GetObject(bitmap, sizeof(origBitmap), &origBitmap); GetObject(copy, sizeof(copyBitmap), ©Bitmap); orig_is_dib = (origBitmap.bmBits != NULL); copy_is_dib = (copyBitmap.bmBits != NULL); if (copy_is_dib && dibExpected && copyBitmap.bmBitsPixel == 24 && (expectedDepth == 16 || expectedDepth == 32)) { /* Windows 95 doesn't create DIBs with a depth of 16 or 32 bit */ if (GetVersion() & 0x80000000) { expectedDepth = 24; } } if (copy_is_dib && !dibExpected && !(flags & LR_CREATEDIBSECTION)) { /* It's not forbidden to create a DIB section if the flag LR_CREATEDIBSECTION is absent. Windows 9x does this if the bitmap has a depth that doesn't match the screen depth, Windows NT doesn't */ dibExpected = TRUE; expectedDepth = origBitmap.bmBitsPixel; } ok((!(dibExpected ^ copy_is_dib) && (copyBitmap.bmWidth == expectedWidth) && (copyBitmap.bmHeight == expectedHeight) && (copyBitmap.bmBitsPixel == expectedDepth)), "CopyImage ((%s, %dx%d, %u bpp), %d, %d, %#x): Expected (%s, %dx%d, %u bpp), got (%s, %dx%d, %u bpp)\n", orig_is_dib ? "DIB" : "DDB", origBitmap.bmWidth, origBitmap.bmHeight, origBitmap.bmBitsPixel, copyWidth, copyHeight, flags, dibExpected ? "DIB" : "DDB", expectedWidth, expectedHeight, expectedDepth, copy_is_dib ? "DIB" : "DDB", copyBitmap.bmWidth, copyBitmap.bmHeight, copyBitmap.bmBitsPixel); DeleteObject(copy); } } static void test_CopyImage_Bitmap(int depth) { HBITMAP ddb, dib; HDC screenDC; BITMAPINFO * info; VOID * bits; int screen_depth; unsigned int i; /* Create a device-independent bitmap (DIB) */ info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); info->bmiHeader.biSize = sizeof(info->bmiHeader); info->bmiHeader.biWidth = 2; info->bmiHeader.biHeight = 2; info->bmiHeader.biPlanes = 1; info->bmiHeader.biBitCount = depth; info->bmiHeader.biCompression = BI_RGB; for (i=0; i < 256; i++) { info->bmiColors[i].rgbRed = i; info->bmiColors[i].rgbGreen = i; info->bmiColors[i].rgbBlue = 255 - i; info->bmiColors[i].rgbReserved = 0; } dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); /* Create a device-dependent bitmap (DDB) */ screenDC = GetDC(NULL); screen_depth = GetDeviceCaps(screenDC, BITSPIXEL); if (depth == 1 || depth == screen_depth) { ddb = CreateBitmap(2, 2, 1, depth, NULL); } else { ddb = NULL; } ReleaseDC(NULL, screenDC); if (ddb != NULL) { test_CopyImage_Check(ddb, 0, 0, 0, 2, 2, depth == 1 ? 1 : screen_depth, FALSE); test_CopyImage_Check(ddb, 0, 0, 5, 2, 5, depth == 1 ? 1 : screen_depth, FALSE); test_CopyImage_Check(ddb, 0, 5, 0, 5, 2, depth == 1 ? 1 : screen_depth, FALSE); test_CopyImage_Check(ddb, 0, 5, 5, 5, 5, depth == 1 ? 1 : screen_depth, FALSE); test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); DeleteObject(ddb); } if (depth != 1) { test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE); } test_CopyImage_Check(dib, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); test_CopyImage_Check(dib, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); test_CopyImage_Check(dib, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); test_CopyImage_Check(dib, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); DeleteObject(dib); if (depth == 1) { /* Special case: A monochrome DIB is converted to a monochrome DDB if the colors in the color table are black and white. Skip this test on Windows 95, it always creates a monochrome DDB in this case */ if (!(GetVersion() & 0x80000000)) { info->bmiHeader.biBitCount = 1; info->bmiColors[0].rgbRed = 0xFF; info->bmiColors[0].rgbGreen = 0; info->bmiColors[0].rgbBlue = 0; info->bmiColors[1].rgbRed = 0; info->bmiColors[1].rgbGreen = 0xFF; info->bmiColors[1].rgbBlue = 0; dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE); test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE); DeleteObject(dib); info->bmiHeader.biBitCount = 1; info->bmiColors[0].rgbRed = 0; info->bmiColors[0].rgbGreen = 0; info->bmiColors[0].rgbBlue = 0; info->bmiColors[1].rgbRed = 0xFF; info->bmiColors[1].rgbGreen = 0xFF; info->bmiColors[1].rgbBlue = 0xFF; dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE); test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE); test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE); test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE); DeleteObject(dib); info->bmiHeader.biBitCount = 1; info->bmiColors[0].rgbRed = 0xFF; info->bmiColors[0].rgbGreen = 0xFF; info->bmiColors[0].rgbBlue = 0xFF; info->bmiColors[1].rgbRed = 0; info->bmiColors[1].rgbGreen = 0; info->bmiColors[1].rgbBlue = 0; dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE); test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE); test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE); test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE); DeleteObject(dib); } } HeapFree(GetProcessHeap(), 0, info); } static void test_initial_cursor(void) { HCURSOR cursor, cursor2; DWORD error; cursor = GetCursor(); /* Check what handle GetCursor() returns if a cursor is not set yet. */ SetLastError(0xdeadbeef); cursor2 = LoadCursor(NULL, IDC_WAIT); todo_wine { ok(cursor == cursor2, "cursor (%p) is not IDC_WAIT (%p).\n", cursor, cursor2); } error = GetLastError(); ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); } static void test_icon_info_dbg(HICON hIcon, UINT exp_cx, UINT exp_cy, UINT exp_bpp, int line) { ICONINFO info; DWORD ret; BITMAP bmMask, bmColor; ret = GetIconInfo(hIcon, &info); ok_(__FILE__, line)(ret, "GetIconInfo failed\n"); /* CreateIcon under XP causes info.fIcon to be 0 */ ok_(__FILE__, line)(info.xHotspot == exp_cx/2, "info.xHotspot = %u\n", info.xHotspot); ok_(__FILE__, line)(info.yHotspot == exp_cy/2, "info.yHotspot = %u\n", info.yHotspot); ok_(__FILE__, line)(info.hbmMask != 0, "info.hbmMask is NULL\n"); ret = GetObject(info.hbmMask, sizeof(bmMask), &bmMask); ok_(__FILE__, line)(ret == sizeof(bmMask), "GetObject(info.hbmMask) failed, ret %u\n", ret); if (exp_bpp == 1) ok_(__FILE__, line)(info.hbmColor == 0, "info.hbmColor should be NULL\n"); if (info.hbmColor) { HDC hdc; UINT display_bpp; hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(0, hdc); ret = GetObject(info.hbmColor, sizeof(bmColor), &bmColor); ok_(__FILE__, line)(ret == sizeof(bmColor), "GetObject(info.hbmColor) failed, ret %u\n", ret); ok_(__FILE__, line)(bmColor.bmBitsPixel == display_bpp /* XP */ || bmColor.bmBitsPixel == exp_bpp /* Win98 */, "bmColor.bmBitsPixel = %d\n", bmColor.bmBitsPixel); ok_(__FILE__, line)(bmColor.bmWidth == exp_cx, "bmColor.bmWidth = %d\n", bmColor.bmWidth); ok_(__FILE__, line)(bmColor.bmHeight == exp_cy, "bmColor.bmHeight = %d\n", bmColor.bmHeight); ok_(__FILE__, line)(bmMask.bmBitsPixel == 1, "bmMask.bmBitsPixel = %d\n", bmMask.bmBitsPixel); ok_(__FILE__, line)(bmMask.bmWidth == exp_cx, "bmMask.bmWidth = %d\n", bmMask.bmWidth); ok_(__FILE__, line)(bmMask.bmHeight == exp_cy, "bmMask.bmHeight = %d\n", bmMask.bmHeight); } else { ok_(__FILE__, line)(bmMask.bmBitsPixel == 1, "bmMask.bmBitsPixel = %d\n", bmMask.bmBitsPixel); ok_(__FILE__, line)(bmMask.bmWidth == exp_cx, "bmMask.bmWidth = %d\n", bmMask.bmWidth); ok_(__FILE__, line)(bmMask.bmHeight == exp_cy * 2, "bmMask.bmHeight = %d\n", bmMask.bmHeight); } } #define test_icon_info(a,b,c,d) test_icon_info_dbg((a),(b),(c),(d),__LINE__) static void test_CreateIcon(void) { static const BYTE bmp_bits[1024]; HICON hIcon; HBITMAP hbmMask, hbmColor; BITMAPINFO *bmpinfo; ICONINFO info; HDC hdc; void *bits; UINT display_bpp; hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); /* these crash under XP hIcon = CreateIcon(0, 16, 16, 1, 1, bmp_bits, NULL); hIcon = CreateIcon(0, 16, 16, 1, 1, NULL, bmp_bits); */ hIcon = CreateIcon(0, 16, 16, 1, 1, bmp_bits, bmp_bits); ok(hIcon != 0, "CreateIcon failed\n"); test_icon_info(hIcon, 16, 16, 1); DestroyIcon(hIcon); hIcon = CreateIcon(0, 16, 16, 1, display_bpp, bmp_bits, bmp_bits); ok(hIcon != 0, "CreateIcon failed\n"); test_icon_info(hIcon, 16, 16, display_bpp); DestroyIcon(hIcon); hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); hbmColor = CreateBitmap(16, 16, 1, display_bpp, bmp_bits); ok(hbmColor != 0, "CreateBitmap failed\n"); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = 0; info.hbmColor = 0; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(!hIcon, "CreateIconIndirect should fail\n"); ok(GetLastError() == 0xdeadbeaf, "wrong error %u\n", GetLastError()); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = 0; info.hbmColor = hbmColor; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(!hIcon, "CreateIconIndirect should fail\n"); ok(GetLastError() == 0xdeadbeaf, "wrong error %u\n", GetLastError()); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = hbmMask; info.hbmColor = hbmColor; hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 16, display_bpp); DestroyIcon(hIcon); DeleteObject(hbmMask); DeleteObject(hbmColor); hbmMask = CreateBitmap(16, 32, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = hbmMask; info.hbmColor = 0; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 16, 1); DestroyIcon(hIcon); DeleteObject(hbmMask); DeleteObject(hbmColor); /* test creating an icon from a DIB section */ bmpinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO,bmiColors[256])); bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpinfo->bmiHeader.biWidth = 32; bmpinfo->bmiHeader.biHeight = 32; bmpinfo->bmiHeader.biPlanes = 1; bmpinfo->bmiHeader.biBitCount = 8; bmpinfo->bmiHeader.biCompression = BI_RGB; hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); ok(hbmColor != NULL, "Expected a handle to the DIB\n"); if (bits) memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); bmpinfo->bmiHeader.biBitCount = 1; hbmMask = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); ok(hbmMask != NULL, "Expected a handle to the DIB\n"); if (bits) memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = hbmColor; info.hbmColor = hbmMask; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); DestroyIcon(hIcon); DeleteObject(hbmColor); bmpinfo->bmiHeader.biBitCount = 16; hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); ok(hbmColor != NULL, "Expected a handle to the DIB\n"); if (bits) memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = hbmColor; info.hbmColor = hbmMask; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); DestroyIcon(hIcon); DeleteObject(hbmColor); bmpinfo->bmiHeader.biBitCount = 32; hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); ok(hbmColor != NULL, "Expected a handle to the DIB\n"); if (bits) memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); info.fIcon = TRUE; info.xHotspot = 8; info.yHotspot = 8; info.hbmMask = hbmColor; info.hbmColor = hbmMask; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); DestroyIcon(hIcon); DeleteObject(hbmMask); DeleteObject(hbmColor); HeapFree( GetProcessHeap(), 0, bmpinfo ); ReleaseDC(0, hdc); } /* Shamelessly ripped from dlls/oleaut32/tests/olepicture.c */ /* 1x1 pixel gif */ static const unsigned char gifimage[35] = { 0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff, 0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44, 0x01,0x00,0x3b }; /* 1x1 pixel jpg */ static const unsigned char jpgimage[285] = { 0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2c, 0x01,0x2c,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x05,0x03,0x04,0x04,0x04,0x03,0x05, 0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x07,0x0c,0x08,0x07,0x07,0x07,0x07,0x0f,0x0b, 0x0b,0x09,0x0c,0x11,0x0f,0x12,0x12,0x11,0x0f,0x11,0x11,0x13,0x16,0x1c,0x17,0x13, 0x14,0x1a,0x15,0x11,0x11,0x18,0x21,0x18,0x1a,0x1d,0x1d,0x1f,0x1f,0x1f,0x13,0x17, 0x22,0x24,0x22,0x1e,0x24,0x1c,0x1e,0x1f,0x1e,0xff,0xdb,0x00,0x43,0x01,0x05,0x05, 0x05,0x07,0x06,0x07,0x0e,0x08,0x08,0x0e,0x1e,0x14,0x11,0x14,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0xff,0xc0, 0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11, 0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4, 0x00,0x14,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,0x03,0x01, 0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xb2,0xc0,0x07,0xff,0xd9 }; /* 1x1 pixel png */ static const unsigned char pngimage[285] = { 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53, 0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b, 0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5, 0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41, 0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59, 0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; /* 1x1 pixel bmp */ static const unsigned char bmpimage[66] = { 0x42,0x4d,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x28,0x00, 0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00, 0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00, 0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00, 0x00,0x00 }; /* 2x2 pixel gif */ static const unsigned char gif4pixel[42] = { 0x47,0x49,0x46,0x38,0x37,0x61,0x02,0x00,0x02,0x00,0xa1,0x00,0x00,0x00,0x00,0x00, 0x39,0x62,0xfc,0xff,0x1a,0xe5,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x02,0x00, 0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b }; static void test_LoadImageFile(const unsigned char * image_data, unsigned int image_size, const char * ext, BOOL expect_success) { HANDLE handle; BOOL ret; DWORD error, bytes_written; char filename[64]; strcpy(filename, "test."); strcat(filename, ext); /* Create the test image. */ handle = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ok(handle != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); ret = WriteFile(handle, image_data, image_size, &bytes_written, NULL); ok(bytes_written == image_size, "test file created improperly.\n"); CloseHandle(handle); /* Load as cursor. For all tested formats, this should fail */ SetLastError(0xdeadbeef); handle = LoadImageA(NULL, filename, IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); ok(handle == NULL, "LoadImage(%s) as IMAGE_CURSOR succeeded incorrectly.\n", ext); error = GetLastError(); ok(error == 0 || broken(error == 0xdeadbeef) || /* Win9x */ broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ "Last error: %u\n", error); if (handle != NULL) DestroyCursor(handle); /* Load as icon. For all tested formats, this should fail */ SetLastError(0xdeadbeef); handle = LoadImageA(NULL, filename, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); ok(handle == NULL, "LoadImage(%s) as IMAGE_ICON succeeded incorrectly.\n", ext); error = GetLastError(); ok(error == 0 || broken(error == 0xdeadbeef) || /* Win9x */ broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ "Last error: %u\n", error); if (handle != NULL) DestroyIcon(handle); /* Load as bitmap. Should succeed if bmp, fail for everything else */ SetLastError(0xdeadbeef); handle = LoadImageA(NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); if (expect_success) ok(handle != NULL, "LoadImage(%s) as IMAGE_BITMAP failed.\n", ext); else ok(handle == NULL, "LoadImage(%s) as IMAGE_BITMAP succeeded incorrectly.\n", ext); error = GetLastError(); ok(error == 0 || error == 0xdeadbeef, /* Win9x, WinMe */ "Last error: %u\n", error); if (handle != NULL) DeleteObject(handle); DeleteFileA(filename); } static void test_LoadImage(void) { HANDLE handle; BOOL ret; DWORD error, bytes_written; CURSORICONFILEDIR *icon_data; CURSORICONFILEDIRENTRY *icon_entry; BITMAPINFOHEADER *icon_header; ICONINFO icon_info; #define ICON_WIDTH 32 #define ICON_HEIGHT 32 #define ICON_AND_SIZE (ICON_WIDTH*ICON_HEIGHT/8) #define ICON_BPP 32 #define ICON_SIZE \ (sizeof(CURSORICONFILEDIR) + sizeof(BITMAPINFOHEADER) \ + ICON_AND_SIZE + ICON_AND_SIZE*ICON_BPP) /* Set icon data. */ icon_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ICON_SIZE); icon_data->idReserved = 0; icon_data->idType = 1; icon_data->idCount = 1; icon_entry = icon_data->idEntries; icon_entry->bWidth = ICON_WIDTH; icon_entry->bHeight = ICON_HEIGHT; icon_entry->bColorCount = 0; icon_entry->bReserved = 0; icon_entry->xHotspot = 1; icon_entry->yHotspot = 1; icon_entry->dwDIBSize = ICON_SIZE - sizeof(CURSORICONFILEDIR); icon_entry->dwDIBOffset = sizeof(CURSORICONFILEDIR); icon_header = (BITMAPINFOHEADER *) ((BYTE *) icon_data + icon_entry->dwDIBOffset); icon_header->biSize = sizeof(BITMAPINFOHEADER); icon_header->biWidth = ICON_WIDTH; icon_header->biHeight = ICON_HEIGHT*2; icon_header->biPlanes = 1; icon_header->biBitCount = ICON_BPP; icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ /* Create the icon. */ handle = CreateFileA("icon.ico", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ok(handle != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); ret = WriteFile(handle, icon_data, ICON_SIZE, &bytes_written, NULL); ok(bytes_written == ICON_SIZE, "icon.ico created improperly.\n"); CloseHandle(handle); /* Test loading an icon as a cursor. */ SetLastError(0xdeadbeef); handle = LoadImageA(NULL, "icon.ico", IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); ok(handle != NULL, "LoadImage() failed.\n"); error = GetLastError(); ok(error == 0 || broken(error == 0xdeadbeef) || /* Win9x */ broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ "Last error: %u\n", error); /* Test the icon information. */ SetLastError(0xdeadbeef); ret = GetIconInfo(handle, &icon_info); ok(ret, "GetIconInfo() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); if (ret) { ok(icon_info.fIcon == FALSE, "fIcon != FALSE.\n"); ok(icon_info.xHotspot == 1, "xHotspot is %u.\n", icon_info.xHotspot); ok(icon_info.yHotspot == 1, "yHotspot is %u.\n", icon_info.yHotspot); ok(icon_info.hbmColor != NULL || broken(!icon_info.hbmColor) /* no color cursor support */, "No hbmColor!\n"); ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); } /* Clean up. */ SetLastError(0xdeadbeef); ret = DestroyCursor(handle); ok(ret, "DestroyCursor() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); HeapFree(GetProcessHeap(), 0, icon_data); DeleteFileA("icon.ico"); test_LoadImageFile(bmpimage, sizeof(bmpimage), "bmp", 1); test_LoadImageFile(gifimage, sizeof(gifimage), "gif", 0); test_LoadImageFile(gif4pixel, sizeof(gif4pixel), "gif", 0); test_LoadImageFile(jpgimage, sizeof(jpgimage), "jpg", 0); test_LoadImageFile(pngimage, sizeof(pngimage), "png", 0); } static void test_CreateIconFromResource(void) { HANDLE handle; BOOL ret; DWORD error; BITMAPINFOHEADER *icon_header; INT16 *hotspot; ICONINFO icon_info; #define ICON_RES_WIDTH 32 #define ICON_RES_HEIGHT 32 #define ICON_RES_AND_SIZE (ICON_WIDTH*ICON_HEIGHT/8) #define ICON_RES_BPP 32 #define ICON_RES_SIZE \ (sizeof(BITMAPINFOHEADER) + ICON_AND_SIZE + ICON_AND_SIZE*ICON_BPP) #define CRSR_RES_SIZE (2*sizeof(INT16) + ICON_RES_SIZE) /* Set icon data. */ hotspot = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CRSR_RES_SIZE); /* Cursor resources have an extra hotspot, icon resources not. */ hotspot[0] = 3; hotspot[1] = 3; icon_header = (BITMAPINFOHEADER *) (hotspot + 2); icon_header->biSize = sizeof(BITMAPINFOHEADER); icon_header->biWidth = ICON_WIDTH; icon_header->biHeight = ICON_HEIGHT*2; icon_header->biPlanes = 1; icon_header->biBitCount = ICON_BPP; icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ /* Test creating a cursor. */ SetLastError(0xdeadbeef); handle = CreateIconFromResource((PBYTE) hotspot, CRSR_RES_SIZE, FALSE, 0x00030000); ok(handle != NULL, "Create cursor failed.\n"); /* Test the icon information. */ SetLastError(0xdeadbeef); ret = GetIconInfo(handle, &icon_info); ok(ret, "GetIconInfo() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); if (ret) { ok(icon_info.fIcon == FALSE, "fIcon != FALSE.\n"); ok(icon_info.xHotspot == 3, "xHotspot is %u.\n", icon_info.xHotspot); ok(icon_info.yHotspot == 3, "yHotspot is %u.\n", icon_info.yHotspot); ok(icon_info.hbmColor != NULL || broken(!icon_info.hbmColor) /* no color cursor support */, "No hbmColor!\n"); ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); } /* Clean up. */ SetLastError(0xdeadbeef); ret = DestroyCursor(handle); ok(ret, "DestroyCursor() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); /* Test creating an icon. */ SetLastError(0xdeadbeef); handle = CreateIconFromResource((PBYTE) icon_header, ICON_RES_SIZE, TRUE, 0x00030000); ok(handle != NULL, "Create icon failed.\n"); /* Test the icon information. */ SetLastError(0xdeadbeef); ret = GetIconInfo(handle, &icon_info); ok(ret, "GetIconInfo() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); if (ret) { ok(icon_info.fIcon == TRUE, "fIcon != TRUE.\n"); /* Icons always have hotspot in the middle */ ok(icon_info.xHotspot == ICON_WIDTH/2, "xHotspot is %u.\n", icon_info.xHotspot); ok(icon_info.yHotspot == ICON_HEIGHT/2, "yHotspot is %u.\n", icon_info.yHotspot); ok(icon_info.hbmColor != NULL, "No hbmColor!\n"); ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); } /* Clean up. */ SetLastError(0xdeadbeef); ret = DestroyCursor(handle); ok(ret, "DestroyCursor() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); HeapFree(GetProcessHeap(), 0, hotspot); } static HICON create_test_icon(HDC hdc, int width, int height, int bpp, BOOL maskvalue, UINT32 *color, int colorSize) { ICONINFO iconInfo; BITMAPINFO bitmapInfo; UINT32 *buffer = NULL; UINT32 mask = maskvalue ? 0xFFFFFFFF : 0x00000000; memset(&bitmapInfo, 0, sizeof(bitmapInfo)); bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = width; bitmapInfo.bmiHeader.biHeight = height; bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biBitCount = bpp; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = colorSize; iconInfo.fIcon = TRUE; iconInfo.xHotspot = 0; iconInfo.yHotspot = 0; iconInfo.hbmMask = CreateBitmap( width, height, 1, 1, &mask ); if(!iconInfo.hbmMask) return NULL; iconInfo.hbmColor = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, NULL, 0); if(!iconInfo.hbmColor || !buffer) { DeleteObject(iconInfo.hbmMask); return NULL; } memcpy(buffer, color, colorSize); return CreateIconIndirect(&iconInfo); } static BOOL color_match(COLORREF a, COLORREF b) { /* 5-bit accuracy is a sufficient test. This will match, so long as * colors are never truncated to less that 3x5-bit accuracy i.e. * paletized. */ return (a & 0x00F8F8F8) == (b & 0x00F8F8F8); } static void check_alpha_draw(HDC hdc, BOOL drawiconex, BOOL alpha, int bpp, int line) { HICON hicon; UINT32 mask; UINT32 color[2]; COLORREF modern_expected, legacy_expected, result; mask = 0x00000000; color[0] = 0x00A0B0C0; color[1] = alpha ? 0xFF000000 : 0x00000000; modern_expected = alpha ? 0x00FFFFFF : 0x00C0B0A0; legacy_expected = 0x00C0B0A0; hicon = create_test_icon(hdc, 2, 1, bpp, 0, color, sizeof(color)); if (!hicon) return; SetPixelV(hdc, 0, 0, 0x00FFFFFF); if(drawiconex) DrawIconEx(hdc, 0, 0, hicon, 2, 1, 0, NULL, DI_NORMAL); else DrawIcon(hdc, 0, 0, hicon); result = GetPixel(hdc, 0, 0); ok (color_match(result, modern_expected) || /* Windows 2000 and up */ broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ "%s. Expected a close match to %06X (modern) or %06X (legacy) with %s. " "Got %06X from line %d\n", alpha ? "Alpha blending" : "Not alpha blending", modern_expected, legacy_expected, drawiconex ? "DrawIconEx" : "DrawIcon", result, line); } static void check_DrawIcon(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, COLORREF background, COLORREF modern_expected, COLORREF legacy_expected, int line) { COLORREF result; HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color)); if (!hicon) return; SetPixelV(hdc, 0, 0, background); SetPixelV(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1, background); SetPixelV(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), background); DrawIcon(hdc, 0, 0, hicon); result = GetPixel(hdc, 0, 0); ok (color_match(result, modern_expected) || /* Windows 2000 and up */ broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ "Overlaying Mask %d on Color %06X with DrawIcon. " "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n", maskvalue, color, modern_expected, legacy_expected, result, line); result = GetPixel(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1); ok (color_match(result, modern_expected) || /* Windows 2000 and up */ broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ "Overlaying Mask %d on Color %06X with DrawIcon. " "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n", maskvalue, color, modern_expected, legacy_expected, result, line); result = GetPixel(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); ok (color_match(result, background), "Overlaying Mask %d on Color %06X with DrawIcon. " "Expected unchanged background color %06X. Got %06X from line %d\n", maskvalue, color, background, result, line); } static void test_DrawIcon(void) { BITMAPINFO bitmapInfo; HDC hdcDst = NULL; HBITMAP bmpDst = NULL; HBITMAP bmpOld = NULL; UINT32 *bits = 0; hdcDst = CreateCompatibleDC(0); ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n"); if (!hdcDst) return; if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8) { skip("Windows will distort DrawIcon colors at 8-bpp and less due to palletizing.\n"); goto cleanup; } memset(&bitmapInfo, 0, sizeof(bitmapInfo)); bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = GetSystemMetrics(SM_CXICON)+1; bitmapInfo.bmiHeader.biHeight = GetSystemMetrics(SM_CYICON)+1; bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32); bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0); ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n"); if (!bmpDst || !bits) goto cleanup; bmpOld = SelectObject(hdcDst, bmpDst); /* Mask is only heeded if alpha channel is always zero */ check_DrawIcon(hdcDst, FALSE, 0x00A0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); check_DrawIcon(hdcDst, TRUE, 0x00A0B0C0, 32, 0x00FFFFFF, 0x003F4F5F, 0x003F4F5F, __LINE__); /* Test alpha blending */ /* Windows 2000 and up will alpha blend, earlier Windows versions will not */ check_DrawIcon(hdcDst, FALSE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); check_DrawIcon(hdcDst, TRUE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__); check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__); check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__); check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__); check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__); check_DrawIcon(hdcDst, FALSE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__); check_DrawIcon(hdcDst, TRUE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__); /* Test detecting of alpha channel */ /* If a single pixel's alpha channel is non-zero, the icon will be alpha blended, otherwise it will be draw with and + xor blts. */ check_alpha_draw(hdcDst, FALSE, FALSE, 32, __LINE__); check_alpha_draw(hdcDst, FALSE, TRUE, 32, __LINE__); cleanup: if(bmpOld) SelectObject(hdcDst, bmpOld); if(bmpDst) DeleteObject(bmpDst); if(hdcDst) DeleteDC(hdcDst); } static void check_DrawIconEx(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, UINT flags, COLORREF background, COLORREF modern_expected, COLORREF legacy_expected, int line) { COLORREF result; HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color)); if (!hicon) return; SetPixelV(hdc, 0, 0, background); DrawIconEx(hdc, 0, 0, hicon, 1, 1, 0, NULL, flags); result = GetPixel(hdc, 0, 0); ok (color_match(result, modern_expected) || /* Windows 2000 and up */ broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ "Overlaying Mask %d on Color %06X with DrawIconEx flags %08X. " "Expected a close match to %06X (modern) or %06X (legacy). Got %06X from line %d\n", maskvalue, color, flags, modern_expected, legacy_expected, result, line); } static void test_DrawIconEx(void) { BITMAPINFO bitmapInfo; HDC hdcDst = NULL; HBITMAP bmpDst = NULL; HBITMAP bmpOld = NULL; UINT32 bits = 0; hdcDst = CreateCompatibleDC(0); ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n"); if (!hdcDst) return; if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8) { skip("Windows will distort DrawIconEx colors at 8-bpp and less due to palletizing.\n"); goto cleanup; } memset(&bitmapInfo, 0, sizeof(bitmapInfo)); bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = 1; bitmapInfo.bmiHeader.biHeight = 1; bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32); bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0); ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n"); if (!bmpDst || !bits) goto cleanup; bmpOld = SelectObject(hdcDst, bmpDst); /* Test null, image only, and mask only drawing */ check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, 0, 0x00102030, 0x00102030, 0x00102030, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, 0, 0x00102030, 0x00102030, 0x00102030, __LINE__); check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_MASK, 0x00FFFFFF, 0x00000000, 0x00000000, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_MASK, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, __LINE__); todo_wine { check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, DI_IMAGE, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, DI_IMAGE, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); } /* Test normal drawing */ check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); todo_wine check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x003F4F5F, 0x003F4F5F, __LINE__); check_DrawIconEx(hdcDst, FALSE, 0xFFA0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); /* Test alpha blending */ /* Windows 2000 and up will alpha blend, earlier Windows versions will not */ check_DrawIconEx(hdcDst, TRUE, 0xFFA0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__); check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_NORMAL, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_NORMAL, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__); check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__); check_DrawIconEx(hdcDst, FALSE, 0x01FFFFFF, 32, DI_NORMAL, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__); check_DrawIconEx(hdcDst, TRUE, 0x01FFFFFF, 32, DI_NORMAL, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__); /* Test detecting of alpha channel */ /* If a single pixel's alpha channel is non-zero, the icon will be alpha blended, otherwise it will be draw with and + xor blts. */ check_alpha_draw(hdcDst, TRUE, FALSE, 32, __LINE__); check_alpha_draw(hdcDst, TRUE, TRUE, 32, __LINE__); cleanup: if(bmpOld) SelectObject(hdcDst, bmpOld); if(bmpDst) DeleteObject(bmpDst); if(hdcDst) DeleteDC(hdcDst); } static void check_DrawState_Size(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, UINT flags, int line) { COLORREF result, background; BOOL passed[2]; HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color)); background = 0x00FFFFFF; /* Set color of the 2 pixels that will be checked afterwards */ SetPixelV(hdc, 0, 0, background); SetPixelV(hdc, 2, 2, background); /* Let DrawState calculate the size of the icon (it's 1x1) */ DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, (DST_ICON | flags )); result = GetPixel(hdc, 0, 0); passed[0] = color_match(result, background); result = GetPixel(hdc, 2, 2); passed[0] = passed[0] & color_match(result, background); /* Check if manually specifying the icon size DOESN'T work */ /* IMPORTANT: For Icons, DrawState wants the size of the source image, not the * size in which it should be ultimately drawn. Therefore giving * width/height 2x2 if the icon is only 1x1 pixels in size should * result in drawing it with size 1x1. The size parameters must be * ignored if a Icon has to be drawn! */ DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 2, 2, (DST_ICON | flags )); result = GetPixel(hdc, 0, 0); passed[1] = color_match(result, background); result = GetPixel(hdc, 2, 2); passed[1] = passed[0] & color_match(result, background); if(!passed[0]&&!passed[1]) ok (passed[1], "DrawState failed to draw a 1x1 Icon in the correct size, independent of the " "width and height settings passed to it, for Icon with: Overlaying Mask %d on " "Color %06X with flags %08X. Line %d\n", maskvalue, color, (DST_ICON | flags), line); else if(!passed[1]) ok (passed[1], "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height " "parameters passed to it are bigger than the real Icon size, for Icon with: Overlaying " "Mask %d on Color %06X with flags %08X. Line %d\n", maskvalue, color, (DST_ICON | flags), line); else ok (passed[0], "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height " "parameters passed to it are 0, for Icon with: Overlaying Mask %d on " "Color %06X with flags %08X. Line %d\n", maskvalue, color, (DST_ICON | flags), line); } static void check_DrawState_Color(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, UINT flags, COLORREF background, COLORREF modern_expected, COLORREF legacy_expected, int line) { COLORREF result; HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color)); if (!hicon) return; /* Set color of the pixel that will be checked afterwards */ SetPixelV(hdc, 1, 1, background); DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, ( DST_ICON | flags )); /* Check the color of the pixel is correct */ result = GetPixel(hdc, 1, 1); ok (color_match(result, modern_expected) || /* Windows 2000 and up */ broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ "DrawState drawing Icon with Overlaying Mask %d on Color %06X with flags %08X. " "Expected a close match to %06X (modern) or %06X (legacy). Got %06X from line %d\n", maskvalue, color, (DST_ICON | flags), modern_expected, legacy_expected, result, line); } static void test_DrawState(void) { BITMAPINFO bitmapInfo; HDC hdcDst = NULL; HBITMAP bmpDst = NULL; HBITMAP bmpOld = NULL; UINT32 bits = 0; hdcDst = CreateCompatibleDC(0); ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n"); if (!hdcDst) return; if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8) { skip("Windows will distort DrawIconEx colors at 8-bpp and less due to palletizing.\n"); goto cleanup; } memset(&bitmapInfo, 0, sizeof(bitmapInfo)); bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.bmiHeader.biWidth = 3; bitmapInfo.bmiHeader.biHeight = 3; bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32); bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0); ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n"); if (!bmpDst || !bits) goto cleanup; bmpOld = SelectObject(hdcDst, bmpDst); /* potential flags to test with DrawState are: */ /* DSS_DISABLED embosses the icon */ /* DSS_MONO draw Icon using a brush as parameter 5 */ /* DSS_NORMAL draw Icon without any modifications */ /* DSS_UNION draw the Icon dithered */ check_DrawState_Size(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, __LINE__); check_DrawState_Color(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); cleanup: if(bmpOld) SelectObject(hdcDst, bmpOld); if(bmpDst) DeleteObject(bmpDst); if(hdcDst) DeleteDC(hdcDst); } static void test_DestroyCursor(void) { static const BYTE bmp_bits[4096]; ICONINFO cursorInfo; HCURSOR cursor, cursor2; BOOL ret; DWORD error; UINT display_bpp; HDC hdc; hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(0, hdc); cursorInfo.fIcon = FALSE; cursorInfo.xHotspot = 0; cursorInfo.yHotspot = 0; cursorInfo.hbmMask = CreateBitmap(32, 32, 1, 1, bmp_bits); cursorInfo.hbmColor = CreateBitmap(32, 32, 1, display_bpp, bmp_bits); cursor = CreateIconIndirect(&cursorInfo); ok(cursor != NULL, "CreateIconIndirect returned %p\n", cursor); if(!cursor) { return; } SetCursor(cursor); SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); ok(!ret || broken(ret) /* succeeds on win9x */, "DestroyCursor on the active cursor succeeded\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); if (!ret) { cursor2 = GetCursor(); ok(cursor2 == cursor, "Active was set to %p when trying to destroy it\n", cursor2); SetCursor(NULL); /* Trying to destroy the cursor properly fails now with * ERROR_INVALID_CURSOR_HANDLE. This happens because we called * DestroyCursor() 2+ times after calling SetCursor(). The calls to * GetCursor() and SetCursor(NULL) in between make no difference. */ ret = DestroyCursor(cursor); todo_wine { ok(!ret, "DestroyCursor succeeded.\n"); error = GetLastError(); ok(error == ERROR_INVALID_CURSOR_HANDLE || error == 0xdeadbeef, /* vista */ "Last error: 0x%08x\n", error); } } DeleteObject(cursorInfo.hbmMask); DeleteObject(cursorInfo.hbmColor); /* Try testing DestroyCursor() now using LoadCursor() cursors. */ cursor = LoadCursor(NULL, IDC_ARROW); SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); ok(ret || broken(!ret) /* fails on win9x */, "DestroyCursor on the active cursor failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); /* Try setting the cursor to a destroyed OEM cursor. */ SetLastError(0xdeadbeef); SetCursor(cursor); error = GetLastError(); todo_wine { ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); } /* Check if LoadCursor() returns the same handle with the same icon. */ cursor2 = LoadCursor(NULL, IDC_ARROW); ok(cursor2 == cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2); /* Check if LoadCursor() returns the same handle with a different icon. */ cursor2 = LoadCursor(NULL, IDC_WAIT); ok(cursor2 != cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2); } START_TEST(cursoricon) { test_argc = winetest_get_mainargs(&test_argv); if (test_argc >= 3) { /* Child process. */ sscanf (test_argv[2], "%x", (unsigned int *) &parent); ok(parent != NULL, "Parent not found.\n"); if (parent == NULL) ExitProcess(1); do_child(); return; } test_CopyImage_Bitmap(1); test_CopyImage_Bitmap(4); test_CopyImage_Bitmap(8); test_CopyImage_Bitmap(16); test_CopyImage_Bitmap(24); test_CopyImage_Bitmap(32); test_initial_cursor(); test_CreateIcon(); test_LoadImage(); test_CreateIconFromResource(); test_DrawIcon(); test_DrawIconEx(); test_DrawState(); test_DestroyCursor(); do_parent(); test_child_process(); finish_child_process(); }