BMP格式以及用纯C实现Load和Save
2013-09-25 09:40
405 查看
BMP格式以及用纯C实现Load和Save
1 存储结构
BMP文件存储结构的格式可以在Windows中的WINGDI.h文件中找到定义。
BMP文件总体上由4部分组成,分别是位图文件头、位图信息头、调色板和图像数据,如表5-1所示。
BMP文件的组成结构
下面来详细看一下每个组成部分的细节。
1.1 位图文件头(bitmap-file header)
位图文件头(bitmap-file header)包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段。
打开WINGDI.h文件,搜索"BITMAPFILEHEADER"就可以定位到BMP文件的位图文件头的数据结构定义。
tagBITMAPFILEHEADER结构
1.2 位图信息头(bitmap-information header)
位图信息头(bitmap-information header)包含了位图信息头的大小、图像的宽高、图像的色深、压缩说明图像数据的大小和其他一些参数。
打开WINGDI.h文件,搜索"tagBITMAPINFOHEADER"就可以定位到BMP文件的位图信息头的数据结构定义。
tagBITMAPFILEHEADER结构
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;
}
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; |
字 段 名 | 大小(单位:字节) | 描 述 |
bfType | 2 | 位图类别,根据不同的操作 系统而不同,在Windows 中,此字段的值总为'BM' |
bfSize | 4 | BMP图像文件的大小 |
bfReserved1 | 2 | 总为0 |
bfReserved2 | 2 | 总为0 |
bfOffBits | 4 | BMP图像数据的地址 |
位图信息头(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; |
字 段 名 | 大小 (单位: 字节) | 描 述 |
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图像来说,当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色 |
按照单个像素占用的位数,有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;
}
相关文章推荐
- 备忘录模式
- Android开发环境搭建完全图解
- 内核大页实现与分析
- 空间索引网格大小无效的解决方法The spatial index grid size is invalid
- JQuery Tips(3)----关于$()包装集内元素的改变
- ARM裸机程序开发9(GPIO)
- web客户端ocx控件远程下载
- BattleLAN在win7中无法运行一按启动便自动消失
- 优秀开源软件归类
- VC2005调试时看不到局部变量的值
- 表达式树
- Connecting OVS Bridges with Patch Ports
- 1.输入一个数组长度,动态创建数组,所有元素随机生成,输出元素中的最大值;
- JQuery Tips(4)----一些关于提高JQuery性能的Tips
- apache环境Zf2要装的intl验证
- 基于V-ISA检测机制的华为AntiDDos解决方案
- 正则表达式
- 利用FireBug使JQuery的学习更加轻松愉快
- C++排序之选择排序(3)
- STM32的两种debug方式