zoukankan      html  css  js  c++  java
  • 如何将Icon转成Bitmap(对ICON的内部格式讲的比较清楚)

    最近工作中有个需求是将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运算,我们将中间红色部分扣出来贴了上去,而其他周围区域保持不变, 这种方式实际上也是DrawIconExTransparentBlt的实现原理。
     
    理解了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代码实际上可以参考这里:

    http://www.cnblogs.com/weiym/p/3928348.html

  • 相关阅读:
    我的知识库(4) java获取页面编码(Z)
    知识库(3)JAVA 正则表达式 (超详细)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts
    某人总结的《英语听力的技巧 》,挺搞的
    我的知识库(5)java单例模式详解
    构建可扩展程序
    SerialPort (RS232 Serial COM Port) in C# .NET
    Python学习笔记——String、Sequences
    UI题目我的答案
    jQuery学习系列学会操纵Form表单元素(1)
  • 原文地址:https://www.cnblogs.com/findumars/p/5786065.html
Copyright © 2011-2022 走看看