表面上看GetBitmapBits(...) 和 GetObject(...)都可以获取位图句柄中的数据,例如:
第一种方法:
HBITMAP hbitmap = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(32710), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
GetObject(hbitmap, sizeof(BITMAP), &bitmap);
第二种方法:
bitmap.bmWidth = 23;
bitmap.bmHeight = 23;
bitmap.bmBitsPixel = 32;
bitmap.bmWidthBytes = (bitmap.bmWidth * bitmap.bmBitsPixel / 8 + 3) / 4 * 4;; // 按照微软的说法,待存储图像数据每行字节数为4的倍数
hdc = CreateCompatibleDC(hdc);
hbitmap= CreateCompatibleBitmap(hdc, bitmap.bmWidth, bitmap.bmHeight); // 创建与设备描述表兼容的位图
GetObject(hdc, sizeof(BITMAP), &bitmap);
int size = bitmap.bmWidth * bitmap.bmWidthBytes;
unsigned char * pBits = (unsigned char *)malloc(size); //在堆上申请
int nBytes = GetBitmapBits(hbitmap, size, pBits);
----------------------------------------------------------------------
通过比较上面两种方法,发现:
1. 第一种方法,使用起来很简单,而且API已经将数据按照4的倍数对齐,并且保存到bitmap中了,不用我们去处理这些细节,
这种方法适用于位图数据已知的情况下调用。
2. 第二种方法,适用于双缓冲画图的情况下,获取DC的位图内存数据指针,然后直接用两个for循环遍历操作每一个像素。
3. 如果不小心将这两种方法混合使用了,那么悲剧就会不定期上演,产生悲剧的关键原因在于bitmap.bmWidthBytes这个变量
的计算方法,对于4个通道的位图,两种方法都会正常的默契工作。但对于一般的使用LoadImage加载的位图,是3个通道的
位图,GetObject得出的结果是安装标准方法,向上取4的倍数对齐一行像素的,而GetBitmapBits得出的结果就比较悲催了,
至少在win7下面是这样的,比如,上面的例子中,我申请一张23x23的内存位图,安装标准计算方法,可得:
bitmap.bmWidthBytes = (23 * 24 / 8 + 3)/ 4 * 4 = 72 个像素每行
但实际上 int nBytes = GetBitmapBits(hbitmap, size, pBits);的返回值 nBytes = 1610 =23 * 70;即GetBitmapBits计算的结果是
70个像素每行,与标准计算方法得出的72个像素每行,截然不同,够坑爹吧,这几天被微软挖的这个坑坑惨了。
4. bitmap.bmBits的第一个字节对应实际图片中的左下角,这一点也很坑爹,而GetBitmapBits(hbitmap, size, pBits);得到的
pBits恰恰相反,第一个字节对应图片的左上角。看到了吧,老美就喜欢玩双重标准。
5. 使用CreateCompatibleBitmap(...)创建的位图,貌似默认是4个通道的,也即hdc都是4个通道的。
6. 最后一点,也是最重要的是:MSDN上对GetBitmapBits(...)的特别说明是
Note This function is provided only for compatibility with 16-bit versions of Windows. Applications should use theGetDIBits function.
意思就是说GetBitmapBits只提供给16位版本的窗口。其他情况下,应该用GetDIBBits(...)