Skip to content

Commit a8b34bd

Browse files
authored
Merge pull request #7549 from gamblor21/gif_displayio_support
Animated GIF support
2 parents ecbe9d1 + c637ec9 commit a8b34bd

File tree

15 files changed

+2214
-13
lines changed

15 files changed

+2214
-13
lines changed

lib/AnimatedGIF/AnimatedGIF.cpp

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//
2+
// GIF Animator
3+
// written by Larry Bank
4+
5+
// Arduino port started 7/5/2020
6+
// Original GIF code written 20+ years ago :)
7+
// The goal of this code is to decode images up to 480x320
8+
// using no more than 22K of RAM (if sent directly to an LCD display)
9+
//
10+
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
11+
// Licensed under the Apache License, Version 2.0 (the "License");
12+
// you may not use this file except in compliance with the License.
13+
// You may obtain a copy of the License at
14+
// http://www.apache.org/licenses/LICENSE-2.0
15+
// Unless required by applicable law or agreed to in writing, software
16+
// distributed under the License is distributed on an "AS IS" BASIS,
17+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
// See the License for the specific language governing permissions and
19+
// limitations under the License.
20+
//===========================================================================
21+
#include "AnimatedGIF.h"
22+
23+
// Here is all of the actual code...
24+
#include "gif.inl"
25+
26+
//
27+
// Memory initialization
28+
//
29+
int AnimatedGIF::open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw)
30+
{
31+
_gif.iError = GIF_SUCCESS;
32+
_gif.pfnRead = readMem;
33+
_gif.pfnSeek = seekMem;
34+
_gif.pfnDraw = pfnDraw;
35+
_gif.pfnOpen = NULL;
36+
_gif.pfnClose = NULL;
37+
_gif.GIFFile.iSize = iDataSize;
38+
_gif.GIFFile.pData = pData;
39+
return GIFInit(&_gif);
40+
} /* open() */
41+
42+
int AnimatedGIF::openFLASH(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw)
43+
{
44+
_gif.iError = GIF_SUCCESS;
45+
_gif.pfnRead = readFLASH;
46+
_gif.pfnSeek = seekMem;
47+
_gif.pfnDraw = pfnDraw;
48+
_gif.pfnOpen = NULL;
49+
_gif.pfnClose = NULL;
50+
_gif.GIFFile.iSize = iDataSize;
51+
_gif.GIFFile.pData = pData;
52+
return GIFInit(&_gif);
53+
} /* openFLASH() */
54+
55+
//
56+
// Returns the first comment block found (if any)
57+
//
58+
int AnimatedGIF::getComment(char *pDest)
59+
{
60+
int32_t iOldPos;
61+
62+
iOldPos = _gif.GIFFile.iPos; // keep old position
63+
(*_gif.pfnSeek)(&_gif.GIFFile, _gif.iCommentPos);
64+
(*_gif.pfnRead)(&_gif.GIFFile, (uint8_t *)pDest, _gif.sCommentLen);
65+
(*_gif.pfnSeek)(&_gif.GIFFile, iOldPos);
66+
pDest[_gif.sCommentLen] = 0; // zero terminate the string
67+
return (int)_gif.sCommentLen;
68+
} /* getComment() */
69+
70+
//
71+
// Allocate a block of memory to hold the entire canvas (as 8-bpp)
72+
//
73+
int AnimatedGIF::allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc)
74+
{
75+
if (_gif.iCanvasWidth > 0 && _gif.iCanvasHeight > 0 && _gif.pFrameBuffer == NULL)
76+
{
77+
// Allocate a little extra space for the current line
78+
// as RGB565 or RGB888
79+
int iCanvasSize = _gif.iCanvasWidth * (_gif.iCanvasHeight+3);
80+
_gif.pFrameBuffer = (unsigned char *)(*pfnAlloc)(iCanvasSize);
81+
if (_gif.pFrameBuffer == NULL)
82+
return GIF_ERROR_MEMORY;
83+
return GIF_SUCCESS;
84+
}
85+
return GIF_INVALID_PARAMETER;
86+
} /* allocFrameBuf() */
87+
//
88+
// Set the DRAW callback behavior to RAW (default)
89+
// or COOKED (requires allocating a frame buffer)
90+
//
91+
int AnimatedGIF::setDrawType(int iType)
92+
{
93+
if (iType != GIF_DRAW_RAW && iType != GIF_DRAW_COOKED)
94+
return GIF_INVALID_PARAMETER; // invalid drawing mode
95+
_gif.ucDrawType = (uint8_t)iType;
96+
return GIF_SUCCESS;
97+
} /* setDrawType() */
98+
//
99+
// Release the memory used by the frame buffer
100+
//
101+
int AnimatedGIF::freeFrameBuf(GIF_FREE_CALLBACK *pfnFree)
102+
{
103+
if (_gif.pFrameBuffer)
104+
{
105+
(*pfnFree)(_gif.pFrameBuffer);
106+
_gif.pFrameBuffer = NULL;
107+
return GIF_SUCCESS;
108+
}
109+
return GIF_INVALID_PARAMETER;
110+
} /* freeFrameBuf() */
111+
//
112+
// Return a pointer to the frame buffer (if it was allocated)
113+
//
114+
uint8_t * AnimatedGIF::getFrameBuf()
115+
{
116+
return _gif.pFrameBuffer;
117+
} /* getFrameBuf() */
118+
119+
int AnimatedGIF::getCanvasWidth()
120+
{
121+
return _gif.iCanvasWidth;
122+
} /* getCanvasWidth() */
123+
124+
int AnimatedGIF::getCanvasHeight()
125+
{
126+
return _gif.iCanvasHeight;
127+
} /* getCanvasHeight() */
128+
129+
int AnimatedGIF::getLoopCount()
130+
{
131+
return _gif.iRepeatCount;
132+
} /* getLoopCount() */
133+
134+
int AnimatedGIF::getInfo(GIFINFO *pInfo)
135+
{
136+
return GIF_getInfo(&_gif, pInfo);
137+
} /* getInfo() */
138+
139+
int AnimatedGIF::getLastError()
140+
{
141+
return _gif.iError;
142+
} /* getLastError() */
143+
144+
//
145+
// File (SD/MMC) based initialization
146+
//
147+
int AnimatedGIF::open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw)
148+
{
149+
_gif.iError = GIF_SUCCESS;
150+
_gif.pfnRead = pfnRead;
151+
_gif.pfnSeek = pfnSeek;
152+
_gif.pfnDraw = pfnDraw;
153+
_gif.pfnOpen = pfnOpen;
154+
_gif.pfnClose = pfnClose;
155+
_gif.GIFFile.fHandle = (*pfnOpen)(szFilename, &_gif.GIFFile.iSize);
156+
if (_gif.GIFFile.fHandle == NULL) {
157+
_gif.iError = GIF_FILE_NOT_OPEN;
158+
return 0;
159+
}
160+
return GIFInit(&_gif);
161+
162+
} /* open() */
163+
164+
void AnimatedGIF::close()
165+
{
166+
if (_gif.pfnClose)
167+
(*_gif.pfnClose)(_gif.GIFFile.fHandle);
168+
} /* close() */
169+
170+
void AnimatedGIF::reset()
171+
{
172+
(*_gif.pfnSeek)(&_gif.GIFFile, 0);
173+
} /* reset() */
174+
175+
void AnimatedGIF::begin(unsigned char ucPaletteType)
176+
{
177+
memset(&_gif, 0, sizeof(_gif));
178+
if (ucPaletteType != GIF_PALETTE_RGB565_LE && ucPaletteType != GIF_PALETTE_RGB565_BE && ucPaletteType != GIF_PALETTE_RGB888)
179+
_gif.iError = GIF_INVALID_PARAMETER;
180+
_gif.ucPaletteType = ucPaletteType;
181+
_gif.ucDrawType = GIF_DRAW_RAW; // assume RAW pixel handling
182+
_gif.pFrameBuffer = NULL;
183+
} /* begin() */
184+
//
185+
// Play a single frame
186+
// returns:
187+
// 1 = good result and more frames exist
188+
// 0 = no more frames exist, a frame may or may not have been played: use getLastError() and look for GIF_SUCCESS to know if a frame was played
189+
// -1 = error
190+
int AnimatedGIF::playFrame(bool bSync, int *delayMilliseconds, void *pUser)
191+
{
192+
int rc;
193+
#if !defined( __MACH__ ) && !defined( __LINUX__ )
194+
long lTime = millis();
195+
#endif
196+
197+
if (_gif.GIFFile.iPos >= _gif.GIFFile.iSize-1) // no more data exists
198+
{
199+
(*_gif.pfnSeek)(&_gif.GIFFile, 0); // seek to start
200+
}
201+
if (GIFParseInfo(&_gif, 0))
202+
{
203+
_gif.pUser = pUser;
204+
if (_gif.iError == GIF_EMPTY_FRAME) // don't try to decode it
205+
return 0;
206+
rc = DecodeLZW(&_gif, 0);
207+
if (rc != 0) // problem
208+
return -1;
209+
}
210+
else
211+
{
212+
// The file is "malformed" in that there is a bunch of non-image data after
213+
// the last frame. Return as if all is well, though if needed getLastError()
214+
// can be used to see if a frame was actually processed:
215+
// GIF_SUCCESS -> frame processed, GIF_EMPTY_FRAME -> no frame processed
216+
if (_gif.iError == GIF_EMPTY_FRAME)
217+
{
218+
if (delayMilliseconds)
219+
*delayMilliseconds = 0;
220+
return 0;
221+
}
222+
return -1; // error parsing the frame info, we may be at the end of the file
223+
}
224+
// Return 1 for more frames or 0 if this was the last frame
225+
if (bSync)
226+
{
227+
#if !defined( __MACH__ ) && !defined( __LINUX__ )
228+
lTime = millis() - lTime;
229+
if (lTime < _gif.iFrameDelay) // need to pause a bit
230+
delay(_gif.iFrameDelay - lTime);
231+
#endif // __LINUX__
232+
}
233+
if (delayMilliseconds) // if not NULL, return the frame delay time
234+
*delayMilliseconds = _gif.iFrameDelay;
235+
return (_gif.GIFFile.iPos < _gif.GIFFile.iSize-10);
236+
} /* playFrame() */

0 commit comments

Comments
 (0)