主要在之前的解碼的基礎上面添加了圖片縮放功能。
大家可以看看BMP解碼過程或者思路。
資料網上非常多,在此就不在說明,直接上代碼。
BMPdecode.c
/*************************************************************************************************************
* 文件名: bmpdecode.c
* 功能: BMP圖片軟件解碼
* 作者: [email protected]
* 創建時間: 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文件系統支持
*作者 : [email protected]
*時間 : 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:地址偏移
*返回 : 數據
*依賴 : 無
*作者 : [email protected]
*時間 : 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)
*作者 : [email protected]
*時間 : 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數據
*返回 : 無
*依賴 : 無
*作者 : [email protected]
*時間 : 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
*依賴 : 需要先解碼頭信息
*作者 : [email protected]
*時間 : 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畫點函數初始化
*參數 : 無
*返回 : 無
*依賴 : 無
*作者 : [email protected]
*時間 : 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)
*作者 : [email protected]
*時間 : 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;
}