STM32单片机图片解码
图片解码首先是最简单的bmp图片解码,关于bmp的结构可自行查阅,代码如下
#ifndef __BMPDECODE_H_ #define __BMPDECODE_H_ #include "ff.h" #include "lcd.h" #include "stdlib.h" #include "usb_type.h" //重定义区 typedef char CHAR; //数据类型重定义,便于移植 typedef short SHORT; //typedef int LONG; //typedef unsigned int DWORD; typedef int BOOL; typedef u8 BYTE; typedef unsigned short WORD; //#define FALSE 0 //#define TRUE 1 //BMP图象数据压缩的类型 #define BI_RGB 0L //无压缩 #define BI_RLE8 1L //每个像素四个bit #define BI_RLE4 2L //每个像素8个bit #define BI_BITFIELDS 3L //每个像素的bit由指定的掩码决定 #define bufferToLong(buffer,t) (LONG)((((u32)bmpbuffer[t])) + (((u32)bmpbuffer[t+1])<<8) +(((u32)bmpbuffer[t+2])<<16) + (((u32)bmpbuffer[t+3])<<24)) #define bufferToWord(buffer,t) (WORD)(((u32)(buffer[t])) + (((u32)(buffer[t+1]))<<8)) #define bufferToDword(buffer,t) (DWORD)((((u32)bmpbuffer[t])) + (((u32)bmpbuffer[t+1])<<8) +(((u32)bmpbuffer[t+2])<<16) + (((u32)bmpbuffer[t+3])<<24)) #define RGB888buffertoRGB565(buffer,t) ((((u16)buffer[t])>>3) + ((((u16)buffer[t+1])>>2)<<5) + ((((u16)buffer[t+2])>>3)<<11)) //BMP文件头 14个字节 typedef __packed struct { WORD bfType ; //文件标志.只对'BM',用来识别BMP位图类型 2 DWORD bfSize ; //文件大小,占四个字节 4 WORD bfReserved1 ;//保留 2 WORD bfReserved2 ;//保留 2 DWORD bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的的偏移量,这一段中存放的就是文件信息 4 }BITMAPFILEHEADER ; //位图文件头 //位图信息头 typedef __packed struct { DWORD biSize ; //说明BITMAPINFOHEADER结构所需要的字数。 LONG biWidth ; //说明图象的宽度,以象素为单位 LONG biHeight ; //说明图象的高度,以象素为单位 WORD biPlanes ; //为目标设备说明位面数,其值将总是被设为1 WORD biBitCount ; //说明比特数/象素,其值为1、4、8、16、24、或32 DWORD biCompression ; //说明图象数据压缩的类型。其值可以是下述值之一: /*BI_RGB:没有压缩; *BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引); *BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成 *BI_BITFIELDS:每个象素的比特由指定的掩码决定。*/ DWORD biSizeImage ;//说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0 LONG biXPelsPerMeter ;//说明水平分辨率,用象素/米表示 LONG biYPelsPerMeter ;//说明垂直分辨率,用象素/米表示 DWORD biClrUsed ; //说明位图实际使用的彩色表中的颜色索引数 DWORD biClrImportant ; //说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 }BITMAPINFOHEADER; //颜色索引表,每个颜色索引占4个字节,16位以及以下才会有这个数据 也就是说 最多65535个数据 //当然,最好不要用16位色的,浪费空间 typedef __packed struct { BYTE rgbBlue ; //指定蓝色强度 BYTE rgbGreen ; //指定绿色强度 BYTE rgbRed ; //指定红色强度 BYTE rgbReserved ;//保留,设置为0 }RGBQUAD ; BOOL BmpDecode(u8 *filename,u16 sx,u16 sy,u16 ex,u16 ey); #endif
u8 bmpbuffer[1024] = {0}; //存储bmp文件的数组 //解码这个BMP文件 //设定显示起始位置以及终止位置 BOOL BmpDecode(u8 *filename,u16 sx,u16 sy,u16 ex,u16 ey) { FIL f_bmp; //文件系统变量,用于读取bmp文件 u16 filereadnum = 0; //用于记录文件字节读取数量 FRESULT res; //文件读取返回信息 BITMAPFILEHEADER bmpfileHead; //位图文件头 BITMAPINFOHEADER bmpinfoHead; //位图信息头 u8 colorByte = 0; //用于标记颜色位 u16 index = 0; //读取文件信息时候用来定位 u16 uitemp = 0; //记录实际数据一行有多少个点 u16 xtemp = 0; u16 ytemp = 0; //显示时辅助计数 u16 colortemp = 0; //颜色缓冲 res=f_open(&f_bmp,(const TCHAR*)filename,FA_READ); //打开文件 if(res!=FR_OK) return FALSE; res = f_read(&f_bmp,bmpbuffer,1024,(UINT*)&filereadnum); //读取文件 if(res!=FR_OK) return FALSE; //获取位图文件头 bmpfileHead.bfType = (WORD)((((u16)bmpbuffer[0])<<8) + bmpbuffer[1+1]); bmpfileHead.bfSize = bufferToDword(bmpbuffer,2); bmpfileHead.bfReserved1 = bufferToWord(bmpbuffer,6); bmpfileHead.bfReserved2 = bufferToWord(bmpbuffer,8); bmpfileHead.bfOffBits = bufferToDword(bmpbuffer,10); //数据段开始地址 //获取位图信息头 bmpinfoHead.biSize = bufferToDword(bmpbuffer,14); bmpinfoHead.biWidth = bufferToLong(bmpbuffer,18); bmpinfoHead.biHeight = bufferToLong(bmpbuffer,22); bmpinfoHead.biPlanes = bufferToWord(bmpbuffer,26); bmpinfoHead.biBitCount = bufferToWord(bmpbuffer,28); //颜色位 bmpinfoHead.biCompression = bufferToDword(bmpbuffer,30); bmpinfoHead.biSizeImage = bufferToDword(bmpbuffer,34); bmpinfoHead.biXPelsPerMeter = bufferToLong(bmpbuffer,38); bmpinfoHead.biYPelsPerMeter = bufferToLong(bmpbuffer,42); bmpinfoHead.biClrUsed = bufferToDword(bmpbuffer,46); bmpinfoHead.biClrImportant = bufferToDword(bmpbuffer,50); index = bmpfileHead.bfOffBits; //所有信息得到,这里应该进行测试了 colorByte = bmpinfoHead.biBitCount/8; //颜色位数 16 / 24 / 32 得到2/3/4 if((bmpinfoHead.biWidth%4) != 0) //不是4的倍数 { uitemp = (((bmpinfoHead.biWidth/4) + 1) * 4); } else //是4的倍数 { uitemp = bmpinfoHead.biWidth ; } if(colorByte == 3) //24位颜色 { while(1) { if(index <= 1021) { colortemp = RGB888buffertoRGB565(bmpbuffer,index); index = index +3; if(sx+xtemp <ex && sy+ytemp < ey) LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp); xtemp++; if(xtemp >= uitemp) { xtemp = 0; ytemp++; } } else { if(index == 1022) { if(sx+xtemp <ex && sy+ytemp < ey) LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp); xtemp++; if(xtemp >= uitemp) { xtemp = 0; ytemp++; } index = 1; } else if(index == 1023) { if(sx+xtemp <ex && sy+ytemp < ey) LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp); xtemp++; if(xtemp >= uitemp) { xtemp = 0; ytemp++; } index = 2; } else index = 0; res = f_read(&f_bmp,bmpbuffer,1024,(UINT*)&filereadnum); //读取文件 if(res||filereadnum==0)break; //读取出错 } } } f_close(&f_bmp); /* 关闭打开的文件 */ LCD_Flush(); return TRUE; }
为了防止图片刷新速度过慢可以在内存中建立一个屏幕缓存,解码完成后一次性刷入屏幕
JPEG图片解码使用网络上一个tjpgdec的库,该库的移植如下
#ifndef __JPGDEC_CALLBACK_H_ #define __JPGDEC_CALLBACK_H_ #include "lcd.h" #include "ff.h" #include "tjpgd.h" #include "stdlib.h" void load_jpg (FIL *fp,void *work,UINT sz_work); void load_file (const char *fn); #endif
// 以下3句宏定义在tjpgd.h中修改 static int MaskL = 0; static int MaskR = LCD_X_SIZE - 1; static int MaskT = 0; static int MaskB = LCD_Y_SIZE - 1; #define SIZE_OF_FILE_BUFFER 4096 BYTE* jpegbuffer; //图像文件数据缓冲区 /********************************************************************************************* 函数名称: STM32_Display 函数功能: 在TFTLCD屏幕上显示图片 入口参数: 见函数头 出口参数: 无 全局变量: 无 备注说明: 无 *********************************************************************************************/ void Lcd_Display_Rect( int left, /*图片左方起始点,即一行的起始点 */ int right, /*图片右方的结束点,即一行的结束点*/ int top, /* 图片上方的起始点,即一列的起始点 */ int bottom, /*图像下方的结束点,即一列的结束点 */ const uint16_t * RGB_Data_Pointer /* 待显示的图像数据,RGB格式*/ ) { int yc, xc, xl, xs; unsigned short pd; if (left > right || top > bottom) { return; // Check varidity } if (left > MaskR || right < MaskL || top > MaskB || bottom < MaskT) { return; // Check if in active area } yc = bottom - top + 1 ; /* Vertical size */ xc = right - left + 1; xs = 0; /* Horizontal size and skip */ // 已经计算出了要绘制的x方向pixels和y方向pixels // 上下左右的值已经校正为,为屏幕上的绝对位置,由此可以算出屏幕缓冲区的起始位置 do { /* Send image data */ xl = xc; do { pd = *RGB_Data_Pointer++; LCD_Draw_Point_Buffer(right - xl,bottom - yc,pd); //显示像素 } while (--xl); RGB_Data_Pointer += xs; } while (--yc); } BYTE Buff[1024] __attribute__ ((aligned(4))); //定义全局数组变量,作为输入的缓冲区,强制4字节对齐 /********************************************************************************************* 函数名称: Jpeg_Data_in_func 函数功能: 用户自定义的用于输入文件数据的功能函数 入口参数: 见函数头 出口参数: 读取或者删除的数据量 全局变量: 无 备注说明: 本函数在解码准备工作中用于读取文件头信息 *********************************************************************************************/ UINT Jpeg_Data_in_func ( JDEC* jd, /*储存待解码的对象信息的结构体 */ BYTE* buff, /* 输入数据缓冲区 (NULL:删除数据) */ UINT nd /*需要从输入数据流读出/删除的数据量*/ ) { UINT rb; FIL * dev = (FIL *)jd->device; /* 待解码的文件的信息,使用FATFS中的FIL结构类型进行定义 */ if (buff) /*读取数据有效,开始读取数据 */ { f_read(dev, buff, nd, &rb); //调用FATFS的f_read函数,用于把jpeg文件的数据读取出来 return rb; /* 返回读取到的字节数目*/ } else { return (f_lseek(dev, f_tell(dev) + nd) == FR_OK) ? nd : 0;/* 重新定位数据点,相当于删除之前的n字节数据 */ } } /********************************************************************************************* 函数名称: STM32_out_func 函数功能: 用户自定义的用于输出RGB位图数据的功能函数 入口参数: 见函数头 出口参数: 1:令解码函数继续执行 全局变量: 无 备注说明: 无 *********************************************************************************************/ UINT Jpeg_Data_out_func ( JDEC* jd, /*储存待解码的对象信息的结构体*/ void* bitmap, /* 指向等待输出的RGB位图数据 的指针*/ JRECT* rect /* 等待输出的矩形图像的参数 */ ) { jd = jd; /* 说明:输出函数中JDEC结构体没有用到 */ //显示一块区域到LCD上 Lcd_Display_Rect(rect->left, rect->right, rect->top, rect->bottom, (uint16_t*)bitmap); return 1; /*返回1,使解码工作继续执行 */ } //加载并显示jpg文件 void load_jpg ( FIL *fp, /* 指向打开的文件执政 */ void *work, /*指向四字节对其的工作区缓存 */ UINT sz_work /*工作区的大小 */ ) { JDEC jd; /* Decoder object (124 bytes) */ JRESULT rc; BYTE scale; /* Prepare to decompress the file */ rc = jd_prepare(&jd, Jpeg_Data_in_func, work, sz_work, fp); if (rc == JDR_OK) { /* 根据图片大小选选择一个刚刚好能够缩放的图片比例 */ for (scale = 0; scale < 3; scale++) { if ((jd.width >> scale) <= LCD_X_SIZE && (jd.height >> scale) <= LCD_Y_SIZE) break; } rc = jd_decomp(&jd, Jpeg_Data_out_func, scale); /* Start to decompress */ } else { //显示错误,将错误信息打印在屏幕上 printf("jpg error %d\r\n",rc); } } /*参数:指向文件名 */ void load_file (const char *fn) { FIL fil; /* Pointer to a file object */ if (f_open(&fil, fn, FA_READ) == FR_OK) { jpegbuffer = NULL; jpegbuffer = malloc(SIZE_OF_FILE_BUFFER); if(jpegbuffer != NULL) { LCD_Fill_Buffer(LCD_BLACK); load_jpg(&fil, (void *)jpegbuffer, SIZE_OF_FILE_BUFFER ); //打开jpg文件并解码显示 } f_close(&fil); if(jpegbuffer != NULL)free(jpegbuffer); LCD_Flush(); } else { printf("open file failed\r\n"); } }
该库的解码性能还不错,使用了图片流的概念来进行解码操作,剩下的是一个GIF图片解码,如下
#ifndef __GIFDECODE_H_ #define __GIFDECODE_H_ #include "ff.h" #include "lcd.h" #include "stdlib.h" #define MAX_NUM_LWZ_BITS 12 #define LCD_MAX_LOG_COLORS 256 #define GIF_INTRO_TERMINATOR ';' //0X3B GIF文件结束符 #define GIF_INTRO_EXTENSION '!' //0X21 #define GIF_INTRO_IMAGE ',' //0X2C #define GIF_COMMENT 0xFE #define GIF_APPLICATION 0xFF #define GIF_PLAINTEXT 0x01 #define GIF_GRAPHICCTL 0xF9 #define PIC_FORMAT_ERR 0x27 //格式错误 #define PIC_SIZE_ERR 0x28 //图片尺寸错误 #define PIC_WINDOW_ERR 0x29 //窗口设定错误 #define PIC_MEM_ERR 0x11 //内存错误 typedef struct { u8 aBuffer[258]; // Input buffer for data block short aCode [(1 << MAX_NUM_LWZ_BITS)]; // This array stores the LZW codes for the compressed strings u8 aPrefix[(1 << MAX_NUM_LWZ_BITS)]; // Prefix character of the LZW code. u8 aDecompBuffer[3000]; // Decompression buffer. The higher the compression, the more bytes are needed in the buffer. u8 * sp; // Pointer into the decompression buffer int CurBit; int LastBit; int GetDone; int LastByte; int ReturnClear; int CodeSize; int SetCodeSize; int MaxCode; int MaxCodeSize; int ClearCode; int EndCode; int FirstCode; int OldCode; }LZW_INFO; //逻辑屏幕描述块 __packed typedef struct { u16 width; //GIF宽度 u16 height; //GIF高度 u8 flag; //标识符 1:3:1:3=全局颜色表标志(1):颜色深度(3):分类标志(1):全局颜色表大小(3) u8 bkcindex; //背景色在全局颜色表中的索引(仅当存在全局颜色表时有效) u8 pixratio; //像素宽高比 }LogicalScreenDescriptor; //图像描述块 __packed typedef struct { u16 xoff; //x方向偏移 u16 yoff; //y方向偏移 u16 width; //宽度 u16 height; //高度 u8 flag; //标识符 1:1:1:2:3=局部颜色表标志(1):交织标志(1):保留(2):局部颜色表大小(3) }ImageScreenDescriptor; //图像描述 __packed typedef struct { LogicalScreenDescriptor gifLSD; //逻辑屏幕描述块 ImageScreenDescriptor gifISD; //图像描述快 u16 colortbl[256]; //当前使用颜色表 u16 bkpcolortbl[256]; //备份颜色表.当存在局部颜色表时使用 u16 numcolors; //颜色表大小 u16 delay; //延迟时间 LZW_INFO *lzw; //LZW信息 }gif89a; extern u8 gifdecoding; //GIF正在解码标记. u8 gif_check_head(FIL *file); //检测GIF头 u16 gif_getrgb565(u8 *ctb); //将RGB888转为RGB565 u8 gif_readcolortbl(FIL *file,gif89a * gif,u16 num); //读取颜色表 u8 gif_getinfo(FIL *file,gif89a * gif); //得到逻辑屏幕描述,图像尺寸等 void gif_savegctbl(gif89a* gif); //保存全局颜色表 void gif_recovergctbl(gif89a* gif); //恢复全局颜色表 void gif_initlzw(gif89a* gif,u8 codesize); //初始化LZW相关参数 u16 gif_getdatablock(FIL *gfile,u8 *buf,u16 maxnum); //读取一个数据块 u8 gif_readextension(FIL *gfile,gif89a* gif, int *pTransIndex,u8 *pDisposal); //读取扩展部分 int gif_getnextcode(FIL *gfile,gif89a* gif); //从LZW缓存中得到下一个LZW码,每个码包含12位 int gif_getnextbyte(FIL *gfile,gif89a* gif); //得到LZW的下一个码 u8 gif_dispimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0,int Transparency, u8 Disposal); //显示图片 void gif_clear2bkcolor(u16 x,u16 y,gif89a* gif,ImageScreenDescriptor pimge); //恢复成背景色 u8 gif_drawimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0); //画GIF图像的一帧 u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height);//在指定区域解码一个GIF文件. void gif_quit(void); //退出当前解码. #endif
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) { mymemset((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 mymemset((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) { LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,*(pTrans+OldIndex)); }else if(Disposal==2) { LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,bkcolor); } Cnt=0; }else { if(OldIndex>=0) { if(OldIndex!=Transparency)LCD_Draw_Point(XPos-1,YPos,*(pTrans+OldIndex)); else if(Disposal==2)LCD_Draw_Point(XPos-1,YPos,bkcolor); } } } OldIndex=Index; } if((OldIndex!=Transparency)||(Disposal==2)) { if(OldIndex!=Transparency)ColorIndex=*(pTrans+OldIndex); else ColorIndex=bkcolor; if(Cnt) { LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,ColorIndex); }else LCD_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)LCD_Fill_Rect(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)LCD_Fill_Rect(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)LCD_Fill_Rect(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)LCD_Fill_Rect(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: //Read image 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文件 //本例子不能显示尺寸大于给定尺寸的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*)malloc(sizeof(FIL)); if(gfile==NULL)res=PIC_MEM_ERR;//申请内存失败 mygif89a=(gif89a*)malloc(sizeof(gif89a)); if(mygif89a==NULL)res=PIC_MEM_ERR;//申请内存失败 mygif89a->lzw=(LZW_INFO*)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); } free(gfile); free(mygif89a->lzw); free(mygif89a); return res; }
这是UCGUI的库,性能差一点的单片机解码起来有点慢,看起来有点卡,哦对了还是用了fatfs文件系统,移植接口如下
/*-----------------------------------------------------------------------*/ /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ /* This is an example of glue functions to attach various exsisting */ /* storage control modules to the FatFs module with a defined API. */ /*-----------------------------------------------------------------------*/ #include "diskio.h" /* FatFs lower layer API */ #include "sdcard.h" /* Example: MMC/SDC contorl */ #define MMC 0 /* Example: Map MMC/SD card to drive number 1 */ /*-----------------------------------------------------------------------*/ /* Get Drive Status */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { DSTATUS stat; int result; switch (pdrv) { case MMC : result = SDCARD_Get_Sector_Count(); if(result == 0)stat = STA_NOINIT; else stat = 0;//OK return stat; } return STA_NOINIT; } /*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { DSTATUS stat; int result; switch (pdrv) { case MMC : result = SDCARD_Init(); if(result == 0)stat = 0; else //STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常 { SPI2_Set_Speed(SPI_SPEED_256); SPI2_Write_Read_Byte(0xff);//提供额外的8个时钟 SPI2_Set_Speed(SPI_SPEED_4); stat = STA_NOINIT;//error } return stat; } return STA_NOINIT; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { DRESULT res; int result; switch (pdrv) { case MMC : result = SDCARD_Read_Sector(buff, sector, count); if(result == 0)res = RES_OK; else res = RES_ERROR; return res; } return RES_PARERR; } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ #if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { DRESULT res; int result; switch (pdrv) { case MMC : result = SDCARD_Write_Sector((u8*)buff, sector, count); if(result == 0)res = RES_OK; else res = RES_ERROR; return res; } return RES_PARERR; } #endif /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; switch (pdrv) { case MMC : switch(cmd) { case CTRL_SYNC: SD_CS=0; if(SDCARD_Wait_Ready()==0)res = RES_OK; else res = RES_ERROR; SD_CS=1; break; case GET_SECTOR_SIZE: *(WORD*)buff = 512; res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = 8; res = RES_OK; break; case GET_SECTOR_COUNT: *(DWORD*)buff = SDCARD_Get_Sector_Count(); res = RES_OK; break; default: res = RES_PARERR; break; } return res; } return RES_PARERR; } #endif
到这里基本就完成了,还需要一个控制界面能控制上一幅图下一幅图的我们留到后面一章再说,工程代码的下载地址为
http://download.csdn.net/detail/dengrengong/8542869