8.3.1 Bitmap格式分析和数据提取
转载:https://www.cnblogs.com/liusiluandzhangkun/p/8856449.html
转载: https://blog.51cto.com/redwolf/229096
1.简介
位图(Bitmap),它Windows显示图片的基本格式,其文件扩展名为*.BMP。在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图个时候才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的
- BMP文件的格式
===
文件信息头BITMAPFILEHEADER
结构体定义如下:
typedef struct tagBITMAPFILEHEADER { /* bmfh */
UINT bfType; //UINT 2字节
DWORD bfSize; //DWORD 4字节
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
位图信息头BITMAPINFOHEADER
注意:.bmp文件中的数据是从左下角开始,一行一行的往上
结构体定义如下:
typedef struct tagBITMAPINFOHEADER { /* bmih */
DWORD biSize;
LONG biWidth; //LONG 四字节
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
RGB颜色阵列
有关RGB三色空间我想大家都很熟悉,这里我想说的是在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位的RGB位图像素数据格式是:
对于32位的RGB位图像素数据格式是:
透明通道也称Alpha通道,该值是该像素点的透明属性,取值在0(全透明)到255(不透明)之间。对于24位的图像来说,因为没有Alpha通道,故整个图像都不透明。
3.加载和使用
1.加载文件头
//Load the file header
BITMAPFILEHEADER header;
memset(&header, 0, sizeof(header));
inf.read((char*)&header, sizeof(header));
if(header.bfType != 0x4D42)
return false;
2.加载位图信息
//Load the image information header
BITMAPINFOHEADER infoheader;
memset(&infoheader, 0, sizeof(infoheader));
inf.read((char*)&infoheader, sizeof(infoheader));
m_iImageWidth = infoheader.biWidth;
m_iImageHeight = infoheader.biHeight;
m_iBitsPerPixel = infoheader.biBitCount;
这里我们得到了3各重要的图形属性:宽,高,以及每个像素颜色所占用的位数。
3.行对齐
由于Windows在进行行扫描的时候最小的单位为4个字节,所以当图片宽 X 每个像素的字节数 != 4的整数倍时要在每行的后面补上缺少的字节,以0填充(一般来说当图像宽度为2的幂时不需要对齐)。位图文件里的数据在写入的时候已经进行了行对齐,也就是说加载的时候不需要再做行对齐。但是这样一来图片数据的长度就不是:宽 X 高 X 每个像素的字节数了,我们需要通过下面的方法计算正确的数据长度:
//Calculate the image data size
int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;
m_iImageDataSize = iLineByteCnt * m_iImageHeight;
4.加载图片数据
对于24位和32位的位图文件,位图数据的偏移量为sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是说现在我们可以直接读取图像数据了。
if(m_pImageData) delete []m_pImageData;
m_pImageData = new unsigned char[m_iImageDataSize];
inf.read((char*)m_pImageData, m_iImageDataSize);
如果你足够细心,就会发现内存m_pImageData里的数据的确是BGR格式,可以用个纯蓝色或者是纯红色的图片测试一下。
5.绘制
4.代码解析
#include <config.h>
#include <pic_operation.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push) /* 将当前pack设置压栈保存 */
#pragma pack(1) /* 必须在结构体定义之前使用 */
typedef struct tagBITMAPFILEHEADER { /* bmfh */
unsigned short bfType;
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER { /* bmih */
unsigned long biSize;
unsigned long biWidth;
unsigned long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
unsigned long biXPelsPerMeter;
unsigned long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop) /* 恢复先前的pack设置 */
/* 是否为.bmp文件 */
int isSupportFrmBMP(unsigned char *aFileHead)
{
if((aFileHead[0] != 0x42) || (aFileHead[1] != 0x4d))
{
return 0;
}
else
{
return 1;
}
}
static int CovertOneLine(int iWidth, int iBppSrc, int iBppDest, unsigned char *pucSrc, unsigned char *pucDest)
{
unsigned int dwRed;
unsigned int dwGreen;
unsigned int dwBlue;
unsigned int dwColor;
int i;
int pos = 0;
unsigned short *pwDstDatas16bpp = (unsigned short *)pucDest;
unsigned int *pwDstDatas32bpp = (unsigned int *)pucDest;
if(iBppSrc != 24)
{
return -1;
}
if(iBppDest == 24)
{
memcpy(pucDest, pucSrc, iWidth * 3);
}
else
{
for(i = 0; i < iWidth; i++)
{
/* 取出颜色 */
dwBlue = pucSrc[pos++];
dwGreen = pucSrc[pos++];
dwRed = pucSrc[pos++];
if(iBppDest == 32)
{
dwColor = (dwRed<<16) | (dwGreen<<8) | (dwBlue<<0);
*pwDstDatas32bpp = dwColor;
pwDstDatas32bpp++;
}
else if(iBppDest == 16)
{
dwRed = dwRed >> 3;
dwGreen = dwGreen >> 2;
dwBlue = dwBlue >> 3;
dwColor = (dwRed<<11) | (dwGreen<<5) | (dwBlue<<0);
*pwDstDatas16bpp = dwColor;
pwDstDatas16bpp++;
}
}
}
return 0;
}
/* ptPixelDatas->iBpp为外部输入的
* 即打开的图片必须为24bit,而得到的图片bpp格式由外部决定
*/
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;
if(iBMPBpp != 24)
{
DBG_PRINTF("iBMPBpp = %d\n", iBMPBpp);
DBG_PRINTF("sizeof(BITMAPFILEHEADER) = %d\n", sizeof(BITMAPFILEHEADER));
return -1;
}
ptPixelDatas->iWidth = iWidth;
//ptPixelDatas->iBpp = iBMPBpp;
ptPixelDatas->iHeight = iHeight;
ptPixelDatas->aucPixelDatas = malloc(iWidth * iHeight * ptPixelDatas->iBpp / 8);
ptPixelDatas->iLineBytes = iWidth * ptPixelDatas->iBpp / 8;
if (NULL == ptPixelDatas->aucPixelDatas)
{
return -1;
}
iLineWidthReal = iWidth * iBMPBpp / 8;
/* 向4字节对齐 */
iLineWidthAlign = (iLineWidthReal + 3) & ~0x3;
pucSrc = aFileHead + ptBITMAPFILEHEADER->bfOffBits;
/* 因为存储的第一个数据为图片中左下角的第一个点
* 因为lcd上显示是从左上角开始的,所以这里跳到数据中最后一行开始取出数据,放入目的。
* 这样取到的点就是左上角的数据。(图解见前面)
*/
pucSrc = pucSrc + (iHeight - 1) * iLineWidthAlign;
pucDest = ptPixelDatas->aucPixelDatas;
for(y = 0; y < iHeight; y++)
{
CovertOneLine(iWidth, iBMPBpp, ptPixelDatas->iBpp, pucSrc, pucDest);
pucSrc -= iLineWidthAlign;
/* 注意从src中取出的数据需要Align,而写到framebuffer中则不需要 */
pucDest += ptPixelDatas->iLineBytes;
}
return 0;
}
static int FreePixelDatasFrmBMP(PT_PixelDatas ptPixelDatas)
{
free(ptPixelDatas->aucPixelDatas);
return 0;
}
T_PicFileParser g_tBMPParser = {
.name = "bmp",
.isSupport = isSupportFrmBMP,
.GetPixelDatas = GetPixelDatasFrmBMP,
.FreePixelDatas = FreePixelDatasFrmBMP,
};