Icon resizing in CreateIcon

Vincent Béron vberon at mecano.gme.usherb.ca
Fri Mar 22 16:31:41 CST 2002


This patch implements icon resizing in CreateIcon if the provided
bitmaps are not GetSystemMetrics(SM_CXICON)xGetSystemMetrics(SM_CYICON).
Fixes bug # 175.

(First patch, so any comments on coding style are welcomed)

Changelog:
Vincent Béron <<vberon at mecano.gme.usherb.ca>>

  include/bitmap.h, objects/bitmap.c, windows/cursoricon.c:
   - Implements icon resizing in CreateIcon.
-------------- next part --------------
diff -urN wine-20020310/include/bitmap.h wine/include/bitmap.h
--- wine-20020310/include/bitmap.h	Tue Mar 12 10:37:49 2002
+++ wine/include/bitmap.h	Thu Mar 21 20:08:50 2002
@@ -66,6 +66,8 @@
 extern INT   BITMAP_GetObject( BITMAPOBJ * bmp, INT count, LPVOID buffer );
 extern BOOL  BITMAP_DeleteObject( HBITMAP16 hbitmap, BITMAPOBJ * bitmap );
 extern INT   BITMAP_GetWidthBytes( INT width, INT depth );
+extern VOID  BITMAP_ResizeMonochrome( INT inWidth, INT inHeight, LPCVOID lpIn,
+                                      INT outWidth, INT outHeight, LPVOID lpOut );
 extern HBITMAP BITMAP_CopyBitmap( HBITMAP hbitmap );
 
   /* objects/dib.c */
