基于STM32设计的数码相册

举报
DS小龙哥 发表于 2023/07/27 16:39:28 2023/07/27
【摘要】 项目是基于STM32设计的数码相册,能够通过LCD显示屏解码显示主流的图片,支持bmp、jpg、gif等格式。用户可以通过按键或者触摸屏来切换图片,同时还可以旋转显示,并能够自适应居中显示,小尺寸图片居中显示,大尺寸图片自动缩小显示(超出屏幕范围)。图片从SD卡中获取。

一、项目介绍

项目是基于STM32设计的数码相册,能够通过LCD显示屏解码显示主流的图片,支持bmp、jpg、gif等格式。用户可以通过按键或者触摸屏来切换图片,同时还可以旋转显示,并能够自适应居中显示,小尺寸图片居中显示,大尺寸图片自动缩小显示(超出屏幕范围)。图片从SD卡中获取。

image-20230618135436038

二、设计思路

2.1 硬件设计

本项目所需的主要硬件:

  • STM32F103ZET6
  • LCD屏幕
  • SD卡模块
  • 按键和触摸屏

2.2 软件设计

(1)解码图片

在STM32芯片中,解码图片需要将读取到的数据存入图形缓冲区中,以便进行图画显示。常用的解码算法有JPEG解码和BMP解码。

(2)图片显示

为了更好的实现图片旋转和缩放功能,在显示图片时需对其进行矩阵运算。通过左右翻转和上下翻转,可实现图片的旋转功能。通过计算图片与显示屏幕之间的比例关系并进行缩放,实现自适应居中和图片的缩放功能。

(3)SD卡

SD卡模块可通过SPI接口与STM32芯片进行通信,读取SD卡中的图片数据,实现对图片的加载和显示。

(4)按键和触摸屏

在使用过程中,用户可以通过按键和触摸屏对图片进行切换、旋转和缩放等操作。通过设置中断处理函数,响应用户的操作并及时更新显示屏幕上的图片。

2.3 图片播放流程图

image-20230618135212377

2.4 显示效果

image-20230618135401101

image-20230618135409530

image-20230618135417655

image-20230618135426430

image-20230618135436038

三、代码设计

3.1 主函数

 #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" //文件系统
 #include "bmp.h" //文件系统
 #include "iic.h"
 #include "at24c02.h"
 #include "xpt2046.h"
 #include "lcd.h"
 ​
 ​
 FATFS fs;  // 用户定义的文件系统结构体
 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(); //触摸屏校准
     
     printf("串口工作正常!\r\n");
     if(SDCardDeviceInit()) 
     {
        printf("SD卡初始化失败!\r\n");
     }
     
     sd_size=GetSDCardSectorCount(); //检测SD卡大小,返回值右移11位得到以M为单位的容量
     printf("SD卡Sizeof:%d\r\n",sd_size>>11);
     
   f_mount(&fs,"0:",1);  // 注册文件系统工作区,驱动器号 0,初始化后其他函数可使用里面的参数
     LcdClear(0xFFFF);
     
     //f_mkdir("0:/目录创建测试!"); //测试OK
     //f_unlink("0:/123"); //删除目录,注意只能删除空目录
     //f_unlink("0:/1.bmp");//删除文件
     //printf("%d\r\n",Show_BMP("1.bmp"));
     
     if(f_opendir(&dir_dp,"0:/bmp")!=FR_OK)printf("目录打开失败!\r\n");
     
     //循环读取目录
     while(f_readdir(&dir_dp,&file_info)==FR_OK)
     {
             if(file_info.fname[0]==0)break;    //判断目录跳出条件,表示目录已经读取完毕
             if(strstr(file_info.fname,".bmp")) //过滤目录
             {
                     printf("文件名称: %s,文件大小: %ld 字节\r\n",file_info.fname,file_info.fsize);
             }else   printf("文件名称: %s,文件大小: %ld 字节\r\n",file_info.fname,file_info.fsize);
     }
     if(f_closedir(&dir_dp)!=FR_OK)printf("目录关闭失败!\r\n");
     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;  
 }  
 ​
 ​
 ​
 /*
 函数功能:实现截图功能
 参    数:
                 char filename:文件名称
 返 回 值:0表示成功,1表示失败
 */
 u8 C_BMP(const char *filename,u32 Width,u32 Height)
 {
     FIL  file; // 用户定义的文件系统结构体
     u8   res;  // 保存文件操作的返回值
     BITMAPFILEHEADER BmpHead; //保存图片文件头的信息
   BITMAPINFOHEADER BmpInfo; //图片参数信息
     char *p;
     u32 cnt,c_32;
     int x,y;
     u16 c_16; //存放16位的颜色
     
     /*1. 创建一张BMP图片*/
     res = f_open(&file,filename, FA_OPEN_ALWAYS | FA_WRITE);
     if(res!=0)return 1;
     
     /*2. 创建BMP的图片头参数*/
     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;              //图片数据的偏移量
   res =f_write(&file,&BmpHead,sizeof(BITMAPFILEHEADER),&cnt);
     if(res!=0)return 1;
     
     /*3. 创建BMP图片的参数*/
     memset(&BmpInfo,0,sizeof(BITMAPINFOHEADER));
     BmpInfo.biSize=sizeof(BITMAPINFOHEADER); //当前结构体大小
     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屏的颜色数据,用于创建BMP图片*/
     for(y=Height-1;y>=0;y--)
     {
           for(x=0;x<Width;x++)
           {
                 c_16=LcdReadPoint(x,y); //读取LCD屏上一个点的颜色
                   c_32=RGB565ToRGB888(c_16); //颜色的转换
                   res =f_write(&file,&c_32,3,&cnt);
                   if(res!=0)return 1;
             }
     }
     
     /*5. 关闭文件*/
     f_close(&file);
 }
 ​
 ​
 /*
 函数功能:BMP图片显示功能
 参    数:
                 char filename:文件名称
 返 回 值:0表示成功,1表示失败
 */
 u8 Show_BMP(const char *filename)
 {
     FIL  file; // 用户定义的文件系统结构体
     u8   res;  // 保存文件操作的返回值
     BITMAPFILEHEADER BmpHead; //保存图片文件头的信息
   BITMAPINFOHEADER BmpInfo; //图片参数信息
     char *p;
     u32 cnt,c_24;
     int x,y;
     u16 c_16; //存放16位的颜色
     
     /*1. 打开一张BMP图片*/
     res = f_open(&file,filename,FA_READ);
     if(res!=0)return 1;
     
     /*2. 读取BMP的图片头参数*/
   res =f_read(&file,&BmpHead,sizeof(BITMAPFILEHEADER),&cnt);
     if(res!=0)return 1;
     
     /*3. 读取BMP图片的参数*/
     res =f_read(&file,&BmpInfo,sizeof(BITMAPINFOHEADER),&cnt);
     if(res!=0)return 1;
     
     /*4.显示BMP图片*/
     f_lseek(&file,BmpHead.bfOffBits); //移动到RGB数据的存放位置
     
     //后期的优化:读取一行的数据,再显示一行。
    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); //转换颜色
                 LcdDrawPoint(x,y,c_16);
          }
      }
     /*5. 关闭文件*/
     f_close(&file);
 }
 ​
 ​

