bmp图片格式的解析
参考资料:
位图文件主要分为如下3个部分:
块名称 | 对应Windows结构体定义 | 大小(Byte) |
---|---|---|
文件信息头 | BITMAPFILEHEADER | 14 |
位图信息头 | BITMAPINFOHEADER | 40 |
RGB颜色阵列 | BYTE* | 由图像长宽尺寸决定 |
1、 文件信息头BITMAPFILEHEADER
结构体定义如下:
typedef struct tagBITMAPFILEHEADER { /* bmfh */
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
其中:
bfType | 说明文件的类型,该值必需是0x4D42,也就是字符’BM’。 |
---|---|
bfSize | 说明该位图文件的大小,用字节为单位 |
bfReserved1 | 保留,必须设置为0 |
bfReserved2 | 保留,必须设置为0 |
bfOffBits | 说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。 |
2、位图信息头BITMAPINFOHEADER
结构体定义如下:
typedef struct tagBITMAPINFOHEADER { /* bmih */
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
其中:
biSize | 说明BITMAPINFOHEADER结构所需要的字数。 |
---|---|
biWidth | 说明图象的宽度,以象素为单位。 |
biHeight | 说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。 |
biPlanes | 为目标设备说明位面数,其值将总是被设为1。 |
biBitCount | 说明比特数/象素,其值为1、4、8、16、24、或32。但是由于我们平时用到的图像绝大部分是24位和32位的,所以我们讨论这两类图像。 |
biCompression | 说明图象数据压缩的类型,同样我们只讨论没有压缩的类型:BI_RGB。 |
biSizeImage | 说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0。 |
biXPelsPerMeter | 说明水平分辨率,用象素/米表示。 |
biYPelsPerMeter | 说明垂直分辨率,用象素/米表示。 |
biClrUsed | 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 |
biClrImportant | 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
3、RGB颜色阵列
有关RGB三色空间我想大家都很熟悉,这里我想说的是在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位的RGB位图像素数据格式是:
蓝色B值 | 绿色G值 | 红色R值 |
---|
对于32位的RGB位图像素数据格式是:
蓝色B值 | 绿色G值 | 红色R值 | 透明通道A值 |
---|
透明通道也称Alpha通道,该值是该像素点的透明属性,取值在0(全透明)到255(不透明)之间。对于24位的图像来说,因为没有Alpha通道,故整个图像都不透明。
创建bmp图标显示
在include定义要使用的结构体
创建一个pic_operation.h:
#ifndef _PIC_OPERATION_H
#define _PIC_OPERATION_H
// 定义像素数据结构体
typedef struct PixelDatas {
int iWidth; // 图像宽度
int iHeight; // 图像高度
int iBpp; // 每像素位数(位/像素)
int iLineBytes; // 每行字节长度
unsigned char *aucPixelDatas; // 像素数据指针
}T_PixelDatas, *PT_PixelDatas;
// 定义图片文件解析器结构体
typedef struct PicFileParser {
char *name; // 文件解析器名称
int (*isSupport)(unsigned char *aFileHead); // 判断文件头是否支持的函数指针
int (*GetPixelDatas)(unsigned char *aFileHead, PT_PixelDatas ptPixelDatas); // 获取像素数据的函数指针
int (*FreePixelDatas)(PT_PixelDatas ptPixelDatas); // 释放像素数据的函数指针
}T_PicFileParser, *PT_PicFileParser;
#endif /* _PIC_OPERATION_H */x
bmp图标显示
/*
* 功能:从BMP文件中解析出像素数据
* 参数:
* aFileHead - 指向BMP文件头的指针
* ptPixelDatas - 像素数据的存储结构体指针,包含宽度、高度和位深度信息
* 返回值:
* 0 - 成功
* -1 - 失败
*/
static int GetPixelDatasFrmBMP(unsigned char *aFileHead, PT_PixelDatas ptPixelDatas)
{
BITMAPFILEHEADER *ptBITMAPFILEHEADER;
BITMAPINFOHEADER *ptBITMAPINFOHEADER;
int iWidth;
int iHeight;
int iBMPBpp;
int y;
unsigned char *pucSrc;
unsigned char *pucDest;
int iLineWidthAlign;
int iLineWidthReal;
ptBITMAPFILEHEADER = (BITMAPFILEHEADER *)aFileHead;
ptBITMAPINFOHEADER = (BITMAPINFOHEADER *)(aFileHead + sizeof(BITMAPFILEHEADER));
iWidth = ptBITMAPINFOHEADER->biWidth;
iHeight = ptBITMAPINFOHEADER->biHeight;
iBMPBpp = ptBITMAPINFOHEADER->biBitCount;
// 只支持24位深度的BMP文件
if (iBMPBpp != 24)
{
return -1;
}
// 初始化像素数据结构体
ptPixelDatas->iWidth = iWidth;
ptPixelDatas->iHeight = iHeight;
ptPixelDatas->aucPixelDatas = malloc(iWidth * iHeight * ptPixelDatas->iBpp / 8);//每个像素占用的字节数 = 位深度 / 8
ptPixelDatas->iLineBytes = iWidth * ptPixelDatas->iBpp / 8;
if (NULL == ptPixelDatas->aucPixelDatas)//分配失败
{
return -1;
}
iLineWidthReal = iWidth * iBMPBpp / 8;//每行像素数据占用的字节数
iLineWidthAlign = (iLineWidthReal + 3) & ~0x3; /* 对行宽度进行4字节对齐,向4取整 */
// 计算数据指针的起始位置
// 将pucSrc指向BITMAPFILEHEADER结构体中bfOffBits成员指定的偏移位置
pucSrc = aFileHead + ptBITMAPFILEHEADER->bfOffBits;
// 依据图像高度和行宽度对齐参数,调整pucSrc的指向位置,使其指向图像数据的起始位置
pucSrc = pucSrc + (iHeight - 1) * iLineWidthAlign;
pucDest = ptPixelDatas->aucPixelDatas;
// 遍历图像高度,进行像素数据转换
for (y = 0; y < iHeight; y++)
{
memcpy(pucDest,pucSrc,iLineWidthReal);
pucSrc -= iLineWidthAlign; // 移动到下一行的起始位置
pucDest += iLineWidthReal; // 移动到下一行的起始位置 // 移动到下一行的起始位置
}
return 0;
}
pucSrc
最初被设置为指向BITMAPFILEHEADER
结构体中bfOffBits
成员指定的偏移位置。这个偏移量通常表示.bmp文件中图像数据的起始位置。- 调整数据指针:关键部分在于这行代码:
// 计算数据指针的起始位置
// 将pucSrc指向BITMAPFILEHEADER结构体中bfOffBits成员指定的偏移位置
pucSrc = aFileHead + ptBITMAPFILEHEADER->bfOffBits;
// 依据图像高度和行宽度对齐参数,调整pucSrc的指向位置,使其指向图像数据的起始位置
pucSrc = pucSrc + (iHeight - 1) * iLineWidthAlign;
这里将pucSrc
指针向后移动,使其指向图像数据的实际起始读取位置。移动距离由(iHeight - 1) * iLineWidthAlign
计算得出,具体解释如下:
iHeight - 1
:由于大多数图像文件(包括.bmp)以从上到下的方式存储像素数据,而LCD屏幕通常以从下到上的顺序显示图像。因此,为了将文件中的像素数据正确地映射到LCD屏幕上,需要从文件底部开始读取数据。这里的iHeight - 1
就是将指针定位到图像数据的最后一行(即屏幕显示的第一行)。iLineWidthAlign
:表示对齐后的每行像素数据占用的字节数。乘以(iHeight - 1)
是为了跳过前面所有行的数据,直接到达最后一行的起始位置。
现在来描述文件存储位置到LCD屏幕的映射关系:
假设您有一个.bmp文件,其中图像数据按照以下方式存储:
+-----------------------+
| 图像数据第1行 |
+-----------------------+
| 图像数据第2行 |
+-----------------------+
| ... |
+-----------------------+
| 图像数据第n行(最后一行)|
+-----------------------+
而LCD屏幕的显示顺序是从下到上,如下所示:
+-----------------------+
| 图像数据第n行(屏幕第一行)|
+-----------------------+
| 图像数据第n-1行 |
+-----------------------+
| ... |
+-----------------------+
| 图像数据第1行 |
+-----------------------+
通过上述代码中对pucSrc
指针的调整,使得它指向图像数据的最后一行(即屏幕显示的第一行)。接着,您可以按照屏幕显示顺序逐行读取pucSrc
指针所指向的像素数据,并将其传递给LCD控制器进行显示。这样就实现了从文件存储位置到LCD屏幕的正确映射。