以下文字大多来自网上的文章,我按照自己的理解记录并且摘抄了部分写在这里。

BMP文件格式相对jpeg,png,gif要简单一些。

跟elf文件类似,文件头,信息头,偏移量等,也有自己的魔数。

打开windows的画图工具,可以保存4种

image

每种模式存储单一像素需要的大小为1bit,4bit,8bit,24bit,即1/8字节,1/2字节,1字节,3字节。

比如256色位图,表示共有256种像素,正好需要1字节来存储一个像素。

 

位图文件可以看做由4部分组成:

1.位图文件头(BitMap File Header)//主要是文件类型,文件大小的定义

2.位图信息头(BitMap Info Header)//主要是位图的属性,宽高,每像素占用bit数,是否压缩等等。

3.颜色表(Color Table)//24位位图没有

4.位图数据区。

文件大小=sizeof(BitMapFileHeader)+sizeof(BitMapInfoHeader)+sizeof(Color Table)+位图数据大小。

 

我们先逐个看下这4部分具体有什么,例子随后给出.

1.位图文件头:14个字节

起始字节 所占字节数 具体内容 标示
1 2 文件类型(“BM”) bfType
3 4 文件大小 bfSize
7 4 保留 bfReserved
11 4 位图数据开始的字节位置 bfOffBits

备注:

有的地方保留字认为是两个,每个占用2个字节。反正是保留字,区别不大,均设为0.

2.位图信息头:40个字节

起始字节 所占字节数 具体内容 标示
15 4 位图信息头的长度(40) biSize
19 4 位图的宽度 biWidth
23 4 位图的高度 biHeight
27 2 位图的位面数(=1) biPlanes
29 2 每个像素所占的位数(1,4,8,24等) biBitCount
31 4 位图压缩类型 biCompression
35 4 图像的大小(以字节为单位,必须为4的倍数) biSizeImage
39 4 位图水平分辨率(像素/米) biXPelsPerMeter
43 4 位图垂直分辨率(像素/米) biYPelsPerMeter
47 4 位图实际使用的颜色数(=0使用所有颜色) biClrUsed
51 4 指定重要的颜色数(=0,都很重要) biClrImportant

备注:

1.有些变量位面数,压缩方式,重要颜色数这些一般不用关心,设为默认即可。

2.biSize是本结构所占字节数,实际上该结构占用40个字节,但Windows每次还是需要您亲自添上

3.biSizeImage表示图像大小(即位图数据的大小),并不能简单的这么计算:

biSizeImage = biWidth*biHeight*biBitCount/8;//error!!

实际需要这么计算,因为

Windows规定一个扫描行所占的字节数必须是
  4的倍数(即以long为单位),不足的以0填充。

 一个扫描行所占的字节数计算方法:
  DataSizePerLine= (biWidth* biBitCount+31)/8; // 一个扫描行所占的字节数
  DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
  位图数据的大小(不压缩情况下):
  biSizeImage = DataSize= DataSizePerLine* biHeight;

因此进一步修改前面的公式:

文件大小=sizeof(BitMapFileHeader)+sizeof(BitMapInfoHeader)+sizeof(Color Table)+DataSizePerLine* biHeight;

3.颜色表

介绍颜色表前,先介绍一个表示当前像素的结构体:

struct RGBQuad

{

unsigned char rgbBlue;//蓝色的亮度(值范围0~255)

unsigned char rgbGreen;//绿色的亮度(值范围0~255)

unsigned char rgbRed;//红色的亮度(值范围0~255)

unsigned char rgbReserved;//保留

};

备注:

这里的定义是bgr,不是rgb,跟rgb正好相反。

颜色表就是一个该结构体的数组,数组大小为2^biBitCount,因此颜色表总长度为2^biBitCount*4个字节。

具体的对不同位数的bmp:

biBitCount 颜色表项数 说明
1 2 像素值为0时,使用第一项颜色,为1时使用第二项颜色
4 16 若点阵位图数据中某个字节为OX1F,则该字节代表2个象素。第一个象素用第2项颜色,第二个象素用第16项
8 256 点阵位图中每一个字节表示一个象素。
24 0 无颜色表。位图数据中三个字节表示一个象素的红、绿、蓝值

 

4.位图数据

这里的文件位置也可以通过BitMapFileHeader直接seek过来。

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。值得一提的是,每一行像素所占的字节数必须是4的整倍数,如果实际像素所占字节数不足4的倍数,则需要补齐,下一行像素值也是从4的倍数字节处后开始存放。

