/* * 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 "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; #define RIFF_FOURCC( c0, c1, c2, c3 ) \ ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \ ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) ) #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F') #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T') #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N') #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h') #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ') #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm') #define ANI_icon_ID RIFF_FOURCC('i', 'c', 'o', 'n') #define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e') #define ANI_FLAG_ICON 0x1 #define ANI_FLAG_SEQUENCE 0x2 typedef struct { DWORD header_size; DWORD num_frames; DWORD num_steps; DWORD width; DWORD height; DWORD bpp; DWORD num_planes; DWORD display_rate; DWORD flags; } ani_header; typedef struct { BYTE data[32*32*4]; BYTE mask_data[32*32/8]; } ani_data32x32x32; typedef struct { CURSORICONFILEDIR icon_info; /* animated cursor frame information */ BITMAPINFOHEADER bmi_header; /* animated cursor frame header */ ani_data32x32x32 bmi_data; /* animated cursor frame DIB data */ } ani_frame32x32x32; typedef struct { DWORD chunk_id; /* ANI_anih_ID */ DWORD chunk_size; /* actual size of data */ ani_header header; /* animated cursor header */ } riff_header_t; typedef struct { DWORD chunk_id; /* ANI_LIST_ID */ DWORD chunk_size; /* actual size of data */ DWORD chunk_type; /* ANI_fram_ID */ } riff_list_t; typedef struct { DWORD chunk_id; /* ANI_icon_ID */ DWORD chunk_size; /* actual size of data */ ani_frame32x32x32 data; /* animated cursor frame */ } riff_icon32x32x32_t; typedef struct { DWORD chunk_id; /* ANI_RIFF_ID */ DWORD chunk_size; /* actual size of data */ DWORD chunk_type; /* ANI_ACON_ID */ riff_header_t header; /* RIFF animated cursor header */ riff_list_t frame_list; /* RIFF animated cursor frame list info */ riff_icon32x32x32_t frames[1]; /* array of animated cursor frames */ } riff_cursor1_t; typedef struct { DWORD chunk_id; /* ANI_RIFF_ID */ DWORD chunk_size; /* actual size of data */ DWORD chunk_type; /* ANI_ACON_ID */ riff_header_t header; /* RIFF animated cursor header */ riff_list_t frame_list; /* RIFF animated cursor frame list info */ riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ } riff_cursor3_t; typedef struct { DWORD chunk_id; /* ANI_rate_ID */ DWORD chunk_size; /* actual size of data */ DWORD rate[3]; /* animated cursor rate data */ } riff_rate3_t; typedef struct { DWORD chunk_id; /* ANI_seq__ID */ DWORD chunk_size; /* actual size of data */ DWORD order[3]; /* animated cursor sequence data */ } riff_seq3_t; typedef struct { DWORD chunk_id; /* ANI_RIFF_ID */ DWORD chunk_size; /* actual size of data */ DWORD chunk_type; /* ANI_ACON_ID */ riff_header_t header; /* RIFF animated cursor header */ riff_seq3_t seq; /* sequence data for three cursor frames */ riff_rate3_t rates; /* rate data for three cursor frames */ riff_list_t frame_list; /* RIFF animated cursor frame list info */ riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ } riff_cursor3_seq_t; #define EMPTY_ICON32 \ { \ ANI_icon_ID, \ sizeof(ani_frame32x32x32), \ { \ { \ 0x0, /* reserved */ \ 0, /* type: icon(1), cursor(2) */ \ 1, /* count */ \ { \ { \ 32, /* width */ \ 32, /* height */ \ 0, /* color count */ \ 0x0, /* reserved */ \ 16, /* x hotspot */ \ 16, /* y hotspot */ \ sizeof(ani_data32x32x32), /* DIB size */ \ sizeof(CURSORICONFILEDIR) /* DIB offset */ \ } \ } \ }, \ { \ sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ \ 32, /* width */ \ 32*2, /* actual height times two */ \ 1, /* planes */ \ 32, /* bpp */ \ BI_RGB, /* compression */ \ 0, /* image size */ \ 0, /* biXPelsPerMeter */ \ 0, /* biYPelsPerMeter */ \ 0, /* biClrUsed */ \ 0 /* biClrImportant */ \ } \ /* DIB data: left uninitialized */ \ } \ } riff_cursor1_t empty_anicursor = { ANI_RIFF_ID, sizeof(empty_anicursor) - sizeof(DWORD)*2, ANI_ACON_ID, { ANI_anih_ID, sizeof(ani_header), { sizeof(ani_header), 1, /* frames */ 1, /* steps */ 32, /* width */ 32, /* height */ 32, /* depth */ 1, /* planes */ 10, /* display rate in jiffies */ ANI_FLAG_ICON /* flags */ } }, { ANI_LIST_ID, sizeof(riff_icon32x32x32_t)*(1 /*frames*/) + sizeof(DWORD), ANI_fram_ID, }, { EMPTY_ICON32 } }; riff_cursor3_t empty_anicursor3 = { ANI_RIFF_ID, sizeof(empty_anicursor3) - sizeof(DWORD)*2, ANI_ACON_ID, { ANI_anih_ID, sizeof(ani_header), { sizeof(ani_header), 3, /* frames */ 3, /* steps */ 32, /* width */ 32, /* height */ 32, /* depth */ 1, /* planes */ 0xbeef, /* display rate in jiffies */ ANI_FLAG_ICON /* flags */ } }, { ANI_LIST_ID, sizeof(riff_icon32x32x32_t)*(3 /*frames*/) + sizeof(DWORD), ANI_fram_ID, }, { EMPTY_ICON32, EMPTY_ICON32, EMPTY_ICON32 } }; riff_cursor3_seq_t empty_anicursor3_seq = { ANI_RIFF_ID, sizeof(empty_anicursor3_seq) - sizeof(DWORD)*2, ANI_ACON_ID, { ANI_anih_ID, sizeof(ani_header), { sizeof(ani_header), 3, /* frames */ 3, /* steps */ 32, /* width */ 32, /* height */ 32, /* depth */ 1, /* planes */ 0xbeef, /* display rate in jiffies */ ANI_FLAG_ICON|ANI_FLAG_SEQUENCE /* flags */ } }, { ANI_seq__ID, sizeof(riff_seq3_t) - sizeof(DWORD)*2, { 2, 0, 1} /* show frames in a uniquely identifiable order */ }, { ANI_rate_ID, sizeof(riff_rate3_t) - sizeof(DWORD)*2, { 0xc0de, 0xcafe, 0xbabe} }, { ANI_LIST_ID, sizeof(riff_icon32x32x32_t)*(3 /*frames*/) + sizeof(DWORD), ANI_fram_ID, }, { EMPTY_ICON32, EMPTY_ICON32, EMPTY_ICON32 } }; #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 BOOL (WINAPI *pGetCursorInfo)(CURSORINFO *); static BOOL (WINAPI *pGetIconInfoExA)(HICON,ICONINFOEXA *); static BOOL (WINAPI *pGetIconInfoExW)(HICON,ICONINFOEXW *); static const BOOL is_win64 = (sizeof(void *) > sizeof(int)); static LRESULT CALLBACK callback_child(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { /* Destroy the cursor. */ case WM_USER+1: { HCURSOR cursor = (HCURSOR)lParam; ICONINFO info; BOOL ret; DWORD error; memset(&info, 0, sizeof(info)); ret = GetIconInfo(cursor, &info); todo_wine ok(ret, "GetIconInfoEx failed with error %u\n", GetLastError()); todo_wine ok(info.hbmColor != NULL, "info.hmbColor was not set\n"); todo_wine ok(info.hbmMask != NULL, "info.hmbColor was not set\n"); DeleteObject(info.hbmColor); DeleteObject(info.hbmMask); SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); error = GetLastError(); 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 DefWindowProcA(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 DefWindowProcA(hwnd, msg, wParam, lParam); } static void do_child(void) { WNDCLASSA 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 = GetModuleHandleA(NULL); class.hIcon = NULL; class.hCursor = NULL; class.hbrBackground = NULL; class.lpszMenuName = NULL; class.lpszClassName = "cursor_child"; SetLastError(0xdeadbeef); ret = RegisterClassA(&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. */ PostMessageA(parent, PROC_INIT, (WPARAM) child, 0); /* Receive messages. */ while ((ret = GetMessageA(&msg, 0, 0, 0))) { ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); TranslateMessage(&msg); DispatchMessageA(&msg); } } static void do_parent(void) { char path_name[MAX_PATH]; PROCESS_INFORMATION info; STARTUPINFOA startup; WNDCLASSA 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 = GetModuleHandleA(NULL); class.hIcon = NULL; class.hCursor = NULL; class.hbrBackground = NULL; class.lpszMenuName = NULL; class.lpszClassName = "cursor_parent"; SetLastError(0xdeadbeef); ret = RegisterClassA(&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 = GetMessageA(&msg, parent, 0, 0))) { ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); TranslateMessage(&msg); DispatchMessageA(&msg); } } static void finish_child_process(void) { SendMessageA(child, WM_CLOSE, 0, 0); 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. */ SendMessageA(child, WM_USER+1, 0, (LPARAM) cursor); } static BOOL color_match(COLORREF a, COLORREF b) { /* 5-bit accuracy is a sufficient test. This will match as long as * colors are never truncated to less that 3x5-bit accuracy i.e. * palettized. */ return (a & 0x00F8F8F8) == (b & 0x00F8F8F8); } static void check_copy_image(HBITMAP bitmap, UINT type, UINT flags, INT copyWidth, INT copyHeight, INT expectedWidth, INT expectedHeight, WORD expectedDepth, BOOL dibExpected) { HBITMAP copy; BITMAP origBitmap; BITMAP copyBitmap; BOOL copy_is_dib; BOOL ret; GetObjectA(bitmap, sizeof(origBitmap), &origBitmap); winetest_push_context("%s, type %#x, flags %#x, size %ux%u", origBitmap.bmBits ? "DIB" : "DDB", type, flags, copyWidth, copyHeight); if (type == IMAGE_BITMAP) { copy = CopyImage(bitmap, type, copyWidth, copyHeight, flags); } else { ICONINFO info = { .fIcon = (type == IMAGE_ICON), .hbmColor = bitmap, .hbmMask = bitmap, }; HICON icon = CreateIconIndirect(&info); ok(!!icon, "Failed to create icon, error %u\n", GetLastError()); copy = CopyImage(icon, type, copyWidth, copyHeight, flags); ret = DestroyIcon(icon); ok(ret, "Failed to destroy icon, error %u\n", GetLastError()); } ok(copy != NULL, "CopyImage() failed\n"); if (copy != NULL) { if (type == IMAGE_BITMAP) { GetObjectA(copy, sizeof(copyBitmap), ©Bitmap); } else { ICONINFO info; ret = GetIconInfo((HICON)copy, &info); ok(ret, "Failed to get icon info, error %u\n", GetLastError()); GetObjectA(info.hbmColor, sizeof(copyBitmap), ©Bitmap); } 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; } if (type != IMAGE_BITMAP) { dibExpected = FALSE; expectedDepth = 32; } ok(copy_is_dib == dibExpected, "Expected %s, got %s\n", dibExpected ? "DIB" : "DDB", copy_is_dib ? "DIB" : "DDB"); ok(copyBitmap.bmWidth == expectedWidth, "Expected width %u, got %u\n", expectedWidth, copyBitmap.bmWidth); ok(copyBitmap.bmHeight == expectedHeight, "Expected height %u, got %u\n", expectedHeight, copyBitmap.bmHeight); ok(copyBitmap.bmBitsPixel == expectedDepth, "Expected depth %u, got %u\n", expectedDepth, copyBitmap.bmBitsPixel); if (type != IMAGE_BITMAP) { ICONINFO info; ret = GetIconInfo((HICON)copy, &info); ok(ret, "Failed to get icon info, error %u\n", GetLastError()); GetObjectA(info.hbmMask, sizeof(copyBitmap), ©Bitmap); ok(!copyBitmap.bmBits, "Expected DDB\n"); ok(copyBitmap.bmWidth == expectedWidth, "Expected mask width %u, got %u\n", expectedWidth, copyBitmap.bmWidth); ok(copyBitmap.bmHeight == expectedHeight, "Expected mask height %u, got %u\n", expectedHeight, copyBitmap.bmHeight); ok(copyBitmap.bmBitsPixel == 1, "Got mask depth %u\n", copyBitmap.bmBitsPixel); } if (type == IMAGE_BITMAP) DeleteObject(copy); else if (type == IMAGE_ICON) DestroyIcon((HICON)copy); else DestroyCursor((HCURSOR)copy); } winetest_pop_context(); } static void do_test_copy_image(UINT type, UINT 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) { check_copy_image(ddb, type, 0, 0, 0, 2, 2, depth == 1 ? 1 : screen_depth, FALSE); check_copy_image(ddb, type, 0, 0, 5, 2, 5, depth == 1 ? 1 : screen_depth, FALSE); check_copy_image(ddb, type, 0, 5, 0, 5, 2, depth == 1 ? 1 : screen_depth, FALSE); check_copy_image(ddb, type, 0, 5, 5, 5, 5, depth == 1 ? 1 : screen_depth, FALSE); check_copy_image(ddb, type, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); check_copy_image(ddb, type, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); check_copy_image(ddb, type, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); check_copy_image(ddb, type, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); check_copy_image(ddb, type, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); check_copy_image(ddb, type, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); check_copy_image(ddb, type, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); check_copy_image(ddb, type, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ check_copy_image(ddb, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); check_copy_image(ddb, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); check_copy_image(ddb, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); check_copy_image(ddb, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); DeleteObject(ddb); } if (depth != 1) { check_copy_image(dib, type, 0, 0, 0, 2, 2, screen_depth, FALSE); check_copy_image(dib, type, 0, 5, 0, 5, 2, screen_depth, FALSE); check_copy_image(dib, type, 0, 0, 5, 2, 5, screen_depth, FALSE); check_copy_image(dib, type, 0, 5, 5, 5, 5, screen_depth, FALSE); } check_copy_image(dib, type, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); check_copy_image(dib, type, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); check_copy_image(dib, type, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); check_copy_image(dib, type, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); check_copy_image(dib, type, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); check_copy_image(dib, type, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); check_copy_image(dib, type, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); check_copy_image(dib, type, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ check_copy_image(dib, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); check_copy_image(dib, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); check_copy_image(dib, type, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); check_copy_image(dib, type, 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); check_copy_image(dib, type, 0, 0, 0, 2, 2, screen_depth, FALSE); check_copy_image(dib, type, 0, 5, 0, 5, 2, screen_depth, FALSE); check_copy_image(dib, type, 0, 0, 5, 2, 5, screen_depth, FALSE); check_copy_image(dib, type, 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); check_copy_image(dib, type, 0, 0, 0, 2, 2, 1, FALSE); check_copy_image(dib, type, 0, 5, 0, 5, 2, 1, FALSE); check_copy_image(dib, type, 0, 0, 5, 2, 5, 1, FALSE); check_copy_image(dib, type, 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); check_copy_image(dib, type, 0, 0, 0, 2, 2, 1, FALSE); check_copy_image(dib, type, 0, 5, 0, 5, 2, 1, FALSE); check_copy_image(dib, type, 0, 0, 5, 2, 5, 1, FALSE); check_copy_image(dib, type, 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 = LoadCursorA(NULL, (LPCSTR)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_(HICON hIcon, UINT exp_cx, UINT exp_cy, UINT exp_mask_cy, UINT exp_bpp, UINT has_color, 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 = GetObjectA(info.hbmMask, sizeof(bmMask), &bmMask); ok_(__FILE__, line)(ret == sizeof(bmMask), "GetObject(info.hbmMask) failed, ret %u\n", ret); ok_(__FILE__, line)(!!info.hbmColor == has_color, "got hbmColor %p\n", info.hbmColor); if (info.hbmColor) { HDC hdc; UINT display_bpp; hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(0, hdc); ret = GetObjectA(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_mask_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_mask_cy, "bmMask.bmHeight = %d\n", bmMask.bmHeight); } if (pGetIconInfoExA) { ICONINFOEXA infoex; memset( &infoex, 0xcc, sizeof(infoex) ); SetLastError( 0xdeadbeef ); infoex.cbSize = sizeof(infoex) - 1; ret = pGetIconInfoExA( hIcon, &infoex ); ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %d\n", GetLastError()); SetLastError( 0xdeadbeef ); infoex.cbSize = sizeof(infoex) + 1; ret = pGetIconInfoExA( hIcon, &infoex ); ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %d\n", GetLastError()); SetLastError( 0xdeadbeef ); infoex.cbSize = sizeof(infoex); ret = pGetIconInfoExA( (HICON)0xdeadbabe, &infoex ); ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_CURSOR_HANDLE, "wrong error %d\n", GetLastError()); infoex.cbSize = sizeof(infoex); ret = pGetIconInfoExA( hIcon, &infoex ); ok_(__FILE__, line)(ret, "GetIconInfoEx failed err %d\n", GetLastError()); ok_(__FILE__, line)(infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID); ok_(__FILE__, line)(infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName); ok_(__FILE__, line)(infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName); } } #define test_icon_info(a,b,c,d,e,f) test_icon_info_(a,b,c,d,e,f,__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; int i; 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, 32, 1, FALSE); DestroyIcon(hIcon); hIcon = CreateCursor(0, 8, 8, 16, 16, bmp_bits, bmp_bits); ok(hIcon != 0, "CreateCursor failed\n"); test_icon_info(hIcon, 16, 16, 32, 1, FALSE); DestroyCursor(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, 16, display_bpp, TRUE); 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, 16, display_bpp, TRUE); 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, 32, 1, FALSE); DestroyIcon(hIcon); info.hbmMask = hbmMask; info.hbmColor = hbmMask; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 32, 32, 1, TRUE); DestroyIcon(hIcon); DeleteObject(hbmMask); for (i = 0; i <= 4; i++) { hbmMask = CreateBitmap(1, i, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); info.fIcon = TRUE; info.xHotspot = 0; info.yHotspot = 0; info.hbmMask = hbmMask; info.hbmColor = 0; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 1, i / 2, max(i,1), 1, FALSE); DestroyIcon(hIcon); DeleteObject(hbmMask); } hbmMask = CreateBitmap(16, 32, 1, 16, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); info.hbmMask = hbmMask; info.hbmColor = 0; SetLastError(0xdeadbeaf); hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 16, 32, 1, FALSE); DestroyIcon(hIcon); DeleteObject(hbmMask); /* 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, 32, 8, TRUE); 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, 32, 8, TRUE); 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, 32, 8, TRUE); 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 with gap between palette and bitmap. Correct bitmap contains only zeroes, gap is 0xFF. */ static unsigned char bmpimage[70] = { 0x42,0x4d,0x46,0x00,0x00,0x00,0xDE,0xAD,0xBE,0xEF,0x42,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,0x55,0x55,0x55,0x00,0xFF,0xFF, 0xFF,0xFF,0x00,0x00,0x00,0x00 }; /* 1x1 pixel bmp using BITMAPCOREHEADER */ static const unsigned char bmpcoreimage[38] = { 0x42,0x4d,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0c,0x00, 0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xff,0xff,0xff,0x00,0x55,0x55, 0x55,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 }; /* An invalid cursor with an invalid dwDIBOffset */ static const unsigned char invalid_dwDIBOffset[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00 }; static const DWORD biSize_tests[] = { 0, sizeof(BITMAPCOREHEADER) - 1, sizeof(BITMAPCOREHEADER) + 1, sizeof(BITMAPINFOHEADER) - 1, sizeof(BITMAPINFOHEADER) + 1, sizeof(BITMAPV4HEADER) - 1, sizeof(BITMAPV4HEADER) + 1, sizeof(BITMAPV5HEADER) - 1, sizeof(BITMAPV5HEADER) + 1, (sizeof(BITMAPCOREHEADER) + sizeof(BITMAPINFOHEADER)) / 2, (sizeof(BITMAPV4HEADER) + sizeof(BITMAPV5HEADER)) / 2, 0xdeadbeef, 0xffffffff }; static void test_LoadImageBitmap(const char * test_desc, HBITMAP hbm) { BITMAP bm; BITMAPINFO bmi; DWORD ret, pixel = 0; HDC hdc = GetDC(NULL); ret = GetObjectA(hbm, sizeof(bm), &bm); ok(ret == sizeof(bm), "GetObject returned %d\n", ret); memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = bm.bmWidth; bmi.bmiHeader.biHeight = bm.bmHeight; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount= 24; bmi.bmiHeader.biCompression= BI_RGB; ret = GetDIBits(hdc, hbm, 0, bm.bmHeight, &pixel, &bmi, DIB_RGB_COLORS); ok(ret == bm.bmHeight, "%s: %d lines were converted, not %d\n", test_desc, ret, bm.bmHeight); ok(color_match(pixel, 0x00ffffff), "%s: Pixel is 0x%08x\n", test_desc, pixel); ReleaseDC(NULL, hdc); } static void test_LoadImageFile(const char * test_desc, 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(ret && 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, "%s: IMAGE_CURSOR succeeded incorrectly.\n", test_desc); 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, "%s: IMAGE_ICON succeeded incorrectly.\n", test_desc); 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 for correct bmp, fail for everything else */ SetLastError(0xdeadbeef); handle = LoadImageA(NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); error = GetLastError(); ok(error == 0 || error == 0xdeadbeef, /* Win9x, WinMe */ "Last error: %u\n", error); if (expect_success) { ok(handle != NULL, "%s: IMAGE_BITMAP failed.\n", test_desc); if (handle != NULL) test_LoadImageBitmap(test_desc, handle); } else ok(handle == NULL, "%s: IMAGE_BITMAP succeeded incorrectly.\n", test_desc); if (handle != NULL) DeleteObject(handle); DeleteFileA(filename); } typedef struct { unsigned width; unsigned height; BOOL invalid_offset; } test_icon_entries_t; static void create_ico_file(const char *filename, const test_icon_entries_t *test_icon_entries, unsigned entry_cnt) { CURSORICONFILEDIRENTRY *icon_entry; BITMAPINFOHEADER *icon_header; CURSORICONFILEDIR *dir; BYTE *buf, *bitmap_ptr; DWORD bytes_written; size_t icon_size; HANDLE file; unsigned i; BOOL ret; const unsigned icon_bpp = 32; icon_size = FIELD_OFFSET(CURSORICONFILEDIR, idEntries[entry_cnt]) + sizeof(BITMAPINFOHEADER)*entry_cnt; for(i=0; iidReserved = 0; dir->idType = 1; dir->idCount = entry_cnt; bitmap_ptr = buf + FIELD_OFFSET(CURSORICONFILEDIR, idEntries[entry_cnt]); for(i=0; iidEntries+i; icon_entry->bWidth = test_icon_entries[i].width; icon_entry->bHeight = test_icon_entries[i].height; icon_entry->bColorCount = 0; icon_entry->bReserved = 0; icon_entry->xHotspot = 1; icon_entry->yHotspot = 1; icon_entry->dwDIBSize = sizeof(BITMAPINFOHEADER) + icon_entry->bWidth * icon_entry->bHeight * icon_bpp / 8; icon_entry->dwDIBOffset = test_icon_entries[i].invalid_offset ? 0xffffffff : bitmap_ptr - buf; icon_header = (BITMAPINFOHEADER*)bitmap_ptr; bitmap_ptr += icon_entry->dwDIBSize; icon_header->biSize = sizeof(BITMAPINFOHEADER); icon_header->biWidth = icon_entry->bWidth; icon_header->biHeight = icon_entry->bHeight; icon_header->biPlanes = 1; icon_header->biBitCount = icon_bpp; icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ } memset(bitmap_ptr, 0xf0, buf+icon_size-bitmap_ptr); /* Create the icon. */ file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); ret = WriteFile(file, buf, icon_size, &bytes_written, NULL); ok(ret && bytes_written == icon_size, "icon.ico created improperly.\n"); CloseHandle(file); HeapFree(GetProcessHeap(), 0, buf); } static void create_bitmap_file(const char *filename, const BITMAPINFO *bmi, const unsigned char *bits) { unsigned int clr_used, bmi_size, bits_size, stride; const BITMAPINFOHEADER *h = &bmi->bmiHeader; BITMAPFILEHEADER hdr; DWORD bytes_written; HANDLE file; BOOL ret; clr_used = h->biBitCount <= 8 ? 1u << h->biBitCount : 0; stride = ((h->biBitCount * h->biWidth + 7) / 8 + 3) & ~3; bits_size = h->biHeight * stride; bmi_size = h->biSize + clr_used * sizeof(RGBQUAD); hdr.bfType = 0x4d42; hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + bmi_size; hdr.bfSize = hdr.bfOffBits + bits_size; hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; file = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed, result %u.\n", GetLastError()); ret = WriteFile(file, &hdr, sizeof(hdr), &bytes_written, NULL); ok(ret && bytes_written == sizeof(hdr), "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n", ret, bytes_written); ret = WriteFile(file, bmi, bmi_size, &bytes_written, NULL); ok(ret && bytes_written == bmi_size, "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n", ret, bytes_written); ret = WriteFile(file, bits, bits_size, &bytes_written, NULL); ok(ret && bytes_written == bits_size, "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n", ret, bytes_written); CloseHandle(file); } static void test_LoadImage_working_directory_run(char *path) { DWORD bytes_written; HANDLE handle; BOOL ret; char path_icon[MAX_PATH]; char path_image[MAX_PATH]; static const test_icon_entries_t icon_desc = {32, 32}; sprintf(path_icon, "%s\\icon.ico", path); sprintf(path_image, "%s\\test.bmp", path); /* Create Files */ create_ico_file(path_icon, &icon_desc, 1); handle = CreateFileA(path_image, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); ok(handle != INVALID_HANDLE_VALUE, "run %s: CreateFileA failed. %u\n", path, GetLastError()); ret = WriteFile(handle, bmpimage, sizeof(bmpimage), &bytes_written, NULL); ok(ret && bytes_written == sizeof(bmpimage), "run %s: Test file created improperly.\n", path); CloseHandle(handle); /* Test cursor */ handle = LoadImageA(NULL, "icon.ico", IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); ok(handle != NULL, "run %s: LoadImage() failed.\n", path); ret = DestroyIcon(handle); ok(ret, "run %s: DestroyIcon failed: %d\n", path, GetLastError()); /* Test image */ handle = LoadImageA(NULL, "test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); ok(handle != NULL, "run %s: LoadImageA failed.\n", path); ret = DeleteObject(handle); ok(ret, "run %s: DeleteObject failed: %d\n", path, GetLastError()); /* Cleanup */ ret = DeleteFileA(path_image); ok(ret, "run %s: DeleteFileA failed: %d\n", path, GetLastError()); ret = DeleteFileA(path_icon); ok(ret, "run %s: DeleteFileA failed: %d\n", path, GetLastError()); } static void test_LoadImage_working_directory(void) { char old_working_dir[MAX_PATH]; char temp_dir_current[MAX_PATH]; char temp_dir_PATH[MAX_PATH]; char executable_path[MAX_PATH]; int pos_slash; char old_PATH[10000]; char new_PATH[10000 + MAX_PATH]; BOOL ret; GetCurrentDirectoryA(ARRAY_SIZE(old_working_dir), old_working_dir); GetTempPathA(ARRAY_SIZE(temp_dir_current), temp_dir_current); strcat(temp_dir_current, "wine-test-dir-current\\"); GetTempPathA(ARRAY_SIZE(temp_dir_PATH), temp_dir_PATH); strcat(temp_dir_PATH, "wine-test-dir-path\\"); GetModuleFileNameA(NULL, executable_path, ARRAY_SIZE(executable_path)); pos_slash = strrchr(executable_path, '\\') - executable_path; executable_path[pos_slash + 1] = 0; CreateDirectoryA(temp_dir_current, NULL); CreateDirectoryA(temp_dir_PATH, NULL); SetCurrentDirectoryA(temp_dir_current); GetEnvironmentVariableA("PATH", old_PATH, ARRAY_SIZE(old_PATH)); sprintf(new_PATH, "%s;%s", old_PATH, temp_dir_PATH); SetEnvironmentVariableA("PATH", new_PATH); test_LoadImage_working_directory_run(temp_dir_current); test_LoadImage_working_directory_run(executable_path); test_LoadImage_working_directory_run(temp_dir_PATH); SetCurrentDirectoryA(old_working_dir); SetEnvironmentVariableA("PATH", old_PATH); ret = RemoveDirectoryA(temp_dir_current); ok(ret, "RemoveDirectoryA failed: %d\n", GetLastError()); ret = RemoveDirectoryA(temp_dir_PATH); ok(ret, "RemoveDirectoryA failed: %d\n", GetLastError()); } static void test_LoadImage(void) { HANDLE handle; BOOL ret; DWORD error; BITMAPINFOHEADER *bitmap_header; ICONINFO icon_info; int i; #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) static const test_icon_entries_t icon_desc = {32, 32}; create_ico_file("icon.ico", &icon_desc, 1); /* 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"); } if (pGetIconInfoExA) { ICONINFOEXA infoex; infoex.cbSize = sizeof(infoex); ret = pGetIconInfoExA( handle, &infoex ); ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); ok( infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID ); ok( infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName ); ok( infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName ); } else win_skip( "GetIconInfoEx not available\n" ); /* Clean up. */ SetLastError(0xdeadbeef); ret = DestroyCursor(handle); ok(ret, "DestroyCursor() failed.\n"); error = GetLastError(); ok(error == 0xdeadbeef, "Last error: %u\n", error); DeleteFileA("icon.ico"); /* Test a system icon */ handle = LoadIconA( 0, (LPCSTR)IDI_HAND ); ok(handle != NULL, "LoadImage() failed.\n"); if (pGetIconInfoExA) { ICONINFOEXA infoexA; ICONINFOEXW infoexW; infoexA.cbSize = sizeof(infoexA); ret = pGetIconInfoExA( handle, &infoexA ); ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); ok( infoexA.wResID == (UINT_PTR)IDI_HAND, "GetIconInfoEx wrong resid %x\n", infoexA.wResID ); /* the A version is broken on 64-bit, it truncates the string after the first char */ if (is_win64 && infoexA.szModName[0] && infoexA.szModName[1] == 0) trace( "GetIconInfoExA broken on Win64\n" ); else ok( GetModuleHandleA(infoexA.szModName) == GetModuleHandleA("user32.dll"), "GetIconInfoEx wrong module %s\n", infoexA.szModName ); ok( infoexA.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoexA.szResName ); infoexW.cbSize = sizeof(infoexW); ret = pGetIconInfoExW( handle, &infoexW ); ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); ok( infoexW.wResID == (UINT_PTR)IDI_HAND, "GetIconInfoEx wrong resid %x\n", infoexW.wResID ); ok( GetModuleHandleW(infoexW.szModName) == GetModuleHandleA("user32.dll"), "GetIconInfoEx wrong module %s\n", wine_dbgstr_w(infoexW.szModName) ); ok( infoexW.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", wine_dbgstr_w(infoexW.szResName) ); } SetLastError(0xdeadbeef); DestroyIcon(handle); test_LoadImageFile("BMP", bmpimage, sizeof(bmpimage), "bmp", 1); test_LoadImageFile("BMP (coreinfo)", bmpcoreimage, sizeof(bmpcoreimage), "bmp", 1); test_LoadImageFile("GIF", gifimage, sizeof(gifimage), "gif", 0); test_LoadImageFile("GIF (2x2 pixel)", gif4pixel, sizeof(gif4pixel), "gif", 0); test_LoadImageFile("JPG", jpgimage, sizeof(jpgimage), "jpg", 0); test_LoadImageFile("PNG", pngimage, sizeof(pngimage), "png", 0); /* Check failure for broken BMP images */ bitmap_header = (BITMAPINFOHEADER *)(bmpimage + sizeof(BITMAPFILEHEADER)); bitmap_header->biHeight = 65536; test_LoadImageFile("BMP (too high)", bmpimage, sizeof(bmpimage), "bmp", 0); bitmap_header->biHeight = 1; bitmap_header->biWidth = 65536; test_LoadImageFile("BMP (too wide)", bmpimage, sizeof(bmpimage), "bmp", 0); bitmap_header->biWidth = 1; for (i = 0; i < ARRAY_SIZE(biSize_tests); i++) { bitmap_header->biSize = biSize_tests[i]; test_LoadImageFile("BMP (broken biSize)", bmpimage, sizeof(bmpimage), "bmp", 0); } bitmap_header->biSize = sizeof(BITMAPINFOHEADER); test_LoadImageFile("Cursor (invalid dwDIBOffset)", invalid_dwDIBOffset, sizeof(invalid_dwDIBOffset), "cur", 0); /* Test in which paths images with a relative path can be found */ test_LoadImage_working_directory(); } 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"); } if (pGetIconInfoExA) { ICONINFOEXA infoex; infoex.cbSize = sizeof(infoex); ret = pGetIconInfoExA( handle, &infoex ); ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); ok( infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID ); ok( infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName ); ok( infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName ); } /* 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); /* Rejection of NULL pointer crashes at least on WNT4WSSP6, W2KPROSP4, WXPPROSP3 * * handle = CreateIconFromResource(NULL, ICON_RES_SIZE, TRUE, 0x00030000); * ok(handle == NULL, "Invalid pointer accepted (%p)\n", handle); */ HeapFree(GetProcessHeap(), 0, hotspot); /* Test creating an animated cursor. */ empty_anicursor.frames[0].data.icon_info.idType = 2; /* type: cursor */ empty_anicursor.frames[0].data.icon_info.idEntries[0].xHotspot = 3; empty_anicursor.frames[0].data.icon_info.idEntries[0].yHotspot = 3; handle = CreateIconFromResource((PBYTE) &empty_anicursor, sizeof(empty_anicursor), FALSE, 0x00030000); ok(handle != NULL, "Create cursor failed.\n"); /* Test the animated cursor's 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); } static int check_cursor_data( HDC hdc, HCURSOR hCursor, void *data, int length) { char *image = NULL; BITMAPINFO *info; ICONINFO iinfo; DWORD ret; int i; ret = GetIconInfo( hCursor, &iinfo ); ok(ret, "GetIconInfo() failed\n"); if (!ret) return 0; ret = 0; info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )); ok(info != NULL, "HeapAlloc() failed\n"); if (!info) return 0; info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); info->bmiHeader.biWidth = 32; info->bmiHeader.biHeight = 32; info->bmiHeader.biPlanes = 1; info->bmiHeader.biBitCount = 32; info->bmiHeader.biCompression = BI_RGB; info->bmiHeader.biSizeImage = 32 * 32 * 4; info->bmiHeader.biXPelsPerMeter = 0; info->bmiHeader.biYPelsPerMeter = 0; info->bmiHeader.biClrUsed = 0; info->bmiHeader.biClrImportant = 0; image = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ); ok(image != NULL, "HeapAlloc() failed\n"); if (!image) goto cleanup; ret = GetDIBits( hdc, iinfo.hbmColor, 0, 32, image, info, DIB_RGB_COLORS ); ok(ret, "GetDIBits() failed\n"); for (i = 0; ret && i < length / sizeof(COLORREF); i++) { ret = color_match( ((COLORREF *)data)[i], ((COLORREF *)image)[i] ); ok(ret, "%04x: Expected 0x%x, actually 0x%x\n", i, ((COLORREF *)data)[i], ((COLORREF *)image)[i] ); } cleanup: HeapFree( GetProcessHeap(), 0, image ); HeapFree( GetProcessHeap(), 0, info ); return ret; } static HCURSOR (WINAPI *pGetCursorFrameInfo)(HCURSOR hCursor, DWORD unk1, DWORD istep, DWORD *rate, DWORD *steps); static void test_GetCursorFrameInfo(void) { DWORD frame_identifier[] = { 0x10Ad, 0xc001, 0x1c05 }; HBITMAP bmp = NULL, bmpOld = NULL; DWORD rate, steps; BITMAPINFOHEADER *icon_header; BITMAPINFO bitmapInfo; HDC hdc = NULL; void *bits = 0; INT16 *hotspot; HANDLE h1, h2; BOOL ret; int i; if (!pGetCursorFrameInfo) { win_skip( "GetCursorFrameInfo not supported, skipping tests.\n" ); return; } hdc = CreateCompatibleDC(0); ok(hdc != 0, "CreateCompatibleDC(0) failed to return a valid DC\n"); if (!hdc) return; 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); bmp = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, &bits, NULL, 0); ok (bmp && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n"); if (!bmp || !bits) goto cleanup; bmpOld = SelectObject(hdc, bmp); #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. */ /* Creating a static cursor. */ SetLastError(0xdeadbeef); h1 = CreateIconFromResource((PBYTE) hotspot, CRSR_RES_SIZE, FALSE, 0x00030000); ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); /* Check GetCursorFrameInfo behavior on a static cursor */ rate = steps = 0xdead; h2 = pGetCursorFrameInfo(h1, 0xdead, 0xdead, &rate, &steps); ok(h1 == h2, "GetCursorFrameInfo() failed: (%p != %p).\n", h1, h2); ok(rate == 0, "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x0).\n", rate); ok(steps == 1, "GetCursorFrameInfo() unexpected param 5 value (%d != 1).\n", steps); /* Clean up static cursor. */ SetLastError(0xdeadbeef); ret = DestroyCursor(h1); ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); /* Creating a single-frame animated cursor. */ empty_anicursor.frames[0].data.icon_info.idType = 2; /* type: cursor */ empty_anicursor.frames[0].data.icon_info.idEntries[0].xHotspot = 3; empty_anicursor.frames[0].data.icon_info.idEntries[0].yHotspot = 3; memcpy( &empty_anicursor.frames[0].data.bmi_data.data[0], &frame_identifier[0], sizeof(DWORD) ); SetLastError(0xdeadbeef); h1 = CreateIconFromResource((PBYTE) &empty_anicursor, sizeof(empty_anicursor), FALSE, 0x00030000); ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); /* Check GetCursorFrameInfo behavior on a single-frame animated cursor */ rate = steps = 0xdead; h2 = pGetCursorFrameInfo(h1, 0xdead, 0, &rate, &steps); ok(h1 == h2, "GetCursorFrameInfo() failed: (%p != %p).\n", h1, h2); ret = check_cursor_data( hdc, h2, &frame_identifier[0], sizeof(DWORD) ); ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame 0.\n"); ok(rate == 0x0, "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x0).\n", rate); ok(steps == empty_anicursor.header.header.num_steps, "GetCursorFrameInfo() unexpected param 5 value (%d != 1).\n", steps); /* Clean up single-frame animated cursor. */ SetLastError(0xdeadbeef); ret = DestroyCursor(h1); ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); /* Creating a multi-frame animated cursor. */ for (i=0; iidReserved = 0; icon_data->idType = 1; icon_data->idCount = 1; icon_entry = icon_data->idEntries; icon_entry->bWidth = 1; icon_entry->bHeight = 1; icon_entry->bColorCount = 0; icon_entry->bReserved = 0; icon_entry->xHotspot = 0; icon_entry->yHotspot = 0; icon_entry->dwDIBSize = icon_size - sizeof(CURSORICONFILEDIR); icon_entry->dwDIBOffset = sizeof(CURSORICONFILEDIR); if (use_core_info) { core_info = (BITMAPCOREINFO *) ((BYTE *) icon_data + icon_entry->dwDIBOffset); core_info->bmciHeader.bcSize = sizeof(BITMAPCOREHEADER); core_info->bmciHeader.bcWidth = 1; core_info->bmciHeader.bcHeight = 2; core_info->bmciHeader.bcPlanes = 1; core_info->bmciHeader.bcBitCount = 1; core_info->bmciColors[0].rgbtBlue = monochrome ? 0x00 : 0xff; core_info->bmciColors[0].rgbtGreen = 0x00; core_info->bmciColors[0].rgbtRed = 0x00; core_info->bmciColors[1].rgbtBlue = 0xff; core_info->bmciColors[1].rgbtGreen = 0xff; core_info->bmciColors[1].rgbtRed = 0xff; } else { bitmap_info = (BITMAPINFO *) ((BYTE *) icon_data + icon_entry->dwDIBOffset); bitmap_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmap_info->bmiHeader.biWidth = 1; bitmap_info->bmiHeader.biHeight = 2; bitmap_info->bmiHeader.biPlanes = 1; bitmap_info->bmiHeader.biBitCount = 1; bitmap_info->bmiHeader.biSizeImage = 0; /* Uncompressed bitmap. */ bitmap_info->bmiColors[0].rgbBlue = monochrome ? 0x00 : 0xff; bitmap_info->bmiColors[0].rgbGreen = 0x00; bitmap_info->bmiColors[0].rgbRed = 0x00; bitmap_info->bmiColors[1].rgbBlue = 0xff; bitmap_info->bmiColors[1].rgbGreen = 0xff; bitmap_info->bmiColors[1].rgbRed = 0xff; } handle = CreateFileA("icon.ico", 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(ret && bytes_written == icon_size, "icon.ico created improperly.\n"); CloseHandle(handle); handle = LoadImageA(NULL, "icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE); ok(handle != NULL || broken(use_core_info && handle == NULL), /* Win 8, 10 */ "LoadImage() failed with %u.\n", GetLastError()); if (handle == NULL) { skip("Icon failed to load: %s, %s\n", monochrome ? "monochrome" : "colored", use_core_info ? "core info" : "bitmap info"); DeleteFileA("icon.ico"); continue; } ret = GetIconInfo(handle, &icon_info); ok(ret, "GetIconInfo() failed with %u.\n", GetLastError()); if (ret) { ok(icon_info.fIcon == TRUE, "fIcon is %u.\n", icon_info.fIcon); ok(icon_info.xHotspot == 0, "xHotspot is %u.\n", icon_info.xHotspot); ok(icon_info.yHotspot == 0, "yHotspot is %u.\n", icon_info.yHotspot); if (monochrome) ok(icon_info.hbmColor == NULL, "Got hbmColor %p!\n", icon_info.hbmColor); else ok(icon_info.hbmColor != NULL, "No hbmColor!\n"); ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); } ret = DestroyIcon(handle); ok(ret, "DestroyIcon() failed with %u.\n", GetLastError()); DeleteFileA("icon.ico"); } HeapFree(GetProcessHeap(), 0, icon_data); } static COLORREF get_color_from_bits(const unsigned char *bits, const BITMAPINFO *bmi, unsigned int row, unsigned int column) { const BITMAPINFOHEADER *h = &bmi->bmiHeader; unsigned int stride, shift, mask; const unsigned char *data; RGBQUAD color; WORD color16; stride = ((h->biBitCount * h->biWidth + 7) / 8 + 3) & ~3; data = bits + row * stride + column * h->biBitCount / 8; if (h->biBitCount >= 24) return RGB(data[2], data[1], data[0]); if (h->biBitCount == 16) { color16 = ((WORD)data[1] << 8) | data[0]; return RGB(((color16 >> 10) & 0x1f) << 3, ((color16 >> 5) & 0x1f) << 3, (color16 & 0x1f) << 3); } shift = 8 - h->biBitCount - (column * h->biBitCount) % 8; mask = ~(~0u << h->biBitCount); color = bmi->bmiColors[(data[0] >> shift) & mask]; return RGB(color.rgbRed, color.rgbGreen, color.rgbBlue); } #define compare_bitmap_bits(a, b, c, d, e, f, g, h) compare_bitmap_bits_(__LINE__, a, b, c, d, e, f, g, h) static void compare_bitmap_bits_(unsigned int line, HDC hdc, HBITMAP bitmap, BITMAPINFO *bmi, size_t result_bits_size, const unsigned char *expected_bits, unsigned int test_index, BOOL allow_todo, const unsigned char *expected_broken_bits) { unsigned char *result_bits; unsigned int row, column; int ret; result_bits = HeapAlloc(GetProcessHeap(), 0, result_bits_size); ret = GetDIBits(hdc, bitmap, 0, bmi->bmiHeader.biHeight, result_bits, bmi, DIB_RGB_COLORS); ok(ret == bmi->bmiHeader.biHeight, "Unexpected GetDIBits result %d, GetLastError() %u.\n", ret, GetLastError()); for (row = 0; row < bmi->bmiHeader.biHeight; ++row) for (column = 0; column < bmi->bmiHeader.biWidth; ++column) { COLORREF result, expected; result = get_color_from_bits(result_bits, bmi, row, column); expected = get_color_from_bits(expected_bits, bmi, row, column); todo_wine_if(allow_todo && result != expected) ok_(__FILE__, line)(result == expected || broken(expected_broken_bits && result == get_color_from_bits(expected_broken_bits, bmi, row, column)), "Colors do not match, got 0x%06x, expected 0x%06x, test_index %u, row %u, column %u.\n", result, expected, test_index, row, column); } HeapFree(GetProcessHeap(), 0, result_bits); } static void test_Image_StretchMode(void) { static const unsigned char test_bits_24[] = { 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, }; static const unsigned char expected_broken_bits_24[] = { 0x3f, 0xff, 0x00, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x3f, 0xff, 0x7f, 0x00, 0xff, 0x3f, 0x00, 0x00, }; static const unsigned char expected_bits_24[] = { 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, }; #define rgb16(r, g, b) ((WORD)(((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3))) static const WORD test_bits_16[] = { rgb16(0x00, 0x20, 0x00), rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x40, 0xff), rgb16(0x00, 0x20, 0x00), rgb16(0x00, 0x60, 0x00), rgb16(0xff, 0x80, 0x00), rgb16(0xff, 0x60, 0x00), rgb16(0x00, 0x80, 0x00), rgb16(0x00, 0x20, 0xff), rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x40, 0xff), rgb16(0x00, 0x20, 0x00), rgb16(0xff, 0x80, 0x00), rgb16(0x00, 0x60, 0xff), rgb16(0x00, 0x80, 0x00), rgb16(0x00, 0x60, 0x00), }; static const WORD expected_bits_16[] = { rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x20, 0x00), rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x20, 0x00), }; #undef rgb16 static const unsigned char test_bits_8[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x55, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, }; static const unsigned char expected_bits_8[] = { 0xff, 0xff, 0x00, 0x00, 0x55, 0xff, 0x00, 0x00, }; static const unsigned char test_bits_1[] = { 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0xc0, 0x0, 0x0, 0x0, }; static const unsigned char expected_bits_1[] = { 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }; static const RGBQUAD colors_bits_1[] = { {0, 0, 0}, {0xff, 0xff, 0xff}, }; static RGBQUAD colors_bits_8[256]; static const struct { LONG width, height, output_width, output_height; WORD bit_count; const unsigned char *test_bits, *expected_bits; size_t test_bits_size, result_bits_size; const RGBQUAD *bmi_colors; size_t bmi_colors_size; BOOL allow_todo; const unsigned char *expected_broken_bits; } tests[] = { {4, 4, 2, 2, 24, test_bits_24, expected_bits_24, sizeof(test_bits_24), sizeof(expected_bits_24), NULL, 0, TRUE, /* Broken on Windows before Win10 1607+ */ expected_broken_bits_24}, {4, 4, 2, 2, 1, test_bits_1, expected_bits_1, sizeof(test_bits_1), sizeof(expected_bits_1), colors_bits_1, sizeof(colors_bits_1)}, {4, 4, 2, 2, 8, test_bits_8, expected_bits_8, sizeof(test_bits_8), sizeof(expected_bits_8), colors_bits_8, sizeof(colors_bits_8), TRUE}, {4, 4, 2, 2, 16, (const unsigned char *)test_bits_16, (const unsigned char *)expected_bits_16, sizeof(test_bits_16), sizeof(expected_bits_16), NULL, 0, TRUE}, }; static const char filename[] = "test.bmp"; BITMAPINFO *bmi, *bmi_output; HBITMAP bitmap, bitmap_copy; unsigned int test_index; unsigned char *bits; size_t bmi_size; unsigned int i; HDC hdc; bmi_size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD); bmi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bmi_size); bmi_output = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bmi_size); bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi->bmiHeader.biPlanes = 1; bmi->bmiHeader.biCompression = BI_RGB; for (i = 0; i < 256; ++i) colors_bits_8[i].rgbRed = colors_bits_8[i].rgbGreen = colors_bits_8[i].rgbBlue = i; hdc = GetDC(NULL); for (test_index = 0; test_index < ARRAY_SIZE(tests); ++test_index) { if (tests[test_index].bmi_colors) memcpy(bmi->bmiColors, tests[test_index].bmi_colors, tests[test_index].bmi_colors_size); else memset(bmi->bmiColors, 0, 256 * sizeof(RGBQUAD)); bmi->bmiHeader.biWidth = tests[test_index].width; bmi->bmiHeader.biHeight = tests[test_index].height; bmi->bmiHeader.biBitCount = tests[test_index].bit_count; memcpy(bmi_output, bmi, bmi_size); bmi_output->bmiHeader.biWidth = tests[test_index].output_width; bmi_output->bmiHeader.biHeight = tests[test_index].output_height; bitmap = CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, (void **)&bits, NULL, 0); ok(bitmap && bits, "CreateDIBSection() failed, result %u.\n", GetLastError()); memcpy(bits, tests[test_index].test_bits, tests[test_index].test_bits_size); bitmap_copy = CopyImage(bitmap, IMAGE_BITMAP, tests[test_index].output_width, tests[test_index].output_height, LR_CREATEDIBSECTION); ok(!!bitmap_copy, "CopyImage() failed, result %u.\n", GetLastError()); compare_bitmap_bits(hdc, bitmap_copy, bmi_output, tests[test_index].result_bits_size, tests[test_index].expected_bits, test_index, tests[test_index].allow_todo, tests[test_index].expected_broken_bits); DeleteObject(bitmap); DeleteObject(bitmap_copy); create_bitmap_file(filename, bmi, tests[test_index].test_bits); bitmap = LoadImageA(NULL, filename, IMAGE_BITMAP, tests[test_index].output_width, tests[test_index].output_height, LR_CREATEDIBSECTION | LR_LOADFROMFILE); ok(!!bitmap, "LoadImageA() failed, result %u.\n", GetLastError()); DeleteFileA(filename); compare_bitmap_bits(hdc, bitmap, bmi_output, tests[test_index].result_bits_size, tests[test_index].expected_bits, test_index, tests[test_index].allow_todo, tests[test_index].expected_broken_bits); DeleteObject(bitmap); } ReleaseDC(0, hdc); HeapFree(GetProcessHeap(), 0, bmi_output); HeapFree(GetProcessHeap(), 0, bmi); } static void test_copy_image(void) { static const UINT types[] = {IMAGE_BITMAP, IMAGE_ICON, IMAGE_CURSOR}; static const UINT depths[] = {1, 4, 8, 16, 24, 32}; unsigned int i, j; for (i = 0; i < ARRAY_SIZE(types); ++i) { for (j = 0; j < ARRAY_SIZE(depths); ++j) do_test_copy_image(types[i], depths[j]); } } START_TEST(cursoricon) { pGetCursorInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorInfo" ); pGetIconInfoExA = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetIconInfoExA" ); pGetIconInfoExW = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetIconInfoExW" ); pGetCursorFrameInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorFrameInfo" ); 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_copy_image(); test_Image_StretchMode(); test_initial_cursor(); test_CreateIcon(); test_LoadImage(); test_CreateIconFromResource(); test_GetCursorFrameInfo(); test_DrawIcon(); test_DrawIconEx(); test_DrawState(); test_SetCursor(); test_ShowCursor(); test_DestroyCursor(); test_PrivateExtractIcons(); test_monochrome_icon(); do_parent(); test_child_process(); finish_child_process(); }