user32: Added DrawIcon alpha blending support.

This commit is contained in:
Joel Holdsworth 2009-05-31 17:00:17 +01:00 committed by Alexandre Julliard
parent 8d833ee2e7
commit a0232508b3
2 changed files with 128 additions and 23 deletions

View File

@ -1598,6 +1598,74 @@ BOOL WINAPI DestroyCursor( HCURSOR hCursor )
return DestroyIcon32(HCURSOR_16(hCursor), CID_WIN32);
}
/***********************************************************************
* bitmap_has_alpha_channel
*
* Analyses bits bitmap to determine if alpha data is present.
*
* PARAMS
* bpp [I] The bits-per-pixel of the bitmap
* bitmapBits [I] A pointer to the bitmap data
* bitmapLength [I] The length of the bitmap in bytes
*
* RETURNS
* TRUE if an alpha channel is discovered, FALSE
*
* NOTE
* Windows' behaviour is that if the icon bitmap is 32-bit and at
* least one pixel has a non-zero alpha, then the bitmap is a
* treated as having an alpha channel transparentcy. Otherwise,
* it's treated as being completely opaque.
*
*/
static BOOL bitmap_has_alpha_channel( int bpp, unsigned char *bitmapBits,
unsigned int bitmapLength )
{
/* Detect an alpha channel by looking for non-zero alpha pixels */
if(bpp == 32)
{
unsigned int offset;
for(offset = 3; offset < bitmapLength; offset += 4)
{
if(bitmapBits[offset] != 0)
{
return TRUE;
break;
}
}
}
return FALSE;
}
/***********************************************************************
* premultiply_alpha_channel
*
* Premultiplies the color channels of a 32-bit bitmap by the alpha
* channel. This is a necessary step that must be carried out on
* the image before it is passed to GdiAlphaBlend
*
* PARAMS
* destBitmap [I] The destination bitmap buffer
* srcBitmap [I] The source bitmap buffer
* bitmapLength [I] The length of the bitmap in bytes
*
*/
static void premultiply_alpha_channel( unsigned char *destBitmap,
unsigned char *srcBitmap,
unsigned int bitmapLength )
{
unsigned char *destPixel = destBitmap;
unsigned char *srcPixel = srcBitmap;
while(destPixel < destBitmap + bitmapLength)
{
unsigned char alpha = srcPixel[3];
*(destPixel++) = *(srcPixel++) * alpha / 255;
*(destPixel++) = *(srcPixel++) * alpha / 255;
*(destPixel++) = *(srcPixel++) * alpha / 255;
*(destPixel++) = *(srcPixel++);
}
}
/***********************************************************************
* DrawIcon (USER32.@)
@ -1606,28 +1674,69 @@ BOOL WINAPI DrawIcon( HDC hdc, INT x, INT y, HICON hIcon )
{
CURSORICONINFO *ptr;
HDC hMemDC;
HBITMAP hXorBits, hAndBits;
HBITMAP hXorBits = NULL, hAndBits = NULL, hBitTemp = NULL;
COLORREF oldFg, oldBg;
unsigned char *xorBitmapBits;
unsigned int dibLength;
TRACE("%p, (%d,%d), %p\n", hdc, x, y, hIcon);
if (!(ptr = GlobalLock16(HICON_16(hIcon)))) return FALSE;
if (!(hMemDC = CreateCompatibleDC( hdc ))) return FALSE;
hAndBits = CreateBitmap( ptr->nWidth, ptr->nHeight, 1, 1, ptr + 1 );
hXorBits = CreateBitmap( ptr->nWidth, ptr->nHeight, ptr->bPlanes,
ptr->bBitsPerPixel, (char *)(ptr + 1)
+ ptr->nHeight * get_bitmap_width_bytes(ptr->nWidth,1) );
dibLength = ptr->nHeight * get_bitmap_width_bytes(
ptr->nWidth, ptr->bBitsPerPixel);
xorBitmapBits = (unsigned char *)(ptr + 1) + ptr->nHeight *
get_bitmap_width_bytes(ptr->nWidth, 1);
oldFg = SetTextColor( hdc, RGB(0,0,0) );
oldBg = SetBkColor( hdc, RGB(255,255,255) );
if (hXorBits && hAndBits)
if(bitmap_has_alpha_channel(ptr->bBitsPerPixel, xorBitmapBits, dibLength))
{
HBITMAP hBitTemp = SelectObject( hMemDC, hAndBits );
BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCAND );
SelectObject( hMemDC, hXorBits );
BitBlt(hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0,SRCINVERT);
SelectObject( hMemDC, hBitTemp );
BITMAPINFOHEADER bmih;
unsigned char *dibBits;
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = ptr->nWidth;
bmih.biHeight = -ptr->nHeight;
bmih.biPlanes = ptr->bPlanes;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
hXorBits = CreateDIBSection(hdc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
(void*)&dibBits, NULL, 0);
if (hXorBits && dibBits)
{
BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
/* Do the alpha blending render */
premultiply_alpha_channel(dibBits, xorBitmapBits, dibLength);
hBitTemp = SelectObject( hMemDC, hXorBits );
GdiAlphaBlend(hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC,
0, 0, ptr->nWidth, ptr->nHeight, pixelblend);
SelectObject( hMemDC, hBitTemp );
}
}
else
{
hAndBits = CreateBitmap( ptr->nWidth, ptr->nHeight, 1, 1, ptr + 1 );
hXorBits = CreateBitmap( ptr->nWidth, ptr->nHeight, ptr->bPlanes,
ptr->bBitsPerPixel, xorBitmapBits);
if (hXorBits && hAndBits)
{
hBitTemp = SelectObject( hMemDC, hAndBits );
BitBlt( hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0, SRCAND );
SelectObject( hMemDC, hXorBits );
BitBlt(hdc, x, y, ptr->nWidth, ptr->nHeight, hMemDC, 0, 0,SRCINVERT);
SelectObject( hMemDC, hBitTemp );
}
}
DeleteDC( hMemDC );
if (hXorBits) DeleteObject( hXorBits );
if (hAndBits) DeleteObject( hAndBits );

View File

@ -1090,26 +1090,22 @@ static void test_DrawIcon(void)
/* Test alpha blending */
/* Windows 2000 and up will alpha blend, earlier Windows versions will not */
check_DrawIcon(hdcDst, FALSE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__);
todo_wine
{
check_DrawIcon(hdcDst, TRUE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__);
check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__);
check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__);
check_DrawIcon(hdcDst, FALSE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
}
check_DrawIcon(hdcDst, FALSE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
check_DrawIcon(hdcDst, TRUE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
/* Test detecting of alpha channel */
/* If a single pixel's alpha channel is non-zero, the icon
will be alpha blended, otherwise it will be draw with
and + xor blts. */
check_alpha_draw(hdcDst, FALSE, FALSE, 32, __LINE__);
todo_wine check_alpha_draw(hdcDst, FALSE, TRUE, 32, __LINE__);
check_alpha_draw(hdcDst, FALSE, TRUE, 32, __LINE__);
cleanup:
if(bmpOld)