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

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; 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值