最近工做中有個需求是將Icon轉成帶Alpha通道的Bitmap, 雖然網上有很多這方面的文章,但不少都是錯的, 這裏記錄下,或許對後來人有用。
要實現這個功能,咱們首先須要理解Icon的格式,咱們能夠看到Icon的結構以下:
typedef
struct _ICONINFO {
BOOL fIcon;
DWORD xHotspot;
DWORD yHotspot;
HBITMAP hbmMask;
HBITMAP hbmColor;
} ICONINFO;
typedef ICONINFO *PICONINFO;
從上面咱們能夠看到Icon和cursor的結構基本同樣,主要都包括一幅mask位圖和一幅color位圖
以下一個8個像素的紅色小圖標:
它的Mask位圖以下:
若是咱們把Mask位圖畫出來,咱們會看到周圍1的區域都是白色的,中間0的區域是黑色的
它的color位圖以下:
若是咱們把color位圖畫出來,咱們會看到除了中間區域是紅色的,周圍0的區域都是黑色的。
思考將Icon畫到目標位圖上時,他們是如何最終合成的?
實際很簡單,就是先和Mask位圖作與(AND)運算,而後再與Color位圖作異或(XOR)運算: AND運算的結果是除了中間區域變成黑色(0),目標位圖的其餘區域都保持不變;XOR 運算的結果是周圍區域只有和0不同 (color位圖)纔會獲得1 (也就是原來是1則保持), 中間區域由於前面通過mask運算後都是0,因此中間R部分XOR後也會保留。
理解了Icon格式,咱們要將Icon轉Bitmap就好辦了, 咱們只要將Color位圖考出來,而後位圖裏將mask部分是1的部分的Alpha通道改爲0就能夠了。
這裏要 注意的是有些icon 的color位圖自己就是帶Alpha通道的,這樣咱們就實際上用不到Mask位圖了,也不用再去改Alpha通道了。
另外對於黑白單色Icon( 好比黑白光標), 咱們不少時候會發現它的color位圖是空的, 這種狀況下全部的數據實際上都存到了Mask位圖裏,這時的Mask位圖高度是Icon高度的2倍,上半部分是mask部分,下班部分保存了color位圖部分。
最後簡單貼下代碼:
HBITMAP IconToBitmap(HICON hIcon, SIZE
*
pTargetSize
=
NULL)
{
ICONINFO info
=
{
0
};
if
(hIcon
==
NULL
||
!
GetIconInfo(hIcon,
&
info)
||
!
info.fIcon)
{
return
NULL;
}
INT nWidth
=
0
;
INT nHeight
=
0
;
if
(pTargetSize
!=
NULL)
{
nWidth
=
pTargetSize
->
cx;
nHeight
=
pTargetSize
->
cy;
}
else
{
if
(info.hbmColor
!=
NULL)
{
BITMAP bmp
=
{
0
};
GetObject(info.hbmColor,
sizeof
(bmp),
&
bmp);
nWidth
=
bmp.bmWidth;
nHeight
=
bmp.bmHeight;
}
}
if(info.hbmColor != NULL)
{
DeleteObject(info.hbmColor);
info.hbmColor = NULL;
}
if(info.hbmMask != NULL)
{
DeleteObject(info.hbmMask);
info.hbmMask = NULL;
}
if
(nWidth
<=
0
||
nHeight
<=
0
)
{
return
NULL;
}
INT nPixelCount
=
nWidth
*
nHeight;
HDC dc
=
GetDC(NULL);
INT
*
pData
=
NULL;
HDC dcMem
=
NULL;
HBITMAP hBmpOld
=
NULL;
bool
*
pOpaque
=
NULL;
HBITMAP dib
=
NULL;
BOOL bSuccess
=
FALSE;
do
{
BITMAPINFOHEADER bi
=
{
0
};
bi.biSize
=
sizeof
(BITMAPINFOHEADER);
bi.biWidth
=
nWidth;
bi.biHeight
=
-
nHeight;
bi.biPlanes
=
1
;
bi.biBitCount
=
32
;
bi.biCompression
=
BI_RGB;
dib
=
CreateDIBSection(dc, (BITMAPINFO
*
)
&
bi, DIB_RGB_COLORS, (VOID
**
)
&
pData, NULL,
0
);
if
(dib
==
NULL)
break
;
memset(pData,
0
, nPixelCount
*
4
);
dcMem
=
CreateCompatibleDC(dc);
if
(dcMem
==
NULL)
break
;
hBmpOld
=
(HBITMAP)SelectObject(dcMem, dib);
::DrawIconEx(dcMem,
0
,
0
, hIcon, nWidth, nHeight,
0
, NULL, DI_MASK);
pOpaque
=
new
(std::nothrow)
bool
[nPixelCount];
if
(pOpaque
==
NULL)
break
;
for
(INT i
=
0
; i
<
nPixelCount;
++
i)
{
pOpaque[i]
=
!
pData[i];
}
memset(pData,
0
, nPixelCount
*
4
);
::DrawIconEx(dcMem,
0
,
0
, hIcon, nWidth, nHeight,
0
, NULL, DI_NORMAL);
BOOL bPixelHasAlpha
=
FALSE;
UINT
*
pPixel
=
(UINT
*
)pData;
for
(INT i
=
0
; i
<
nPixelCount;
++
i,
++
pPixel)
{
if
((
*
pPixel
&
0xff000000
)
!=
0
)
{
bPixelHasAlpha
=
TRUE;
break
;
}
}
if
(
!
bPixelHasAlpha)
{
pPixel
=
(UINT
*
)pData;
for
(INT i
=
0
;i
<
nPixelCount;
++
i,
++
pPixel)
{
if
(pOpaque[i])
{
*
pPixel
|=
0xFF000000
;
}
else
{
*
pPixel
&=
0x00FFFFFF
;
}
}
}
bSuccess
=
TRUE;
}
while
(FALSE);
if
(pOpaque
!=
NULL)
{
delete []pOpaque;
pOpaque
=
NULL;
}
if
(dcMem
!=
NULL)
{
SelectObject(dcMem, hBmpOld);
DeleteDC(dcMem);
}
ReleaseDC(NULL, dc);
if
(
!
bSuccess)
{
if
(dib
!=
NULL)
{
DeleteObject(dib);
dib
=
NULL;
}
}
return
dib;
}
另外感慨Webkit是個寶庫, 咱們的Icon轉Bitmap代碼實際上能夠參考這裏: