Windows位图和DIBSection之间有什么区别?

5

我正在使用以下方式从文件加载DIBSection:

HBITMAP bmpIn = (HBITMAP) LoadImage(NULL, _T("c:\\Temp\\Temp.bmp"), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);

根据实际经验,我发现以下是加载位图和我过去使用的位图之间存在的差异,但我找不到任何文档说明应该有什么区别。

  • 行在内存中从上到下排序,而不是从下到上。我已经验证了.bmp文件本身是自下而上排序的。
  • 行填充是2字节的倍数,而不是4字节。

我还发现,当您使用CreateDIBSection从头创建DIBSection时,存在已记录的差异。

  • GetObject返回的DIBSECTION.dsHandle和BITMAP.bmBits值将为NULL。

这两个差异的文档在哪里?我有什么遗漏吗?这是在Windows 7上,但我想象其他版本的Windows也不会有所不同。

编辑:一些额外的细节。这是temp.bmp的十六进制转储;它是一个7x7的图像,右侧有白色条纹,左侧有增加的蓝色值(0x10、0x20等)。您可以看到底部行(00,00,70)是第一行,并且有3个字节的填充。

00: 42 4d de 00 00 00 00 00 00 00 36 00 00 00 28 00
10: 00 00 07 00 00 00 07 00 00 00 01 00 18 00 00 00
20: 00 00 a8 00 00 00 00 00 00 00 00 00 00 00 00 00
30: 00 00 00 00 00 00 70 00 00 00 00 00 00 00 00 00
40: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 60 00
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
60: ff ff ff 00 00 00 50 00 00 00 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 40 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: ff ff ff 00 00 00 30 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00 20 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: ff ff ff 00 00 00 10 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 ff ff ff 00 00 00

这里是一个读取 .bmp 文件并输出内容的示例程序。出于简洁起见,我已删除错误检查。
int _tmain(int argc, _TCHAR* argv[])
{
   HBITMAP bmpIn = (HBITMAP) LoadImage(NULL, argv[1], IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
   FILE * out = _tfopen(argv[2], _T("wb"));
   DIBSECTION obj = {0};
   GetObject(bmpIn, sizeof(obj), &obj);
   cout << "dsBm.bmHeight = " << obj.dsBm.bmHeight << endl;
   cout << "dsBmih.biHeight = " << obj.dsBmih.biHeight << endl;
   cout << "sizeof(DIBSECTION) = " << sizeof(DIBSECTION) << endl;
   fwrite(&obj, sizeof(DIBSECTION), 1, out);
   int stride = (((obj.dsBmih.biWidth * obj.dsBmih.biBitCount) + 15) / 16) * 2;
   int bytecount = abs(obj.dsBmih.biHeight) * stride;
   vector<BYTE> bits(bytecount);
   GetBitmapBits(bmpIn, bytecount, &bits[0]);
   fwrite(&bits[0], 1, bytecount, out);
   fclose(out);
   return 0;
}

以下是上述程序的输出,以及所生成文件的十六进制转储:
dsBm.bmHeight = 7
dsBmih.biHeight = 7
sizeof(DIBSECTION) = 84
00: 00 00 00 00 07 00 00 00 07 00 00 00 18 00 00 00
10: 01 00 18 00 00 00 11 00 28 00 00 00 07 00 00 00
20: 07 00 00 00 01 00 18 00 00 00 00 00 a8 00 00 00
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00
60: 00 00 00 00 00 00 ff ff ff 00 20 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff 00
80: 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 00 00 ff ff ff 00 40 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 ff ff ff 00 50 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff
c0: ff 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 ff ff ff 00 70 00 00 00 00 00 00 00
e0: 00 00 00 00 00 00 00 00 00 00 ff ff ff 00

请查看此链接以获取更多信息:http://msdn.microsoft.com/en-us/library/dd183382(v=vs.85).aspx - Nerdtron
@Nerdtron,那个链接直接与我的观察相矛盾 - 它宣称自上而下的DIB将具有负高度,但在这种情况下并不是这样。 - Mark Ransom
1
GetBitmapBits的文档(在此处:http://msdn.microsoft.com/en-us/library/dd144850(v=vs.85).aspx)指出它“将指定设备相关位图的位复制到缓冲区中”。在这里,您已经加载了一个设备无关位图。因此,可能会发生转换,因为GetBitmapBits的调用者期望得到一些自上而下的东西。我只是猜测,因为我总是调用GetDIBits。请注意,我链接的文档说您不应该调用它,而应该调用GetDIBits。我敢打赌,如果您改为调用GetDIBits,您会得到您所期望的结果。 - Nerdtron
@Nerdtron,GetDIBits是将DDB转换为DIB的函数。它需要一个HDC来进行转换,但在我的情况下,我没有HDC——我从文件中读取数据并写回到文件中,从未将结果传递给GDI。 - Mark Ransom
你正在加载一个DIB并调用GetObject来获取有关该DIB的信息,但然后你又调用了一个API,该API说它从DDB中复制。因此它们可能不会匹配。将GetObject返回的DIB信息与GetBitmapBits返回的DIB信息进行比较可能是不恰当的。我建议尝试调用GetDIBits。由于你已经将位图作为DIB加载并拥有其句柄,我猜想实际上没有进行任何转换。你可能只需传递屏幕DC即可。 - Nerdtron
显示剩余4条评论
1个回答

3

使用GetDIBits而不是GetBitmapBits。 GetBitmapBits的文档(此处)指出,它返回的是设备依赖位图的数据,而您有一个设备无关位图。 它们还指出,这个调用不应该被使用,只是为了16位兼容性而存在。 因此,使用GetDIBits就可以解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接