位图的格式如下:
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 blue ;
BYTE green ;
BYTE red ;
} 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 , , sizeof(BITMAPFILEHEADER) , fileR) ;
if (0x4d42 != bitMapFileHeader.fileType)
{
fclose(fileR) ;
return NULL ;
}
fread(&bitMapInfoHead, , sizeof(BITMAPINFOHEAD) , fileR) ; rgbquad = new RGBQUAD[bitMapInfoHead.clrUsed] ;
for (int icount = ; icount < bitMapInfoHead.clrUsed ; ++icount)
{
fread((char *)&(rgbquad[icount].rgbBlue),,sizeof(BYTE),fileR);
fread((char *)&(rgbquad[icount].rgbGreen),,sizeof(BYTE),fileR);
fread((char *)&(rgbquad[icount].rgbRed),,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))来实现相同的效果。
但是,在xcode 5.0下用#pragma pack(1) 居然可以~!
总结可知:在实现对数据格式有严格要求的功能时,要注意到编译器的优化带来的麻烦。而且要注意PIXEL的定义,一定不能写成red ,green ,blue 。