一、項(xiàng)目介紹
項(xiàng)目是基于STM32設(shè)計(jì)的數(shù)碼相冊(cè),能夠通過(guò)LCD顯示屏解碼顯示主流的圖片,支持bmp、jpg、gif等格式。用戶可以通過(guò)按鍵或者觸摸屏來(lái)切換圖片,同時(shí)還可以旋轉(zhuǎn)顯示,并能夠自適應(yīng)居中顯示,小尺寸圖片居中顯示,大尺寸圖片自動(dòng)縮小顯示(超出屏幕范圍)。圖片從SD卡中獲取。
二、設(shè)計(jì)思路
2.1 硬件設(shè)計(jì)
本項(xiàng)目所需的主要硬件:
- STM32F103ZET6
- LCD屏幕
- SD卡模塊
- 按鍵和觸摸屏
2.2 軟件設(shè)計(jì)
(1)解碼圖片
在STM32芯片中,解碼圖片需要將讀取到的數(shù)據(jù)存入圖形緩沖區(qū)中,以便進(jìn)行圖畫(huà)顯示。常用的解碼算法有JPEG解碼和BMP解碼。
(2)圖片顯示
為了更好的實(shí)現(xiàn)圖片旋轉(zhuǎn)和縮放功能,在顯示圖片時(shí)需對(duì)其進(jìn)行矩陣運(yùn)算。通過(guò)左右翻轉(zhuǎn)和上下翻轉(zhuǎn),可實(shí)現(xiàn)圖片的旋轉(zhuǎn)功能。通過(guò)計(jì)算圖片與顯示屏幕之間的比例關(guān)系并進(jìn)行縮放,實(shí)現(xiàn)自適應(yīng)居中和圖片的縮放功能。
(3)SD卡
SD卡模塊可通過(guò)SPI接口與STM32芯片進(jìn)行通信,讀取SD卡中的圖片數(shù)據(jù),實(shí)現(xiàn)對(duì)圖片的加載和顯示。
(4)按鍵和觸摸屏
在使用過(guò)程中,用戶可以通過(guò)按鍵和觸摸屏對(duì)圖片進(jìn)行切換、旋轉(zhuǎn)和縮放等操作。通過(guò)設(shè)置中斷處理函數(shù),響應(yīng)用戶的操作并及時(shí)更新顯示屏幕上的圖片。
2.3 圖片播放流程圖
2.4 顯示效果
三、代碼設(shè)計(jì)
3.1 主函數(shù)
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include <string.h>
#include <stdio.h>
#include "sd.h" //SD卡
#include "ff.h" //文件系統(tǒng)
#include "bmp.h" //文件系統(tǒng)
#include "iic.h"
#include "at24c02.h"
#include "xpt2046.h"
#include "lcd.h"
FATFS fs; // 用戶定義的文件系統(tǒng)結(jié)構(gòu)體
int main()
{
DIR dir_dp;
FILINFO file_info;
u32 sd_size; //存放SD卡返回的容量
BeepInit(); //蜂鳴器初始化
LedInit(); //LED燈初始化
UsartInit(USART1,72,115200);
KeyInit(); //按鍵初始化
IICInit();
LcdInit();
TOUCH_Init();
//TOUCH_ADJUST(); //觸摸屏校準(zhǔn)
printf("串口工作正常!rn");
if(SDCardDeviceInit())
{
printf("SD卡初始化失敗!rn");
}
sd_size=GetSDCardSectorCount(); //檢測(cè)SD卡大小,返回值右移11位得到以M為單位的容量
printf("SD卡Sizeof:%drn",sd_size>>11);
f_mount(&fs,"0:",1); // 注冊(cè)文件系統(tǒng)工作區(qū),驅(qū)動(dòng)器號(hào) 0,初始化后其他函數(shù)可使用里面的參數(shù)
LcdClear(0xFFFF);
//f_mkdir("0:/目錄創(chuàng)建測(cè)試!"); //測(cè)試OK
//f_unlink("0:/123"); //刪除目錄,注意只能刪除空目錄
//f_unlink("0:/1.bmp");//刪除文件
//printf("%drn",Show_BMP("1.bmp"));
if(f_opendir(&dir_dp,"0:/bmp")!=FR_OK)printf("目錄打開(kāi)失敗!rn");
//循環(huán)讀取目錄
while(f_readdir(&dir_dp,&file_info)==FR_OK)
{
if(file_info.fname[0]==0)break; //判斷目錄跳出條件,表示目錄已經(jīng)讀取完畢
if(strstr(file_info.fname,".bmp")) //過(guò)濾目錄
{
printf("文件名稱: %s,文件大小: %ld 字節(jié)rn",file_info.fname,file_info.fsize);
}else printf("文件名稱: %s,文件大小: %ld 字節(jié)rn",file_info.fname,file_info.fsize);
}
if(f_closedir(&dir_dp)!=FR_OK)printf("目錄關(guān)閉失敗!rn");
while(1)
{
LED1=!LED1;
DelayMs(100);
}
}
3.2 BMP圖片解碼
#include "bmp.h"
unsigned short RGB888ToRGB565(unsigned int n888Color)
{
unsigned short n565Color = 0;
// 獲取RGB單色,并截取高位
unsigned char cRed = (n888Color & RGB888_RED) >> 19;
unsigned char cGreen = (n888Color & RGB888_GREEN) >> 10;
unsigned char cBlue = (n888Color & RGB888_BLUE) >> 3;
// 連接
n565Color = (cRed << 11) + (cGreen << 5) + (cBlue << 0);
return n565Color;
}
unsigned int RGB565ToRGB888(unsigned short n565Color)
{
unsigned int n888Color = 0;
// 獲取RGB單色,并填充低位
unsigned char cRed = (n565Color & RGB565_RED) >> 8;
unsigned char cGreen = (n565Color & RGB565_GREEN) >> 3;
unsigned char cBlue = (n565Color & RGB565_BLUE) << 3;
// 連接
n888Color = (cRed << 16) + (cGreen << 8) + (cBlue << 0);
return n888Color;
}
/*
函數(shù)功能:實(shí)現(xiàn)截圖功能
參 數(shù):
char filename:文件名稱
返 回 值:0表示成功,1表示失敗
*/
u8 C_BMP(const char *filename,u32 Width,u32 Height)
{
FIL file; // 用戶定義的文件系統(tǒng)結(jié)構(gòu)體
u8 res; // 保存文件操作的返回值
BITMAPFILEHEADER BmpHead; //保存圖片文件頭的信息
BITMAPINFOHEADER BmpInfo; //圖片參數(shù)信息
char *p;
u32 cnt,c_32;
int x,y;
u16 c_16; //存放16位的顏色
/*1. 創(chuàng)建一張BMP圖片*/
res = f_open(&file,filename, FA_OPEN_ALWAYS | FA_WRITE);
if(res!=0)return 1;
/*2. 創(chuàng)建BMP的圖片頭參數(shù)*/
memset(&BmpHead,0,sizeof(BITMAPFILEHEADER)); //將指定空間賦值為指定的值
p=(char*)&BmpHead.bfType; //填充BMP圖片的類型
*p='B';
*(p+1)='M';
//BmpHead.bfType=0x4d42;//'B''M' //0x4d42
BmpHead.bfSize=Width*Height*3+54; //圖片的總大小
BmpHead.bfOffBits=54; //圖片數(shù)據(jù)的偏移量
res =f_write(&file,&BmpHead,sizeof(BITMAPFILEHEADER),&cnt);
if(res!=0)return 1;
/*3. 創(chuàng)建BMP圖片的參數(shù)*/
memset(&BmpInfo,0,sizeof(BITMAPINFOHEADER));
BmpInfo.biSize=sizeof(BITMAPINFOHEADER); //當(dāng)前結(jié)構(gòu)體大小
BmpInfo.biWidth=Width;
BmpInfo.biHeight=Height;
BmpInfo.biPlanes=1;
BmpInfo.biBitCount=24;
res =f_write(&file,&BmpInfo,sizeof(BITMAPINFOHEADER),&cnt);
if(res!=0)return 1;
/*4. 讀取LCD屏的顏色數(shù)據(jù),用于創(chuàng)建BMP圖片*/
for(y=Height-1;y>=0;y--)
{
for(x=0;x<Width;x++)
{
c_16=LcdReadPoint(x,y); //讀取LCD屏上一個(gè)點(diǎn)的顏色
c_32=RGB565ToRGB888(c_16); //顏色的轉(zhuǎn)換
res =f_write(&file,&c_32,3,&cnt);
if(res!=0)return 1;
}
}
/*5. 關(guān)閉文件*/
f_close(&file);
}
/*
函數(shù)功能:BMP圖片顯示功能
參 數(shù):
char filename:文件名稱
返 回 值:0表示成功,1表示失敗
*/
u8 Show_BMP(const char *filename)
{
FIL file; // 用戶定義的文件系統(tǒng)結(jié)構(gòu)體
u8 res; // 保存文件操作的返回值
BITMAPFILEHEADER BmpHead; //保存圖片文件頭的信息
BITMAPINFOHEADER BmpInfo; //圖片參數(shù)信息
char *p;
u32 cnt,c_24;
int x,y;
u16 c_16; //存放16位的顏色
/*1. 打開(kāi)一張BMP圖片*/
res = f_open(&file,filename,FA_READ);
if(res!=0)return 1;
/*2. 讀取BMP的圖片頭參數(shù)*/
res =f_read(&file,&BmpHead,sizeof(BITMAPFILEHEADER),&cnt);
if(res!=0)return 1;
/*3. 讀取BMP圖片的參數(shù)*/
res =f_read(&file,&BmpInfo,sizeof(BITMAPINFOHEADER),&cnt);
if(res!=0)return 1;
/*4.顯示BMP圖片*/
f_lseek(&file,BmpHead.bfOffBits); //移動(dòng)到RGB數(shù)據(jù)的存放位置
//后期的優(yōu)化:讀取一行的數(shù)據(jù),再顯示一行。
for(y=0;y<BmpInfo.biHeight;y++)
{
for(x=0;x<BmpInfo.biWidth;x++)
{
res =f_read(&file,&c_24,3,&cnt);
if(res!=0)return 1;
c_16=RGB888ToRGB565(c_24); //轉(zhuǎn)換顏色
LcdDrawPoint(x,y,c_16);
}
}
/*5. 關(guān)閉文件*/
f_close(&file);
}
3.3 jpeg圖片解碼
#include "piclib.h"
#include "nt35310_lcd.h"
_pic_info picinfo; //圖片信息
_pic_phy pic_phy; //圖片顯示物理接口
/*
函數(shù)功能: 劃?rùn)M線函數(shù),需要自己實(shí)現(xiàn)
*/
void Picture_DrawLine(u16 x0,u16 y0,u16 len,u16 color)
{
NT35310_Fill(x0,y0,x0+len-1,y0,color);
}
/*
函數(shù)功能: 矩形填充顏色
函數(shù)參數(shù):
x,y:起始坐標(biāo)
width,height:寬度和高度。
color:顏色數(shù)組
*/
void Picture_FillColor(u16 x,u16 y,u16 width,u16 height,u16 *color)
{
NT35310_DrawRectangle(x,y,x+width-1,y+height-1,*color);
}
/*
函數(shù)功能: 畫(huà)圖初始化,在畫(huà)圖之前,必須先調(diào)用此函數(shù)
函數(shù)參數(shù): 指定畫(huà)點(diǎn)/讀點(diǎn)
*/
void Picture_DisplayInit(void)
{
pic_phy.draw_point=NT35310_DrawPoint; //畫(huà)點(diǎn)函數(shù)實(shí)現(xiàn)
pic_phy.fill=NT35310_Fill; //填充函數(shù)實(shí)現(xiàn),僅GIF需要
pic_phy.draw_hline=Picture_DrawLine; //畫(huà)線函數(shù)實(shí)現(xiàn),僅GIF需要
pic_phy.fillcolor=Picture_FillColor; //顏色填充函數(shù)實(shí)現(xiàn),僅TJPGD需要
picinfo.lcdwidth=Lcd_Width; //得到LCD的寬度像素
picinfo.lcdheight=Lcd_Height; //得到LCD的高度像素
picinfo.ImgWidth=0; //初始化寬度為0
picinfo.ImgHeight=0;//初始化高度為0
picinfo.Div_Fac=0; //初始化縮放系數(shù)為0
picinfo.S_Height=0; //初始化設(shè)定的高度為0
picinfo.S_Width=0; //初始化設(shè)定的寬度為0
picinfo.S_XOFF=0; //初始化x軸的偏移量為0
picinfo.S_YOFF=0; //初始化y軸的偏移量為0
picinfo.staticx=0; //初始化當(dāng)前顯示到的x坐標(biāo)為0
picinfo.staticy=0; //初始化當(dāng)前顯示到的y坐標(biāo)為0
}
/*
函數(shù)功能: 初始化智能畫(huà)點(diǎn)
說(shuō)明: 內(nèi)部調(diào)用
*/
void Picture_PointInit(void)
{
float temp,temp1;
temp=(float)picinfo.S_Width/picinfo.ImgWidth;
temp1=(float)picinfo.S_Height/picinfo.ImgHeight;
if(temp<temp1)temp1=temp;//取較小的那個(gè)
if(temp1>1)temp1=1;
//使圖片處于所給區(qū)域的中間
picinfo.S_XOFF+=(picinfo.S_Width-temp1*picinfo.ImgWidth)/2;
picinfo.S_YOFF+=(picinfo.S_Height-temp1*picinfo.ImgHeight)/2;
temp1*=8192;//擴(kuò)大8192倍
picinfo.Div_Fac=temp1;
picinfo.staticx=0xffff;
picinfo.staticy=0xffff;//放到一個(gè)不可能的值上面
}
/*
函數(shù)功能: 判斷這個(gè)像素是否可以顯示
函數(shù)參數(shù):
(x,y) :像素原始坐標(biāo)
chg :功能變量.
返回值:0,不需要顯示.1,需要顯示
*/
u8 Picture_is_Pixel(u16 x,u16 y,u8 chg)
{
if(x!=picinfo.staticx||y!=picinfo.staticy)
{
if(chg==1)
{
picinfo.staticx=x;
picinfo.staticy=y;
}
return 1;
}else return 0;
}
extern u8 jpg_decode(const u8 *filename);
/*
函數(shù)功能: 繪制圖片
函數(shù)參數(shù):
FileName:要顯示的圖片文件 BMP/JPG/JPEG/GIF
x,y,width,height:坐標(biāo)及顯示區(qū)域尺寸
fast:使能jpeg/jpg小圖片(圖片尺寸小于等于液晶分辨率)快速解碼,0,不使能;1,使能.
函數(shù)說(shuō)明: 圖片在開(kāi)始和結(jié)束的坐標(biāo)點(diǎn)范圍內(nèi)顯示
*/
u8 Picture_DisplayJPG(const u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 fast)
{
u8 res;//返回值
//顯示的圖片高度、寬度
picinfo.S_Height=height;
picinfo.S_Width=width;
//顯示的開(kāi)始坐標(biāo)點(diǎn)
picinfo.S_YOFF=y;
picinfo.S_XOFF=x;
//解碼JPG/JPEG
res=jpg_decode(filename); //解碼JPG/JPEG
return res;
}
3.4 gif圖片解碼
#include "piclib.h"
#include <stm32f10x.h>
#include "gif.h"
#include "ff.h"
#include "delay.h"
#include <string.h>
const u16 _aMaskTbl[16] =
{
0x0000, 0x0001, 0x0003, 0x0007,
0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff,
0x0fff, 0x1fff, 0x3fff, 0x7fff,
};
const u8 _aInterlaceOffset[]={8,8,4,2};
const u8 _aInterlaceYPos []={0,4,2,1};
u8 gifdecoding=0;//標(biāo)記GIF正在解碼.
//檢測(cè)GIF頭
//返回值:0,是GIF89a/87a;非零,非GIF89a/87a
u8 gif_check_head(FIL *file)
{
u8 gifversion[6];
u32 readed;
u8 res;
res=f_read(file,gifversion,6,(UINT*)&readed);
if(res)return 1;
if((gifversion[0]!='G')||(gifversion[1]!='I')||(gifversion[2]!='F')||
(gifversion[3]!='8')||((gifversion[4]!='7')&&(gifversion[4]!='9'))||
(gifversion[5]!='a'))return 2;
else return 0;
}
//將RGB888轉(zhuǎn)為RGB565
//ctb:RGB888顏色數(shù)組首地址.
//返回值:RGB565顏色.
u16 gif_getrgb565(u8 *ctb)
{
u16 r,g,b;
r=(ctb[0]>>3)&0X1F;
g=(ctb[1]>>2)&0X3F;
b=(ctb[2]>>3)&0X1F;
return b+(g<<5)+(r<<11);
}
//讀取顏色表
//file:文件;
//gif:gif信息;
//num:tbl大小.
//返回值:0,OK;其他,失敗;
u8 gif_readcolortbl(FIL *file,gif89a * gif,u16 num)
{
u8 rgb[3];
u16 t;
u8 res;
u32 readed;
for(t=0;t<num;t++)
{
res=f_read(file,rgb,3,(UINT*)&readed);
if(res)return 1;//讀錯(cuò)誤
gif->colortbl[t]=gif_getrgb565(rgb);
}
return 0;
}
//得到邏輯屏幕描述,圖像尺寸等
//file:文件;
//gif:gif信息;
//返回值:0,OK;其他,失敗;
u8 gif_getinfo(FIL *file,gif89a * gif)
{
u32 readed;
u8 res;
res=f_read(file,(u8*)&gif->gifLSD,7,(UINT*)&readed);
if(res)return 1;
if(gif->gifLSD.flag&0x80)//存在全局顏色表
{
gif->numcolors=2<<(gif->gifLSD.flag&0x07);//得到顏色表大小
if(gif_readcolortbl(file,gif,gif->numcolors))return 1;//讀錯(cuò)誤
}
return 0;
}
//保存全局顏色表
//gif:gif信息;
void gif_savegctbl(gif89a* gif)
{
u16 i=0;
for(i=0;i<256;i++)gif->bkpcolortbl[i]=gif->colortbl[i];//保存全局顏色.
}
//恢復(fù)全局顏色表
//gif:gif信息;
void gif_recovergctbl(gif89a* gif)
{
u16 i=0;
for(i=0;i<256;i++)gif->colortbl[i]=gif->bkpcolortbl[i];//恢復(fù)全局顏色.
}
//初始化LZW相關(guān)參數(shù)
//gif:gif信息;
//codesize:lzw碼長(zhǎng)度
void gif_initlzw(gif89a* gif,u8 codesize)
{
memset((u8 *)gif->lzw, 0, sizeof(LZW_INFO));
gif->lzw->SetCodeSize = codesize;
gif->lzw->CodeSize = codesize + 1;
gif->lzw->ClearCode = (1 << codesize);
gif->lzw->EndCode = (1 << codesize) + 1;
gif->lzw->MaxCode = (1 << codesize) + 2;
gif->lzw->MaxCodeSize = (1 << codesize) << 1;
gif->lzw->ReturnClear = 1;
gif->lzw->LastByte = 2;
gif->lzw->sp = gif->lzw->aDecompBuffer;
}
//讀取一個(gè)數(shù)據(jù)塊
//gfile:gif文件;
//buf:數(shù)據(jù)緩存區(qū)
//maxnum:最大讀寫(xiě)數(shù)據(jù)限制
u16 gif_getdatablock(FIL *gfile,u8 *buf,u16 maxnum)
{
u8 cnt;
u32 readed;
u32 fpos;
f_read(gfile,&cnt,1,(UINT*)&readed);//得到LZW長(zhǎng)度
if(cnt)
{
if (buf)//需要讀取
{
if(cnt>maxnum)
{
fpos=f_tell(gfile);
f_lseek(gfile,fpos+cnt);//跳過(guò)
return cnt;//直接不讀
}
f_read(gfile,buf,cnt,(UINT*)&readed);//得到LZW長(zhǎng)度
}else //直接跳過(guò)
{
fpos=f_tell(gfile);
f_lseek(gfile,fpos+cnt);//跳過(guò)
}
}
return cnt;
}
//ReadExtension
//Purpose:
//Reads an extension block. One extension block can consist of several data blocks.
//If an unknown extension block occures, the routine failes.
//返回值:0,成功;
// 其他,失敗
u8 gif_readextension(FIL *gfile,gif89a* gif, int *pTransIndex,u8 *pDisposal)
{
u8 temp;
u32 readed;
u8 buf[4];
f_read(gfile,&temp,1,(UINT*)&readed);//得到長(zhǎng)度
switch(temp)
{
case GIF_PLAINTEXT:
case GIF_APPLICATION:
case GIF_COMMENT:
while(gif_getdatablock(gfile,0,256)>0); //獲取數(shù)據(jù)塊
return 0;
case GIF_GRAPHICCTL://圖形控制擴(kuò)展塊
if(gif_getdatablock(gfile,buf,4)!=4)return 1; //圖形控制擴(kuò)展塊的長(zhǎng)度必須為4
gif->delay=(buf[2]<<8)|buf[1]; //得到延時(shí)
*pDisposal=(buf[0]>>2)&0x7; //得到處理方法
if((buf[0]&0x1)!=0)*pTransIndex=buf[3]; //透明色表
f_read(gfile,&temp,1,(UINT*)&readed); //得到LZW長(zhǎng)度
if(temp!=0)return 1; //讀取數(shù)據(jù)塊結(jié)束符錯(cuò)誤.
return 0;
}
return 1;//錯(cuò)誤的數(shù)據(jù)
}
//從LZW緩存中得到下一個(gè)LZW碼,每個(gè)碼包含12位
//返回值:<0,錯(cuò)誤.
// 其他,正常.
int gif_getnextcode(FIL *gfile,gif89a* gif)
{
int i,j,End;
long Result;
if(gif->lzw->ReturnClear)
{
//The first code should be a clearcode.
gif->lzw->ReturnClear=0;
return gif->lzw->ClearCode;
}
End=gif->lzw->CurBit+gif->lzw->CodeSize;
if(End>=gif->lzw->LastBit)
{
int Count;
if(gif->lzw->GetDone)return-1;//Error
gif->lzw->aBuffer[0]=gif->lzw->aBuffer[gif->lzw->LastByte-2];
gif->lzw->aBuffer[1]=gif->lzw->aBuffer[gif->lzw->LastByte-1];
if((Count=gif_getdatablock(gfile,&gif->lzw->aBuffer[2],300))==0)gif->lzw->GetDone=1;
if(Count<0)return -1;//Error
gif->lzw->LastByte=2+Count;
gif->lzw->CurBit=(gif->lzw->CurBit-gif->lzw->LastBit)+16;
gif->lzw->LastBit=(2+Count)*8;
End=gif->lzw->CurBit+gif->lzw->CodeSize;
}
j=End>>3;
i=gif->lzw->CurBit>>3;
if(i==j)Result=(long)gif->lzw->aBuffer[i];
else if(i+1==j)Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+1]<<8);
else Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+1]<<8)|((long)gif->lzw->aBuffer[i+2]<<16);
Result=(Result>>(gif->lzw->CurBit&0x7))&_aMaskTbl[gif->lzw->CodeSize];
gif->lzw->CurBit+=gif->lzw->CodeSize;
return(int)Result;
}
//得到LZW的下一個(gè)碼
//返回值:<0,錯(cuò)誤(-1,不成功;-2,讀到結(jié)束符了)
// >=0,OK.(LZW的第一個(gè)碼)
int gif_getnextbyte(FIL *gfile,gif89a* gif)
{
int i,Code,Incode;
while((Code=gif_getnextcode(gfile,gif))>=0)
{
if(Code==gif->lzw->ClearCode)
{
//Corrupt GIFs can make this happen
if(gif->lzw->ClearCode>=(1<<MAX_NUM_LWZ_BITS))return -1;//Error
//Clear the tables
memset((u8*)gif->lzw->aCode,0,sizeof(gif->lzw->aCode));
for(i=0;i<gif->lzw->ClearCode;++i)gif->lzw->aPrefix[i]=i;
//Calculate the'special codes' independence of the initial code size
//and initialize the stack pointer
gif->lzw->CodeSize=gif->lzw->SetCodeSize+1;
gif->lzw->MaxCodeSize=gif->lzw->ClearCode<<1;
gif->lzw->MaxCode=gif->lzw->ClearCode+2;
gif->lzw->sp=gif->lzw->aDecompBuffer;
//Read the first code from the stack after clear ingand initializing*/
do
{
gif->lzw->FirstCode=gif_getnextcode(gfile,gif);
}while(gif->lzw->FirstCode==gif->lzw->ClearCode);
gif->lzw->OldCode=gif->lzw->FirstCode;
return gif->lzw->FirstCode;
}
if(Code==gif->lzw->EndCode)return -2;//End code
Incode=Code;
if(Code>=gif->lzw->MaxCode)
{
*(gif->lzw->sp)++=gif->lzw->FirstCode;
Code=gif->lzw->OldCode;
}
while(Code>=gif->lzw->ClearCode)
{
*(gif->lzw->sp)++=gif->lzw->aPrefix[Code];
if(Code==gif->lzw->aCode[Code])return Code;
if((gif->lzw->sp-gif->lzw->aDecompBuffer)>=sizeof(gif->lzw->aDecompBuffer))return Code;
Code=gif->lzw->aCode[Code];
}
*(gif->lzw->sp)++=gif->lzw->FirstCode=gif->lzw->aPrefix[Code];
if((Code=gif->lzw->MaxCode)<(1<<MAX_NUM_LWZ_BITS))
{
gif->lzw->aCode[Code]=gif->lzw->OldCode;
gif->lzw->aPrefix[Code]=gif->lzw->FirstCode;
++gif->lzw->MaxCode;
if((gif->lzw->MaxCode>=gif->lzw->MaxCodeSize)&&(gif->lzw->MaxCodeSize<(1<<MAX_NUM_LWZ_BITS)))
{
gif->lzw->MaxCodeSize<<=1;
++gif->lzw->CodeSize;
}
}
gif->lzw->OldCode=Incode;
if(gif->lzw->sp>gif->lzw->aDecompBuffer)return *--(gif->lzw->sp);
}
return Code;
}
//DispGIFImage
//Purpose:
// This routine draws a GIF image from the current pointer which should point to a
// valid GIF data block. The size of the desired image is given in the image descriptor.
//Return value:
// 0 if succeed
// 1 if not succeed
//Parameters:
// pDescriptor - Points to a IMAGE_DESCRIPTOR structure, which contains infos about size, colors and interlacing.
// x0, y0 - Obvious.
// Transparency - Color index which should be treated as transparent.
// Disposal - Contains the disposal method of the previous image. If Disposal == 2, the transparent pixels
// of the image are rendered with the background color.
u8 gif_dispimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0,int Transparency, u8 Disposal)
{
u32 readed;
u8 lzwlen;
int Index,OldIndex,XPos,YPos,YCnt,Pass,Interlace,XEnd;
int Width,Height,Cnt,ColorIndex;
u16 bkcolor;
u16 *pTrans;
Width=gif->gifISD.width;
Height=gif->gifISD.height;
XEnd=Width+x0-1;
bkcolor=gif->colortbl[gif->gifLSD.bkcindex];
pTrans=(u16*)gif->colortbl;
f_read(gfile,&lzwlen,1,(UINT*)&readed);//得到LZW長(zhǎng)度
gif_initlzw(gif,lzwlen);//Initialize the LZW stack with the LZW code size
Interlace=gif->gifISD.flag&0x40;//是否交織編碼
for(YCnt=0,YPos=y0,Pass=0;YCnt<Height;YCnt++)
{
Cnt=0;
OldIndex=-1;
for(XPos=x0;XPos<=XEnd;XPos++)
{
if(gif->lzw->sp>gif->lzw->aDecompBuffer)Index=*--(gif->lzw->sp);
else Index=gif_getnextbyte(gfile,gif);
if(Index==-2)return 0;//Endcode
if((Index<0)||(Index>=gif->numcolors))
{
//IfIndex out of legal range stop decompressing
return 1;//Error
}
//If current index equals old index increment counter
if((Index==OldIndex)&&(XPos<=XEnd))Cnt++;
else
{
if(Cnt)
{
if(OldIndex!=Transparency)
{
pic_phy.draw_hline(XPos-Cnt-1,YPos,Cnt+1,*(pTrans+OldIndex));
}else if(Disposal==2)
{
pic_phy.draw_hline(XPos-Cnt-1,YPos,Cnt+1,bkcolor);
}
Cnt=0;
}else
{
if(OldIndex>=0)
{
if(OldIndex!=Transparency)pic_phy.draw_point(XPos-1,YPos,*(pTrans+OldIndex));
else if(Disposal==2)pic_phy.draw_point(XPos-1,YPos,bkcolor);
}
}
}
OldIndex=Index;
}
if((OldIndex!=Transparency)||(Disposal==2))
{
if(OldIndex!=Transparency)ColorIndex=*(pTrans+OldIndex);
else ColorIndex=bkcolor;
if(Cnt)
{
pic_phy.draw_hline(XPos-Cnt-1,YPos,Cnt+1,ColorIndex);
}else pic_phy.draw_point(XEnd,YPos,ColorIndex);
}
//Adjust YPos if image is interlaced
if(Interlace)//交織編碼
{
YPos+=_aInterlaceOffset[Pass];
if((YPos-y0)>=Height)
{
++Pass;
YPos=_aInterlaceYPos[Pass]+y0;
}
}else YPos++;
}
return 0;
}
/*
函數(shù)功能: 恢復(fù)成背景色
函數(shù)參數(shù):
x,y:坐標(biāo)
gif:gif信息.
pimge:圖像描述塊信息
*/
void gif_clear2bkcolor(u16 x,u16 y,gif89a* gif,ImageScreenDescriptor pimge)
{
u16 x0,y0,x1,y1;
u16 color=gif->colortbl[gif->gifLSD.bkcindex];
if(pimge.width==0||pimge.height==0)return;//直接不用清除了,原來(lái)沒(méi)有圖像!!
if(gif->gifISD.yoff>pimge.yoff)
{
x0=x+pimge.xoff;
y0=y+pimge.yoff;
x1=x+pimge.xoff+pimge.width-1;;
y1=y+gif->gifISD.yoff-1;
if(x0<x1&&y0<y1&&x1<320&&y1<320)pic_phy.fill(x0,y0,x1,y1,color); //設(shè)定xy,的范圍不能太大.
}
if(gif->gifISD.xoff>pimge.xoff)
{
x0=x+pimge.xoff;
y0=y+pimge.yoff;
x1=x+gif->gifISD.xoff-1;;
y1=y+pimge.yoff+pimge.height-1;
if(x0<x1&&y0<y1&&x1<320&&y1<320)pic_phy.fill(x0,y0,x1,y1,color);
}
if((gif->gifISD.yoff+gif->gifISD.height)<(pimge.yoff+pimge.height))
{
x0=x+pimge.xoff;
y0=y+gif->gifISD.yoff+gif->gifISD.height-1;
x1=x+pimge.xoff+pimge.width-1;;
y1=y+pimge.yoff+pimge.height-1;
if(x0<x1&&y0<y1&&x1<320&&y1<320)pic_phy.fill(x0,y0,x1,y1,color);
}
if((gif->gifISD.xoff+gif->gifISD.width)<(pimge.xoff+pimge.width))
{
x0=x+gif->gifISD.xoff+gif->gifISD.width-1;
y0=y+pimge.yoff;
x1=x+pimge.xoff+pimge.width-1;;
y1=y+pimge.yoff+pimge.height-1;
if(x0<x1&&y0<y1&&x1<320&&y1<320)pic_phy.fill(x0,y0,x1,y1,color);
}
}
/*
函數(shù)功能: 畫(huà)GIF圖像的一幀
函數(shù)參數(shù):
gfile:gif文件
x0,y0:開(kāi)始顯示的坐標(biāo)
*/
u8 gif_drawimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0)
{
u32 readed;
u8 res,temp;
u16 numcolors;
ImageScreenDescriptor previmg;
u8 Disposal;
int TransIndex;
u8 Introducer;
TransIndex=-1;
do
{
res=f_read(gfile,&Introducer,1,(UINT*)&readed);//讀取一個(gè)字節(jié)
if(res)return 1;
switch(Introducer)
{
case GIF_INTRO_IMAGE://圖像描述
previmg.xoff=gif->gifISD.xoff;
previmg.yoff=gif->gifISD.yoff;
previmg.width=gif->gifISD.width;
previmg.height=gif->gifISD.height;
res=f_read(gfile,(u8*)&gif->gifISD,9,(UINT*)&readed);//讀取一個(gè)字節(jié)
if(res)return 1;
if(gif->gifISD.flag&0x80)//存在局部顏色表
{
gif_savegctbl(gif);//保存全局顏色表
numcolors=2<<(gif->gifISD.flag&0X07);//得到局部顏色表大小
if(gif_readcolortbl(gfile,gif,numcolors))return 1;//讀錯(cuò)誤
}
if(Disposal==2)gif_clear2bkcolor(x0,y0,gif,previmg);
gif_dispimage(gfile,gif,x0+gif->gifISD.xoff,y0+gif->gifISD.yoff,TransIndex,Disposal);
while(1)
{
f_read(gfile,&temp,1,(UINT*)&readed);//讀取一個(gè)字節(jié)
if(temp==0)break;
readed=f_tell(gfile);//還存在塊.
if(f_lseek(gfile,readed+temp))break;//繼續(xù)向后偏移
}
if(temp!=0)return 1;//Error
return 0;
case GIF_INTRO_TERMINATOR://得到結(jié)束符了
return 2; //代表圖像解碼完成了.
case GIF_INTRO_EXTENSION:
res=gif_readextension(gfile,gif,&TransIndex,&Disposal);//讀取圖像擴(kuò)展塊消息
if(res)return 1;
break;
default:
return 1;
}
}while(Introducer!=GIF_INTRO_TERMINATOR);//讀到結(jié)束符了
return 0;
}
/*
函數(shù)功能: 退出當(dāng)前解碼
*/
void gif_quit(void)
{
gifdecoding=0;
}
/*
函數(shù)功能: 解碼一個(gè)gif文件
函數(shù)參數(shù):
filename:帶路徑的gif文件名字
x,y,width,height:顯示坐標(biāo)及區(qū)域大小.
*/
u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height)
{
u8 res=0;
u16 dtime=0;//解碼延時(shí)
gif89a *mygif89a;
FIL *gfile;
gfile=(FIL*)SRAM_Malloc(sizeof(FIL));
if(gfile==NULL)res=PIC_MEM_ERR;//申請(qǐng)內(nèi)存失敗
mygif89a=(gif89a*)SRAM_Malloc(sizeof(gif89a));
if(mygif89a==NULL)res=PIC_MEM_ERR;//申請(qǐng)內(nèi)存失敗
mygif89a->lzw=(LZW_INFO*)SRAM_Malloc(sizeof(LZW_INFO));
if(mygif89a->lzw==NULL)res=PIC_MEM_ERR;//申請(qǐng)內(nèi)存失敗
if(res==0)//OK
{
res=f_open(gfile,(TCHAR *)filename,FA_READ);
if(res==0)//打開(kāi)文件ok
{
if(gif_check_head(gfile))res=PIC_FORMAT_ERR;
if(gif_getinfo(gfile,mygif89a))res=PIC_FORMAT_ERR;
if(mygif89a->gifLSD.width>width||mygif89a->gifLSD.height>height)res=PIC_SIZE_ERR;//尺寸太大.
else
{
x=(width-mygif89a->gifLSD.width)/2+x;
y=(height-mygif89a->gifLSD.height)/2+y;
}
gifdecoding=1;
while(gifdecoding&&res==0)//解碼循環(huán)
{
res=gif_drawimage(gfile,mygif89a,x,y);//顯示一張圖片
if(mygif89a->gifISD.flag&0x80)gif_recovergctbl(mygif89a);//恢復(fù)全局顏色表
if(mygif89a->delay)dtime=mygif89a->delay;
else dtime=10;//默認(rèn)延時(shí)
while(dtime--&&gifdecoding)delay_ms(10);//延遲
if(res==2)
{
res=0;
break;
}
}
}
f_close(gfile);
}
SRAM_Free(gfile);
SRAM_Free(mygif89a->lzw);
SRAM_Free(mygif89a);
return res;
}