BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit、16bit、24bit或者32bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
BMP文件一般由四部分组成:
1、文件头
2、文件信息头
3、调色板(不一定有)
4、位图数据
调色板不是必须的,只有在单色位图、16色位图、256色位图中才有,也是位图深度为1bit、4bit、8bit的位图才有调色板。关于BMP文件的定义,在windows.h这个头文件中都有描述,一般来说,一个BMP文件用C语言代码可以这样表示:
struct BMP
{
BITMAPFILEHEADER fileHeader; //文件头
BITMAPINFOHEADER infoHeader; //文件信息头
PALETTEENTRY palette[n]; //调色板信息,n为颜色数目
byte *pixels; //像素数据指针
};
下面将对BMP文件给出详细的解析
1、BMP文件头
BMP文件头在Windows.h中被声明为:BITMAPFILEHEADER。该结构完整的声明如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
2、位图信息头
BMP信息头在windows.h中被声明为:BITMAPINFOHEADER 。该结构完整的声明如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
3、调色板
调色板的目的是建立一个颜色索引,从而节省存储空间。在调色板中,保存着位图用到的所有颜色,而位图数据部分储存的是颜色的索引,读取bmp文件的像素数据时,通过索引找到相对应的颜色。调色板不一定会有,像16位色、24位色和32位色的位图就没有调色板。调色板是一个RGBQUAD类型的的数组,数组的大小跟颜色数目有关。RGBQUAD结构体在windows.h中的定义如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD
该结构体占用四个字节,前三个表示颜色的三个分量,取值范围是0到255,分别是蓝、绿、红(不是红、绿、蓝),第四个是保留项,一般都是0。调色板一般可这样定义:
RGBQUAD palette[n];
其中n表示颜色的数目,对于单色位图,颜色数目只有两个,n=2;对于4bit的位图,颜色数是16,n=16;对于8bit的位图,颜色数是256,n=256;而对于16bit、24bit和32bit的位图,由于颜色过多,不适宜使用调色板,因此没有调色板,n=0。由于色彩的需要,现在大多数位图都是24位的真彩色,甚至有32位的。
4、位图数据
位图数据一般可以保存在一个二维的数组里,值得注意的是:
(1)window系统扫描BMP图像时是逐行按每四个字节进行扫描的,也就是说,位图每行的字节长度应该是4的倍数,如果不是4的倍数,要补全,没有像素的地方,用0填充。例如一个16*16的单色位图,它的宽度为16像素,每像素用1bit表示,则每行的字节长度为2字节,但是2字节不是4的倍数,所以要将行的字节数扩充为4字节,这样的话,相当于位图变为32*16大小了。多出的部分只是占个位置,方便系统扫描罢了,不会影响原来的位图显示。
(2)window系统显示位图时,扫描像素数据时时按照B、G、R的顺序来的,而不是R、G、B,因此在填充位图数据时,要注意颜色分量的存储顺序。
下面用C语言创建BMP文件,该文件可以是单色位图、16色位图、256色位图、16bit位图、24bit位图或者是32bit位图。的位图数据没有经过压缩,调色板的有无根据颜色深度而定。
/*********************************************************************************************************************/
//函数名:SaveBmp
//功能: 将像素数据保存文BMP文件
//参数: 分别为文件名、图片宽度、图片高度、每像素比特数、调色板指针、像素数据指针
//返回值:如果保存成功,返回true,否则为false
//说明: bCount参数可以是1、4、8、16、24、32,crPalette参数是一个指向调色板数组的指针,如果没有调色板,该项可以写NULL。
// bPixels是指向像素数据的指针,该项应该与bCount项相匹配。
/**********************************************************************************************************************/
#include
#include
bool SaveBmp(const char *fileName, int bWidth, int bHeight, int bCount, RGBQUAD *crPalette, byte *bPixels)
{
//调色板字节数
int paletteSize;
switch (bCount)
{
case 1:
paletteSize = 8;
break;
case 4:
paletteSize = 64;
break;
case 8:
paletteSize = 1024;
break;
default:
paletteSize = 0;
break;
}
//需要写进文件的像素字节数
int pixelsSize;
pixelsSize = 4 * ((bCount*bWidth+31) / 32 )*bHeight;
//文件头
BITMAPFILEHEADER bmpFileHeader;
bmpFileHeader.bfType = 'MB';
bmpFileHeader.bfSize = 54+paletteSize+pixelsSize;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits =54+paletteSize ;
//信息头
BITMAPINFOHEADER bmpInfoHeader;
bmpInfoHeader.biBitCount = bCount;
bmpInfoHeader.biClrImportant = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biHeight = bHeight;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biSize =40;
bmpInfoHeader.biSizeImage =pixelsSize;
bmpInfoHeader.biWidth = bWidth;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
//分配新的像素缓冲
byte *pPixels;
if ( (bCount*bWidth)%32==0)
{
//如果行的字节数刚好是4的倍数,则直接利用原来的像素缓冲
pPixels = bPixels;
}
else
{
//否则,新创建一个缓冲区,使得每行的字节数刚好是4的倍数
pPixels = (byte*)malloc(pixelsSize);
//像素缓冲初始化为0
memset(pPixels, 0, pixelsSize);
int oldRow, newRow;
//原来的像素缓冲每行所占的字节数
oldRow = (bCount*bWidth+7)/ 8;
//新的像素缓冲每行所占的字节数
newRow = pixelsSize / bHeight;
//将旧的像素缓冲的数据复制到新的像素缓冲
for (int i = 0;i "wb");
if (!fp)
{
return false;
}
//写入文件头
fwrite(&bmpFileHeader, 14, 1, fp);
//写入信息头
fwrite(&bmpInfoHeader, 40, 1, fp);
//写入调色板
fwrite(crPalette,paletteSize, 1, fp);
//写入像素数据
fwrite(pPixels,pixelsSize, 1, fp);
fclose(fp);
//如果像素缓冲区是动态创建的,则释放缓冲
if(pPixels!=bPixels)free(pPixels);
return true;
}
以下代码是读取BMP文件,返回的是位图信息、调色板和像素数据。
/***************************************************************************************************************/
//函数名:LoadBmp
//功能: 载入bmp文件
//参数: 分别为文件名、位图信息头结构、调色板二重指针、像素数据二重指针
//返回值:如果读取文件成功,则返回true,否则返回false
//说明: 文件的相关信息都会被填充到bmpInfoHeader指向的结构中,crPalette、bPixels参数分别传回调色板和像素数据的指针
/***************************************************************************************************************/
#include
#include
bool LoadBmp(const char *fileName, BITMAPINFOHEADER *bmpInfoHeader, RGBQUAD **crPalette, byte **bPixels)
{
BITMAPFILEHEADER fileHeader;
FILE *fp;
fp = fopen(fileName, "rb");
if (!fp)
{
return false;
}
//读取文件头
fread(&fileHeader, 14, 1, fp);
//读取信息头
fread(bmpInfoHeader, 40, 1, fp);
//调色板字节数
int paletteSize;
switch ((*bmpInfoHeader).biBitCount)
{
case 1:
paletteSize = 8;
break;
case 4:
paletteSize = 64;
break;
case 8:
paletteSize = 1024;
break;
default:
paletteSize = 0;
break;
}
//动态创建调色板缓冲区
*crPalette = (RGBQUAD*)malloc(paletteSize);
//读入调色板数据
fread(*crPalette, paletteSize, 1, fp);
int pixelsSize;
pixelsSize = fileHeader.bfSize - fileHeader.bfOffBits;
//动态创建像素缓冲区
*bPixels = (byte*)malloc(pixelsSize);
//读入像素数据
fread(*bPixels,pixelsSize, 1, fp);
fclose(fp);
return false;
}