user: CopyImage: Handle most flags

Michael Kaufmann hallo at michael-kaufmann.ch
Wed Aug 9 09:14:49 CDT 2006


Changelog:
  - CopyImage: Handle the flags LR_COPYDELETEORG, LR_CREATEDIBSECTION, 
and LR_MONOCHROME
  - New tests
  - Should fix bug 5094
-------------- next part --------------
Index: dlls/user/cursoricon.c
===================================================================
RCS file: /home/wine/wine/dlls/user/cursoricon.c,v
retrieving revision 1.26
diff -u -r1.26 cursoricon.c
--- dlls/user/cursoricon.c	23 May 2006 12:48:46 -0000	1.26
+++ dlls/user/cursoricon.c	9 Aug 2006 13:50:50 -0000
@@ -2295,28 +2295,203 @@
  *    Success: Handle to newly created image
  *    Failure: NULL
  *
- * FIXME
- *    implementation still lacks nearly all features, see LR_* defines in winuser.h.
+ * BUGS
+ *    Only Windows NT 4.0 supports the LR_COPYRETURNORG flag for bitmaps,
+ *    all other versions (95/2000/XP have been tested) ignore it.
+ *
+ * NOTES
+ *    If LR_CREATEDIBSECTION is absent, the copy will be monochrome for
+ *    a monochrome source bitmap or if LR_MONOCHROME is present, otherwise
+ *    the copy will have the same depth as the screen.
+ *    The content of the image will only be copied if the bit depth of the
+ *    original image is compatible with the bit depth of the screen, or
+ *    if the source is a DIB section.
+ *    The LR_MONOCHROME flag is ignored if LR_CREATEDIBSECTION is present.
  */
 HICON WINAPI CopyImage( HANDLE hnd, UINT type, INT desiredx,
                              INT desiredy, UINT flags )
 {
+    TRACE("hnd=%p, type=%u, desiredx=%d, desiredy=%d, flags=%x\n",
+          hnd, type, desiredx, desiredy, flags);
+
     switch (type)
     {
         case IMAGE_BITMAP:
         {
-            HBITMAP res;
-            BITMAP bm;
+            HBITMAP res = NULL;
+            DIBSECTION ds;
+            int objSize;
+            BITMAPINFO * bi;
+
+            objSize = GetObjectW( hnd, sizeof(ds), &ds );
+            if (!objSize) return 0;
+            if ((desiredx < 0) || (desiredy < 0)) return 0;
 
-            if (!GetObjectW( hnd, sizeof(bm), &bm )) return 0;
-            bm.bmBits = NULL;
-            if ((res = CreateBitmapIndirect(&bm)))
+            if (flags & LR_COPYFROMRESOURCE)
             {
-                char *buf = HeapAlloc( GetProcessHeap(), 0, bm.bmWidthBytes * bm.bmHeight );
-                GetBitmapBits( hnd, bm.bmWidthBytes * bm.bmHeight, buf );
-                SetBitmapBits( res, bm.bmWidthBytes * bm.bmHeight, buf );
-                HeapFree( GetProcessHeap(), 0, buf );
+                FIXME("The flag LR_COPYFROMRESOURCE is not implemented for bitmaps\n");
             }
+
+            if (desiredx == 0) desiredx = ds.dsBm.bmWidth;
+            if (desiredy == 0) desiredy = ds.dsBm.bmHeight;
+
+            /* Allocate memory for a BITMAPINFOHEADER structure and a
+               color table. The maximum number of colors in a color table
+               is 256 which corresponds to a bitmap with depth 8.
+               Bitmaps with higher depths don't have color tables. */
+            bi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+            if (!bi) return 0;
+
+            bi->bmiHeader.biSize        = sizeof(bi->bmiHeader);
+            bi->bmiHeader.biPlanes      = ds.dsBm.bmPlanes;
+            bi->bmiHeader.biBitCount    = ds.dsBm.bmBitsPixel;
+            bi->bmiHeader.biCompression = BI_RGB;
+
+            if (flags & LR_CREATEDIBSECTION)
+            {
+                /* Create a DIB section. LR_MONOCHROME is ignored */
+                void * bits;
+                HDC dc = CreateCompatibleDC(NULL);
+
+                if (objSize == sizeof(DIBSECTION))
+                {
+                    /* The source bitmap is a DIB.
+                       Get its attributes to create an exact copy */
+                    memcpy(bi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
+                }
+
+                /* Get the color table or the color masks */
+                GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
+
+                bi->bmiHeader.biWidth  = desiredx;
+                bi->bmiHeader.biHeight = desiredy;
+                bi->bmiHeader.biSizeImage = 0;
+
+                res = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &bits, NULL, 0);
+                DeleteDC(dc);
+            }
+            else
+            {
+                /* Create a device-dependent bitmap */
+
+                BOOL monochrome = (flags & LR_MONOCHROME);
+
+                if (objSize == sizeof(DIBSECTION))
+                {
+                    /* The source bitmap is a DIB section.
+                       Get its attributes */
+                    HDC dc = CreateCompatibleDC(NULL);
+                    bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
+                    bi->bmiHeader.biBitCount = ds.dsBm.bmBitsPixel;
+                    GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
+                    DeleteDC(dc);
+
+                    if (!monochrome && ds.dsBm.bmBitsPixel == 1)
+                    {
+                        /* Look if the colors of the DIB are black and white */
+
+                        monochrome = 
+                              (bi->bmiColors[0].rgbRed == 0xff
+                            && bi->bmiColors[0].rgbGreen == 0xff
+                            && bi->bmiColors[0].rgbBlue == 0xff
+                            && bi->bmiColors[0].rgbReserved == 0
+                            && bi->bmiColors[1].rgbRed == 0
+                            && bi->bmiColors[1].rgbGreen == 0
+                            && bi->bmiColors[1].rgbBlue == 0
+                            && bi->bmiColors[1].rgbReserved == 0)
+                            ||
+                              (bi->bmiColors[0].rgbRed == 0
+                            && bi->bmiColors[0].rgbGreen == 0
+                            && bi->bmiColors[0].rgbBlue == 0
+                            && bi->bmiColors[0].rgbReserved == 0
+                            && bi->bmiColors[1].rgbRed == 0xff
+                            && bi->bmiColors[1].rgbGreen == 0xff
+                            && bi->bmiColors[1].rgbBlue == 0xff
+                            && bi->bmiColors[1].rgbReserved == 0);
+                    }
+                }
+                else if (!monochrome)
+                {
+                    monochrome = ds.dsBm.bmBitsPixel == 1;
+                }
+
+                if (monochrome)
+                {
+                    res = CreateBitmap(desiredx, desiredy, 1, 1, NULL);
+                }
+                else
+                {
+                    HDC screenDC = GetDC(NULL);
+                    res = CreateCompatibleBitmap(screenDC, desiredx, desiredy);
+                    ReleaseDC(NULL, screenDC);
+                }
+            }
+
+            if (res)
+            {
+                /* Only copy the bitmap if it's a DIB section or if it's
+                   compatible to the screen */
+                BOOL copyContents;
+
+                if (objSize == sizeof(DIBSECTION))
+                {
+                    copyContents = TRUE;
+                }
+                else
+                {
+                    HDC screenDC = GetDC(NULL);
+                    int screen_depth = GetDeviceCaps(screenDC, BITSPIXEL);
+                    ReleaseDC(NULL, screenDC);
+
+                    copyContents = (ds.dsBm.bmBitsPixel == 1 || ds.dsBm.bmBitsPixel == screen_depth);
+                }
+
+                if (copyContents)
+                {
+                    /* The source bitmap may already be selected in a device context,
+                       use GetDIBits/StretchDIBits and not StretchBlt  */
+
+                    HDC dc;
+                    void * bits;
+
+                    dc = CreateCompatibleDC(NULL);
+
+                    bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
+                    bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
+                    bi->bmiHeader.biSizeImage = 0;
+                    bi->bmiHeader.biClrUsed = 0;
+                    bi->bmiHeader.biClrImportant = 0;
+
+                    /* Fill in biSizeImage */
+                    GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
+                    bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bi->bmiHeader.biSizeImage);
+
+                    if (bits)
+                    {
+                        HBITMAP oldBmp;
+
+                        /* Get the image bits of the source bitmap */
+                        GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, bits, bi, DIB_RGB_COLORS);
+
+                        /* Copy it to the destination bitmap */
+                        oldBmp = SelectObject(dc, res);
+                        StretchDIBits(dc, 0, 0, desiredx, desiredy,
+                                      0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight,
+                                      bits, bi, DIB_RGB_COLORS, SRCCOPY);
+                        SelectObject(dc, oldBmp);
+
+                        HeapFree(GetProcessHeap(), 0, bits);
+                    }
+
+                    DeleteDC(dc);
+                }
+
+                if (flags & LR_COPYDELETEORG)
+                {
+                    DeleteObject(hnd);
+                }
+            }
+            HeapFree(GetProcessHeap(), 0, bi);
             return (HICON)res;
         }
         case IMAGE_ICON:
