BMP文件解析

BMP文件通常是不压缩的,所以它们通常比同一幅图像的压缩图像文件格式要大很多。例如,一个800×600的24位几乎占据1.4MB空间。因此它们通常不适合在因特网或者其他低速或者有容量限制的媒介上进行传输。

根据颜色深度的不同,图像上的一个像素可以用一个或者多个字节表示,它由n/8所确定(n是位深度,1字节包含8个数据位)。图片浏览器等基于字节的ASCII值计算像素的颜色,然后从调色板中读出相应的值。更为详细的信息请参阅下面关于位图文件的部分。

n位2n种颜色的包含调色板的位图近似字节数可以用下面的公式计算:

BMP文件大小,其中高度(height)和宽度(width)都以像素为单位。

需要注意的是上面公式中的54是位图文件的文件头,4*2(^n)是彩色调色板的大小。 如果位图文件不包含调色板,如24位,32位位图,则位图的近似字节数可以用下面的公式计算:

BMP文件大小,其中高度(height)和宽度(width)都以像素为单位。

另外需要注意的是这是一个近似值,对于n位的位图图像来说,尽管可能有最多2(^n)种颜色,一个特定的图像可能并不会使用这些所有的颜色。由于彩色调色板仅仅定义了图像所用的颜色,所以实际的彩色调色板将小于4*2(^n)。

如果想知道这些值是如何得到的,请参考下面文件格式的部分。

由于存储算法本身决定的因素,根据几个图像参数的不同计算出的大小与实际的文件大小将会有一些细小的差别。


BMP文件如果像素色深是8位及以下,那么会采用调色板形成索引位图,否则不会采用调色板


