您的位置:首页 > 其它

BMP格式以及用纯C实现Load和Save

2013-09-25 09:40 405 查看
BMP格式以及用纯C实现Load和Save

1 存储结构

BMP文件存储结构的格式可以在Windows中的WINGDI.h文件中找到定义。

BMP文件总体上由4部分组成,分别是位图文件头、位图信息头、调色板和图像数据,如表5-1所示。

BMP文件的组成结构

位图文件头(bitmap-file header)
位图信息头(bitmap-information header)
彩色表/调色板(color table)
位图数据(bitmap-data)
下面来详细看一下每个组成部分的细节。

1.1 位图文件头(bitmap-file header)

位图文件头(bitmap-file header)包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段。

打开WINGDI.h文件,搜索"BITMAPFILEHEADER"就可以定位到BMP文件的位图文件头的数据结构定义。
typedef struct tagBITMAPFILEHEADER {

WORD    bfType;

DWORD   bfSize; 

WORD    bfReserved1; 

WORD    bfReserved2; 

DWORD   bfOffBits; 

} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

tagBITMAPFILEHEADER结构

字 段 名

大小(单位:字节)

描 述

bfType

2
位图类别,根据不同的操作

系统而不同,在Windows

中,此字段的值总为'BM'

bfSize

4
BMP图像文件的大小

bfReserved1

2
总为0

bfReserved2

2
总为0

bfOffBits

4
BMP图像数据的地址

1.2 位图信息头(bitmap-information header)

位图信息头(bitmap-information header)包含了位图信息头的大小、图像的宽高、图像的色深、压缩说明图像数据的大小和其他一些参数。

打开WINGDI.h文件,搜索"tagBITMAPINFOHEADER"就可以定位到BMP文件的位图信息头的数据结构定义。
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;

tagBITMAPFILEHEADER结构
字 段 名

大小

(单位:

字节)

描 述

biSize

4
本结构的大小,根据不同的操作系统而不同,在Windows中,此字段的值总为28h字节=40字节

biWidth

4
BMP图像的宽度,单位像素

biHeight

4
总为0

biPlanes

2
总为0

biBitCount

2
BMP图像的色深,即一个像素用多少位表示,常见有1、4、8、16、24和32,分别对应单色、16色、256色、16位高彩色、24位真彩色和32位增强型真彩色

biCompression

4
压缩方式,0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定

biSizeImage

4
BMP图像数据大小,必须是4的倍数,图像数据大小不是4的倍数时用0填充补足

biXPelsPerMeter

4
水平分辨率,单位像素/m

biYPelsPerMeter

4
垂直分辨率,单位像素/m

biClrUsed

4
BMP图像使用的颜色,0表示使用全部颜色,对于256色位图来说,此值为100h=256

biClrImportant

4
重要的颜色数,此值为0时所有颜色都重要,对于使用调色板的BMP图像来说,当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色

2 BMP种类

按照单个像素占用的位数,有1,4,8,16,24,32几种

常用的几种说明如下:

(1)8位,灰度图,像素值范围[0,255]

(2)16位

biBitCount=16 表示位图最多有216种颜色。每个色素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。这种格式也被称作555 16位位图。如果biCompression成员的值是BI_BITFIELDS,那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows
95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码"与"上像素值,从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。

(3)24位,一个通道占用一个字节

(4)32位,32位的BMP一个象素占3个字节,还有8位的alpha

3 C语言实现Load和Save

3.1 从磁盘读入BMP文件代码

// 自定义结构体

typedef struct s_MIImage

{

byte* pData; // 图像数据

int nImgW; // 图像宽度

int nImgH; // 图像高度

int nBits; // 1,4,8,24,32

BOOL bInit; // 是否已进行初始化,TRUE:已初始化。不能直接用指针来判断pData是否已初始化,因为pData的初始值不为NULL

// 也不能用(!bInit)来判断是否初始化,必须用(TRUE!=bInit)

}s_MIImage;

int MILoadBMP(OUT s_MIImage* pMIImg, char* pImgPath)

{

BITMAPFILEHEADER bf; //BMP文件头结构体

BITMAPINFOHEADER bi; //BMP信息头结构体

FILE* fp; //指向文件的指针

RGBQUAD *ipRGB = NULL;

DWORD LineBytes; // 每行的字节数

DWORD ImgBytesSize; // 图像总字节数

DWORD NumColors;

int i;

// 先释放图像空间

if (pMIImg->bInit == TRUE)

{

// 不能用FREE,因为pMIImg->pData的初始值不为NULL

free(pMIImg->pData);

}

pMIImg->pData = NULL;

// 打开文件

fp=fopen(pImgPath,"rb");

if(fp == NULL)

{

return -1;

}

//读取信息头、文件头

fread(&bf,sizeof(BITMAPFILEHEADER),1,fp); //把指针fp所指向的文件的头信息写入bf(地址)

fread(&bi,sizeof(BITMAPINFOHEADER),1,fp);

// 计算行字节数和总字节数

LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount); //计算位图的实际宽度并确保它为32的倍数

