位图(bitmap)最简单,其文件扩展名为*.BMP。bmp文件存储的是RGB颜色,可以直接把RGB数据放在LCD上显示。在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图时候才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的(Flash中使用了适量图,是按相同颜色区域存储的)。
位图文件(*.BMP)的格式
位图文件主要分为如下3个部分:
块名称 | 对应Windows结构体定义 | 大小(Byte) |
---|---|---|
文件信息头 | BITMAPFILEHEADER | 14 |
位图信息头 | BITMAPINFOHEADER | 40 |
RGB颜色阵列 | BYTE* | 由图像长宽尺寸决定 |
1、文件信息头BITMAPFILEHEADER
typedef struct tagBITMAPFILEHEADER { /* bmfh */
UINT bfType; //说明文件的类型,该值必需是0x4D42,也就是字符'BM'。
DWORD bfSize; //说明该位图文件的大小,用字节为单位
UINT bfReserved1; //保留
UINT bfReserved2; //保留
DWORD bfOffBits; //说明从文件头开始到实际的图象数据之间的字节的偏移量
} BITMAPFILEHEADER;
2、位图信息头BITMAPINFOHEADER
typedef struct tagBITMAPINFOHEADER { /* bmih */
DWORD biSize; //说明BITMAPINFOHEADER结构所需要的字数
LONG biWidth; //说明图象的宽度,以象素为单位
LONG biHeight; //说明图象的高度,以象素为单位
WORD biBitCount; //说明比特数/象素bpp,其值为1、4、8、16、24、或32
DWORD biSizeImage; //说明图象的大小,以字节为单位
…………….
} BITMAPINFOHEADER;
3、RGB颜色阵列
在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位(3byte)的RGB位图像素数据格式是:
蓝色B值 | 绿色G值 | 红色R值 |
---|
对于32位(4byte)的RGB位图像素数据格式是:
蓝色B值 | 绿色G值 | 红色R值 | 透明通道A值 |
---|
注意!!!
1.由于Windows在进行行扫描的时候最小的单位为4个字节,所以当 图片宽x每个像素的字节数 != 4的整数倍 时要在每行的后面补上缺少的字节,以0填充。
4字节对齐–iLineWidthAlign = (iLineWidthReal + 3) & ~0x3。
2.bmp文件中的像素的存储是从左下角开始按行排列,而在LCD上显示是从右上角开始按行排列,要进行转换。
3.bmp文件中颜色排列是BGR,bpp转换时要注意。
总结:自定义一个图片的象素数据结构体PixelDatas,解析bmp文件,将该结构体信息填充完整,即获得了该bmp文件的所有信息。把BMP文件中的象素数据,取出并转换为能在显示设备上使用的格式。填充图片文件解析模块结构体PicFileParser并向上注册。
typedef struct PicFileParser { //图片文件解析模块结构体
char *name; /* 图片文件解析模块的名字 */
int (*isSupport)(PT_FileMap ptFileMap); /* 是否支持某文件 */
/* 从文件中解析出图像的象素数据 */
int (*GetPixelDatas)(PT_FileMap ptFileMap, PT_PixelDatas ptPixelDatas);
int (*FreePixelDatas)(PT_PixelDatas ptPixelDatas); /* 释放图像的象素数据所占内存 */
struct PicFileParser *ptNext; /* 链表 */
}T_PicFileParser, *PT_PicFileParser;
typedef struct PixelDatas { //图片的象素数据结构体
int iWidth; /* 宽度: 一行有多少个象素 */
int iHeight; /* 高度: 一列有多少个象素 */
int iBpp; /* 一个象素用多少位来表示 */
int iLineBytes; /* 一行数据有多少字节 */
int iTotalBytes; /* 所有字节数 */
/* 象素数据存储的地方,将bmp文件中数据拷贝到自己分配的缓冲区中 */
unsigned char *aucPixelDatas;
}T_PixelDatas, *PT_PixelDatas;
BMP模块是否支持该文件,即该文件是否为BMP文件
static int isBMPFormat(PT_FileMap ptFileMap){
unsigned char *aFileHead = ptFileMap->pucFileMapMem;
if (aFileHead[0] != 0x42 || aFileHead[1] != 0x4d) //bmp文件前两个字节为BM
return 0;
else
return 1;
}
把BMP文件中的象素数据,取出并转换为能在显示设备上使用的格式。但是bmp文件中的像素的存储是从左下角开始按行排列,而在LCD上显示是从右上角开始按行排列,要进行转换。
static int GetPixelDatasFrmBMP(PT_FileMap ptFileMap, PT_PixelDatas ptPixelDatas){
unsigned char *aFileHead = ptFileMap->pucFileMapMem; //文件映射的起始地址
BITMAPFILEHEADER *ptBITMAPFILEHEADER = (BITMAPFILEHEADER *)aFileHead; //文件信息头
BITMAPINFOHEADER *ptBITMAPINFOHEADER = (BITMAPINFOHEADER *)(aFileHead + sizeof(BITMAPFILEHEADER)); //位图信息头
int iWidth = ptBITMAPINFOHEADER->biWidth; //宽
int iHeight = ptBITMAPINFOHEADER->biHeight; //高
int iBMPBpp = ptBITMAPINFOHEADER->biBitCount; //bpp,源24位
ptPixelDatas->iWidth = iWidth; //设置PixelDatas结构体内容,bpp为输入参数
ptPixelDatas->iHeight = iHeight;
//ptPixelDatas->iBpp = iBpp;
ptPixelDatas->iLineBytes = iWidth * ptPixelDatas->iBpp / 8;
ptPixelDatas->iTotalBytes = ptPixelDatas->iHeight * ptPixelDatas->iLineBytes;
ptPixelDatas->aucPixelDatas = malloc(ptPixelDatas->iTotalBytes); //分配缓冲区
int iLineWidthReal = iWidth * iBMPBpp / 8; // 行字节数
int iLineWidthAlign = (iLineWidthReal + 3) & ~0x3; // 向4取整后的行字节数
//源:bmp文件的最后一行
unsigned char *pucSrc = aFileHead + ptBITMAPFILEHEADER->bfOffBits;
pucSrc = pucSrc + (iHeight - 1) * iLineWidthAlign;
//目的:PixelDatas结构体内的aucPixelDatas指针
unsigned char *pucDest = ptPixelDatas->aucPixelDatas;
for (int y = 0; y < iHeight; y++){
CovertOneLine(iWidth, iBMPBpp, ptPixelDatas->iBpp, pucSrc, pucDest);
pucSrc -= iLineWidthAlign;
pucDest += ptPixelDatas->iLineBytes;
}
return 0;
}
static int FreePixelDatasForBMP(PT_PixelDatas ptPixelDatas){
free(ptPixelDatas->aucPixelDatas);
return 0;
}
把BMP文件中一行的象素数据,转换为能在显示设备上使用的格式,但是bmp文件中颜色排列是BGR,bpp转换时要注意改为RGB顺序。
static int CovertOneLine(int iWidth, int iSrcBpp, int iDstBpp, unsigned char *pudSrcDatas, unsigned char *pudDstDatas){
unsigned int dwRed,dwGreen,dwBlue;
unsigned int dwColor;
unsigned short *pwDstDatas16bpp = (unsigned short *)pudDstDatas; //16位bpp,2字节
unsigned int *pwDstDatas32bpp = (unsigned int *)pudDstDatas; //32位bpp,4字节
if (iDstBpp == 24) //源24bpp,目的的bpp相同,直接复制
memcpy(pudDstDatas, pudSrcDatas, iWidth*3);
else{
for (int i = 0; i < iWidth; i++){
dwBlue = pudSrcDatas[pos++]; //取出蓝绿红数据,3字节
dwGreen = pudSrcDatas[pos++];
dwRed = pudSrcDatas[pos++];
if (iDstBpp == 32){ //算出32位的bpp,红绿蓝
dwColor = (dwRed << 16) | (dwGreen << 8) | dwBlue;
*pwDstDatas32bpp = dwColor;
pwDstDatas32bpp++;
}
else if (iDstBpp == 16){ //算出16位的bpp,红绿蓝565
/* 565 */
dwRed = dwRed >> 3;
dwGreen = dwGreen >> 2;
dwBlue = dwBlue >> 3;
dwColor = (dwRed << 11) | (dwGreen << 5) | (dwBlue);
*pwDstDatas16bpp = dwColor;
pwDstDatas16bpp++;
}
}
}
return 0;
}