位图的格式如下:
1.文件头信息块
0000-0001 :文件标识,为字母ASCII码“BM”。
0002-0005 :文件大小。
0006-0009 :保留,每字节以“00”填写。
000A-000D :记录图像数据区的起始位置。各字节的信息含义依次为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。
2.图像描述信息块
000E-0011:图像描述信息块的大小,常为28H。
0012-0015:图像宽度。
0016-0019:图像高度。
001A-001B:图像的plane总数(恒为1)。
001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。
001E-0021:数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。
0022-0025:图像区数据的大小。
0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。
3.颜色表
颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号0的颜色,接下来表示颜色号1的颜色,依此类推。
4.图像数据区
颜色表接下来位是位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位;16色图像每点占4位;256色图像每点占8位;真彩色图像每点占24位。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:图像数据信息大小=(图像宽度*图像高度*记录像素的位数)/8。 然而,未压缩的图像信息区的大小。除了真彩色模式外,其余的均大于或等于数据信息的大小。这是为什么呢?原因有两个:
1.BMP文件记录一行图像是以字节为单位的。因此,就不存在一个字节中的数据位信息表示的点在不同的两行中。也就是说,设显示模式位16色,在每个字节分配两个点信息时,如果图像的宽度位奇数,那么最后一个像素点的信息将独占一个字节,这个字节的后4位将没有意义。接下来的一个字节将开始记录下一行的信息。
2.为了显示的方便,除了真彩色外,其他的每中颜色模式的行字节数要用数据“00”补齐为4的整数倍。如果显示模式为16色,当图像宽为19时,存储时每行则要补充4-(19/2+1)%4=2个字节(加1是因为里面有一个像素点要独占了一字节)。如果显示模式为256色,当图像宽为19时,每行也要补充4-19%4=1个字节。
一下代码实现的是位图宽,高是4的整数倍,颜色位深是24位。
头文件定义:
#pragma pack(1) typedef unsigned char BYTE ; typedef unsigned short WORD ; typedef unsigned int DWORD ; typedef long LONG ; typedef struct BW_BITMAPFILEHEADER { WORD fileType ; DWORD fileSize ; WORD fileReserved1 ; WORD fileReserved2 ; DWORD offSet ; } BITMAPFILEHEADER ; typedef struct BW_BITMAPFINFOHEADER { DWORD headSize ; LONG width ; LONG height ; WORD plant ; WORD bitCount ; DWORD compression ; DWORD sizeImage ; LONG XPelPerMeter ; LONG YPelPerMeter ; DWORD clrUsed ; DWORD clrImportant ; }BITMAPINFOHEAD; typedef struct BW_RGBQUAD { BYTE rgbBlue ; BYTE rgbGreen ; BYTE rgbRed ; BYTE rgbReserved ; }RGBQUAD; typedef struct BW_PIXEL { BYTE red ; BYTE green ; BYTE blue ; } PIXEL ; class BW_BITMAP { public: bool ReadBMP(char*) ; BITMAPFILEHEADER bitMapFileHeader ; BITMAPINFOHEAD bitMapInfoHead ; RGBQUAD *rgbquad ; PIXEL* pixelData ; };
具体cpp文件:
bool BW_BITMAP::ReadBMP(char *fileName) { FILE *fileR ,fileW ; fileR = fopen(fileName , "rb") ; if (fileR != NULL) { //BW_BITMAP* bitMap = new BW_BITMAP ; fread(&bitMapFileHeader , 1 , sizeof(BITMAPFILEHEADER) , fileR) ; if (0x4d42 != bitMapFileHeader.fileType) { fclose(fileR) ; return NULL ; } fread(&bitMapInfoHead, 1, sizeof(BITMAPINFOHEAD) , fileR) ; rgbquad = new RGBQUAD[bitMapInfoHead.clrUsed] ; for (int icount = 0 ; icount < bitMapInfoHead.clrUsed ; ++icount) { fread((char *)&(rgbquad[icount].rgbBlue),1,sizeof(BYTE),fileR); fread((char *)&(rgbquad[icount].rgbGreen),1,sizeof(BYTE),fileR); fread((char *)&(rgbquad[icount].rgbRed),1,sizeof(BYTE),fileR); //fread((char *)&(bitMap->rgbquad[icount].rgbReserved),1,sizeof(BYTE),fileR); } int width = bitMapInfoHead.width ; int height = bitMapInfoHead.height ; pixelData = new PIXEL[width * height * sizeof(PIXEL)]; //初始化原始图片的像素数组 //fseek(fpi,54,SEEK_SET); //读出图片的像素数据 fread(pixelData,sizeof(PIXEL) * width,height,fileR); fclose(fileR); return true ; } else { //cout<<"file open error!"<<endl; return false ; } } }
在写该段代码时要注意在头文件的文件头使用#pragma pack(1),这是告诉编译器使用边界1对齐(也就是不对齐)。
如果不是用#pragma pack(1),经过测试有如下结果:sizeof(BITMAPFILEHEADER)的值为16,而不是14。说明编译器对其使用了4为边界对齐。
如果实在linux环境下,要使用__attribute__((packed))来实现相同的效果。
总结可知:在实现对数据格式有严格要求的功能时,要注意到编译器的优化带来的麻烦。