浅析BMP位图文件结构(含Demo)
作者:一点一滴的Beer http://beer.cnblogs.com/
关于BMP位图格式在网上可以找到比较详细的相关文档,有兴趣的可以搜索标题为“BMP文件结构的探索”的文章,可以在搜索结果中找到一个WORD文档,里面有很详细的介绍。很感谢这个文档的作者(ID是WhatIf),总结得很详细而且还附有详细的应用代码(文档我会放在本文最后面的附件部分)。因为文档中写得很详细,所以我在此就结合自己写的程序示例来介绍下位图的主要结构,用兴趣的可以将附件文件下载下来,结合本节给的相关测试代码进行学习和研究。下面直接引用其描述:
Bmp文件是非常常用的位图文件,无论是游戏还是其他都被广泛使用。针对bmp文件的处理也有一堆现成的api进行调用,然而文件内部究竟怎样,如何自己来解析这样的文件呢?为了消除无聊,我用了几天时间来研究了一下,同时作为学习笔记,进行记录。
首先,整个bmp文件的内容可以分为3到4块。之所以分为3到4块而不是固定的值,是因为,对于bmp来说可能存在调色板或者一些掩码。具体稍候讨论。
第一块是bmp的文件头用于描述整个bmp文件的情况。结构如下:
typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, *PBITMAPFILEHEADER; |
这些信息相当有用,如果你想直接来解析bmp文件。第一个bfType用于表示文件类型,如果它是bmp文件,那么它这个位置的值一定是”BM” 也就是0x4D42。第二个bfSize表示整个文件的字节数。第三第四个 则保留,目前无意义,最后一个相当重要,表示,位图的数据信息离文件头的偏移量,以字节为单位。
第二块是位图信息头,即BITMAPINFOHEADER,用于描述整个位图文件的情况。以下挑重要的数据进行解释
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //表示本结构的大小 LONG biWidth; //位图的宽度 LONG biHeight; //位图的高度 WORD biPlanes; //永远为1 ,由于没有用过所以 没做研究 附msdn解释 //Specifies the number of planes for the target device. This value must be set to 1. WORD biBitCount; //位图的位数 分为1 4 8 16 24 32 本文没对1 4 进行研究 DWORD biCompression; //本以为压缩类型,但是却另外有作用,稍候解释 DWORD biSizeImage; //表示位图数据区域的大小以字节为单位 LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER; |
第三块就是调色板信息或者掩码部分,如果是8位位图 则存放调色板 ;16 与32位位图则存放RGB颜色的掩码,这些掩码以DWORD大小来存放。
最后一块就是位图的数据实体。
通过上面网友的一段描述,可能大家和我一样,只是大概了解到位图作为一个文件的存在肯定不仅仅只是对每个像素进行简单地储存的,肯定会有一些附加的信息的。但是单单根据上面的描述,还是不够形象,所以,就用一个简单的例子来实验,帮助了解位图的结构。 |
实验:准备一张编码格式为RGB565的位图文件,分辨率是320*240。然后用VS2005建立C++程序对文件进行读取和分析。
开始实验:
在Windows资源管理器中,把鼠标悬停在位图上方,可以看到系统提示的一些位图信息“尺寸:320×40 大小:151K”。这个是通过如下计算方式得到的:位图文件除去位图数据实体外的三个部分的大小在同一文件系统下都是一样的,都为14+46+2*3=66字节(自己可以查看这些结构体数据的详细定义然后自己验证),位图编码为RGB565,也就是说每个彩色像素点是用的2个字节表示,R/G/B三个分量分别用了5/6/5位表示,5+5+6为16,刚好是两个字节,所以位图数据实体大小为320*240*2/1024=150K,再加上前面的66字节,整个位图的大小就是150K到151K之间了,而根据Windows系统的磁盘分块存储原理(最后一块数据即使比单位存储块1K要小,也会占用一个1K的存储块),所以此文件实际占用的空间为151K。此位图的结构示意如下图:
用VS2005建立C++项目,使用如下的测试代码:
void ReadBmpDemo() { CFile hFile; hFile.Open(L "recvBmpData565.bmp" ,CFile::modeRead); //以打开的形式读取文件 //******************************************* //***------Step 1:读取文件信息头---------**** //******************************************* BITMAPFILEHEADER *pFileHead = NULL; // 位图文件的头指针 DWORD dwFileHeadSize = sizeof (BITMAPFILEHEADER); // 位图文件的头区域大小 pFileHead=(BITMAPFILEHEADER*) malloc (dwFileHeadSize); //申请一片dwFileHeadSize字节大小的内存区 hFile.Read(pFileHead,dwFileHeadSize); //从图片的文件当前位置读取一片内容:文件信息头 //******************************************* //***------Step 2:读取位图信息头---------**** //******************************************* BITMAPINFOHEADER *pBmpInfoHeader=NULL; DWORD dwBmpInfoHeadSize= sizeof (BITMAPINFOHEADER); pBmpInfoHeader=(BITMAPINFOHEADER*) malloc (dwBmpInfoHeadSize); //位图信息头 hFile.Read(pBmpInfoHeader,dwBmpInfoHeadSize); //读入第二块数据块 pBmpInfoHeader->biCompression = BI_RGB; //RGB555格式---修改压缩信息 //***************************************************** //***------Step 3:读取调色板信息或掩码部分---------**** //***************************************************** RGBQUAD *pRgbQuad=NULL; DWORD dwRgbQuadSize=3* sizeof (RGBQUAD); //本次实验的RGB565位图有三块掩码. pRgbQuad=(RGBQUAD*) malloc (dwRgbQuadSize); hFile.Read(pRgbQuad,dwRgbQuadSize); //***************************************************** //***------Step 4:读取位图的数据实体---------********** //***************************************************** BYTE *pBmpData=NULL; //DWORD dwBmpDataSize=2*(pBmpInfoHeader->biWidth)*(pBmpInfoHeader->biHeight);//因为高度是负的,所以暂时不用此方法 DWORD dwBmpDataSize=(pFileHead->bfSize) - (pFileHead->bfOffBits); //文件头中的文件大小和数据实体偏移量之间的差 pBmpData=( BYTE *) malloc (dwBmpDataSize); //申请一片指定字节大小的内存区. hFile.Read(pBmpData,dwBmpDataSize); hFile.Close(); //************************************************************** //***------Step 5:将RGB565的数据实体转成RGB555---------********* //************************************************************** //因为不涉及到本部分内容,所以没有实现 //******************************************* //***------Step Final:释放动态内存区---------**** //******************************************* free (pFileHead); pFileHead=NULL; free (pBmpInfoHeader); pBmpInfoHeader=NULL; free (pRgbQuad); pRgbQuad=NULL; free (pBmpData); pBmpData=NULL; } |
将准备的位图文件素材放到此项目的根目录下。然后断点调试,看运行的中间结果。
位图文件头:从这里面了解到位图文件相关信息,文件类型为19778(即位图的文件类型编号:0x4D42),文件大小为153666,位图数据实体偏移文件头部66。
位图信息头:从这个数据我们可以看到这个位图的相关信息,图片宽度为320,高度为240(当biHeight>0的时候位图是倒置的,它小于0的时候正常),位图的像素存储长度是16位(也就是RGB565的编码方式单像素点占用的长度),图片压缩类型为3(用来指示位图的编码方式是RGB565还是RGB555的,详细介绍可以查看WahtIf写的那个文章“BMP文件结构的探索”中的示例代码),位图数据实体大小为153600。
位图的调色板或掩码部分:对于RGB565的位图,这里面是三个颜色分量的掩码,这个直接关系到此位图文件在Windows下的显示的效果。这三个数据在对位图进行颜色分量提取的时候有比较大的作用,在WahtIf写的那个文章“BMP文件结构的探索”中有示例代码。
位图文件的数据实体部分:这里面以字节为单位存储着位图的每个个像素点的色彩信息,也是位图文件中数据的主体部分。此部分的数据长度在位图信息头中可以得到。
总结:在了解了位图文件的存储结构后,后面对位图的一切操作和变换都是围绕着这些数据来进行的,到时候不会再看着一张数字图片而茫然不知所措了,而是可以根据自己的需求提取自己想要的信息了。
附件示例:
示例C++项目和RGB565图片素材一张:
WinXpBmpDemo.rar :https://files.cnblogs.com/beer/WinXpBmpDemo.rar
参考文章:
《BMP文件结构的探索》
http://blog.csdn.net/hollysky/archive/2005/04/12/345086.aspx
------------------------------------------------------------------
Author:一点一滴的Beer
Email /Gtalk:dreamzsm@gmail.com
From:http://www.cnblogs.com/beer
Notes:欢迎转贴,但请在页面中加个链接注明出处
Time:2010-11-21 in Whu