[PATCH 2/4] user32: Implement stretching of cursor and icon objects in CopyImage().

Zebediah Figura z.figura12 at gmail.com
Sun May 23 23:47:18 CDT 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/user32/cursoricon.c       |  92 +++++++++++++--
 dlls/user32/tests/cursoricon.c | 203 ++++++++++++++++++++++-----------
 2 files changed, 219 insertions(+), 76 deletions(-)

diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c
index ee6d27acf2e..fc95fa1c56f 100644
--- a/dlls/user32/cursoricon.c
+++ b/dlls/user32/cursoricon.c
@@ -2776,6 +2776,21 @@ HANDLE WINAPI LoadImageW( HINSTANCE hinst, LPCWSTR name, UINT type,
     return 0;
 }
 
+
+/* StretchBlt from src to dest; helper for CopyImage(). */
+static void stretch_bitmap( HBITMAP dst, HBITMAP src, int dst_width, int dst_height, int src_width, int src_height )
+{
+    HDC src_dc = CreateCompatibleDC( 0 ), dst_dc = CreateCompatibleDC( 0 );
+
+    SelectObject( src_dc, src );
+    SelectObject( dst_dc, dst );
+    StretchBlt( dst_dc, 0, 0, dst_width, dst_height, src_dc, 0, 0, src_width, src_height, SRCCOPY );
+
+    DeleteDC( src_dc );
+    DeleteDC( dst_dc );
+}
+
+
 /******************************************************************************
  *		CopyImage (USER32.@) Creates new image and copies attributes to it
  *
@@ -2974,23 +2989,84 @@ HANDLE WINAPI CopyImage( HANDLE hnd, UINT type, INT desiredx,
         case IMAGE_ICON:
         case IMAGE_CURSOR:
         {
+            struct cursoricon_frame *frame;
             struct cursoricon_object *icon;
-            HICON res = 0;
             int depth = (flags & LR_MONOCHROME) ? 1 : get_display_bpp();
+            ICONINFO info;
+            HICON res;
+
+            if (!(icon = get_icon_ptr( hnd ))) return 0;
+
+            if (icon->rsrc && (flags & LR_COPYFROMRESOURCE))
+            {
+                hnd = CURSORICON_Load( icon->module, icon->resname, desiredx, desiredy, depth,
+                                       !icon->is_icon, flags );
+                release_user_handle_ptr( icon );
+                if (!(icon = get_icon_ptr( hnd ))) return 0;
+            }
+            frame = get_icon_frame( icon, 0 );
 
             if (flags & LR_DEFAULTSIZE)
             {
                 if (!desiredx) desiredx = GetSystemMetrics( type == IMAGE_ICON ? SM_CXICON : SM_CXCURSOR );
                 if (!desiredy) desiredy = GetSystemMetrics( type == IMAGE_ICON ? SM_CYICON : SM_CYCURSOR );
             }
-
-            if (!(icon = get_icon_ptr( hnd ))) return 0;
-
-            if (icon->rsrc && (flags & LR_COPYFROMRESOURCE))
-                res = CURSORICON_Load( icon->module, icon->resname, desiredx, desiredy, depth,
-                                       !icon->is_icon, flags );
             else
-                res = CopyIcon( hnd ); /* FIXME: change size if necessary */
+            {
+                if (!desiredx) desiredx = frame->width;
+                if (!desiredy) desiredy = frame->height;
+            }
+
+            info.fIcon = icon->is_icon;
+            info.xHotspot = icon->hotspot.x;
+            info.yHotspot = icon->hotspot.y;
+
+            if (desiredx == frame->width && desiredy == frame->height)
+            {
+                info.hbmColor = frame->color;
+                info.hbmMask = frame->mask;
+                res = CreateIconIndirect( &info );
+            }
+            else
+            {
+                if (frame->color)
+                {
+                    if (!(info.hbmColor = create_color_bitmap( desiredx, desiredy )))
+                    {
+                        release_user_handle_ptr( icon );
+                        return 0;
+                    }
+                    stretch_bitmap( info.hbmColor, frame->color, desiredx, desiredy,
+                                    frame->width, frame->height );
+
+                    if (!(info.hbmMask = CreateBitmap( desiredx, desiredy, 1, 1, NULL )))
+                    {
+                        DeleteObject( info.hbmColor );
+                        release_user_handle_ptr( icon );
+                        return 0;
+                    }
+                    stretch_bitmap( info.hbmMask, frame->mask, desiredx, desiredy,
+                                    frame->width, frame->height );
+                }
+                else
+                {
+                    info.hbmColor = NULL;
+
+                    if (!(info.hbmMask = CreateBitmap( desiredx, desiredy * 2, 1, 1, NULL )))
+                    {
+                        release_user_handle_ptr( icon );
+                        return 0;
+                    }
+                    stretch_bitmap( info.hbmColor, frame->color, desiredx, desiredy * 2,
+                                    frame->width, frame->height * 2 );
+                }
+
+                res = CreateIconIndirect( &info );
+
+                DeleteObject( info.hbmColor );
+                DeleteObject( info.hbmMask );
+            }
+
             release_user_handle_ptr( icon );
 
             if (res && (flags & LR_COPYDELETEORG)) DeleteObject( hnd );
diff --git a/dlls/user32/tests/cursoricon.c b/dlls/user32/tests/cursoricon.c
index f60c845f8bc..39ffe9d88e6 100644
--- a/dlls/user32/tests/cursoricon.c
+++ b/dlls/user32/tests/cursoricon.c
@@ -486,22 +486,53 @@ static BOOL color_match(COLORREF a, COLORREF b)
     return (a & 0x00F8F8F8) == (b & 0x00F8F8F8);
 }
 
-static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, INT copyWidth, INT copyHeight,
-                                  INT expectedWidth, INT expectedHeight, WORD expectedDepth, BOOL dibExpected)
+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 orig_is_dib;
     BOOL copy_is_dib;
+    BOOL ret;
 
-    copy = CopyImage(bitmap, IMAGE_BITMAP, copyWidth, copyHeight, flags);
+    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)
     {
-        GetObjectA(bitmap, sizeof(origBitmap), &origBitmap);
-        GetObjectA(copy, sizeof(copyBitmap), &copyBitmap);
-        orig_is_dib = (origBitmap.bmBits != NULL);
+        if (type == IMAGE_BITMAP)
+        {
+            GetObjectA(copy, sizeof(copyBitmap), &copyBitmap);
+        }
+        else
+        {
+            ICONINFO info;
+
+            ret = GetIconInfo((HICON)copy, &info);
+            ok(ret, "Failed to get icon info, error %u\n", GetLastError());
+            GetObjectA(info.hbmColor, sizeof(copyBitmap), &copyBitmap);
+        }
         copy_is_dib = (copyBitmap.bmBits != NULL);
 
         if (copy_is_dib && dibExpected
@@ -525,21 +556,49 @@ static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, INT copyWidth, INT
             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);
+        if (type != IMAGE_BITMAP)
+        {
+            dibExpected = FALSE;
+            expectedDepth = 32;
+        }
 
-        DeleteObject(copy);
+        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), &copyBitmap);
+
+            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 test_CopyImage_Bitmap(int depth)
+static void do_test_copy_image(UINT type, UINT depth)
 {
     HBITMAP ddb, dib;
     HDC screenDC;
@@ -582,53 +641,53 @@ static void test_CopyImage_Bitmap(int depth)
 
     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);
+        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);
 
-        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);
+        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);
 
-        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);
+        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 */
-        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);
+        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)
     {
-        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);
+        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);
     }
 
-    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);
+    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);
 
-    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);
+    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 */
-    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);
+    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);
 
@@ -651,10 +710,10 @@ static void test_CopyImage_Bitmap(int depth)
             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);
+            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;
@@ -666,10 +725,10 @@ static void test_CopyImage_Bitmap(int depth)
             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);
+            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;
@@ -681,10 +740,10 @@ static void test_CopyImage_Bitmap(int depth)
             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);
+            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);
         }
     }
@@ -3018,6 +3077,19 @@ static void test_Image_StretchMode(void)
     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" );
@@ -3039,12 +3111,7 @@ START_TEST(cursoricon)
         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_copy_image();
     test_Image_StretchMode();
     test_initial_cursor();
     test_CreateIcon();
-- 
2.30.2




More information about the wine-devel mailing list