Bitmap文件的数据结构和手动填写

一、bitmap文件简述

bitmap文件,即我们所说的bmp位图文件,它是Windows操作系统中标准的图像文件格式。分为设备相关位图DDB和设备无关位图DIB,不过现在大都采用DIB而DDB则是微软为了兼容Windows3.0而留下的历史遗留产品。BMP文件没有采用任何压缩,因此一般都很大。一般一个BMP文件分为四个部分BMP文件头,BMP位图信息头,BMP颜色表,以及位图数据。

在讨论BMP数据结构之前先定义几个将要用到的类型:

typedef unsigned long  DWORD;
typedef int            BOOL;
typedef unsigned char  BYTE;
typedef unsigned short WORD;
typedef float          FLOAT;
typedef unsigned char  byte;

BMP文件头的数据结构如下:

//BMP文件头
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;//位图文件的类型,必须为BM(1-2字节)
DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)文件头的偏移量表示,以字节为单位

}BitmapFileHeader;


BMP位图信息头数据结构如下:

//位图信息头
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;//本结构所占用字节数(15-18字节)
long biWidth;//位图的宽度,以像素为单位(19-22字节)
long biHeight;//位图的高度,以像素为单位(23-26字节)
WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
WORD biBitCount;

//每个像素所需的位数,必须是1(双色),(29-30字节)
//4(16色),8(256色)16(高彩色)或24(真彩色)之一
DWORD biCompression;
//位图压缩类型,必须是0(不压缩),(31-34字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
long biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
long biYPedsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
}BitmapInfoHeader;


BMP颜色表数据结构如下:

//颜色表,对于biBitCount的值不为真彩色24bits的位图,需要使用颜色表来查询相应写颜色。即数据域存的是索引,根据索引来查询颜色表,从而获得这一像素的颜色

//这一部分在文件中的长度是不固定的,biBitCount为24的位图没有这一部分,数据紧跟在位图信息头之后,而bitBiBitCount为1,4,8,16的BMP文件,这一部分分别有2,16,256,65536个RGBQUAD表项。
typedef struct tagRGBQUAD
{
BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
BYTE rgbGreen;//绿色的亮度(值范围为0-255)
BYTE rgbRed;//红色的亮度(值范围为0-255)
BYTE rgbReserved;//保留,必须为0
}RGBQuad;


因此整个BMP文件可以表示成下面的数据结构:

//bmp图像
typedef struct Bitmap
{
BitmapFileHeader *FileHeader;//文件头
BitmapInfoHeader *InfoHeader;//位图信息头
RGBQuad *rgbQuad;//颜色表
byte *data;//位图数据,即每一个像素的值
} Bitmap;


讨论完BMP数据结构,这里给出一个读入BMP文件的参考函数:

