BMP文件格式
BMP到底是何方神圣?
BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device, 设备无关位图),是Windows系统中广泛使用的图像文件格式。由于它可以不作任何变换 地保存图像像素域的数据,因此成为我们取得RAW数据的重要来源。Windows的图形用户界面(graphical user interfaces)也在它的内建图像子系统GDI中对BMP格式提供了支持。
那么BMP的格式到底什么?
BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
bmp文件头(bmp file header):提供文件的格式、大小等信息
位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压 缩方式、颜色索引等信息
调色板(color palette):可选,如使用索引来表示图像,调色板就是索引 与其对应的颜色的映射表
位图数据(bitmap data):就是图像数据啦^_^
下面结合Windows结构体的定义,通过一个表来分析这四个部分。
我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像我们称为真彩色,这种情况下是不需要调色板的,也就是所位图信息头后面紧跟的就是位图数据了。因此,我们常常见到有这样一种说法:位图文件从文件头开始偏移54个字节就是位图数据了,这其实说的是24或32位图的情况。这也就解释了我们按照这种程序写出来的程序为什么对某些位图文件没用了。
下面针对一幅特定的图像进行分析,来看看在位图文件中这四个数据段的排布以及组成。
我们使用的图像显示如下:
int biSize // 信息头所需的字节数(14-17字节)
int biWidth // 位图的宽(18-21字节)
int biHeight // 位图的高(22-25字节)
int biPlanes // 目标设备的级别,必须是1(26-27字节)
int biBitcount // 每个像素所需的位数(28-29字节),必须是1位(双色)、4位(16色)、8位(256色)或者24位(真彩色)之一。
int biCompression // 位图压缩类型,必须是0(不压缩)(30-33字节)、1(BI_RLEB压缩类型)或2(BI_RLE4压缩类型)之一。
int biSizeImage // 实际位图图像的大小,即整个实际绘制的图像大小(34-37字节)
int biXPelsPerMeter // 位图水平分辨率,每米像素数(38-41字节)这个数是系统默认值
int biYPelsPerMeter // 位图垂直分辨率,每米像素数(42-45字节)这个数是系统默认值
int biClrUsed // 位图实际使用的颜色表中的颜色数(46-49字节),如果为0的话,说明全部使用了
int biClrImportant // 位图显示过程中重要的颜色数(50-53字节),如果为0的话,说明全部重要
咱们再根据解析图来重点说明下biSize、biHeight、biBitCount。
<!--EndFragment-->
<!--EndFragment-->
根据上面我列出的信息可知,
在信息头的第一个信息就是biSize,即这个信息头所占的总共的字节空间为40个。
那么我们再结合上面所说的文件头的总字节空间为14个,那么我们相加一下是不是就是54呢!那么偏移量bfOffBits 就是54咯!不过你可能就会问了,为什么这样就是54了?怎么来的?是不是所有的bmp文件的偏移量bfOffBits 都是54呢?呵呵,不是的,不是所有的bmp文件的偏移量bfOffBits 都是54,对于24位的位图来说是的,对于其他的bmp文件就不一定了!这个偏移量很重要,大家在读取和编写的时候一定要注意它的准确性。
咱们再返回上面biSize,根据前面文件头是占14个字节,那么biSize的地址就是第14-17字节,那么对应上图,那么也就是28 00 00 00.而根据我们上面所说的机器的存储是小端存储,所以正确的读数是00 00 00 28。
而在读写后面的位图数据的时候,一定要注意,这个height到底是正数还是负数。如果是正数,那么位图数据读写的时候顺序就是从左到右,从下到上。否则就是从左到右,从上到下。因为我们这个图中height是正数,所以下面讨论位图数据的时候,就按照从左到右,从下到上的顺序来描述了。
咱们看完了bmp文件的第二块信息头,接下来的第三块颜色表就要根据第二块信息头来了,什么意思?因为如果bmp文件是24位或者32位的图像,那么就没有颜色表这一块,只有当位数少于24位的时候,才需要考虑这一块。
三.颜色表
颜色表其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。它在文件中的布局就像一个二维数组palette[N][4],其中N表示总的颜色索引数,每行的四个元素分别表示该索引对应的B、G、R和Alpha的值,每个分量占一个字节。如不设透明通道时,Alpha为0。具体的信息,大家可以google或者度娘一下。
四.位图数据
好了,接下来就是我们最重要的一块了,写入或者读取bmp文件的实际数据了,也就是我们画的图像的每个像素。我们知道一个像素由三个int值来构成,即RGB,红色分量,绿色分量,蓝色分量。而一个像素在在内存中是占三个字节的,即每个分量占据一个字节。那么我们就需要得到每个像素的分量,然后按小端存储的方式按BGR顺序存入内存中。是否这样就可以了?No,No,No。不是的,因为我们必须还要注意Windows默认的扫描的最小单位是4字节,如果数据对齐满足这个值的话对于数据的获取速度等都是有很大的增益的。因此,BMP图像顺应了这个要求,要求每行的数据的长度必须是4的倍数,如果不够需要进行比特填充(以0填充),这样可以达到按行的快速存取。这时,位图数据区的大小就未必是 图片宽×每像素字节数×图片高能表示的了,因为每行可能还需要进行比特填充。