ImgBytesSize=(DWORD)LineBytes*bi.biHeight;

if (bi.biClrUsed != 0 )

{

NumColors=(DWORD)bi.biClrUsed;

}

else

{

switch (bi.biBitCount)

{

case 1:

NumColors=2;

break;

case 4:

NumColors=16;

break;

case 8:

NumColors=256;

break;

case 24:

NumColors=0;

break;

case 32:

NumColors=0;

break;

}

}

//分配调色板内存

if ( (bi.biBitCount!=24) && (bi.biBitCount!=32) )

{

ipRGB=(RGBQUAD *)malloc(NumColors*sizeof(RGBQUAD));

fread(ipRGB,sizeof(RGBQUAD),NumColors,fp);

}

// 初始化图像

pMIImg->pData = (byte*)malloc(sizeof(byte) * ImgBytesSize); // 分配图像内存, 外部释放

if (!pMIImg->pData)

{

FREE(ipRGB);

return -1;

}

pMIImg->nBits = bi.biBitCount;

pMIImg->nImgW = bi.biWidth;

pMIImg->nImgH = bi.biHeight;

pMIImg->bInit = TRUE;

if ( (bi.biBitCount==32) || (bi.biBitCount==24) )

{

fseek(fp, 4, SEEK_CUR); //sizeof(RGBQUAD)

for (i=pMIImg->nImgH-1; i>=0; --i)

{

fread(pMIImg->pData+i*LineBytes, 1, LineBytes, fp);

}

}

else

{

for (i=pMIImg->nImgH-1; i>=0; --i)

{

fread(pMIImg->pData+i*LineBytes, 1, LineBytes, fp);

}

}

fclose(fp);

fp = NULL;

FREE(ipRGB);

return 0;

}

3.2 将内存中的图像数组写入磁盘BMP

int MISaveBMP(OUT char* pImgPath, s_MIImage* pMIImg)

{

BITMAPFILEHEADER bf; //BMP文件头结构体

BITMAPINFOHEADER bi; //BMP信息头结构体

RGBQUAD *ipRGB = NULL;

DWORD NumColors;

int i;

DWORD nLineBytes = (DWORD)WIDTHBYTES(pMIImg->nImgW * pMIImg->nBits);

FILE* fp = NULL;

if ( (!pImgPath) || (!pImgPath) || (TRUE!=pMIImg->bInit))

{

return -1;

}

// 写入另一个文件

fp = fopen(pImgPath, "wb");

memset(&bf, 0, sizeof(bf));

*((char*)&(bf.bfType)) = 'B';

*(((char*)&(bf.bfType))+1) = 'M';

bf.bfReserved1 = 0;

bf.bfReserved2 = 0;

bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

bf.bfSize = pMIImg->nImgW * pMIImg->nImgH * (pMIImg->nBits>>3) + bf.bfOffBits;

memset(&bi, 0, sizeof(bi));

bi.biSize = sizeof(bi);

bi.biBitCount = pMIImg->nBits;

bi.biWidth = pMIImg->nImgW;

bi.biHeight = pMIImg->nImgH;

bi.biCompression = BI_RGB;

bi.biPlanes = 1;

bi.biClrUsed = 0;

fwrite(&bf,sizeof(BITMAPFILEHEADER),1,fp);

fwrite(&bi,sizeof(BITMAPINFOHEADER),1,fp);

if (bi.biClrUsed != 0 )

{

NumColors=(DWORD)bi.biClrUsed;

}

else

{

switch (bi.biBitCount)

{

case 1:

NumColors=2;

break;

case 4:

NumColors=16;

break;

case 8:

NumColors=256;

break;

case 24:

NumColors=0;

break;

case 32:

NumColors=0;

break;

}

}

//分配调色板内存

if ( (bi.biBitCount!=24) && (bi.biBitCount!=32) )

{

ipRGB=(RGBQUAD *)malloc(NumColors*sizeof(RGBQUAD));

fread(ipRGB,sizeof(RGBQUAD),NumColors,fp);

}

if ( (bi.biBitCount!=24) && (bi.biBitCount!=32) )

{

fwrite(ipRGB,sizeof(RGBQUAD),NumColors,fp);

for (i=(bi.biHeight)-1 ;i>=0;i--)

{

fwrite(pMIImg->pData+nLineBytes*i, sizeof(byte), nLineBytes, fp);

}

}

else

{

for (i=(bi.biHeight)-1 ;i>=0;i--)

{

fwrite(pMIImg->pData+nLineBytes*i, sizeof(byte), nLineBytes, fp);

}

}

fclose(fp);

fp = NULL;

FREE(ipRGB);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: