BMP文件格式解析

2016到了,大家新年好,新的东西,福利来了

bmp文件结构解析:

一个bmp图片最多由4大部分组成:BITMAPFILEHEADER结构体,BITMAPINFOHEADER结构体,RGBQUAD结构体(这个结构体可以有,也可以没有),DIB数据区。其中DIB意思就是Device-Independent Bitmap(设备无关位图)。

一个bmp文件以BITMAPFILEHEADER结构体开始,

typedef struct tagBITMAPFILEHEADER {

WORD bfType;//固定为0x4d42;

    DWORD bfSize; //文件大小

    WORD bfReserved1; //保留字,不考虑

    WORD bfReserved2; //保留字,同上

    DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和

    } BITMAPFILEHEADER;

BITMAPFILEHEADER的第1个属性是bfType(2字节),这里恒定等于0x4D42。由于内存中的数据排列高位在左,低位在右,所以内存中从左往右看就显示成(42 4D),所以头两个 字节显示为(42 4D)就是这样形成的,以后的数据都是这个特点,不再作重复说明。

BITMAPFILEHEADER的第2个属性是bfSize(4字节),表示整个bmp文件的大小。

BITMAPFILEHEADER的第3个、第4个属性分别是bfReserved1、bfReserved2(各2字节),这里是2个保留属性,都为0,这里等于&H0000、0x0000。

BITMAPFILEHEADER的第5个属性是bfOffBits(4字节),表示DIB数据区在bmp文件中的位置偏移量,比如等于0x00000076=118,表示数据区从文件开始往后数的118字节开始。

BITMAPFILEHEADER结构体这里就讲完了,大家会发现BITMAPFILEHEADER只占了bmp文件开始的14字节长度,但需要 特别说明的是:我们在编程时,经常是以二进制的形式打开一个bmp文件,然后将其开头的14个字节读入自己定义的BITMAPFILEHEADER结构体中,如果按上述定义结构体的方式,需要注意,这个自己定义的结构体在内存中由于字节对齐,会占用16字节的空间,因而直接读入16字节,按字节顺序赋值给结构体,出来的数据是错误的,这样的话,可以先将这14字节的数据读入到一个缓冲器中,然后从缓冲器中按字节对结构体进行赋值。详细程序见后附录。鉴于此中原因,在vb中定义一个BITMAPFILEHEADER结构体变量,其长度占了16个字节,原因就是第1个属性本来应该只分配2个字节,但实际被 分配了4个字节,多出来2个字节,所以如果想保存一张bmp图片,写入BITMAPFILEHEADER结构体时一定要注意这一点。

接下来是BITMAPINFO结构体部分。BITMAPINFO段由两部分组成:BITMAPINFOHEADER结构体和RGBQUAD结构 体。其中RGBQUAD结构体表示图片的颜色信息,有些时候可以省略,一般的24位图片和32位图片都不带RGBQUAD结构体,因为DIB数据区直接表 示的RGB值,一般4位图片和8位图片才带有RGBQUAD结构体。(多少位的图片就是用多少位来表示一个颜色信息,例如4位图片表示用4个bit来表示一个颜色信息。)一个bmp文件中有没有RGBQUAD结构体,可以根据前面BITMAPFILEHEADER结构体的第5个属性bfOffBits来判 断,因为BITMAPINFOHEADER结构体长度为40bit,如果BITMAPINFOHEADER结构体结束后还未到DIB数据区的偏移量,就说明接下来的数据是RGBQUAD结构体部分。

下面进入正题BITMAPINFOHEADER部分。

typedef struct tagBITMAPINFOHEADER{

//public:

DWORD biSize; //指定此结构体的长度,为40

LONG biWidth; //位图宽

LONG biHeight; //位图高

WORD biPlanes; //平面数,为1

WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32

DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩

DWORD biSizeImage; //实际位图数据占用的字节数

LONG biXPelsPerMeter; //X方向分辨率

LONG biYPelsPerMeter; //Y方向分辨率

DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)

DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的

} BITMAPINFOHEADER;

 

BITMAPINFOHEADER的第1个属性是biSize(4字节),表示BITMAPINFOHEADER结构体的长度,最常见的长度是40字节。

BITMAPINFOHEADER的第2个属性是biWidth(4字节),表示bmp图片的宽度

BITMAPINFOHEADER的第3个属性是biHeight(4字节),表示bmp图片的高度

BITMAPINFOHEADER的第4个属性是biPlanes(2字节),表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1,这里等于0x0001。

BITMAPINFOHEADER的第5个属性是biBitCount(2字节),表示bmp图片的颜色位数,即1位图(单色或二值图像),8位图,16位图,24位图、32位图等等。

BITMAPINFOHEADER的第6个属性是biCompression(4字节),表示图片的压缩属性,bmp图片是不压缩的,等于0,所以这里为0x00000000。

BITMAPINFOHEADER的第7个属性是biSizeImage(4字节),表示bmp图片数据区的大小,当上一个数值

biCompression等于0时,这里的值可以省略不填,所以这里等于0x00000000。

BITMAPINFOHEADER的第8个属性是biXPelsPerMeter(4字节),表示图片X轴每米多少像素,可省略,这里等于0x00000EC3=3779像素/米。

BITMAPINFOHEADER的第9个属性是biYPelsPerMeter(4字节),表示图片Y轴每米多少像素,可省略,这里等于0x00000EC3=3779像素/米。