@@ -2325,6 +2500,7 @@
                 /* Should call CURSORICON_ExtCopy but more testing
                  * needs to be done before we change this
                  */
+                if (flags) FIXME("Flags are ignored\n");
                 return CopyCursor(hnd);
     }
     return 0;
Index: dlls/user/tests/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/user/tests/Makefile.in,v
retrieving revision 1.18
diff -u -r1.18 Makefile.in
--- dlls/user/tests/Makefile.in	8 Jun 2006 10:07:29 -0000	1.18
+++ dlls/user/tests/Makefile.in	9 Aug 2006 13:51:06 -0000
@@ -8,6 +8,7 @@
 CTESTS = \
 	class.c \
 	clipboard.c \
+	cursoricon.c \
 	dce.c \
 	dde.c \
 	dialog.c \
--- /dev/null	2006-08-09 17:25:45.021585648 +0200
+++ dlls/user/tests/cursoricon.c	2006-08-09 15:47:17.000000000 +0200
@@ -0,0 +1,246 @@
+/* Unit test suite for cursors and icons.
+ *
+ * Copyright 2006 Michael Kaufmann
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, LONG copyWidth, LONG copyHeight,
+                                  LONG expectedWidth, LONG expectedHeight, WORD expectedDepth, BOOL dibExpected)
+{
+    HBITMAP copy;
+    BITMAP origBitmap;
+    BITMAP copyBitmap;
+    BOOL orig_is_dib;
+    BOOL copy_is_dib;
+
+    copy = (HBITMAP) CopyImage(bitmap, IMAGE_BITMAP, copyWidth, copyHeight, flags);
+    ok(copy != NULL, "CopyImage() failed\n");
+    if (copy != NULL)
+    {
+        GetObject(bitmap, sizeof(origBitmap), &origBitmap) != 0, 
+        GetObject(copy, sizeof(copyBitmap), &copyBitmap);
+        orig_is_dib = (origBitmap.bmBits != NULL);
+        copy_is_dib = (copyBitmap.bmBits != NULL);
+
+        if (copy_is_dib && dibExpected
+            && copyBitmap.bmBitsPixel == 24
+            && (expectedDepth == 16 || expectedDepth == 32))
+        {
+            /* Windows 95 doesn't create DIBs with a depth of 16 or 32 bit */
+            if (GetVersion() & 0x80000000)
+            {
+                expectedDepth = 24;
+            }
+        }
+
+        if (copy_is_dib && !dibExpected && !(flags & LR_CREATEDIBSECTION))
+        {
+            /* It's not forbidden to create a DIB section if the flag
+               LR_CREATEDIBSECTION is absent.
+               Windows 9x does this if the bitmap has a depth that doesn't
+               match the screen depth, Windows NT doesn't */
+            dibExpected = TRUE;
+            expectedDepth = origBitmap.bmBitsPixel;
+        }
+
+        ok((!(dibExpected ^ copy_is_dib)
+             && (copyBitmap.bmWidth == expectedWidth)
+             && (copyBitmap.bmHeight == expectedHeight)
+             && (copyBitmap.bmBitsPixel == expectedDepth)),
+             "CopyImage ((%s, %ldx%ld, %u bpp), %ld, %ld, %#x): Expected (%s, %ldx%ld, %u bpp), got (%s, %ldx%ld, %u bpp)\n",
+                  orig_is_dib ? "DIB" : "DDB", (long)origBitmap.bmWidth, (long)origBitmap.bmHeight, (unsigned int)origBitmap.bmBitsPixel,
+                  (long)copyWidth, (long)copyHeight, (unsigned int)flags,
+                  dibExpected ? "DIB" : "DDB", (long)expectedWidth, (long)expectedHeight, (unsigned int)expectedDepth,
+                  copy_is_dib ? "DIB" : "DDB", (long)copyBitmap.bmWidth, (long)copyBitmap.bmHeight, (unsigned int)copyBitmap.bmBitsPixel);
+
+        DeleteObject(copy);
+    }
+}
+
+static void test_CopyImage_Bitmap(int depth)
+{
+    HBITMAP ddb, dib;
+    HDC screenDC;
+    BITMAPINFO * info;
+    VOID * bits;
+    int screen_depth;
+    unsigned int i;
+
+    /* Create a device-independent bitmap (DIB) */
+    info = (BITMAPINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+    info->bmiHeader.biSize = sizeof(info->bmiHeader);
+    info->bmiHeader.biWidth = 2;
+    info->bmiHeader.biHeight = 2;
+    info->bmiHeader.biPlanes = 1;
+    info->bmiHeader.biBitCount = depth;
+    info->bmiHeader.biCompression = BI_RGB;
+
+    for (i=0; i < 256; i++)
+    {
+        info->bmiColors[i].rgbRed = i;
+        info->bmiColors[i].rgbGreen = i;
+        info->bmiColors[i].rgbBlue = 255 - i;
+        info->bmiColors[i].rgbReserved = 0;
+    }
+
+    dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0);
+
+    /* Create a device-dependent bitmap (DDB) */
+    screenDC = GetDC(NULL);
+    screen_depth = GetDeviceCaps(screenDC, BITSPIXEL);
+    if (depth == 1 || depth == screen_depth)
+    {
+        ddb = CreateBitmap(2, 2, 1, depth, NULL);
+    }
+    else
+    {
+        ddb = NULL;
+    }
+    ReleaseDC(NULL, screenDC);
+
+    if (ddb != NULL)
+    {
+        test_CopyImage_Check(ddb, 0, 0, 0, 2, 2, depth == 1 ? 1 : screen_depth, FALSE);
+        test_CopyImage_Check(ddb, 0, 0, 5, 2, 5, depth == 1 ? 1 : screen_depth, FALSE);
+        test_CopyImage_Check(ddb, 0, 5, 0, 5, 2, depth == 1 ? 1 : screen_depth, FALSE);
+        test_CopyImage_Check(ddb, 0, 5, 5, 5, 5, depth == 1 ? 1 : screen_depth, FALSE);
+
+        test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE);
+
+        test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE);
+
+        /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */
+        test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE);
+        test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE);
+
+        DeleteObject(ddb);
+    }
+
+    if (depth != 1)
+    {
+        test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE);
+        test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE);
+        test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE);
+        test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE);
+    }
+
+    test_CopyImage_Check(dib, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE);
+    test_CopyImage_Check(dib, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE);
+    test_CopyImage_Check(dib, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE);
+    test_CopyImage_Check(dib, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE);
+
+    test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE);
+    test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE);
+    test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE);
+    test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE);
+
+    /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */
+    test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE);
+    test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE);
+    test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE);
+    test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE);
+
+    DeleteObject(dib);
+
+    if (depth == 1)
+    {
+        /* Special case: A monochrome DIB is converted to a monochrome DDB if
+           the colors in the color table are black and white.
+
+           Skip this test on Windows 95, it always creates a monochrome DDB
+           in this case */
+
+        if (!(GetVersion() & 0x80000000))
+        {
+            info->bmiHeader.biBitCount = 1;
+            info->bmiColors[0].rgbRed = 0xFF;
+            info->bmiColors[0].rgbGreen = 0;
+            info->bmiColors[0].rgbBlue = 0;
+            info->bmiColors[1].rgbRed = 0;
+            info->bmiColors[1].rgbGreen = 0xFF;
+            info->bmiColors[1].rgbBlue = 0;
+
+            dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0);
+            test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE);
+            test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE);
+            DeleteObject(dib);
+
+            info->bmiHeader.biBitCount = 1;
+            info->bmiColors[0].rgbRed = 0;
+            info->bmiColors[0].rgbGreen = 0;
+            info->bmiColors[0].rgbBlue = 0;
+            info->bmiColors[1].rgbRed = 0xFF;
+            info->bmiColors[1].rgbGreen = 0xFF;
+            info->bmiColors[1].rgbBlue = 0xFF;
+
+            dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0);
+            test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE);
+            DeleteObject(dib);
+
+            info->bmiHeader.biBitCount = 1;
+            info->bmiColors[0].rgbRed = 0xFF;
+            info->bmiColors[0].rgbGreen = 0xFF;
+            info->bmiColors[0].rgbBlue = 0xFF;
+            info->bmiColors[1].rgbRed = 0;
+            info->bmiColors[1].rgbGreen = 0;
+            info->bmiColors[1].rgbBlue = 0;
+
+            dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0);
+            test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE);
+            test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE);
+            DeleteObject(dib);
+        }
+    }
+
+    HeapFree(GetProcessHeap(), 0, info);
+}
+
+START_TEST(cursoricon)
+{
+    test_CopyImage_Bitmap(1);
+    test_CopyImage_Bitmap(4);
+    test_CopyImage_Bitmap(8);
+    test_CopyImage_Bitmap(16);
+    test_CopyImage_Bitmap(24);
+    test_CopyImage_Bitmap(32);
+}


More information about the wine-patches mailing list