图片解码首先是最简单的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[] = {};       //存储bmp文件的数组

//解码这个BMP文件
//设定显示起始位置以及终止位置
BOOL BmpDecode(u8 *filename,u16 sx,u16 sy,u16 ex,u16 ey)
{
FIL f_bmp; //文件系统变量,用于读取bmp文件
u16 filereadnum = ; //用于记录文件字节读取数量
FRESULT res; //文件读取返回信息
BITMAPFILEHEADER bmpfileHead; //位图文件头
BITMAPINFOHEADER bmpinfoHead; //位图信息头
u8 colorByte = ; //用于标记颜色位
u16 index = ; //读取文件信息时候用来定位
u16 uitemp = ; //记录实际数据一行有多少个点
u16 xtemp = ;
u16 ytemp = ; //显示时辅助计数
u16 colortemp = ; //颜色缓冲 res=f_open(&f_bmp,(const TCHAR*)filename,FA_READ); //打开文件
if(res!=FR_OK) return FALSE;
res = f_read(&f_bmp,bmpbuffer,,(UINT*)&filereadnum); //读取文件
if(res!=FR_OK) return FALSE; //获取位图文件头
bmpfileHead.bfType = (WORD)((((u16)bmpbuffer[])<<) + bmpbuffer[+]);
bmpfileHead.bfSize = bufferToDword(bmpbuffer,);
bmpfileHead.bfReserved1 = bufferToWord(bmpbuffer,);
bmpfileHead.bfReserved2 = bufferToWord(bmpbuffer,);
bmpfileHead.bfOffBits = bufferToDword(bmpbuffer,); //数据段开始地址
//获取位图信息头
bmpinfoHead.biSize = bufferToDword(bmpbuffer,);
bmpinfoHead.biWidth = bufferToLong(bmpbuffer,);
bmpinfoHead.biHeight = bufferToLong(bmpbuffer,);
bmpinfoHead.biPlanes = bufferToWord(bmpbuffer,);
bmpinfoHead.biBitCount = bufferToWord(bmpbuffer,); //颜色位
bmpinfoHead.biCompression = bufferToDword(bmpbuffer,);
bmpinfoHead.biSizeImage = bufferToDword(bmpbuffer,);
bmpinfoHead.biXPelsPerMeter = bufferToLong(bmpbuffer,);
bmpinfoHead.biYPelsPerMeter = bufferToLong(bmpbuffer,);
bmpinfoHead.biClrUsed = bufferToDword(bmpbuffer,);
bmpinfoHead.biClrImportant = bufferToDword(bmpbuffer,);
index = bmpfileHead.bfOffBits;
//所有信息得到,这里应该进行测试了 colorByte = bmpinfoHead.biBitCount/; //颜色位数 16 / 24 / 32 得到2/3/4
if((bmpinfoHead.biWidth%) != ) //不是4的倍数
{
uitemp = (((bmpinfoHead.biWidth/) + ) * );
}
else //是4的倍数
{
uitemp = bmpinfoHead.biWidth ;
}
if(colorByte == ) //24位颜色
{
while()
{
if(index <= )
{
colortemp = RGB888buffertoRGB565(bmpbuffer,index);
index = index +;
if(sx+xtemp <ex && sy+ytemp < ey)
LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
xtemp++;
if(xtemp >= uitemp)
{
xtemp = ;
ytemp++;
}
}
else
{
if(index == )
{
if(sx+xtemp <ex && sy+ytemp < ey)
LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
xtemp++;
if(xtemp >= uitemp)
{
xtemp = ;
ytemp++;
}
index = ;
}
else if(index == )
{
if(sx+xtemp <ex && sy+ytemp < ey)
LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
xtemp++;
if(xtemp >= uitemp)
{
xtemp = ;
ytemp++;
}
index = ;
}
else index = ;
res = f_read(&f_bmp,bmpbuffer,,(UINT*)&filereadnum); //读取文件
if(res||filereadnum==)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 = ;
static int MaskR = LCD_X_SIZE - ;
static int MaskT = ;
static int MaskB = LCD_Y_SIZE - ; #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 + ; /* Vertical size */
xc = right - left + ; xs = ; /* 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[] __attribute__ ((aligned())); //定义全局数组变量,作为输入的缓冲区,强制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 : ;/* 重新定位数据点,相当于删除之前的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,使解码工作继续执行 */
} //加载并显示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 = ; scale < ; 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[]; // Input buffer for data block
short aCode [( << MAX_NUM_LWZ_BITS)]; // This array stores the LZW codes for the compressed strings
u8 aPrefix[( << MAX_NUM_LWZ_BITS)]; // Prefix character of the LZW code.
u8 aDecompBuffer[]; // 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[]; //当前使用颜色表
u16 bkpcolortbl[]; //备份颜色表.当存在局部颜色表时使用
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[] =
{
0x0000, 0x0001, 0x0003, 0x0007,
0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff,
0x0fff, 0x1fff, 0x3fff, 0x7fff,
};
const u8 _aInterlaceOffset[]={,,,};
const u8 _aInterlaceYPos []={,,,}; u8 gifdecoding=;//标记GIF正在解码. //检测GIF头
//返回值:0,是GIF89a/87a;非零,非GIF89a/87a
u8 gif_check_head(FIL *file)
{
u8 gifversion[];
u32 readed;
u8 res;
res=f_read(file,gifversion,,(UINT*)&readed);
if(res)return ;
if((gifversion[]!='G')||(gifversion[]!='I')||(gifversion[]!='F')||
(gifversion[]!='')||((gifversion[]!='')&&(gifversion[]!=''))||
(gifversion[]!='a'))return ;
else return ;
} //将RGB888转为RGB565
//ctb:RGB888颜色数组首地址.
//返回值:RGB565颜色.
u16 gif_getrgb565(u8 *ctb)
{
u16 r,g,b;
r=(ctb[]>>)&0X1F;
g=(ctb[]>>)&0X3F;
b=(ctb[]>>)&0X1F;
return b+(g<<)+(r<<);
} //读取颜色表
//file:文件;
//gif:gif信息;
//num:tbl大小.
//返回值:0,OK;其他,失败;
u8 gif_readcolortbl(FIL *file,gif89a * gif,u16 num)
{
u8 rgb[];
u16 t;
u8 res;
u32 readed;
for(t=;t<num;t++)
{
res=f_read(file,rgb,,(UINT*)&readed);
if(res)return ;//读错误
gif->colortbl[t]=gif_getrgb565(rgb);
}
return ;
} //得到逻辑屏幕描述,图像尺寸等
//file:文件;
//gif:gif信息;
//返回值:0,OK;其他,失败;
u8 gif_getinfo(FIL *file,gif89a * gif)
{
u32 readed;
u8 res;
res=f_read(file,(u8*)&gif->gifLSD,,(UINT*)&readed);
if(res)return ;
if(gif->gifLSD.flag&0x80)//存在全局颜色表
{
gif->numcolors=<<(gif->gifLSD.flag&0x07);//得到颜色表大小
if(gif_readcolortbl(file,gif,gif->numcolors))return ;//读错误
}
return ;
} //保存全局颜色表
//gif:gif信息;
void gif_savegctbl(gif89a* gif)
{
u16 i=;
for(i=;i<;i++)gif->bkpcolortbl[i]=gif->colortbl[i];//保存全局颜色.
} //恢复全局颜色表
//gif:gif信息;
void gif_recovergctbl(gif89a* gif)
{
u16 i=;
for(i=;i<;i++)gif->colortbl[i]=gif->bkpcolortbl[i];//恢复全局颜色.
} //初始化LZW相关参数
//gif:gif信息;
//codesize:lzw码长度
void gif_initlzw(gif89a* gif,u8 codesize)
{
mymemset((u8 *)gif->lzw, , sizeof(LZW_INFO));
gif->lzw->SetCodeSize = codesize;
gif->lzw->CodeSize = codesize + ;
gif->lzw->ClearCode = ( << codesize);
gif->lzw->EndCode = ( << codesize) + ;
gif->lzw->MaxCode = ( << codesize) + ;
gif->lzw->MaxCodeSize = ( << codesize) << ;
gif->lzw->ReturnClear = ;
gif->lzw->LastByte = ;
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,,(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[];
f_read(gfile,&temp,,(UINT*)&readed);//得到长度
switch(temp)
{
case GIF_PLAINTEXT:
case GIF_APPLICATION:
case GIF_COMMENT:
while(gif_getdatablock(gfile,,)>); //获取数据块
return ;
case GIF_GRAPHICCTL://图形控制扩展块
if(gif_getdatablock(gfile,buf,)!=)return ; //图形控制扩展块的长度必须为4
gif->delay=(buf[]<<)|buf[]; //得到延时
*pDisposal=(buf[]>>)&0x7; //得到处理方法
if((buf[]&0x1)!=)*pTransIndex=buf[]; //透明色表
f_read(gfile,&temp,,(UINT*)&readed); //得到LZW长度
if(temp!=)return ; //读取数据块结束符错误.
return ;
}
return ;//错误的数据
} //从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=;
return gif->lzw->ClearCode;
}
End=gif->lzw->CurBit+gif->lzw->CodeSize;
if(End>=gif->lzw->LastBit)
{
int Count;
if(gif->lzw->GetDone)return-;//Error
gif->lzw->aBuffer[]=gif->lzw->aBuffer[gif->lzw->LastByte-];
gif->lzw->aBuffer[]=gif->lzw->aBuffer[gif->lzw->LastByte-];
if((Count=gif_getdatablock(gfile,&gif->lzw->aBuffer[],))==)gif->lzw->GetDone=;
if(Count<)return -;//Error
gif->lzw->LastByte=+Count;
gif->lzw->CurBit=(gif->lzw->CurBit-gif->lzw->LastBit)+;
gif->lzw->LastBit=(+Count)*;
End=gif->lzw->CurBit+gif->lzw->CodeSize;
}
j=End>>;
i=gif->lzw->CurBit>>;
if(i==j)Result=(long)gif->lzw->aBuffer[i];
else if(i+==j)Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+]<<);
else Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+]<<)|((long)gif->lzw->aBuffer[i+]<<);
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))>=)
{
if(Code==gif->lzw->ClearCode)
{
//Corrupt GIFs can make this happen
if(gif->lzw->ClearCode>=(<<MAX_NUM_LWZ_BITS))return -;//Error
//Clear the tables
mymemset((u8*)gif->lzw->aCode,,sizeof(gif->lzw->aCode));
for(i=;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+;
gif->lzw->MaxCodeSize=gif->lzw->ClearCode<<;
gif->lzw->MaxCode=gif->lzw->ClearCode+;
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 -;//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)<(<<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<(<<MAX_NUM_LWZ_BITS)))
{
gif->lzw->MaxCodeSize<<=;
++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-;
bkcolor=gif->colortbl[gif->gifLSD.bkcindex];
pTrans=(u16*)gif->colortbl;
f_read(gfile,&lzwlen,,(UINT*)&readed);//得到LZW长度
gif_initlzw(gif,lzwlen);//Initialize the LZW stack with the LZW code size
Interlace=gif->gifISD.flag&0x40;//是否交织编码
for(YCnt=,YPos=y0,Pass=;YCnt<Height;YCnt++)
{
Cnt=;
OldIndex=-;
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==-)return ;//Endcode
if((Index<)||(Index>=gif->numcolors))
{
//IfIndex out of legal range stop decompressing
return ;//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-,YPos,Cnt+,*(pTrans+OldIndex));
}else if(Disposal==)
{
LCD_Draw_Hline(XPos-Cnt-,YPos,Cnt+,bkcolor);
}
Cnt=;
}else
{
if(OldIndex>=)
{
if(OldIndex!=Transparency)LCD_Draw_Point(XPos-,YPos,*(pTrans+OldIndex));
else if(Disposal==)LCD_Draw_Point(XPos-,YPos,bkcolor);
}
}
}
OldIndex=Index;
}
if((OldIndex!=Transparency)||(Disposal==))
{
if(OldIndex!=Transparency)ColorIndex=*(pTrans+OldIndex);
else ColorIndex=bkcolor;
if(Cnt)
{
LCD_Draw_Hline(XPos-Cnt-,YPos,Cnt+,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 ;
} //恢复成背景色
//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==||pimge.height==)return;//直接不用清除了,原来没有图像!!
if(gif->gifISD.yoff>pimge.yoff)
{
x0=x+pimge.xoff;
y0=y+pimge.yoff;
x1=x+pimge.xoff+pimge.width-;;
y1=y+gif->gifISD.yoff-;
if(x0<x1&&y0<y1&&x1<&&y1<)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-;;
y1=y+pimge.yoff+pimge.height-;
if(x0<x1&&y0<y1&&x1<&&y1<)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-;
x1=x+pimge.xoff+pimge.width-;;
y1=y+pimge.yoff+pimge.height-;
if(x0<x1&&y0<y1&&x1<&&y1<)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-;
y0=y+pimge.yoff;
x1=x+pimge.xoff+pimge.width-;;
y1=y+pimge.yoff+pimge.height-;
if(x0<x1&&y0<y1&&x1<&&y1<)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=-;
do
{
res=f_read(gfile,&Introducer,,(UINT*)&readed);//读取一个字节
if(res)return ;
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,,(UINT*)&readed);//读取一个字节
if(res)return ;
if(gif->gifISD.flag&0x80)//存在局部颜色表
{
gif_savegctbl(gif);//保存全局颜色表
numcolors=<<(gif->gifISD.flag&0X07);//得到局部颜色表大小
if(gif_readcolortbl(gfile,gif,numcolors))return ;//读错误
}
if(Disposal==)gif_clear2bkcolor(x0,y0,gif,previmg);
gif_dispimage(gfile,gif,x0+gif->gifISD.xoff,y0+gif->gifISD.yoff,TransIndex,Disposal);
while()
{
f_read(gfile,&temp,,(UINT*)&readed);//读取一个字节
if(temp==)break;
readed=f_tell(gfile);//还存在块.
if(f_lseek(gfile,readed+temp))break;//继续向后偏移
}
if(temp!=)return ;//Error
return ;
case GIF_INTRO_TERMINATOR://得到结束符了
return ;//代表图像解码完成了.
case GIF_INTRO_EXTENSION:
//Read image extension*/
res=gif_readextension(gfile,gif,&TransIndex,&Disposal);//读取图像扩展块消息
if(res)return ;
break;
default:
return ;
}
}while(Introducer!=GIF_INTRO_TERMINATOR);//读到结束符了
return ;
} //退出当前解码.
void gif_quit(void)
{
gifdecoding=;
} //解码一个gif文件
//本例子不能显示尺寸大于给定尺寸的gif图片!!!
//filename:带路径的gif文件名字
//x,y,width,height:显示坐标及区域大小.
u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height)
{
u8 res=;
u16 dtime=;//解码延时
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==)//OK
{
res=f_open(gfile,(TCHAR *)filename,FA_READ);
if(res==)//打开文件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)/+x;
y=(height-mygif89a->gifLSD.height)/+y;
}
gifdecoding=;
while(gifdecoding&&res==)//解码循环
{
res=gif_drawimage(gfile,mygif89a,x,y);//显示一张图片
if(mygif89a->gifISD.flag&0x80)gif_recovergctbl(mygif89a);//恢复全局颜色表
if(mygif89a->delay)dtime=mygif89a->delay;
else dtime=;//默认延时
while(dtime--&&gifdecoding)Delay_Ms();//延迟
if(res==)
{
res=;
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 == )stat = STA_NOINIT;
else stat = ;//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 == )stat = ;
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 == )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 == )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=;
if(SDCARD_Wait_Ready()==)res = RES_OK;
else res = RES_ERROR;
SD_CS=;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = ;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = ;
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