void readbmp(FILE *fp,Bitmap *sr)
{
//给bitmap的域开空间
//先写文件头和位图信息头,然后根据所得信息来写RGBQUAD和data
(*sr).FileHeader=(BitmapFileHeader *)malloc(sizeof(BitmapFileHeader));
(*sr).InfoHeader=(BitmapInfoHeader *)malloc(sizeof(BitmapInfoHeader));
//文件头
fread(&(*(*sr).FileHeader).bfType,sizeof(WORD),1,fp);
printf("%x\n",(*(*sr).FileHeader).bfType);
fread(&(*(*sr).FileHeader).bfSize,sizeof(DWORD),1,fp);
fread(&(*(*sr).FileHeader).bfReserved1,sizeof(WORD),1,fp);
fread(&(*(*sr).FileHeader).bfReserved2,sizeof(WORD),1,fp);
fread(&(*(*sr).FileHeader).bfOffBits,sizeof(DWORD),1,fp);
//位图信息头
fread(&(*(*sr).InfoHeader).biSize,sizeof(DWORD),1,fp);
fread(&(*(*sr).InfoHeader).biWidth,sizeof(long),1,fp);
fread(&(*(*sr).InfoHeader).biHeight,sizeof(long),1,fp);
fread(&(*(*sr).InfoHeader).biPlanes,sizeof(WORD),1,fp);
fread(&(*(*sr).InfoHeader).biBitCount,sizeof(WORD),1,fp);
fread(&(*(*sr).InfoHeader).biCompression,sizeof(DWORD),1,fp);
fread(&(*(*sr).InfoHeader).biSizeImage,sizeof(DWORD),1,fp);
fread(&(*(*sr).InfoHeader).biXPelsPerMeter,sizeof(long),1,fp);
fread(&(*(*sr).InfoHeader).biYPelsPerMeter,sizeof(long),1,fp);
fread(&(*(*sr).InfoHeader).biClrUsed,sizeof(DWORD),1,fp);
fread(&(*(*sr).InfoHeader).biClrImportant,sizeof(DWORD),1,fp);

//根据biBitCount来读入颜色表
switch((*(*sr).InfoHeader).biBitCount)
{
case 1:
{
break;
}
case 4:
{
break;
}
case 8:
{
//共256个RGBQUAD表项,即rgbQUAD为一个大小256的数组的指针
(*sr).rgbQuad=(RGBQuad *)malloc(sizeof(RGBQuad)*256);
for(int i=0;i<256;i++)
{
fread( &(*((*sr).rgbQuad+i)).rgbBlue,sizeof(BYTE),1,fp);
fread( &(*((*sr).rgbQuad+i)).rgbGreen,sizeof(BYTE),1,fp);
fread( &(*((*sr).rgbQuad+i)).rgbRed,sizeof(BYTE),1,fp);
fread( &(*((*sr).rgbQuad+i)).rgbReserved,sizeof(BYTE),1,fp);
}
break;
}
case 16:
{
break;
}
case 24:
{
//真彩色,不需要颜色表
(*sr).rgbQuad=NULL;
break;
}
default:break;
}
//读入位图数据,未按照像素,只按照字节读入
(*sr).data=(byte *)malloc(sizeof(byte)*((*(*sr).FileHeader).bfSize-(*(*sr).FileHeader).bfOffBits));
fread((*sr).data,sizeof(byte),(*(*sr).FileHeader).bfSize-(*(*sr).FileHeader).bfOffBits,fp);
}

这里只给出了biBitCount为8和24的实现,biBitCount为1,4和16可以直接根据biBitCount为8的部分得到。

在调用该函数的时候,需要提供读入文件的指针,同时需要提供已经申请了空间的Bitmap指针。如下示例:

FILE *fp;
if((fp=fopen("1.bmp","r"))==NULL)
{
printf("Cannot read the file!\n");
exit(-1);
}
Bitmap *testbmp=(Bitmap *)malloc(sizeof(Bitmap));
readbmp(fp,testbmp);


二、手动填写一个Bitmap文件

为了加深对于Bitmap文件的理解,可以手动填写一个Bitmap文件。

这里我们需要一个十六进制编辑器,笔者使用的是WinHex软件,将要填写的是一个16×16的24真彩色位图。

使用WinHex新建一个文件,其大小为822字节,文件大小计算如下:

BMP文件头14个字节

位图信息头40个字节

颜色表0个字节(24位真彩色图不需要颜色表)

位图数据16×16×3=768个字节

总共文件大小14+40+768=822个字节

然后参考第一部分的BMP数据结构的讲述,开始填写每一个字节。这里给出截图和图注如下:





到这里,BMP文件的第一部分即BMP文件头填写完了,下面填写BMP的位图信息头。







填写完位图信息头后,不是真彩24位的位图需要填写颜色表,填完颜色表后就可以填写每一个像素了,这里是24位真彩色图,所以直接填写位图数据。


这里值得一说的是,BMP文件数据从前到后在图像上体现出来的是从左到右,从下到上,比如上面这样填写,后面的数据全部为0,看到的效果应该是这样的:

















猜你喜欢

转载自blog.csdn.net/lanruotian/article/details/34497711