BITMAPINFOHEADER的第10个属性是biClrUsed(4字节),表示使用了多少个颜色索引表,一般biBitCount

属性小于16才会用到,等于0时表示有2^biBitCount个颜色索引表,所以这里仍等于0x00000000。

BITMAPINFOHEADER的第11个属性是biClrImportant(4字节),表示有多少个重要的颜色,等于0时表示所有颜色都很重要,所以这里等于0x00000000。

至此BITMAPINFOHEADER结构体结束。

由于这个图片到这里还未到达DIB数据区的偏移量,或者说由于BITMAPINFOHEADER的第5个属性是biBitCount<16,也就是在1位图(只有两种颜色),4位图(只有2^4=16种颜色),8位图(只有2^8=256种颜色)的情况下,此时会有颜色表,也就是接下来的部分:RGBQUAD结构体。

//调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。

//(调色板结构体个数等于使用的颜色数,即是多少色图就有多少个,4位图16色,就有16个RGBQUAD结构体。)

typedef struct tagRGBQUAD {

//public:

BYTE rgbBlue; //该颜色的蓝色分量

BYTE rgbGreen; //该颜色的绿色分量

BYTE rgbRed; //该颜色的红色分量

BYTE rgbReserved; //保留值

} RGBQUAD;

 

看看具体的bmp格式:

以faya.bmp为例 4位共16色bmp图片:大小31930=0x7cba 宽度258=0x102 高度241=0xf1

用十六进制编辑器打开:

wpsF.tmp 

下面一项一项的分析:

BITMAPFILEHEADER结构体

42 4d 固定为0x4d42

ba 7c 00 00 文件大小=31930

00 00 00 00 保留

76 00 00 00 实际位图数据的偏移字节数 在第0xa字节共四字节

 

BITMAPINFOHEADER结构体

28 00 00 00 指定此结构体的长度,为28

02 01 00 00 位图宽0x102=258  在第0x12字节共四字节

f1 00 00 00 位图高0xf1=241   在第0x16字节共四字节

01 00           显示器的平面属,恒等于1

04 00           颜色位数 4

00 00 00 00  图片的压缩属性,等于0

44 7c 00 00  图片数据区的大小  在第0x22字节共四字节  数据结束偏移0x7c44+0x76-1=0x7cb9

00 00 00 00  X轴每米多少像素,省略

00 00 00 00  Y轴每米多少像素,省略

00 00 00 00  颜色索引表个数,等于0时表示有2^biBitCount个颜色索引表

00 00 00 00  有多少个重要的颜色,等于0时表示所有颜色都很重要,

 

BITMAPINFOHEADER的第5个属性是biBitCount<16,也就是在1位图(只有两种颜色),4位图(只有2^4=16种颜色),8位图(只有2^8=256种颜色)的情况下,此时会有颜色表,也就是接下来的部分: 

RGBQUAD结构体

//调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。

//(调色板结构体个数等于使用的颜色数,即是多少色图就有多少个,4位图16色,就有16个RGBQUAD结构体。)

typedef struct tagRGBQUAD {

//public:

BYTE rgbBlue; //该颜色的蓝色分量

BYTE rgbGreen; //该颜色的绿色分量

BYTE rgbRed; //该颜色的红色分量

BYTE rgbReserved; //保留值

} RGBQUAD;

这里举个4位图也就是16色图的例子:一 个RGBQUAD结构体只占用4字节空间,从左到右每个字节依次表示(蓝色,绿色,红色,未使用)。举例的这个图片我数了数总共有16个RGBQUAD结 构体,由于该图片是4位图,2^4正好等于16,所以它把16种颜色全部都枚举出来了,这些颜色就是一个颜色索引表。颜色索引表编号从0开始,总共16个 颜色,所以编号为0-15。这16个RGBQUAD结构体依次为:

编号:(蓝,绿,红,空)   换算成r:g:b=5:6:5  右移r:g:b=3:2:3比如rgbmix(0,255,255) = 0x7FF = 0000 0111 1111 1111

0号:(00,00,00,00)    0x0

1号:(00,00,80,00)    0x8000

2号:(00,80,00,00)    0x400

3号:(00,80,80,00)    0x8400

4号:(80,00,00,00)    0x10

5号:(80,00,80,00)    0x8010

6号:(80,80,00,00)    0x410

7号:(80,80,80,00)    0x8410

8号:(C0,C0,C0,00)    0xc618

9号:(00,00,FF,00)    0xf800

10号:(00,FF,00,00)    0x7e0

11号:(00,FF,FF,00)    0xffe0

12号:(FF,00,00,00)    0x1f

13号:(FF,00,FF,00)    0xf81f

14号:(FF,FF,00,00)    0x7ff

15号:(FF,FF,FF,00)    0xffff

RGBQUAD结构体

00000000

00008000

00800000

00808000

80000000

80008000

80800000

80808000

C0C0C000

0000FF00

00FF0000

00FFFF00

FF000000

FF00FF00

FFFF0000

FFFFFF00

到这里,正好满足DIB数据区的偏移量,所以后面的字节就是图片内容了。这里需要提醒的是所有的DIB数据扫描行是上下颠倒的,也就是说一幅图片先绘制底部的像素,再绘制顶部的像素,所以这些DIB数据所表示的像素点就是从图片的左下角开始,一直表示到图片的右上角。

 

posted on 2016-01-04 14:23  ya20151015  阅读(258)  评论(0编辑  收藏  举报