VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 785|回复: 3

[原创] LoadImage 加载 Bmp 文件失败之我的理解!

[复制链接]
Syc 发表于 2017-8-30 22:52:22 | 显示全部楼层 |阅读模式
前几天看到有位网友在 VC驿站 发了一篇求助帖子,链接在这里:http://www.cctry.com/thread-278805-1-1.html
大概的意思就是调用 LoadImage 加载磁盘上的一个 bmp 类型的图片时,加载失败,显示不出来。当时网上也找了一些资料回复了楼主。
过了几天,总感觉不对劲,虽说帖子楼主的代码有点小问题,没有加 LR_LOADFROMFILE 标志,但是问题的关键不在这。于是今天又深入的研究了下。

手头没有BMP格式的图片,我就用QQ自带的截图工具截了一张图,保存为 bmp 格式的,之后在 vs2013 中加上一句代码:
  1. HBITMAP hbitmap = (HBITMAP)::LoadImage(NULL, "C:\\Users\\Syc\\Desktop\\qq.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_LOADFROMFILE);
复制代码


嘿。。。不出所料,果然,返回值 hbitmap 为 NULL,问题出现了,qq.bmp 的图片路径肯定是没问题的。有问题咱们就解决问题,于是我在调用完 LoadImage 之后接着调用 GetLastError 想看看错误码是多少,奇怪的是 GetLastError 返回 0,意思就是没有错误。奇怪了。。。
到底是什么问题呢?

网上东找找西找找,在 stackoverflow.com 上面也有个别的几个人遇到相同的问题了。也是 LoadImage 返回 NULL,但是 GetLastError 返回 0。有个人回答的结果是说这种情况是由于 bmp 文件的格式有问题。LoadImage 解析失败了,所以返回NULL。但是这个 qq.bmp 的图片我用 Windodws 7 系统自带的照片查看器双击打开图片也能正常显示啊,貌似格式没有啊。那能是什么原因呢?后来在 https://stackoverflow.com/questi ... saved-with-ms-paint 这篇帖子上看到作者与我的问题一样。但是之后他用 Windows 系统自带的画图工具 mspaint 打开这个bmp图片,之后另存为 24位位图的BMP格式,之后,再用 LoadImage 打开就成功的返回句柄了。我试了一下确实是这么回事,我这边也可以。但是为什么呢?mspaint 到底改了什么?

帖子上那个人的回答是这样的:

There are lots of variations of BMP format. It's quite possible that the original image was in a variant that LoadImage cannot directly convert to a DIB. So you converted it in Paint to a BMP variant that it could open.

In particular, there are variants where raw PNG of JPG data can be packed into a BMP container. This is typically used to pass the compressed image data directly to a printer that can decompress itself. (In my experience, only a few printers actually support this.) I don't think the GDI API can actually do much else with BMPs of this type. Paint, on the other hand, has codecs for PNG and JPG, so I'd expect its repertoire might include those formats, even when they're packed in a BMP header.

意思是 BMP 格式有很多变种,有的是压缩的,有的是将 PNG 或者 JPG 格式的图片包含在 BMP 的内部,这样的话 LoadImage 就显示不出来了。我大概想了想可能是这样吧。可后来又感觉不对劲。既然BMP文件的格式是公开的,那么我也检查下看看。

QQ截图之后的BMP图片名字是:qq.bmp,之后我用 mspaint 将 qq.bmp 另存为 24位位图的 ok.bmp

我分别对这两个文件按照 bmp 的文件格式将文件头读出来。关于 bmp 的文件格式,大家网上找下,有很多说明的,bmp文件的第一个文件头结构是:BITMAPFILEHEADER,占用 14 个字节,第二个文件头结构是:BITMAPINFOHEADER,占用 40 个字节。

之后,我用如下代码分别对 qq.bmp 和 ok.bmp 两个文件读取一下文件头:
  1. BITMAPFILEHEADER bitmap_file_header = { 0 };
  2. int file_header_len = sizeof(BITMAPFILEHEADER);

  3. BITMAPINFOHEADER bitmap_info_header = { 0 };
  4. int file_info_len = sizeof(BITMAPINFOHEADER);

  5. CFile mFile;
  6. BOOL bRet = mFile.Open(strImgFile, CFile::modeReadWrite);
  7. bRet = mFile.Read(&bitmap_file_header, file_header_len);
  8. bRet = mFile.Read(&bitmap_info_header, file_info_len);

  9. mFile.Close();
复制代码


对比结果如下:


看到了吧,bitmap_info_header 的 biHeight 成员,qq.bmp 的是 -275,ok.bmp 的是正的 275。差别就在这。

于是到网上找关于 biHeight 是负数的情况。找到的资料如下:

图像的高度可以为负值。负数值表示接下来的像素内容将从左上角从左到右、从上到下读取;而正数值表示接下来的像素内容将从右下角从左到右、从下到上读取。位图信息头BITMAPINFOHEADER中的 biHeight 不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:


位图信息头BITMAPINFOHEADER中的biHeight不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:


位图信息头BITMAPINFOHEADER中的biHeight不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:


此类情况模式常用的场合是:用户自己产生一幅图像,譬如在数据采集系统中常常需要将数据进行图像显示。这时用户需要开辟一块内存并按一定方式填充该内存。由于顺序存储方式对用户来说更加直观,操作更加方便,所有常使用第二种模式。这时,在显示和保存位图时将BITMAPINFOHEADER中的biHeight设为负数即可。

这回明白了吧。看来还是 LoadImage 支持的不够好啊。。。好了原因找到了,解决方案也就有了,大家可以自己写一个转换函数,将 Top-Down 与 Bottom-Up 相互转换。如果嫌麻烦的话可以直接使用其他的一些现成的类,比如 ATL 中的 CImage,或者开源的 CxImage 来加载任意格式的图片文件。他们都能无缝对接 BITMAP 对象。方便在 VC 中使用啊。好了,就说到这里吧。有问题跟帖,大家一起讨论。


参考文档:

关于BMP
http://blog.csdn.net/u010839382/article/details/51576335

Top-Down vs. Bottom-Up DIBs
https://msdn.microsoft.com/en-us ... 07212(v=vs.85).aspx

LoadImage() with QRCode bitmap failing unless file is opened/saved with MS Paint first
https://stackoverflow.com/questi ... saved-with-ms-paint

BITMAPINFOHEADER structure
https://msdn.microsoft.com/en-us ... 18229(v=vs.85).aspx

BMP文件格式详解(BMP file format)
https://www.cnblogs.com/Matrix_Y ... /12/02/1615295.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入驿站

x

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

softcheng 发表于 2017-8-31 11:46:04 | 显示全部楼层
这说明什么,说明 windows API 就只能搞自己工具的图片,QQ 截图的不标准么?   还是 API 本身就限制, 这种问题,在实际中,鬼知道怎么解决,变态

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

softcheng 发表于 2017-8-31 11:48:08 | 显示全部楼层
那下次load bmp 的时候,先查查头和其他信息对不对  ,有的bmp不标准 不是BM 头

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

戴钰一族 发表于 4 天前 | 显示全部楼层
微软很软!

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入驿站

本版积分规则

展开

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号 )

返回顶部
x

VC驿站微信公众号cctry2009

GMT+8, 2017-10-21 23:36

Powered by Discuz!

© 2009-2017 cctry.com

快速回复 返回顶部 返回列表