以下文字大多来自网上的文章,我按照自己的理解记录并且摘抄了部分写在这里。
BMP文件格式相对jpeg,png,gif要简单一些。
跟elf文件类似,文件头,信息头,偏移量等,也有自己的魔数。
打开windows的画图工具,可以保存4种
每种模式存储单一像素需要的大小为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。
读取其中的必要信息,代码如下:
结构体定义部分:
- /*
- * =====================================================================================
- * Filename: Defs.h
- * Description: defines the structure in bmp
- * Version: 1.0
- * Created: 2013-3-8 22:53:32
- * Compiler: gcc
- * Author: y, izualzhy@163.com
- * =====================================================================================
- */
- #pragma pack(2)
- //位图文件头
- struct BitMapFileHeader
- {
- unsigned short bfType;//位图文件类型,必须BM,(16进制0x4d42)
- unsigned int bfSize;//文件大小,以字节为单位
- unsigned short bfReserved1;//保留字,必须为0
- unsigned short bfReserved2;//保留字,必须为0
- unsigned int bfOffBits;//位图数据的起始位置,也就是数据区的起始位置,单位为字节
- };
- //位图信息头
- struct BitMapInfoHeader
- {
- unsigned int biSize;//位图信息头的长度,一般为40
- unsigned int biWidth;//位图的宽度
- unsigned int biHeight;//位图的高度
- unsigned short biPlanes;//位图的位面数=1
- unsigned short biBitCount;//每个像素所占的位数,
- //必须为1(双色),4(16色),8(256色),24(真彩色),32(32真彩色)之一
- unsigned int biCompression;//位图压缩类型
- //必须为0(不压缩),1(BI_RLE8压缩类型),2(BI_RLE4压缩类型)之一
- unsigned int biSizeImage;//位图的大小,以字节为单位,必须是4的倍数
- unsigned int biXPelsPerMeter;//位图水平分辨率,像素/米
- unsigned int biYPelsPerMeter;//位图垂直分辨率,像素/米
- unsigned int biClrUsed;//位图实际使用的颜色数
- unsigned int biClrImportant;//指定重要的颜色数
- };
- struct RGBQuad
- {
- unsigned char rgbBlue;//蓝色的亮度值
- unsigned char rgbGreen;//绿色的亮度值
- unsigned char rgbRed;//红色的亮度值
- unsigned char rgbReserved;//保留
- };
- #pragma pack()
main.cpp:
- #include <stdio.h>
- #include "Defs.h"
- int main(int argc, char* argv[])
- {
- if (argc < 2)
- {
- return -1;
- }
- FILE* fp = fopen(argv[1], "r");
- if (!fp)
- {
- printf("fopen error!\n");
- return -1;
- }
- fseek(fp, 0, SEEK_END);
- unsigned int fileSize = ftell(fp);
- printf("seek fileSize: %d\n", fileSize);
- fseek(fp, 0, SEEK_SET);
- printf("==============Read BitMapFileHeader=========\n");
- BitMapFileHeader bmfHeader;
- fread(&bmfHeader, sizeof(bmfHeader), 1, fp);
- char type[3] = {0};
- type[0] = (bmfHeader.bfType & 0xff);
- type[1] = (bmfHeader.bfType & 0xff00) >> 8;
- printf("filetype: %s\n", type);//文件类型
- printf("fileSize: %d\n", bmfHeader.bfSize);//文件大小,与实际文件大小一致
- printf("bitmapDataOffset: %d\n", bmfHeader.bfOffBits);//位图数据开始位置
- printf("==============Read BitMapInfoHeader=========\n");
- BitMapInfoHeader bmiHeader;
- fread(&bmiHeader, sizeof(bmiHeader), 1, fp);
- printf("sizeof BitMapInfoHeader: %d, %d\n", sizeof(BitMapInfoHeader), bmiHeader.biSize);//位图文件信息头结构体大小
- printf("width: %d, height: %d\n", bmiHeader.biWidth, bmiHeader.biHeight);//位图宽高
- printf("bit per perls: %d\n", bmiHeader.biBitCount);//每个像素所占的位数
- int dataSizePerLine = (bmiHeader.biWidth * bmiHeader.biBitCount + 31)/8;
- dataSizePerLine = dataSizePerLine/4*4;
- const int imageSize = bmiHeader.biHeight * dataSizePerLine;
- printf("ImageSize: %d, %d\n", bmiHeader.biSizeImage, imageSize);//图像的大小,4的倍数
- const int colorTableOffset = ftell(fp);//颜色表开始的位置
- printf("colorTableOffset: %d, bitmapDataOffset: %d, colorTableCount: %d\n"
- , colorTableOffset, bmfHeader.bfOffBits, (bmfHeader.bfOffBits - colorTableOffset)/sizeof(RGBQuad));
- fclose(fp);
- return 0;
- }
输出为:
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