3.3 jpeg图片解码

 #include "piclib.h"
 #include "nt35310_lcd.h"
 _pic_info picinfo;      //图片信息
 _pic_phy pic_phy;         //图片显示物理接口    
 ​
 /*
 函数功能: 划横线函数,需要自己实现
 */
 void Picture_DrawLine(u16 x0,u16 y0,u16 len,u16 color)
 {
     NT35310_Fill(x0,y0,x0+len-1,y0,color);  
 }
 ​
 /*
 函数功能: 矩形填充颜色
 函数参数:
         x,y:起始坐标
         width,height:宽度和高度。
         color:颜色数组
 */
 void Picture_FillColor(u16 x,u16 y,u16 width,u16 height,u16 *color)
 {  
     NT35310_DrawRectangle(x,y,x+width-1,y+height-1,*color); 
 }
 ​
 /*
 函数功能: 画图初始化,在画图之前,必须先调用此函数
 函数参数: 指定画点/读点
 */
 void Picture_DisplayInit(void)
 {
     pic_phy.draw_point=NT35310_DrawPoint;    //画点函数实现
     pic_phy.fill=NT35310_Fill;                       //填充函数实现,仅GIF需要
     pic_phy.draw_hline=Picture_DrawLine;     //画线函数实现,仅GIF需要
     pic_phy.fillcolor=Picture_FillColor;     //颜色填充函数实现,仅TJPGD需要 
     picinfo.lcdwidth=Lcd_Width;                          //得到LCD的宽度像素
     picinfo.lcdheight=Lcd_Height;                        //得到LCD的高度像素
 ​
     picinfo.ImgWidth=0; //初始化宽度为0
     picinfo.ImgHeight=0;//初始化高度为0
     picinfo.Div_Fac=0;  //初始化缩放系数为0
     picinfo.S_Height=0; //初始化设定的高度为0
     picinfo.S_Width=0;  //初始化设定的宽度为0
     picinfo.S_XOFF=0;     //初始化x轴的偏移量为0
     picinfo.S_YOFF=0;     //初始化y轴的偏移量为0
     picinfo.staticx=0;  //初始化当前显示到的x坐标为0
     picinfo.staticy=0;  //初始化当前显示到的y坐标为0
 }
 ​
 ​
 /*
 函数功能: 初始化智能画点
 说明: 内部调用
 */
 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;//取较小的那个    
     if(temp1>1)temp1=1;   
     //使图片处于所给区域的中间
     picinfo.S_XOFF+=(picinfo.S_Width-temp1*picinfo.ImgWidth)/2;
     picinfo.S_YOFF+=(picinfo.S_Height-temp1*picinfo.ImgHeight)/2;
     temp1*=8192;//扩大8192倍    
     picinfo.Div_Fac=temp1;
     picinfo.staticx=0xffff;
     picinfo.staticy=0xffff;//放到一个不可能的值上面                                                        
 }  
 ​
 /*
 函数功能: 判断这个像素是否可以显示
 函数参数:
             (x,y) :像素原始坐标
             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);
 ​
 /*
 函数功能: 绘制图片
 函数参数:
                 FileName:要显示的图片文件  BMP/JPG/JPEG/GIF
                 x,y,width,height:坐标及显示区域尺寸
                 fast:使能jpeg/jpg小图片(图片尺寸小于等于液晶分辨率)快速解码,0,不使能;1,使能.
                 函数说明: 图片在开始和结束的坐标点范围内显示
 */
 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;
 ​
     //显示的开始坐标点
     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;//标记GIF正在解码.
 ​
 //检测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转为RGB565
 //ctb:RGB888颜色数组首地址.
 //返回值: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;//读错误
         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;//读错误 
     }      
     return 0;
 }
 //保存全局颜色表    
 //gif:gif信息;
 void gif_savegctbl(gif89a* gif)
 {
     u16 i=0;
     for(i=0;i<256;i++)gif->bkpcolortbl[i]=gif->colortbl[i];//保存全局颜色.
 }
 //恢复全局颜色表    
 //gif:gif信息;
 void gif_recovergctbl(gif89a* gif)
 {
     u16 i=0;
     for(i=0;i<256;i++)gif->colortbl[i]=gif->bkpcolortbl[i];//恢复全局颜色.
 }
 ​
 //初始化LZW相关参数       
 //gif:gif信息;
 //codesize:lzw码长度
 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;
 }
 ​
 //读取一个数据块
 //gfile:gif文件;
 //buf:数据缓存区
 //maxnum:最大读写数据限制
 u16 gif_getdatablock(FIL *gfile,u8 *buf,u16 maxnum) 
 {
     u8 cnt;
     u32 readed;
     u32 fpos;
     f_read(gfile,&cnt,1,(UINT*)&readed);//得到LZW长度            
     if(cnt) 
     {
         if (buf)//需要读取 
         {
             if(cnt>maxnum)
             {
                 fpos=f_tell(gfile);
                 f_lseek(gfile,fpos+cnt);//跳过
                 return cnt;//直接不读
             }
             f_read(gfile,buf,cnt,(UINT*)&readed);//得到LZW长度  
         }else   //直接跳过
         {
             fpos=f_tell(gfile);
             f_lseek(gfile,fpos+cnt);//跳过
         }
     }
     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);//得到长度      
     switch(temp)
     {
         case GIF_PLAINTEXT:
         case GIF_APPLICATION:
         case GIF_COMMENT:
             while(gif_getdatablock(gfile,0,256)>0);         //获取数据块
             return 0;
         case GIF_GRAPHICCTL://图形控制扩展块
             if(gif_getdatablock(gfile,buf,4)!=4)return 1;   //图形控制扩展块的长度必须为4 
             gif->delay=(buf[2]<<8)|buf[1];                  //得到延时 
             *pDisposal=(buf[0]>>2)&0x7;                     //得到处理方法
             if((buf[0]&0x1)!=0)*pTransIndex=buf[3];         //透明色表 
             f_read(gfile,&temp,1,(UINT*)&readed);           //得到LZW长度   
             if(temp!=0)return 1;                            //读取数据块结束符错误.
             return 0;
     }
     return 1;//错误的数据
 }
 ​
 //从LZW缓存中得到下一个LZW码,每个码包含12位
 //返回值:<0,错误.
 //       其他,正常.
 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的下一个码
 //返回值:<0,错误(-1,不成功;-2,读到结束符了)
 //       >=0,OK.(LZW的第一个码)
 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长度     
     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;
 }
 ​
 ​
 /*
 函数功能: 恢复成背景色
 函数参数:
                     x,y:坐标
                     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;//直接不用清除了,原来没有图像!!
     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); //设定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);
     }   
 }
 ​
 ​
 /*
 函数功能: 画GIF图像的一帧
 函数参数:
                 gfile:gif文件
                 x0,y0:开始显示的坐标
 */
 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);//读取一个字节
         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);//读取一个字节
                 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;//读错误 
                 }
                 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);//读取一个字节
                     if(temp==0)break;
                     readed=f_tell(gfile);//还存在块.    
                     if(f_lseek(gfile,readed+temp))break;//继续向后偏移     
               }
                 if(temp!=0)return 1;//Error 
                 return 0;
             case GIF_INTRO_TERMINATOR://得到结束符了
                 return 2;                               //代表图像解码完成了.
             case GIF_INTRO_EXTENSION:
                 res=gif_readextension(gfile,gif,&TransIndex,&Disposal);//读取图像扩展块消息
                 if(res)return 1;
                 break;
             default:
                 return 1;
         }
     }while(Introducer!=GIF_INTRO_TERMINATOR);//读到结束符了
     return 0;
 }
 ​
 ​
 /*
 函数功能: 退出当前解码
 */
 void gif_quit(void)
 {
     gifdecoding=0;
 }
 ​
 ​
 /*
 函数功能: 解码一个gif文件
 函数参数:
             filename:带路径的gif文件名字
             x,y,width,height:显示坐标及区域大小.
 */
 u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height)
 {
     u8 res=0;
     u16 dtime=0;//解码延时
     gif89a *mygif89a;
     FIL *gfile;
     gfile=(FIL*)SRAM_Malloc(sizeof(FIL));
     if(gfile==NULL)res=PIC_MEM_ERR;//申请内存失败 
     mygif89a=(gif89a*)SRAM_Malloc(sizeof(gif89a));
     if(mygif89a==NULL)res=PIC_MEM_ERR;//申请内存失败    
     mygif89a->lzw=(LZW_INFO*)SRAM_Malloc(sizeof(LZW_INFO));
     if(mygif89a->lzw==NULL)res=PIC_MEM_ERR;//申请内存失败 
     if(res==0)//OK
     {
         res=f_open(gfile,(TCHAR *)filename,FA_READ);
         if(res==0)//打开文件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)//解码循环
             {    
                 res=gif_drawimage(gfile,mygif89a,x,y);//显示一张图片
                 if(mygif89a->gifISD.flag&0x80)gif_recovergctbl(mygif89a);//恢复全局颜色表
                 if(mygif89a->delay)dtime=mygif89a->delay;
                 else dtime=10;//默认延时
                 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;
 }
 ​
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。