diff -urN wine-20020310/objects/bitmap.c wine/objects/bitmap.c
--- wine-20020310/objects/bitmap.c	Tue Mar 12 10:37:53 2002
+++ wine/objects/bitmap.c	Thu Mar 21 20:08:57 2002
@@ -68,6 +68,88 @@
 }
 
 /***********************************************************************
+ *           BITMAP_ResizeMonochrome
+ *
+ * Resize a monochrome bitmap from (inWidth,inHeight) to (outWidth, outHeight).
+ */
+VOID BITMAP_ResizeMonochrome( INT inWidth, INT inHeight, LPCVOID lpIn,
+                              INT outWidth, INT outHeight, LPVOID lpOut)
+{
+    BOOL hstretch, vstretch;
+    INT nBytesRowSrc, nBytesSrc;
+    INT nBytesRowDst, nBytesDst;
+    INT xinc, xoff, xsrc, xdst;
+    INT yinc, ysrc, ydst;
+    LPBYTE lpRowSrc, lpRowDst;
+
+    hstretch = (inWidth < outWidth);
+    vstretch = (inHeight < outHeight);
+    nBytesRowSrc = BITMAP_GetWidthBytes(inWidth, 1);
+    nBytesSrc = nBytesRowSrc * inHeight;
+    nBytesRowDst = BITMAP_GetWidthBytes(outWidth, 1);
+    nBytesDst = nBytesRowDst * outHeight;
+
+    if(inWidth != outWidth || inHeight != outHeight) {
+        memset(lpOut, 0, nBytesDst);
+
+        if(hstretch) {
+            xinc = ((int)inWidth << 16) / outWidth;
+            xoff = ((inWidth << 16) - (xinc * outWidth)) / 2;
+        } else {
+            xinc = ((int)outWidth << 16) / inWidth;
+            xoff = ((outWidth << 16) - (xinc * inWidth)) / 2;
+        }
+
+        if(vstretch) {
+            yinc = ((int)inHeight << 16) / outHeight;
+            ysrc = ((inHeight << 16) - (yinc * outHeight)) / 2;
+            for(ydst = 0; ydst < outHeight; ysrc += yinc, ydst++) {
+                lpRowSrc = (LPBYTE)lpIn + nBytesRowSrc * (ysrc >> 16);
+                lpRowDst = (LPBYTE)lpOut + nBytesRowDst * ydst;
+                if(hstretch) {
+                    xsrc = xoff;
+                    for(xdst = 0; xdst < outWidth; xsrc += xinc, xdst++)
+                        lpRowDst[xdst >> 3] |=
+                                ((lpRowSrc[(xsrc >> 16) >> 3] >> (7 - ((xsrc >> 16) % 8))) & 0x01) <<
+                                (7 - (xdst % 8));
+                } else {
+                    xdst = xoff;
+                    for(xsrc = 0; xsrc < inWidth; xsrc++, xdst += xinc)
+                        lpRowDst[(xdst >> 16) >> 3] |=
+                                ((lpRowSrc[xsrc >> 3] >> (7 - (xsrc % 8))) & 0x01) <<
+                                (7 - ((xdst >> 16) % 8));
+                }
+            }
+        } else {
+            yinc = ((int)outHeight << 16) / inHeight;
+            ydst = ((outHeight << 16) - (yinc * inHeight)) / 2;
+            for(ysrc = 0; ysrc < inHeight; ysrc++, ydst += yinc) {
+                lpRowSrc = (LPBYTE)lpIn + nBytesRowSrc * ysrc;
+                lpRowDst = (LPBYTE)lpOut + nBytesRowDst * (ydst >> 16);
+                if(hstretch) {
+                    xsrc = xoff;
+                    for(xdst = 0; xdst < outWidth; xsrc += xinc, xdst++)
+                        lpRowDst[xdst >> 3] |=
+                                ((lpRowSrc[(xsrc >> 16) >> 3] >> (7 - ((xsrc >> 16) % 8))) & 0x01) <<
+                                (7 - (xdst % 8));
+                } else {
+                    xdst = xoff;
+                    for(xsrc = 0; xsrc < inWidth; xsrc++, xdst += xinc)
+                        lpRowDst[(xdst >> 16) >> 3] |=
+                                ((lpRowSrc[xsrc >> 3] >> (7 - (xsrc % 8))) & 0x01) <<
+                                (7 - ((xdst >> 16) % 8));
+                }
+            }
+        }
+    } else {
+        memcpy(lpOut, lpIn, nBytesDst);
+    }
+
+    return;
+}
+
+
+/***********************************************************************
  *           CreateUserBitmap    (GDI.407)
  */
 HBITMAP16 WINAPI CreateUserBitmap16( INT16 width, INT16 height, UINT16 planes,
diff -urN wine-20020310/windows/cursoricon.c wine/windows/cursoricon.c
--- wine-20020310/windows/cursoricon.c	Tue Mar 12 10:37:57 2002
+++ wine/windows/cursoricon.c	Thu Mar 21 21:37:09 2002
@@ -387,7 +387,7 @@
     *ptr = NULL;
     if (!(bits = map_fileW( filename ))) return FALSE;
 
-    /* FIXME: test for inimated icons
+    /* FIXME: test for animated icons
      * hack to load the first icon from the *.ani file
      */
     if ( *(LPDWORD)bits==0x46464952 ) /* "RIFF" */
@@ -1158,20 +1158,18 @@
  *
  * BUGS
  *
- *  - The provided bitmaps are not resized!
- *  - The documentation says the lpXORbits bitmap must be in a device 
- *    dependent format. But we must still resize it and perform depth 
- *    conversions if necessary.
+ *  - You may wonder why we check if bBitsPixel == 1: StretchDIBits seemed to
+ *    replace the white parts of the bitmap by blue. This behavior was there
+ *    before the resizing: StretchDIBits (or another function called by it) is
+ *    most probably the culprit.
+ *  - Before using StretchDIBits, we must realign the scanlines of lpXORbits on
+ *    32bits rather than 16bits. This is probably a bug in StretchDIBits.
  *  - I'm a bit unsure about the how the 'device dependent format' thing works.
  *    I did some tests on windows and found that if you provide a 16bpp bitmap 
  *    in lpXORbits, then its format but be 565 RGB if the screen's bit depth 
  *    is 16bpp but it must be 555 RGB if the screen's bit depth is anything
  *    else. I don't know if this is part of the GDI specs or if this is a 
  *    quirk of the graphics card driver.
- *  - You may think that we check whether the bit depths match or not 
- *    as an optimization. But the truth is that the conversion using 
- *    CreateDIBitmap does not work for some bit depth (e.g. 8bpp) and I have 
- *    no idea why.
  *  - I'm pretty sure that all the things we do in CreateIcon should 
  *    also be done in CreateIconIndirect...
  */
@@ -1185,57 +1183,92 @@
     LPCVOID   lpXORbits)  /* [in] the icon's 'color' bitmap */
 {
     HICON hIcon;
-    HDC hdc;
+    HDC hdc, hdcMem;
+    ICONINFO iinfo;
+    BITMAPINFO bmi;
+    INT nNeededCX, nNeededCY;
+    LPBYTE lpANDbitsrszd, lpXORbitsrszd;
+    HBITMAP hbmANDrszd = 0, hbmXORrszd = 0;
+    HBITMAP hbmtemp;
+    INT nBytesRowSrc;
+    INT nBytesRowDst, nBytesDst;
+    INT y;
 
     TRACE_(icon)("%dx%dx%d, xor=%p, and=%p\n",
                  nWidth, nHeight, bPlanes * bBitsPixel, lpXORbits, lpANDbits);
 
-    hdc=GetDC(0);
-    if (!hdc)
-        return 0;
-
-    if (GetDeviceCaps(hdc,BITSPIXEL)==bBitsPixel) {
-        CURSORICONINFO info;
+    if(!(hdc=GetDC(0))) return 0;
 
-        info.ptHotSpot.x = ICON_HOTSPOT;
-        info.ptHotSpot.y = ICON_HOTSPOT;
-        info.nWidth = nWidth;
-        info.nHeight = nHeight;
-        info.nWidthBytes = 0;
-        info.bPlanes = bPlanes;
-        info.bBitsPerPixel = bBitsPixel;
-
-        hIcon=CreateCursorIconIndirect16( 0, &info, lpANDbits, lpXORbits );
+    /* AND bitmask (always monochrpme) */
+    nNeededCX = GetSystemMetrics(SM_CXICON);
+    nNeededCY = GetSystemMetrics(SM_CYICON);
+    nBytesRowDst = BITMAP_GetWidthBytes(nNeededCX, 1);
+    nBytesDst = nBytesRowDst * nNeededCY;
+    if((lpANDbitsrszd = HeapAlloc(GetProcessHeap(), 0, nBytesDst))) {
+        BITMAP_ResizeMonochrome(nWidth, nHeight, lpANDbits,
+                                nNeededCX, nNeededCY, lpANDbitsrszd);
+        hbmANDrszd = CreateBitmap(nNeededCX, nNeededCY, 1, 1, lpANDbitsrszd);
+        HeapFree(GetProcessHeap(), 0, lpANDbitsrszd);
+    }
+
+    /* XOR bitmask (bit depth of bitmap must be converted to screen depth) */
+    /* Test for bBitsPixel == 1 to get correct white if monochrome */
+    if(bBitsPixel == 1) {
+        if((lpXORbitsrszd = HeapAlloc(GetProcessHeap(), 0, nBytesDst))) {
+            BITMAP_ResizeMonochrome(nWidth, nHeight, lpXORbits,
+                                    nNeededCX, nNeededCY, lpXORbitsrszd);
+            hbmXORrszd = CreateBitmap(nNeededCX, nNeededCY, 1, 1, lpXORbitsrszd);
+            HeapFree(GetProcessHeap(), 0, lpXORbitsrszd);
+        }
     } else {
-        ICONINFO iinfo;
-        BITMAPINFO bmi;
-
-        iinfo.fIcon=TRUE;
-        iinfo.xHotspot=ICON_HOTSPOT;
-        iinfo.yHotspot=ICON_HOTSPOT;
-        iinfo.hbmMask=CreateBitmap(nWidth,nHeight,1,1,lpANDbits);
-
-        bmi.bmiHeader.biSize=sizeof(bmi.bmiHeader);
-        bmi.bmiHeader.biWidth=nWidth;
-        bmi.bmiHeader.biHeight=-nHeight;
-        bmi.bmiHeader.biPlanes=bPlanes;
-        bmi.bmiHeader.biBitCount=bBitsPixel;
-        bmi.bmiHeader.biCompression=BI_RGB;
-        bmi.bmiHeader.biSizeImage=0;
-        bmi.bmiHeader.biXPelsPerMeter=0;
-        bmi.bmiHeader.biYPelsPerMeter=0;
-        bmi.bmiHeader.biClrUsed=0;
-        bmi.bmiHeader.biClrImportant=0;
-
-        iinfo.hbmColor = CreateDIBitmap( hdc, &bmi.bmiHeader,
-                                         CBM_INIT, lpXORbits,
-                                         &bmi, DIB_RGB_COLORS );
-
-        hIcon=CreateIconIndirect(&iinfo);
-        DeleteObject(iinfo.hbmMask);
-        DeleteObject(iinfo.hbmColor);
+        nBytesRowSrc = BITMAP_GetWidthBytes(nWidth, bBitsPixel);
+        /* Somebody (MS? Wine?) wants the bitmap in 32bit chunks
+           for StretchDIBits rather than 16bits as everywhere else */
+        nBytesRowDst = 4 * ((nWidth * bBitsPixel + 31) >> 5);
+        nBytesDst = nBytesRowDst * nHeight;
+        if((lpXORbitsrszd = HeapAlloc(GetProcessHeap(), 0, nBytesDst))) {
+            for(y = 0; y < nHeight; y++)
+                memcpy((LPBYTE)lpXORbitsrszd + (y * nBytesRowDst),
+                       (LPBYTE)lpXORbits + (y * nBytesRowSrc), nBytesRowSrc);
+
+            bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+            bmi.bmiHeader.biWidth = nWidth;
+            bmi.bmiHeader.biHeight = -nHeight;
+            bmi.bmiHeader.biPlanes = bPlanes;
+            bmi.bmiHeader.biBitCount = bBitsPixel;
+            bmi.bmiHeader.biCompression = BI_RGB;
+            bmi.bmiHeader.biSizeImage = 0;
+            bmi.bmiHeader.biXPelsPerMeter = 0;
+            bmi.bmiHeader.biYPelsPerMeter = 0;
+            bmi.bmiHeader.biClrUsed = 0;
+            bmi.bmiHeader.biClrImportant = 0;
+
+            screen_dc = CreateDCA("DISPLAY", NULL, NULL, NULL);
+            hbmXORrszd = CreateCompatibleBitmap(screen_dc, nNeededCX, nNeededCY);
+            hdcMem = CreateCompatibleDC(screen_dc);
+            SetStretchBltMode(hdcMem, COLORONCOLOR);
+            hbmtemp = SelectObject(hdcMem, hbmXORrszd);
+            StretchDIBits(hdcMem, 0, 0, nNeededCX, nNeededCY,
+                          0, 0, nWidth, nHeight, lpXORbitsrszd,
+                          &bmi, DIB_RGB_COLORS, SRCCOPY);
+            SelectObject(hdcMem, hbmtemp);
+            DeleteDC(hdcMem);
+            HeapFree(GetProcessHeap(), 0, lpXORbitsrszd);
+        }
     }
+
+    /* Creation of the icon itself */
+    iinfo.fIcon = TRUE;
+    iinfo.xHotspot = ICON_HOTSPOT;
+    iinfo.yHotspot = ICON_HOTSPOT;
+    iinfo.hbmMask = hbmANDrszd;
+    iinfo.hbmColor = hbmXORrszd;
+
+    hIcon = CreateIconIndirect(&iinfo);
+    DeleteObject(iinfo.hbmColor);
+    DeleteObject(iinfo.hbmMask);
     ReleaseDC(0,hdc);
+
     return hIcon;
 }
 
@@ -1262,7 +1295,7 @@
         return 0;
     FarSetOwner16( handle, hInstance );
     ptr = (char *)GlobalLock16( handle );
-    memcpy( ptr, info, sizeof(*info) );
+    memcpy( ptr, info, sizeof(CURSORICONINFO) );
     memcpy( ptr + sizeof(CURSORICONINFO), lpANDbits, sizeAnd );
     memcpy( ptr + sizeof(CURSORICONINFO) + sizeAnd, lpXORbits, sizeXor );
     GlobalUnlock16( handle );


More information about the wine-patches mailing list