主要在之前的解码的基础上面添加了图片缩放功能。
大家可以看看BMP解码过程或者思路。
资料网上非常多,在此就不在说明,直接上代码。
BMPdecode.c
/*************************************************************************************************************
* 文件名: bmpdecode.c
* 功能: BMP图片软件解码
* 作者: cp1300@139.com
* 创建时间: 2012年12月7日20:30
* 最后修改时间:2012年12月9日
* 详细: 只支持非压缩的BMP,16bit,24bit,32bit
* 图片文件最大由BMP_MAX_BUFF决定
* 因为使用了FATFS,以及比较多的临时变量,内联函数可能需要比较大的堆栈
* 因为添加了图片缩放功能,因此程序比以前的效率稍微低下了一点,主要是因为画点的时候需要判断了
*************************************************************************************************************/
#include "tft_lcd.h"
#include "system.h"
#include "bmpdecode.h"
#include "ff.h"
//图片缓冲区,目前定义为10MB,也就意味着最大只能打开10MB的位图文件
#define BMP_MAX_BUFF 10*1024*1024 //定义缓冲区最大大小
static u8 BmpImageBuff[BMP_MAX_BUFF]; //图片缓冲区
//显示窗口的最大值,一般定义为显示器大小
#define LCD_MAX_WIDTH 800 //最大宽度
#define LCD_MAX_HEIGHT 480 //最大高度
//图像数据压缩类型,目前只支持没有压缩的位图
#define BI_RGB 0 //没有压缩
#define BI_RLE8 1 //每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引)
#define BI_RLE4 2 //每个象素4比特的RLE压缩编码,压缩格式由2字节组成
#define BI_BITFIELDS 3 //每个象素的比特由指定的掩码决定
//16,24,32位BMP文件头部信息结构
typedef struct
{
u16 Invalid; //无效的填充字节,用于让数据对齐
u16 bfType ; //文件标志.只对'BM',用来识别BMP位图类型
u32 bfSize ; //文件大小,占四个字节
u32 bfReserved1 ; //保留
u32 bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的的偏移
u32 bmfHeaderSize; //图像描述信息块的大小,常为28H
u32 biWidth ; //说明图象的宽度,以象素为单位
u32 biHeight ; //说明图象的高度,以象素为单位
u16 biPlanes ; //为目标设备说明位面数,其值将总是被设为1
u16 biBitCount ; //说明比特数/象素,其值为1、4、8、16、24、或32
u32 biCompression ; //说明图象数据压缩的类型。其值可以是下述值之一:
//BI_RGB:没有压缩;
//BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);
//BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
//BI_BITFIELDS:每个象素的比特由指定的掩码决定。
u32 biSizeImage ; //说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0
u32 biXPelsPerMeter ;//说明水平分辨率,用象素/米表示
u32 biYPelsPerMeter ;//说明垂直分辨率,用象素/米表示
u32 biClrImportant ; //说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。
}BMPFILEHEADER;
//BMP图片相关的信息结构
struct BMP_IMAGE
{
u16 bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的的偏移
u16 biWidth ; //说明图象的宽度,以象素为单位
u16 biHeight ; //说明图象的高度,以象素为单位
u16 biBitCount ; //说明比特数/象素,其值为1、4、8、16、24、或32
u32 biSizeImage; //位图数据的大小
u32 biSizeFile; //位图文件大小
}BmpFile;
//图像信息
struct BMPPIC_POS
{
u32 ImgWidth; //图像的实际宽度和高度
u32 ImgHeight;
u32 Div_Fac; //图像缩放系数(扩大了10000倍)
u32 S_Height; //设定的显示高度和宽度
u32 S_Width;
u32 S_XOFFSET; //X,Y起始偏移量
u32 S_YOFFSET;
u32 staticx; //当前显示的X,Y坐标
u32 staticy;
}BMPPICINFO;
//内部函数声明
static BMP_ERROR OpenBmpFile(const char *FileName,u8 *buff,u32 FileMaxSize); //打开BMP图像,并将数据读取到内存
__inline u8 ReadByteData(u32 Offset); //读取一字节指定偏移的图片数据
static BMP_ERROR DecodingHead(u8 *FileBuff); //解码BMP图片头部
__inline void BmpImageDrow(u16 x,u16 y,u16 data); //BMP图像画点函数
static BMP_ERROR BmpDecode(u16 x1,u16 y1,u16 x2,u16 y2,u8 *BmpBuff); //BMP图像解码核心
static void ImageDrow_Init(void); //BMP画点函数初始化
/*************************************************************************************************************************
*函数 : static BMP_ERROR OpenBmpFile(const char *FileName,u8 *buff,u32 FileMaxSize)
*功能 : 打开BMP图像,并将数据读取到内存
*参数 : FileName:文件名,路径指针;buff:读取缓冲区;FileMaxSize:文件最大限制
*返回 : BMP_ERROR
*依赖 : FATFS文件系统支持
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121207
*说明 : 调用FATFS打开BMP图片文件
*************************************************************************************************************************/
static BMP_ERROR OpenBmpFile(const char *FileName,u8 *buff,u32 FileMaxSize)
{
FIL file;
UINT cnt;
int status;
BMP_ERROR error = BMP_OK;
status = f_open(&file,FileName,FA_READ); //只读方式打开文件
if(status != FR_OK) //打开文件错误
{
uart_printf("open \"%s\" error!\r\n",FileName);
status = BMP_OPEN_ERROR;
}
else
{
//获取文件大小
uart_printf("file size : %dB\r\n",file.fsize); //输出文件大小
if(file.fsize > FileMaxSize)
{
uart_printf("file size > %d\r\n",FileMaxSize);
status = BMP_SIZE_ERROR;
}
else
{
status = f_read(&file,buff,file.fsize,&cnt); //读取文件
uart_printf("Read File Num=%d\r\n",cnt);
if(cnt == file.fsize) //判断文件是否读取完毕
{
uart_printf("read file end!\r\n");
BmpFile.biSizeFile = file.fsize; //存储位图文件大小
status = BMP_OK;
}
else
{
uart_printf("read file error!\r\n");
status = BMP_READ_ERROR;
}
}
}
f_close(&file);
return error;
}
/*************************************************************************************************************************
*函数 : __inline u8 ReadByteData(u32 Offset)
*功能 : 读取一字节指定偏移的图片数据
*参数 : Offset:地址偏移
*返回 : 数据
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121207
*说明 : 方便移植
*************************************************************************************************************************/
__inline u8 ReadByteData(u32 Offset)
{
return BmpImageBuff[Offset];
}
/*************************************************************************************************************************
*函数 : static BMP_ERROR DecodingHead(u8 *FileBuff)
*功能 : 解码BMP图片头部
*参数 : *FileBuff:bmp图像数据缓冲区指针
*返回 : BMP_ERROR
*依赖 : static BMP_ERROR OpenBmpFile(const char *FileName,u8 *buff,u32 FileMaxSize)
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121207
*说明 : 注意BMP的头部信息并没有按照32BIT对齐,在前面插了两个字节后即可对齐,非对齐访问会发生异常
*************************************************************************************************************************/
static BMP_ERROR DecodingHead(u8 *FileBuff)
{
BMP_ERROR error = BMP_OK;
u8 buff[52];
u8 cnt;
BMPFILEHEADER *pbmp;//临时指针,用于获取BMP文件的头部信息
u8 *p = buff + 2; //BMP头部共50字节,前面的两个字节用于偏移对齐
for(cnt = 0;cnt < 50;cnt ++)
{
p[cnt] = FileBuff[cnt]; //复制(读取)头部到缓冲区
}
pbmp = (BMPFILEHEADER*)buff; //得到BMP的头部信息
BmpFile.bfOffBits = pbmp->bfOffBits; //位图数据偏移地址偏移
BmpFile.biBitCount = pbmp->biBitCount; //位图数据的颜色深度只支持16bit,24bit,32bit
BmpFile.biWidth = pbmp->biWidth; //位图的水平像素数
BmpFile.biHeight = pbmp->biHeight; //位图的垂直像素数
BmpFile.biSizeImage = pbmp->biSizeImage;//位图的数据大小,有的图片这个值为0
/****************************************************/
//调试
uart_printf("地址偏移:%d\r\n",BmpFile.bfOffBits);
uart_printf("颜色深度:%d\r\n",BmpFile.biBitCount);
uart_printf("水平分辨率:%d\r\n",BmpFile.biWidth);
uart_printf("垂直分辨率:%d\r\n",BmpFile.biHeight);
uart_printf("数据大小:%d\r\n",BmpFile.biSizeImage);
uart_printf("位图标志:%X\r\n",pbmp->bfType);
/****************************************************/
if(pbmp->bfType == 0x4d42) //检测位图头部信息
{
if((BmpFile.biBitCount != 16) && (BmpFile.biBitCount != 24) && (BmpFile.biBitCount != 32))
error = BMP_TYPE_ERROR;
else
error = BMP_OK;
}
else
error = BMP_TYPE_ERROR;
return error;
}
/*************************************************************************************************************************
*函数 : __inline void BmpImageDrow(u16 x,u16 y,u16 data)
*功能 : BMP图像画点函数
*参数 : x,y:xy坐标,data:RGB565数据
*返回 : 无
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121207
*说明 : 因为进行缩放的时候,有些点不用进行显示
*************************************************************************************************************************/
__inline void BmpImageDrow(u16 x,u16 y,u16 data)
{
if(x != BMPPICINFO.staticx || y != BMPPICINFO.staticy)
{
BMPPICINFO.staticx = x;
BMPPICINFO.staticy = y;
LCD_DrawPoint(x+BMPPICINFO.S_XOFFSET,y+BMPPICINFO.S_YOFFSET,data);
}
}
/*************************************************************************************************************************
*函数 : static BMP_ERROR BmpDecode(u16 x1,u16 y1,u16 x2,u16 y2,u8 *BmpBuff)
*功能 : BMP图像解码核心
*参数 : x1,y1:窗口起始坐标;x2,y2:窗口结束坐标;BmpBuff:图像缓冲区指针
*返回 : BMP_ERROR
*依赖 : 需要先解码头信息
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121209
*说明 : 解码图像
*************************************************************************************************************************/
static BMP_ERROR BmpDecode(u16 x1,u16 y1,u16 x2,u16 y2,u8 *BmpBuff)
{
BMP_ERROR error = BMP_OK;
u32 data; //存放24BIT图片的一个像素颜色值
u32 bmp_cnt; //已经读取的照片数据计数
u16 uiTemp; //x轴方向实际存储的像素数据字节数
u16 xValid; //x轴方向有效的像素数据字节数
u16 xcnt; //X轴方向像素点数据字节数计数
u16 x,y; //画点坐标
u8 temp;
if((BmpFile.biWidth * BmpFile.biBitCount / 8) % 4)//水平像素字节数不是4的整数倍
uiTemp = ((BmpFile.biWidth * BmpFile.biBitCount / 8) / 4 + 1) * 4;//将水平像素字节数扩展成4的整数倍
else
uiTemp = BmpFile.biWidth * BmpFile.biBitCount / 8;
xValid = BmpFile.biWidth * (BmpFile.biBitCount / 8); //计算水平有效地数据字节数
y = BMPPICINFO.ImgHeight - 1; //BMP图片由左下角到右上角刷新,因此起点y坐标要加上图片的高度
x = 0;
bmp_cnt = BmpFile.bfOffBits;//将文件数据开始赋值给数据计数器
xcnt = 0; //X方向像素点计数器清零
do
{
switch (BmpFile.biBitCount)//判断图片颜色深度
{
case 32: //32BIT ARGB8888 //之所以使用一个字节读取一方面方便移植到内存较小的单片机上面执行,另一方面避免内存非对齐访问产生异常。
{
data = ReadByteData(bmp_cnt++);
temp = ReadByteData(bmp_cnt++);
data |= temp << 8;
temp = ReadByteData(bmp_cnt++);
data |= temp << 16;
bmp_cnt ++;
data = RGB565(data); //RGB888 ---> RGB565
}break;
case 24: //24BIT RGB888 //可能存在4字节对齐问题
{
data = ReadByteData(bmp_cnt++);
temp = ReadByteData(bmp_cnt++);
data |= temp << 8;
temp = ReadByteData(bmp_cnt++);
data |= temp << 16;
xcnt += 3;
data = RGB565(data); //RGB888 ---> RGB565
}break;
case 16: //16BIT RGB555 //可能存在4字节对齐问题
{
data = ReadByteData(bmp_cnt++);
temp = ReadByteData(bmp_cnt++);
data |= temp << 8;
xcnt += 2;
data = ((data & 0xffe0) << 1) | (data & 0x001f);//RGB555 ---> RGB565
data |= ((data & BIT6) ? BIT5 : 0);
};break;
default : break;//只支持16BIT,24BIT,32BIT图片的解码
}
switch(BMPPICINFO.Div_Fac)
{
case 10000 : BmpImageDrow(x,y,data);break; //图片无需缩放
default : BmpImageDrow(x*BMPPICINFO.Div_Fac/10000,y*BMPPICINFO.Div_Fac/10000,data);break; //图片需要缩放
}
x ++;
if(x == BMPPICINFO.ImgWidth) //一行解码完成,换行
{
x = 0;
if(y == 0) break;
y --;
}
if(xcnt == xValid) //插值,无效数据,跳过
{
bmp_cnt += uiTemp - xcnt;
xcnt = 0;
}
}while(bmp_cnt < BmpFile.biSizeFile); //判断数据是否读取完毕
uart_printf("bmp_cnt = 0x%X; BmpFile.biSizeFile = 0x%X\r\n",bmp_cnt,BmpFile.biSizeFile);
return error;
}
/*************************************************************************************************************************
*函数 : static void ImageDrow_Init(void)
*功能 : BMP画点函数初始化
*参数 : 无
*返回 : 无
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121209
*说明 : 用于计算显示坐标以及缩放系数
*************************************************************************************************************************/
static void ImageDrow_Init(void)
{
float temp,temp1;
BMPPICINFO.ImgWidth = BmpFile.biWidth; //获取图像实际宽度
BMPPICINFO.ImgHeight = BmpFile.biHeight; //获取图像实际高度
temp=(float)BMPPICINFO.S_Width / BMPPICINFO.ImgWidth;
temp1=(float)BMPPICINFO.S_Height / BMPPICINFO.ImgHeight;
if(temp < temp1) temp1 = temp; //取较小的那个边的缩放系数
if(temp1 > 1) temp1 = 1; //不能放大,原图大小显示
BMPPICINFO.Div_Fac = temp1 * 10000; //将缩放系数扩大10000倍
//计算偏移,将图片置于显示区域的起始位置
BMPPICINFO.S_XOFFSET += (BMPPICINFO.S_Width - temp1 * BMPPICINFO.ImgWidth) / 2;
BMPPICINFO.S_YOFFSET += (BMPPICINFO.S_Height - temp1 * BMPPICINFO.ImgHeight) / 2;
//将当前显示坐标放到一个不可能的值上面
BMPPICINFO.staticx = 0xffff;
BMPPICINFO.staticy = 0xffff;
}
/*************************************************************************************************************************
*函数 : BMP_ERROR ShowBmpImage(u16 x1,u16 y1,u16 x2,u16 y2,const char *BmpFile)
*功能 : 指定位置显示一张BMP图片
*参数 : x1,y1:窗口起始坐标;x2,y2:窗口结束坐标;BmpFile:BMP文件路径以及名称
*返回 : BMP_ERROR
*依赖 : static BMP_ERROR BmpDecode(u16 x1,u16 y1,u16 x2,u16 y2,u8 *BmpBuff)
*作者 : cp1300@139.com
*时间 : 20121207
*最后修改时间 : 20121207
*说明 : 直接在LCD上面显示一张BMP图片,需要FATFS支持
* 当结束窗口坐标为0的时候结束坐标自动换成屏幕结束坐标
*************************************************************************************************************************/
BMP_ERROR ShowBmpImage(u16 x1,u16 y1,u16 x2,u16 y2,const char *BmpFile)
{
BMP_ERROR error;
BMPPICINFO.S_Width = (((x2 - x1 + 1) > LCD_MAX_WIDTH) || (x2 == 0)) ? (LCD_MAX_WIDTH-x1): (x2 - x1 + 1); //计算设定的显示宽度和高度
BMPPICINFO.S_Height = (((y2 - y1 + 1) > LCD_MAX_HEIGHT) || (y2 == 0)) ? (LCD_MAX_HEIGHT-y1): (y2 - y1 + 1);
BMPPICINFO.S_XOFFSET = x1; //初始化偏移为起始坐标
BMPPICINFO.S_YOFFSET = y1;
error = OpenBmpFile(BmpFile,BmpImageBuff,BMP_MAX_BUFF);
if(error == BMP_OK)
{
error = DecodingHead(BmpImageBuff);
if(error == BMP_OK)
{
ImageDrow_Init();
error = BmpDecode(x1,y1,x2,y2,BmpImageBuff);
}
}
return error;
}