STM32单片机图片解码的更多相关文章

  1. 关于stm32的正交解码

    关于正交解码,我先解释何为正交解码,,,,其实名字挺高大上的,,,,还是先说编码器吧 看一下我用过的一种编码器 编码器的 线 数 ,是说编码器转一圈输出多少个脉冲,,,如果一个编码器是500线,,,说 ...

  2. 龙邱STM32单片机用J-LINK下载无法被识别的解决方法

    问题如下: 按照正常步骤使用keil5给龙邱的stm32下载程序,SWD下载方式提示no cortex-m sw device found,JTAG方式提示no cortex-m device fou ...

  3. Android实现GIF图片解码与播放

    Android实现GIF图片解码与播放 如何在Android中播放GIF图片呢?如果直接按以前的方法,分解图片,可能相对比较麻烦. 今天给大伙介绍一种新的方式,构造自己的Android图片解码帮助类, ...

  4. STM32单片机在Keil5下仿真的问题解决及GPIO口初始化、使用

    STM32单片机在Keil5下仿真的问题解决及GPIO口初始化.使用 最近看了视频,里面有仿真,可以清楚看到GPIO口的数据变化,也想尝试下,DUG时却出现*** error 65: access v ...

  5. STM32单片机学习心得——概述

    我校的课程真是跟不上时代发展,甚至还在教授8051/8052单片机的内容,于是不甘寂寞的我就自己踏入了STM32单片机的坑-- 首先,我现在大二,刚学完模拟电子技术,还没有学习数字电路技术,于是自学单 ...

  6. 关于STM32单片机的IAP实现

    基于STM32F103单片机的IAP实现(虽然该篇文章不会详细写出实现细节,但是会从一个全局的角度讲述,实际的实现细节只需根据datasheet即可完成). 一.基础概念 什么是IAP?IAP即在应用 ...

  7. 基于STM32单片机光学指纹识别模块(FPM10A)全教程(基于C语言)

    本文转载,其来源在参考中:1,稍加修改,因为近期使用到这个模块,故而加以整理! 1.平台 首先我使用的是 奋斗 STM32 开发板 MINI板 基于STM32单片机光学指纹识别模块(FPM10A)全教 ...

  8. 51单片机和STM32单片机区别在那里

    ​大部分朋友可能都知道51单片机和stm32单片机也知道一般入门会先学习51单片机在学习stm32单片机会简单一些,但是对于51单片机和stm32单片机的具体区别却不知道了,有些人觉得没必要,但是我个 ...

  9. STM32单片机复位后GPIO电平状态

    stm32单片机gpio共有八种工作模式,如下图: stm32单片机是一个低功耗的处理器,当复位以后,gpio默认是高阻状态,也就是浮空输入.这样的好处是: 1.降低了单片机的功耗 2.把gpio模式 ...

随机推荐

  1. keepalived: Compile & startup

    first get keepalived source from git: git clone https://github.com/acassen/keepalived then unzip and ...

  2. php 批量导入数据的一种思维

    <?php $str="风湿免疫科 消化内科 内分泌科 神经内科 感染内科 心血管内科放疗中心";$arr=explode(' ',$str);$sql="&quo ...

  3. Android中布局文件中使用onClick属性

    安卓开发中,布局文件中的控件有一个属性,是onClick,例如:           <Button             android:id="@+id/button1" ...

  4. hadoop yarn

    简介: 本文介绍了 Hadoop 自 0.23.0 版本后新的 map-reduce 框架(Yarn) 原理,优势,运作机制和配置方法等:着重介绍新的 yarn 框架相对于原框架的差异及改进:并通过 ...

  5. jquery_api(CSS)

    outerWidth([options]) 获取第一个匹配元素外部宽度(默认包括补白和边框). 此方法对可见和隐藏元素均有效. outerHeight([options]) 获取第一个匹配元素外部高度 ...

  6. java 基础的几种算法

    1:冒泡排序:2个之间进行循环筛选   public void sort(int[] a) { int temp = 0; for (int i = a.length - 1; i > 0; i ...

  7. Adobe Acrobat 9 Pro 注册码

    来自百度知道,记录与此,以备后用http://zhidao.baidu.com/question/177914535.html 如果你的系统盘是C盘,那么就删除:c:/Documents and Se ...

  8. 使用ReTrofit做缓存(结合上拉加载和下拉刷新)

    1. noCache 不使用缓存,全部走网络 2. noStore 不使用缓存,也不存储缓存 3. onlyIfCached 只使用缓存 4. maxAge 设置最大失效时间,失效则不使用 需要服务器 ...

  9. STL笔记之【map之移除元素】

    //---------------------------------------------------------// 移除map中满足条件的元素//----------------------- ...

  10. linux通过history查看命令执行时间

    Linux的bash内部命令history就可以显示命令行的命令历史,默认环境执行 history 命令后,通常只会显示已执行命令的序号和命令本身.如果想要查看命令历史的时间戳,那么可以执行:# ex ...