[USER32_WINETEST] Sync with Wine Staging 4.18 except win.c (PR #1980). CORE-16441
[reactos.git] / modules / rostests / winetests / user32 / cursoricon.c
index e42875f..8ce1eb1 100644 (file)
@@ -1053,10 +1053,6 @@ static const DWORD biSize_tests[] = {
     0xffffffff
 };
 
     0xffffffff
 };
 
-#ifndef __REACTOS__
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-#endif
-
 static void test_LoadImageBitmap(const char * test_desc, HBITMAP hbm)
 {
     BITMAP bm;
 static void test_LoadImageBitmap(const char * test_desc, HBITMAP hbm)
 {
     BITMAP bm;
@@ -1207,6 +1203,126 @@ static void create_ico_file(const char *filename, const test_icon_entries_t *tes
     HeapFree(GetProcessHeap(), 0, buf);
 }
 
     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];
+    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;
 static void test_LoadImage(void)
 {
     HANDLE handle;
@@ -1330,9 +1446,10 @@ static void test_LoadImage(void)
     bitmap_header->biSize = sizeof(BITMAPINFOHEADER);
 
     test_LoadImageFile("Cursor (invalid dwDIBOffset)", invalid_dwDIBOffset, sizeof(invalid_dwDIBOffset), "cur", 0);
     bitmap_header->biSize = sizeof(BITMAPINFOHEADER);
 
     test_LoadImageFile("Cursor (invalid dwDIBOffset)", invalid_dwDIBOffset, sizeof(invalid_dwDIBOffset), "cur", 0);
-}
 
 
-#undef ARRAY_SIZE
+    /* Test in which paths images with a relative path can be found */
+    test_LoadImage_working_directory();
+}
 
 static void test_CreateIconFromResource(void)
 {
 
 static void test_CreateIconFromResource(void)
 {
@@ -2539,7 +2656,7 @@ static void test_PrivateExtractIcons(void)
 
     static const test_icon_entries_t icon_desc[] = {{0,0,TRUE}, {16,16,TRUE}, {32,32}, {64,64,TRUE}};
 
 
     static const test_icon_entries_t icon_desc[] = {{0,0,TRUE}, {16,16,TRUE}, {32,32}, {64,64,TRUE}};
 
-    create_ico_file("extract.ico", icon_desc, sizeof(icon_desc)/sizeof(*icon_desc));
+    create_ico_file("extract.ico", icon_desc, ARRAY_SIZE(icon_desc));
 
     ret = PrivateExtractIconsA("extract.ico", 0, 32, 32, &icon, NULL, 1, 0);
     ok(ret == 1, "PrivateExtractIconsA returned %u\n", ret);
 
     ret = PrivateExtractIconsA("extract.ico", 0, 32, 32, &icon, NULL, 1, 0);
     ok(ret == 1, "PrivateExtractIconsA returned %u\n", ret);
@@ -2668,6 +2785,205 @@ static void test_monochrome_icon(void)
     HeapFree(GetProcessHeap(), 0, icon_data);
 }
 
     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) compare_bitmap_bits_(__LINE__, a, b, c, d, e, f, g)
+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 todo)
+{
+    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(todo)
+            ok_(__FILE__, line)(result == expected, "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_bits_24[] =
+    {
+        0x3f, 0xff, 0x00,  0x3f, 0xff, 0x3f,  0x00, 0x00,
+        0x3f, 0xff, 0x7f,  0x00, 0xff, 0x3f,  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 todo;
+    }
+    tests[] =
+    {
+        {4, 4, 2, 2, 24, test_bits_24, expected_bits_24,
+                sizeof(test_bits_24), sizeof(expected_bits_24), NULL, 0, TRUE},
+        {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), FALSE},
+        {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), FALSE},
+        {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, FALSE},
+    };
+    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].todo);
+        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].todo);
+        DeleteObject(bitmap);
+    }
+    ReleaseDC(0, hdc);
+    HeapFree(GetProcessHeap(), 0, bmi_output);
+    HeapFree(GetProcessHeap(), 0, bmi);
+}
+
 START_TEST(cursoricon)
 {
     pGetCursorInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorInfo" );
 START_TEST(cursoricon)
 {
     pGetCursorInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorInfo" );
@@ -2695,6 +3011,7 @@ START_TEST(cursoricon)
     test_CopyImage_Bitmap(16);
     test_CopyImage_Bitmap(24);
     test_CopyImage_Bitmap(32);
     test_CopyImage_Bitmap(16);
     test_CopyImage_Bitmap(24);
     test_CopyImage_Bitmap(32);
+    test_Image_StretchMode();
     test_initial_cursor();
     test_CreateIcon();
     test_LoadImage();
     test_initial_cursor();
     test_CreateIcon();
     test_LoadImage();