位图的一个像素值所占的字节数:
  当biBitCount=1时,8个像素占1个字节;
  当biBitCount=4时,2个像素占1个字节;
  当biBitCount=8时,1个像素占1个字节;
  当biBitCount=24时,1个像素占3个字节;

如果有颜色表,位图数据内为索引值,根据该索引值在颜色表内查找对应的像素。

如果没有颜色表,位图数据存储的就是像素值。

biBitCount=1 表示位图最多有两种颜色,黑色和白色。图像数据中的每一位(0或1)表示一个像素(黑色或白色)。

biBitCount=4 表示位图最多有16种颜色。每个像素用4位表示,并用这4位作为颜色表的表项来查找该像素的颜色。例如,如果位图中的第一个字节的十六进制数为1F,它表示两个像素,第一像素的颜色就在颜色表的第2表项中查找,第二个像素的颜色在颜色表的第16表项中查找。

biBitCount=8 表示位图最多有256种颜色。每个像素用8位表示,并用这8位作为颜色表的表项来查找该像素的颜色。例如,如果位图中的第一个字节的十六进制数为1F,这个像素的颜色就在颜色表的第32表项中查找。

biBitCount=24 表示位图最多有224=16 777 216种颜色。颜色表为空。每3个字节代表一个像素,每个字节分别表示R、G、B三分量的值。

biBitCount如果写颜色表的话大小为2^24*4=64M,所以不再使用颜色表。。。

 

说了这么多,我们看个例子,利用windows的画图软件,分别存为提到的4种bmp为1.bmp,2.bmp,3.bmp,4.bmp。

读取其中的必要信息,代码如下:

结构体定义部分:

Code Snippet
  1. /*
  2. * =====================================================================================
  3. *       Filename:  Defs.h
  4. *    Description:  defines the structure in bmp
  5. *        Version:  1.0
  6. *        Created:  2013-3-8 22:53:32
  7. *       Compiler:  gcc
  8. *         Author:  y, [email protected]
  9. * =====================================================================================
  10. */
  11. #pragma pack(2)
  12. //位图文件头
  13. struct BitMapFileHeader
  14. {
  15.     unsigned short bfType;//位图文件类型,必须BM,(16进制0x4d42)
  16.     unsigned int bfSize;//文件大小,以字节为单位
  17.     unsigned short bfReserved1;//保留字,必须为0
  18.     unsigned short bfReserved2;//保留字,必须为0
  19.     unsigned int bfOffBits;//位图数据的起始位置,也就是数据区的起始位置,单位为字节
  20. };
  21. //位图信息头
  22. struct BitMapInfoHeader
  23. {
  24.     unsigned int biSize;//位图信息头的长度,一般为40
  25.     unsigned int biWidth;//位图的宽度
  26.     unsigned int biHeight;//位图的高度
  27.     unsigned short biPlanes;//位图的位面数=1
  28.     unsigned short biBitCount;//每个像素所占的位数,
  29.     //必须为1(双色),4(16色),8(256色),24(真彩色),32(32真彩色)之一
  30.     unsigned int biCompression;//位图压缩类型
  31.     //必须为0(不压缩),1(BI_RLE8压缩类型),2(BI_RLE4压缩类型)之一
  32.     unsigned int biSizeImage;//位图的大小,以字节为单位,必须是4的倍数
  33.     unsigned int biXPelsPerMeter;//位图水平分辨率,像素/米
  34.     unsigned int biYPelsPerMeter;//位图垂直分辨率,像素/米
  35.     unsigned int biClrUsed;//位图实际使用的颜色数
  36.     unsigned int biClrImportant;//指定重要的颜色数
  37. };
  38.  
  39. struct RGBQuad
  40. {
  41.     unsigned char rgbBlue;//蓝色的亮度值
  42.     unsigned char rgbGreen;//绿色的亮度值
  43.     unsigned char rgbRed;//红色的亮度值
  44.     unsigned char rgbReserved;//保留
  45. };
  46. #pragma pack()

main.cpp:

 