1.BMP的头文件[必须】:

typedef struct tagBITMAPFILEHEADER {

        WORD    bfType; // 42 4D是windows BMP文件标识,其它的文件还有其它的标识

        DWORD   bfSize; // 整个文件的大小

        WORD    bfReserved1; // 保留位不使用,为0

        WORD    bfReserved2;  // 保留位不使用,为0

        DWORD   bfOffBits; // 位图数据文件,在整个文件中的偏移(跳过BMP头,位图头,调色板(如果有))

} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;


2.BMP位图信息头说明文[必须]

typedef struct tagBITMAPINFOHEADER{

        DWORD      biSize; // 信息头的大小

        LONG       biWidth; // 图像数据宽度,单位是像素

        LONG       biHeight; // 图像数据高度,单位是像素,且如果是正数那么是倒向的位图,也就是2D屏幕坐标系翻转类似物体坐标系(y轴由下到上,位图数据也由左下角一行行的存放),如果是负数,那么图像是正向的,也就是2D屏幕坐标系(y轴由上到下,位图数据也由左上角一行行存放).

        WORD       biPlanes; // 颜色平面数,总是1

        WORD       biBitCount; // 每个像素的比特数,一般是8,16,24,32;8的话一般包含调色板,其它位数多的,调色板太大了,一般不用。

        DWORD      biCompression; // 0不压缩;1,2比特游程编码就是霍夫曼编码上的值+重复数标记; 3是比特域编码减少字节的使用,4是位图包含JPEG图像用于打印,5是位图包含PNG图像用于打印。

        DWORD      biSizeImage; // 图像数据的字节数大小。

        LONG       biXPelsPerMeter; // 水平分辨率,像素/米,给打印参考或显示缩放图片兼容用的,具体屏幕上的显示大小会根据像素数/屏幕像素密度得到。

        LONG       biYPelsPerMeter; // 垂直分辨率,像素/米,给打印参考或显示缩放图片兼容用的,具体屏幕上的显示大小会根据像素数/屏幕像素密度得到。

        DWORD      biClrUsed; // 说明位图实际使用的调色板中的颜色索引数如果是8位一像素那么这里是256;为0采用所用调色板中的索引或者没有调色板。

        DWORD      biClrImportant; // 调色板中对图像显示有重要影响的颜色索引数如果是8位一像素那么这里是256;为0都重要,或者没有调色板。

} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

3.调色板[可选]
紧跟在位图信息头后面,是一个palette[N][4]模型的数据结构,行是N例如256;包含四列是一个unsigned int类型的整型ARGB,ARGB在内存中的存储序列为:BGRA,每列代表了一个颜色分量,alpha没有则是0。位图数据就是用一个Byte标记调色板中的0~N-1的索引
BMP文件头14字节,位图信息头40字节,如果有调色板256的那么是1024字节,没有调色板那么是54个字节偏移为位图数据了,有调色板那么是1078个字节才是位图信息数据。

4.位图数据(是索引或者像素值) [必须]

位图图片像素数据真正存放的地方。    

8位BMP,每个位图存放的是调色板中的索引,如果像素高度是正数那么是物体坐标系,位图数据由左下角一行行往上存放

    如果是非调色板真彩色像素,那么存放的就是颜色值,而不是索引,像素高度是正数那么是物体坐标系由左下角开始,像素高度是负数那么是屏幕物体坐标系由左上角开始。24RGB按照BGR的顺序来存储每个像素的各颜色通道的值,一个像素的所有颜色分量值都存完后才存下一个下一个像素,不进行交织存储。32位数据按照BGRA的顺序存储,其余与24位位图的方式一样

当高度为正数确实是物体坐标系形式,无论是内存中还是二进制文件中,像素由图像的左下角一行行往上存放;当高度为负数那么是屏幕坐标系,像素是由图像左上角一行行往下存放;如果一行不足4字节的倍数,那么填充00使得满足4字节的倍数


5.window下的按4Byte对齐规则 (索引或者像素值都要遵守)[读取规则]
Windows默认的扫描的最小单位是4字节(内存中存放位图数据的行对齐,存放到二进制文件也是如此),如果数据对齐满足这个值的话对于数据的获取速度等都是有很大的增益的。因此,BMP图像顺应了这个要求,要求每行的数据的长度必须是4的倍数,如果不够需要进行比特填充(以0填充),这样可以达到按行的快速存取。这时,位图数据区的大小就未必是 图片宽×每像素字节数×图片高 能表示的了,因为每行可能还需要进行比特填充。

这个填充后的RowSize可以用: ((bmiHeader.biWidth * bitCount + 31) >> 5) <<2来表示。
也可以用:(((bmiHeader.biWidth * bitCount + 31) & ~31) >> 3)表示。
每行跳过的字节数为:
 int alignBytes = (((bmiHeader.biWidth * bitCount + 31) & ~31) >> 3)
  - ((bmiHeader.biWidth * bitCount) >> 3);

读取代码:
//原图像每一行去除偏移量的字节数
 int lineSize = bitCount * srcW >> 3;
 //偏移量,windows系统要求每个扫描行按四字节对齐
 // 数n加上一个数r-1,又与上非r-1,其实是求得数n加上足够的偏移后[n, n+r-1]内的关于r的唯一倍数k。
 // 数k是数n不经过填充或者经过最小填充后的是r的倍数。
 // alignBytes是不用填充或者填充后的,相对于原来的数,填充的字节数。
 int alignBytes = (((bmiHeader.biWidth * bitCount + 31) & ~31) >> 3)
  - ((bmiHeader.biWidth * bitCount) >> 3);
 //原图像缓存    
 int srcBufSize = lineSize * srcH;
 BYTE* srcBuf = new BYTE[srcBufSize];
 int i,j;
 //读取文件中数据
 for (i = 0; i < srcH; i++)
 {        
  fread(&srcBuf[lineSize * i],lineSize,1,pFile); // 读得原来图像位图数据
  fseek(pFile,alignBytes,SEEK_CUR); // 跳过对齐字节数
 }

参考文章:

http://blog.csdn.net/o_sun_o/article/details/8351037


你可能感兴趣的:(游戏数学和算法,图形图像美术相关)