From d1b3bd7221525bb7b35b29e3b9e7e62bc7272598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gardou?= Date: Thu, 18 Oct 2012 18:37:37 +0000 Subject: [PATCH] [SHELL32] - cope with alpha channel when shortcut-overlaying an icon. CORE-6099 #comment Fixed by rev 57574 #resolve svn path=/trunk/; revision=57574 --- reactos/dll/win32/shell32/iconcache.cpp | 225 +++++++++++++++++------- 1 file changed, 159 insertions(+), 66 deletions(-) diff --git a/reactos/dll/win32/shell32/iconcache.cpp b/reactos/dll/win32/shell32/iconcache.cpp index cb84678c189..16158f019fd 100644 --- a/reactos/dll/win32/shell32/iconcache.cpp +++ b/reactos/dll/win32/shell32/iconcache.cpp @@ -83,26 +83,38 @@ static int SIC_LoadOverlayIcon(int icon_idx); * NOTES * Creates a new icon as a copy of the passed-in icon, overlayed with a * shortcut image. + * FIXME: This should go to the ImageList implementation! */ static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large) -{ ICONINFO SourceIconInfo, ShortcutIconInfo, TargetIconInfo; - HICON ShortcutIcon, TargetIcon; - BITMAP SourceBitmapInfo, ShortcutBitmapInfo; - HDC SourceDC = NULL, - ShortcutDC = NULL, - TargetDC = NULL, - ScreenDC = NULL; - HBITMAP OldSourceBitmap = NULL, - OldShortcutBitmap = NULL, +{ + ICONINFO ShortcutIconInfo, TargetIconInfo; + HICON ShortcutIcon = NULL, TargetIcon; + BITMAP TargetBitmapInfo, ShortcutBitmapInfo; + HDC ShortcutDC = NULL, + TargetDC = NULL; + HBITMAP OldShortcutBitmap = NULL, OldTargetBitmap = NULL; static int s_imgListIdx = -1; + ZeroMemory(&ShortcutIconInfo, sizeof(ShortcutIconInfo)); + ZeroMemory(&TargetIconInfo, sizeof(TargetIconInfo)); - /* Get information about the source icon and shortcut overlay */ - if (! GetIconInfo(SourceIcon, &SourceIconInfo) - || 0 == GetObjectW(SourceIconInfo.hbmColor, sizeof(BITMAP), &SourceBitmapInfo)) + /* Get information about the source icon and shortcut overlay. + * We will write over the source bitmaps to get the final ones */ + if (! GetIconInfo(SourceIcon, &TargetIconInfo)) + return NULL; + + /* Is it possible with the ImageList implementation? */ + if(!TargetIconInfo.hbmColor) { - return NULL; + /* Maybe we'll support this at some point */ + FIXME("1bpp icon wants its overlay!\n"); + goto fail; + } + + if(!GetObjectW(TargetIconInfo.hbmColor, sizeof(BITMAP), &TargetBitmapInfo)) + { + goto fail; } /* search for the shortcut icon only once */ @@ -121,23 +133,25 @@ static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large) } else ShortcutIcon = NULL; - if (NULL == ShortcutIcon - || ! GetIconInfo(ShortcutIcon, &ShortcutIconInfo) - || 0 == GetObjectW(ShortcutIconInfo.hbmColor, sizeof(BITMAP), &ShortcutBitmapInfo)) + if (!ShortcutIcon || !GetIconInfo(ShortcutIcon, &ShortcutIconInfo)) { - return NULL; + goto fail; + } + + /* Is it possible with the ImageLists ? */ + if(!ShortcutIconInfo.hbmColor) + { + /* Maybe we'll support this at some point */ + FIXME("Should draw 1bpp overlay!\n"); + goto fail; + } + + if(!GetObjectW(ShortcutIconInfo.hbmColor, sizeof(BITMAP), &ShortcutBitmapInfo)) + { + goto fail; } - TargetIconInfo = SourceIconInfo; - TargetIconInfo.hbmMask = NULL; - TargetIconInfo.hbmColor = NULL; - - /* Setup the source, shortcut and target masks */ - SourceDC = CreateCompatibleDC(NULL); - if (NULL == SourceDC) goto fail; - OldSourceBitmap = (HBITMAP)SelectObject(SourceDC, SourceIconInfo.hbmMask); - if (NULL == OldSourceBitmap) goto fail; - + /* Setup the masks */ ShortcutDC = CreateCompatibleDC(NULL); if (NULL == ShortcutDC) goto fail; OldShortcutBitmap = (HBITMAP)SelectObject(ShortcutDC, ShortcutIconInfo.hbmMask); @@ -145,44 +159,120 @@ static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large) TargetDC = CreateCompatibleDC(NULL); if (NULL == TargetDC) goto fail; - TargetIconInfo.hbmMask = CreateCompatibleBitmap(TargetDC, SourceBitmapInfo.bmWidth, - SourceBitmapInfo.bmHeight); - if (NULL == TargetIconInfo.hbmMask) goto fail; - ScreenDC = GetDC(NULL); - if (NULL == ScreenDC) goto fail; - TargetIconInfo.hbmColor = CreateCompatibleBitmap(ScreenDC, SourceBitmapInfo.bmWidth, - SourceBitmapInfo.bmHeight); - ReleaseDC(NULL, ScreenDC); - if (NULL == TargetIconInfo.hbmColor) goto fail; OldTargetBitmap = (HBITMAP)SelectObject(TargetDC, TargetIconInfo.hbmMask); if (NULL == OldTargetBitmap) goto fail; - /* Create the target mask by ANDing the source and shortcut masks */ - if (! BitBlt(TargetDC, 0, 0, SourceBitmapInfo.bmWidth, SourceBitmapInfo.bmHeight, - SourceDC, 0, 0, SRCCOPY) || - ! BitBlt(TargetDC, 0, SourceBitmapInfo.bmHeight - ShortcutBitmapInfo.bmHeight, - ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight, - ShortcutDC, 0, 0, SRCAND)) + /* Create the complete mask by ANDing the source and shortcut masks. + * NOTE: in an ImageList, all icons have the same dimensions */ + if (!BitBlt(TargetDC, 0, 0, ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight, + ShortcutDC, 0, 0, SRCAND)) { goto fail; } - /* Setup the source and target xor bitmap */ - if (NULL == SelectObject(SourceDC, SourceIconInfo.hbmColor) || + /* + * We must remove or add the alpha component to the shortcut overlay: + * If we don't, SRCCOPY will copy it to our resulting icon, resulting in a + * partially transparent icons where it shouldn't be, and to an invisible icon + * if the underlying icon don't have any alpha channel information. (16bpp only icon for instance). + * But if the underlying icon has alpha channel information, then we must mark the overlay information + * as opaque. + * NOTE: This code sucks(tm) and should belong to the ImageList implementation. + * NOTE2: there are better ways to do this. + */ + if(ShortcutBitmapInfo.bmBitsPixel == 32) + { + BOOL add_alpha; + BYTE buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])]; + BITMAPINFO* lpbmi = (BITMAPINFO*)buffer; + PVOID bits; + PULONG pixel; + INT i, j; + + /* Find if the source bitmap has an alpha channel */ + if(TargetBitmapInfo.bmBitsPixel != 32) add_alpha = FALSE; + else + { + ZeroMemory(buffer, sizeof(buffer)); + lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + lpbmi->bmiHeader.biWidth = TargetBitmapInfo.bmWidth; + lpbmi->bmiHeader.biHeight = TargetBitmapInfo.bmHeight; + lpbmi->bmiHeader.biPlanes = 1; + lpbmi->bmiHeader.biBitCount = 32; + + bits = HeapAlloc(GetProcessHeap(), 0, TargetBitmapInfo.bmHeight * TargetBitmapInfo.bmWidthBytes); + + if(!bits) goto fail; + + if(!GetDIBits(TargetDC, TargetIconInfo.hbmColor, 0, TargetBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS)) + { + ERR("GetBIBits failed!\n"); + HeapFree(GetProcessHeap(), 0, bits); + goto fail; + } + + i = j = 0; + pixel = (PULONG)bits; + + for(i=0; ibmiHeader.biSize = sizeof(BITMAPINFOHEADER); + lpbmi->bmiHeader.biWidth = ShortcutBitmapInfo.bmWidth; + lpbmi->bmiHeader.biHeight = ShortcutBitmapInfo.bmHeight; + lpbmi->bmiHeader.biPlanes = 1; + lpbmi->bmiHeader.biBitCount = 32; + + if(!GetDIBits(TargetDC, ShortcutIconInfo.hbmColor, 0, ShortcutBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS)) + { + ERR("GetBIBits failed!\n"); + HeapFree(GetProcessHeap(), 0, bits); + goto fail; + } + + pixel = (PULONG)bits; + /* Remove alpha channel component or make it totally opaque */ + for(i=0; ibmiHeader.biCompression = BI_RGB; + + /* Set the bits again */ + if(!SetDIBits(TargetDC, ShortcutIconInfo.hbmColor, 0, ShortcutBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS)) + { + ERR("SetBIBits failed!, %lu\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, bits); + goto fail; + } + HeapFree(GetProcessHeap(), 0, bits); + } + + /* Now do the copy. We overwrite the original icon data */ + if (NULL == SelectObject(ShortcutDC, ShortcutIconInfo.hbmColor) || NULL == SelectObject(TargetDC, TargetIconInfo.hbmColor)) - { - goto fail; - } - - /* Copy the source color bitmap to the target */ - if (! BitBlt(TargetDC, 0, 0, SourceBitmapInfo.bmWidth, SourceBitmapInfo.bmHeight, - SourceDC, 0, 0, SRCCOPY)) goto fail; - - /* Copy the source xor bitmap to the target and clear out part of it by using - the shortcut mask */ - if (NULL == SelectObject(ShortcutDC, ShortcutIconInfo.hbmColor)) goto fail; - if (!MaskBlt(TargetDC, 0, SourceBitmapInfo.bmHeight - ShortcutBitmapInfo.bmHeight, - ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight, + goto fail; + if (!MaskBlt(TargetDC, 0, 0, ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight, ShortcutDC, 0, 0, ShortcutIconInfo.hbmMask, 0, 0, MAKEROP4(0xAA0000, SRCCOPY))) { @@ -192,11 +282,9 @@ static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large) /* Clean up, we're not goto'ing to 'fail' after this so we can be lazy and not set handles to NULL */ SelectObject(TargetDC, OldTargetBitmap); - DeleteObject(TargetDC); + DeleteDC(TargetDC); SelectObject(ShortcutDC, OldShortcutBitmap); - DeleteObject(ShortcutDC); - SelectObject(SourceDC, OldSourceBitmap); - DeleteObject(SourceDC); + DeleteDC(ShortcutDC); /* Create the icon using the bitmaps prepared earlier */ TargetIcon = CreateIconIndirect(&TargetIconInfo); @@ -204,19 +292,24 @@ static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large) /* CreateIconIndirect copies the bitmaps, so we can release our bitmaps now */ DeleteObject(TargetIconInfo.hbmColor); DeleteObject(TargetIconInfo.hbmMask); + /* Delete what GetIconInfo gave us */ + DeleteObject(ShortcutIconInfo.hbmColor); + DeleteObject(ShortcutIconInfo.hbmMask); + DestroyIcon(ShortcutIcon); return TargetIcon; fail: /* Clean up scratch resources we created */ if (NULL != OldTargetBitmap) SelectObject(TargetDC, OldTargetBitmap); + if (NULL != TargetDC) DeleteDC(TargetDC); + if (NULL != OldShortcutBitmap) SelectObject(ShortcutDC, OldShortcutBitmap); + if (NULL != ShortcutDC) DeleteDC(ShortcutDC); if (NULL != TargetIconInfo.hbmColor) DeleteObject(TargetIconInfo.hbmColor); if (NULL != TargetIconInfo.hbmMask) DeleteObject(TargetIconInfo.hbmMask); - if (NULL != TargetDC) DeleteObject(TargetDC); - if (NULL != OldShortcutBitmap) SelectObject(ShortcutDC, OldShortcutBitmap); - if (NULL != ShortcutDC) DeleteObject(ShortcutDC); - if (NULL != OldSourceBitmap) SelectObject(SourceDC, OldSourceBitmap); - if (NULL != SourceDC) DeleteObject(SourceDC); + if (NULL != ShortcutIconInfo.hbmColor) DeleteObject(ShortcutIconInfo.hbmColor); + if (NULL != ShortcutIconInfo.hbmMask) DeleteObject(ShortcutIconInfo.hbmColor); + if (NULL != ShortcutIcon) DestroyIcon(ShortcutIcon); return NULL; }