This patch fixes an application that draws user32 icons manually:
hicon = LoadImage(NULL, "oic_ques.ico", IMAGE_ICON, 256, 256, LR_LOADFROMFILE); GetIconInfo(hicon, &info); GetObject(info.hbmMask, sizeof(bm), &bm);
hMemDC = CreateCompatibleDC(hdc);
SelectObject(hMemDC, info.hbmColor); StretchBlt(hdc, 200, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
SelectObject(hMemDC, info.hbmMask); StretchBlt(hdc, 400, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND); SelectObject(hMemDC, info.hbmColor); StretchBlt(hdc, 400, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCINVERT);
For testing purposes I edited dlls/user32/resources/oic_ques.ico and removed everything except the 256x256 32 bpp PNG image. Then I created a test app that draws separately mask and color bitmaps from the icon info and then draws an icon using the code above. With this patch under Wine the mask and the final icon visually look similar to what I see under Windows.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/user32/cursoricon.c | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+)
diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c index 0adb73bf56..c561c70364 100644 --- a/dlls/user32/cursoricon.c +++ b/dlls/user32/cursoricon.c @@ -1131,6 +1131,93 @@ done: return alpha; }
+static HBITMAP create_alpha_mask( HBITMAP color ) +{ + HBITMAP alpha; + char info_buf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)]; + BITMAPINFO *info = (BITMAPINFO *)info_buf; + BITMAP bm; + HDC hdc; + void *bits; + unsigned char *ptr; + int i; + + if (!GetObjectW( color, sizeof(bm), &bm )) return 0; + if (bm.bmBitsPixel != 32) return 0; + + if (!(hdc = CreateCompatibleDC( 0 ))) return 0; + + info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info->bmiHeader.biWidth = bm.bmWidth; + info->bmiHeader.biHeight = -bm.bmHeight; + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = 32; + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrUsed = 0; + info->bmiHeader.biClrImportant = 0; + if (!(alpha = CreateDIBSection( hdc, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done; + + GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS ); + if (!bmi_has_alpha( info, bits )) + { + DeleteObject( alpha ); + alpha = 0; + goto done; + } + + for (i = 0, ptr = bits; i < bm.bmWidth * bm.bmHeight; i++, ptr += 4) + { + if (ptr[3] == 0xff) + ptr[0] = ptr[1] = ptr[2] = 0; + else + ptr[0] = ptr[1] = ptr[2] = 0xff; + + ptr[3] = 0; + } + +done: + DeleteDC( hdc ); + return alpha; +} + +static HBITMAP create_mask_from_alpha( HBITMAP color ) +{ + BITMAP bm; + HBITMAP alpha, mask; + HDC hdc_dst, hdc_src; + + alpha = create_alpha_mask( color ); + if (!alpha) return 0; + + GetObjectW( alpha, sizeof(bm), &bm ); + + mask = CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL ); + if (!mask) return 0; + + hdc_src = CreateCompatibleDC( 0 ); + hdc_dst = CreateCompatibleDC( 0 ); + if (hdc_src && hdc_dst) + { + SelectObject( hdc_src, alpha ); + SelectObject( hdc_dst, mask ); + BitBlt( hdc_dst, 0, 0, bm.bmWidth, bm.bmHeight, hdc_src, 0, 0, SRCCOPY ); + } + else + { + DeleteObject( mask ); + mask = 0; + } + + DeleteDC( hdc_src ); + DeleteDC( hdc_dst ); + DeleteObject( alpha ); + + return mask; +} +
/*********************************************************************** * create_icon_from_bmi @@ -1296,6 +1383,16 @@ static HICON create_icon_from_bmi( const BITMAPINFO *bmi, DWORD maxsize, HMODULE 0, 0, bmi_width, bmi_height, mask_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY ); } + else + { + HBITMAP alpha_mask = create_mask_from_alpha( color ); + + if (alpha_mask) + { + DeleteObject( mask ); + mask = alpha_mask; + } + } ret = TRUE;
done:
Dmitry Timoshkov dmitry@baikal.ru writes:
This patch fixes an application that draws user32 icons manually:
hicon = LoadImage(NULL, "oic_ques.ico", IMAGE_ICON, 256, 256, LR_LOADFROMFILE); GetIconInfo(hicon, &info); GetObject(info.hbmMask, sizeof(bm), &bm);
hMemDC = CreateCompatibleDC(hdc);
SelectObject(hMemDC, info.hbmColor); StretchBlt(hdc, 200, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
SelectObject(hMemDC, info.hbmMask); StretchBlt(hdc, 400, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND); SelectObject(hMemDC, info.hbmColor); StretchBlt(hdc, 400, 0, 200, 200, hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCINVERT);
For testing purposes I edited dlls/user32/resources/oic_ques.ico and removed everything except the 256x256 32 bpp PNG image. Then I created a test app that draws separately mask and color bitmaps from the icon info and then draws an icon using the code above. With this patch under Wine the mask and the final icon visually look similar to what I see under Windows.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru
dlls/user32/cursoricon.c | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+)
diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c index 0adb73bf56..c561c70364 100644 --- a/dlls/user32/cursoricon.c +++ b/dlls/user32/cursoricon.c @@ -1131,6 +1131,93 @@ done: return alpha; }
+static HBITMAP create_alpha_mask( HBITMAP color ) +{
- HBITMAP alpha;
- char info_buf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];
- BITMAPINFO *info = (BITMAPINFO *)info_buf;
- BITMAP bm;
- HDC hdc;
- void *bits;
- unsigned char *ptr;
- int i;
- if (!GetObjectW( color, sizeof(bm), &bm )) return 0;
- if (bm.bmBitsPixel != 32) return 0;
- if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
- info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info->bmiHeader.biWidth = bm.bmWidth;
- info->bmiHeader.biHeight = -bm.bmHeight;
- info->bmiHeader.biPlanes = 1;
- info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
- info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
- info->bmiHeader.biXPelsPerMeter = 0;
- info->bmiHeader.biYPelsPerMeter = 0;
- info->bmiHeader.biClrUsed = 0;
- info->bmiHeader.biClrImportant = 0;
- if (!(alpha = CreateDIBSection( hdc, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
- GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS );
- if (!bmi_has_alpha( info, bits ))
- {
DeleteObject( alpha );
alpha = 0;
goto done;
- }
- for (i = 0, ptr = bits; i < bm.bmWidth * bm.bmHeight; i++, ptr += 4)
- {
if (ptr[3] == 0xff)
ptr[0] = ptr[1] = ptr[2] = 0;
else
ptr[0] = ptr[1] = ptr[2] = 0xff;
ptr[3] = 0;
- }
+done:
- DeleteDC( hdc );
- return alpha;
+}
+static HBITMAP create_mask_from_alpha( HBITMAP color ) +{
- BITMAP bm;
- HBITMAP alpha, mask;
- HDC hdc_dst, hdc_src;
- alpha = create_alpha_mask( color );
- if (!alpha) return 0;
- GetObjectW( alpha, sizeof(bm), &bm );
- mask = CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL );
- if (!mask) return 0;
- hdc_src = CreateCompatibleDC( 0 );
- hdc_dst = CreateCompatibleDC( 0 );
- if (hdc_src && hdc_dst)
- {
SelectObject( hdc_src, alpha );
SelectObject( hdc_dst, mask );
BitBlt( hdc_dst, 0, 0, bm.bmWidth, bm.bmHeight, hdc_src, 0, 0, SRCCOPY );
- }
- else
- {
DeleteObject( mask );
mask = 0;
- }
- DeleteDC( hdc_src );
- DeleteDC( hdc_dst );
- DeleteObject( alpha );
- return mask;
+}
This seems to be a lot of redundant code. It shouldn't be necessary to allocate a new DIB section and copy the bits again, we already have access to the original data. Also the mask bitmap is already allocated, and we already have code for blitting to it. You should be able to simply build a mask_bits array and go through the existing code paths.