Code Snippet
  1. #include <stdio.h>
  2. #include "Defs.h"
  3.  
  4. int main(int argc, char* argv[])
  5. {
  6.     if (argc < 2)
  7.     {
  8.         return -1;
  9.     }
  10.     FILE* fp = fopen(argv[1], "r");
  11.     if (!fp)
  12.     {
  13.         printf("fopen error!\n");
  14.         return -1;
  15.     }
  16.  
  17.     fseek(fp, 0, SEEK_END);
  18.     unsigned int fileSize = ftell(fp);
  19.     printf("seek fileSize: %d\n", fileSize);
  20.     fseek(fp, 0, SEEK_SET);
  21.  
  22.     printf("==============Read BitMapFileHeader=========\n");
  23.     BitMapFileHeader bmfHeader;
  24.     fread(&bmfHeader, sizeof(bmfHeader), 1, fp);
  25.     char type[3] = {0};
  26.     type[0] = (bmfHeader.bfType & 0xff);
  27.     type[1] = (bmfHeader.bfType & 0xff00) >> 8;
  28.     printf("filetype: %s\n", type);//文件类型
  29.     printf("fileSize: %d\n", bmfHeader.bfSize);//文件大小,与实际文件大小一致
  30.     printf("bitmapDataOffset: %d\n", bmfHeader.bfOffBits);//位图数据开始位置
  31.  
  32.     printf("==============Read BitMapInfoHeader=========\n");
  33.     BitMapInfoHeader bmiHeader;
  34.     fread(&bmiHeader, sizeof(bmiHeader), 1, fp);
  35.     printf("sizeof BitMapInfoHeader: %d, %d\n", sizeof(BitMapInfoHeader), bmiHeader.biSize);//位图文件信息头结构体大小
  36.     printf("width: %d, height: %d\n", bmiHeader.biWidth, bmiHeader.biHeight);//位图宽高
  37.     printf("bit per perls: %d\n", bmiHeader.biBitCount);//每个像素所占的位数
  38.     int dataSizePerLine = (bmiHeader.biWidth * bmiHeader.biBitCount + 31)/8;
  39.     dataSizePerLine = dataSizePerLine/4*4;
  40.     const int imageSize = bmiHeader.biHeight * dataSizePerLine;
  41.     printf("ImageSize: %d, %d\n", bmiHeader.biSizeImage, imageSize);//图像的大小,4的倍数
  42.  
  43.     const int colorTableOffset = ftell(fp);//颜色表开始的位置
  44.     printf("colorTableOffset: %d, bitmapDataOffset: %d, colorTableCount: %d\n"
  45.             , colorTableOffset, bmfHeader.bfOffBits, (bmfHeader.bfOffBits - colorTableOffset)/sizeof(RGBQuad));
  46.     fclose(fp);
  47.     return 0;
  48. }

输出为:

y@y-VirtualBox:/mnt/mydocuments/Training/Git/Training/bmp$ ./a.out 1.bmp
seek fileSize: 2862
==============Read BitMapFileHeader=========
filetype: BM
fileSize: 2862
bitmapDataOffset: 62
==============Read BitMapInfoHeader=========
sizeof BitMapInfoHeader: 40, 40
width: 200, height: 100
bit per perls: 1
ImageSize: 2800, 2800
colorTableOffset: 54, bitmapDataOffset: 62, colorTableCount: 2
y@y-VirtualBox:/mnt/mydocuments/Training/Git/Training/bmp$ ./a.out 2.bmp
seek fileSize: 10118
==============Read BitMapFileHeader=========
filetype: BM
fileSize: 10118
bitmapDataOffset: 118
==============Read BitMapInfoHeader=========
sizeof BitMapInfoHeader: 40, 40
width: 200, height: 100
bit per perls: 4
ImageSize: 10000, 10000
colorTableOffset: 54, bitmapDataOffset: 118, colorTableCount: 16
y@y-VirtualBox:/mnt/mydocuments/Training/Git/Training/bmp$ ./a.out 3.bmp
seek fileSize: 21078
==============Read BitMapFileHeader=========
filetype: BM
fileSize: 21078
bitmapDataOffset: 1078
==============Read BitMapInfoHeader=========
sizeof BitMapInfoHeader: 40, 40
width: 200, height: 100
bit per perls: 8
ImageSize: 20000, 20000
colorTableOffset: 54, bitmapDataOffset: 1078, colorTableCount: 256
y@y-VirtualBox:/mnt/mydocuments/Training/Git/Training/bmp$ ./a.out 4.bmp
seek fileSize: 60054
==============Read BitMapFileHeader=========
filetype: BM
fileSize: 60054
bitmapDataOffset: 54
==============Read BitMapInfoHeader=========
sizeof BitMapInfoHeader: 40, 40
width: 200, height: 100
bit per perls: 24
ImageSize: 60000, 60000
colorTableOffset: 54, bitmapDataOffset: 54, colorTableCount: 0

结果分析:

以3.bmp的输出结果为例:

1.文件大小通过计算可以得出与ftell相同

2.位图数据开始bytes=1078=14+40+4*256+200*100。每个数字的含义对应我们的理论部分。

 

参考资料:

http://www.metsky.com/archives/198.html

http://www.cppblog.com/magicqy/archive/2009/02/16/73975.html

http://www.cnblogs.com/scope/archive/2009/06/19/1507003.html