近期在进行自绘控件的开发时,时常用到灰度图像。譬如真彩色工具条中的Disable状态的图标,譬如真彩菜单Disable状态的图标等。照常来说,可以让控件的使用者去制作相应的图标。然而,为了让控件的使用者在使用控件时尽量简单,一个比较好的办法是在程序中生成Disable状态的图标。本文提供了一个简单的封装类CGrayBitmap,使用它的静态方法DoGray可以根据一个常规图片生成它的灰度图。
原创文章,转帖请注明出处:blog.youkuaiyun.com/sjdev
要对图像进行灰度处理,首先需要获取到图像的数据。GetBitmapBits和GetDIBits都可以得到位图的数据。GetBitmapBits用于获取设备独立位图的数据,GetDIBits用于获取兼容位图的数据。MSDN提示说GetBitmapBits只是为了兼容16位Windows而提供的,所以推荐使用GetDIBits。另外GetPixel也可以获取位图数据,不过在进行灰度处理时不建议使用,因为速度太慢了(在某些特殊情况下,GetPixel还是很有用的)。
首先看看GetDIBits函数的定义吧:
int GetDIBits(HDC hdc, HBITMAP hbmp, UINT uStartScan, UINT uScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage);
注意,GetDIBits的第二个参数hbmp必须为兼容位图;lpvBits用于接收位图数据,如果该参数为空,函数调用将填充lpbi的相应参数;lpbi参数表示指向BITMAPINFO结构的指针,它指定了设备无关位图的数据结构;uUsage用于指定BITMAPINFO结构中bmpColors成员的数据格式,这里咱们使用DIB_RGB_COLORS,它表示颜色表由RGB三个值直接构成(另外一个参数DIB_PAL_COLORS表示使用调色板)。
来看一下使用GetDIBits获取位图数据的代码:
- // 获取对象信息
- BITMAP bm;
- if (!::GetObject(hSrcBitmap, sizeof(BITMAP), (LPBYTE)&bm))
- return NULL;
- // 定义位图信息
- BITMAPINFO bi;
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = bm.bmWidth;
- bi.bmiHeader.biHeight = -bm.bmHeight;
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = 32;
- bi.bmiHeader.biCompression = BI_RGB;
- bi.bmiHeader.biSizeImage = bm.bmWidth * 4 * bm.bmHeight; // 32 bit
- bi.bmiHeader.biClrUsed = 0;
- bi.bmiHeader.biClrImportant = 0;
- // 获取位图数据
- HDC hdc = GetDC(NULL);
- BYTE* pBits = (BYTE*)new BYTE[bi.bmiHeader.biSizeImage];
- ::ZeroMemory(pBits, bi.bmiHeader.biSizeImage);
- if (!::GetDIBits(hdc, hSrcBitmap, 0, bm.bmHeight, pBits, &bi, DIB_RGB_COLORS))
- {
- delete pBits;
- pBits = NULL;
- }
得到数据之后,就可以对于数据进行处理了。由于我们得到的是RGB数据,为了处理方便,定义了一个RGBX结构。灰度算法比较多,这里使用了常用的加权法。
来看一下处理数据的代码:
- // 对位图数据进行处理
- RGBX* pSrc = (RGBX*)pBits;
- RGBX* pDest = (RGBX*)new BYTE[bi.bmiHeader.biSizeImage];
- ::ZeroMemory(pDest, bi.bmiHeader.biSizeImage);
- for (LONG i = 0; i < bm.bmWidth; i++)
- {
- for (LONG j = 0; j < bm.bmHeight; j++)
- {
- RGBX* pRGBSrc = &pSrc[j * bm.bmWidth + i];
- RGBX* pRGBDest = &pDest[j * bm.bmWidth + i];
- *pRGBDest = pRGBSrc->Gray();
- }
- }
将数据处理完之后,我们需要创建一个新的位图并给它设置位图数据,这样,新的位图就生成了。注意SetDIBits参数中指定的位图,必须是兼容位图,所以程序中采用CreateCompatibleBitmap而不是CreateBitmap函数来创建位图。代码如下:
- // 创建新位图,设置位图数据
- HBITMAP hGray = ::CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight);
- ::SetDIBits(hdc, hGray, 0, bm.bmHeight, pDest, &bi, DIB_RGB_COLORS);
附件:
CGrayBitmap类的定义如下:
- #pragma once
- ///
- // CGrayBitmap
- class CGrayBitmap
- {
- ///
- // RGBX
- #pragma pack(push)
- #pragma pack(1)
- typedef struct tagRGBX
- {
- public:
- tagRGBX(BYTE red, BYTE green, BYTE blue)
- {
- btRed = red;
- btBlue = blue;
- btGreen = green;
- btUnused = 0;
- }
- BYTE btBlue;
- BYTE btGreen;
- BYTE btRed;
- protected:
- BYTE btUnused;
- public:
- tagRGBX Gray()
- {
- int r = (int)btRed * 3;
- int g = (int)btGreen * 6;
- int b = (int)btBlue;
- BYTE btGray = (BYTE)((r + g + b) / 10);
- return RGBX(btGray, btGray, btGray);
- }
- }RGBX;
- #pragma pack(pop)
- private:
- CGrayBitmap(){}
- public:
- static HBITMAP DoGray(const HBITMAP hSrcBitmap)
- {
- ASSERT(hSrcBitmap != NULL);
- // 获取对象信息
- BITMAP bm;
- if (!::GetObject(hSrcBitmap, sizeof(BITMAP), (LPBYTE)&bm))
- return NULL;
- // 定义位图信息
- BITMAPINFO bi;
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = bm.bmWidth;
- bi.bmiHeader.biHeight = -bm.bmHeight;
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = 32;
- bi.bmiHeader.biCompression = BI_RGB;
- bi.bmiHeader.biSizeImage = bm.bmWidth * 4 * bm.bmHeight; // 32 bit
- bi.bmiHeader.biClrUsed = 0;
- bi.bmiHeader.biClrImportant = 0;
- // 获取位图数据
- HDC hdc = GetDC(NULL);
- BYTE* pBits = (BYTE*)new BYTE[bi.bmiHeader.biSizeImage];
- ::ZeroMemory(pBits, bi.bmiHeader.biSizeImage);
- if (!::GetDIBits(hdc, hSrcBitmap, 0, bm.bmHeight, pBits, &bi, DIB_RGB_COLORS))
- {
- delete pBits;
- pBits = NULL;
- }
- // 对位图数据进行处理
- RGBX* pSrc = (RGBX*)pBits;
- RGBX* pDest = (RGBX*)new BYTE[bi.bmiHeader.biSizeImage];
- ::ZeroMemory(pDest, bi.bmiHeader.biSizeImage);
- for (LONG i = 0; i < bm.bmWidth; i++)
- {
- for (LONG j = 0; j < bm.bmHeight; j++)
- {
- RGBX* pRGBSrc = &pSrc[j * bm.bmWidth + i];
- RGBX* pRGBDest = &pDest[j * bm.bmWidth + i];
- *pRGBDest = pRGBSrc->Gray();
- }
- }
- // 创建新位图,设置位图数据
- HBITMAP hGray = ::CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight);
- ::SetDIBits(hdc, hGray, 0, bm.bmHeight, pDest, &bi, DIB_RGB_COLORS);
- // 释放资源
- delete[] pBits;
- pBits = NULL;
- delete[] pDest;
- pDest = NULL;
- ::ReleaseDC(NULL,hdc);
- return hGray;
- }
- };