如何將Icon轉成Bitmap

最近工做中有個需求是將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後也會保留。

咱們看到經過AND和XOR運算,咱們將中間紅色部分扣出來貼了上去,而其餘周圍區域保持不變, 這種方式實際上也是 DrawIconEx TransparentBlt 的實現原理。
理解了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代碼實際上能夠參考這裏:
相關文章
相關標籤/搜索