Files
player/Project/Src/JPEG/gif.c
2025-06-27 00:32:57 +08:00

630 lines
17 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "main.h"
#include "ff.h"
#include "gif.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//图片解码 驱动代码-gif解码部分
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/7
//版本V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//升级说明
//无
//////////////////////////////////////////////////////////////////////////////////
struct _pic_phy
{
void (*draw_hline)(int x,int y,int len,u16 color);
void (*draw_point)(int x,int y,u16 color);
void (*fill)(int sx,int xy,int ex,int ey,u16 color);
};
static GIF_DecodeStruct *g_decode=0;
static void draw_hline (int x,int y,int len,u16 color)
{
if (g_decode&&g_decode->frameNumber&&g_decode->frame[g_decode->frameNumber-1])
{
if (x>=0&&x<g_decode->x_size)
{
if (x+len>g_decode->x_size) len=g_decode->x_size-x;
int ds=y*g_decode->x_size+x;
for (int i=0;i<len;i++)
(g_decode->frame[g_decode->frameNumber-1])[ds+i]=color;
}
}
}
static void draw_point (int x,int y,u16 color)
{
if (g_decode&&g_decode->frameNumber&&g_decode->frame[g_decode->frameNumber-1])
{
int ds=y*g_decode->x_size+x;
(g_decode->frame[g_decode->frameNumber-1])[ds]=color;
}
}
static void fill(int sx,int sy,int ex,int ey,u16 color)
{
for (int i=0;i<ey-sy+1;i++)
{
draw_hline (sx,sy+i,ex-sx+1,color);
}
}
static struct _pic_phy pic_phy ={
.draw_hline=draw_hline,
.draw_point=draw_point,
.fill=fill,
};
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)
{
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:
//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文件
//t,解码使用的结构体指针,将在此函数申请内存空间
//filename:带路径的gif文件名字
int GIF_Decode(GIF_DecodeStruct *gif,const u8 *filename)
{
u16 x;u16 y;u16 width;u16 height;
u8 res=0;
u16 dtime=0;//解码延时
gif89a *mygif89a;
FIL *gfile;
// GIF_DecodeStructFree(g_decode);
// g_decode=mymalloc (sizeof (GIF_DecodeStruct));
g_decode=gif;
if(g_decode==0) return 0;
mymemset (g_decode,0,sizeof (GIF_DecodeStruct));
gfile=(FIL*)mymalloc(sizeof(FIL));
mygif89a=(gif89a*)mymalloc(sizeof(gif89a));
mymemset (mygif89a,0,sizeof(gif89a));
mygif89a->lzw=(LZW_INFO*)mymalloc(sizeof(LZW_INFO));
mymemset (mygif89a->lzw,0,sizeof(LZW_INFO));
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;
x=0;y=0;width=mygif89a->gifLSD.width;height=mygif89a->gifLSD.height;
g_decode->x_size=width;g_decode->y_size=height;
gifdecoding=1;
while(gifdecoding&&res==0)//解码循环
{
if (g_decode->frameNumber==GIF_PICCONT_MAX) break;
//为每一帧图像申请内存
g_decode->frame[g_decode->frameNumber]=mymalloc_exm (g_decode->x_size*g_decode->y_size*2);
if (g_decode->frame[g_decode->frameNumber]==0) break;//内存已用完
if (g_decode->frameNumber)
{
//这里把前一张的数据复制到下一张中
mymemcpy (g_decode->frame[g_decode->frameNumber],g_decode->frame[g_decode->frameNumber-1],g_decode->x_size*g_decode->y_size*2);
}
g_decode->frameNumber++;
res=gif_drawimage(gfile,mygif89a,x,y);//显示一张图片
if(mygif89a->gifISD.flag&0x80)gif_recovergctbl(mygif89a);//恢复全局颜色表
if(mygif89a->delay)dtime=mygif89a->delay;
else dtime=10;//默认延时
g_decode->delay[g_decode->frameNumber-1]=dtime*10;
if(res==2)
{
res=0;
break;
}
if (res==1) {GIF_DecodeStructFree(g_decode);break;}//解码失败
}
}
else
{
GIF_DecodeStructFree(g_decode);
}
f_close(gfile);
}
myfree(gfile);
myfree(mygif89a->lzw);
myfree(mygif89a);
return 1;
}
//释放GIF_DecodeStruct 结构体申请的内存
void GIF_DecodeStructFree (GIF_DecodeStruct *gif)
{
if (gif)
{
for (int i=0;i<gif->frameNumber;i++)
{
myfree(gif->frame[i]);
}
// myfree(g_decode);
}
// g_decode=0;
}