Skip to content

Commit 880e0f8

Browse files
authored
Merge pull request #2172 from adafruit/mini_gif_players
adding code and gifs
2 parents d017b96 + 225568d commit 880e0f8

16 files changed

+287
-0
lines changed

Mini_GIF_Players/.feather_rp2040.test.only

Whitespace-only changes.

Mini_GIF_Players/Mini_GIF_Players.ino

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// SPDX-FileCopyrightText: 2022 Limor Fried for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <AnimatedGIF.h>
6+
#include <SdFat.h>
7+
#include <Adafruit_SPIFlash.h>
8+
#include <Adafruit_GFX.h>
9+
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
10+
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
11+
12+
#define TFT_CS 5
13+
#define TFT_DC 6
14+
#define TFT_RST 9
15+
16+
#define DISPLAY_WIDTH 320
17+
#define DISPLAY_HEIGHT 174
18+
19+
#define GIFDIRNAME "/"
20+
#define NUM_LOOPS 5
21+
22+
#if defined(ARDUINO_ARCH_ESP32)
23+
// ESP32 use same flash device that store code.
24+
// Therefore there is no need to specify the SPI and SS
25+
Adafruit_FlashTransport_ESP32 flashTransport;
26+
27+
#elif defined(ARDUINO_ARCH_RP2040)
28+
// RP2040 use same flash device that store code.
29+
// Therefore there is no need to specify the SPI and SS
30+
// Use default (no-args) constructor to be compatible with CircuitPython partition scheme
31+
Adafruit_FlashTransport_RP2040 flashTransport;
32+
33+
// For generic usage: Adafruit_FlashTransport_RP2040(start_address, size)
34+
// If start_address and size are both 0, value that match filesystem setting in
35+
// 'Tools->Flash Size' menu selection will be used
36+
37+
#else
38+
// On-board external flash (QSPI or SPI) macros should already
39+
// defined in your board variant if supported
40+
// - EXTERNAL_FLASH_USE_QSPI
41+
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
42+
#if defined(EXTERNAL_FLASH_USE_QSPI)
43+
Adafruit_FlashTransport_QSPI flashTransport;
44+
45+
#elif defined(EXTERNAL_FLASH_USE_SPI)
46+
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);
47+
48+
#else
49+
#error No QSPI/SPI flash are defined on your board variant.h !
50+
#endif
51+
#endif
52+
53+
Adafruit_SPIFlash flash(&flashTransport);
54+
55+
// file system object from SdFat
56+
FatFileSystem fatfs;
57+
58+
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
59+
AnimatedGIF gif;
60+
File f, root;
61+
62+
void * GIFOpenFile(const char *fname, int32_t *pSize)
63+
{
64+
f = fatfs.open(fname);
65+
if (f)
66+
{
67+
*pSize = f.size();
68+
return (void *)&f;
69+
}
70+
return NULL;
71+
} /* GIFOpenFile() */
72+
73+
void GIFCloseFile(void *pHandle)
74+
{
75+
File *f = static_cast<File *>(pHandle);
76+
if (f != NULL)
77+
f->close();
78+
} /* GIFCloseFile() */
79+
80+
int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
81+
{
82+
int32_t iBytesRead;
83+
iBytesRead = iLen;
84+
File *f = static_cast<File *>(pFile->fHandle);
85+
// Note: If you read a file all the way to the last byte, seek() stops working
86+
if ((pFile->iSize - pFile->iPos) < iLen)
87+
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
88+
if (iBytesRead <= 0)
89+
return 0;
90+
iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
91+
pFile->iPos = f->position();
92+
return iBytesRead;
93+
} /* GIFReadFile() */
94+
95+
int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
96+
{
97+
int i = micros();
98+
File *f = static_cast<File *>(pFile->fHandle);
99+
f->seek(iPosition);
100+
pFile->iPos = (int32_t)f->position();
101+
i = micros() - i;
102+
// Serial.printf("Seek time = %d us\n", i);
103+
return pFile->iPos;
104+
} /* GIFSeekFile() */
105+
106+
// Draw a line of image directly on the LCD
107+
void GIFDraw(GIFDRAW *pDraw)
108+
{
109+
uint8_t *s;
110+
uint16_t *d, *usPalette, usTemp[320];
111+
int x, y, iWidth;
112+
113+
iWidth = pDraw->iWidth;
114+
// Serial.printf("Drawing %d pixels\n", iWidth);
115+
116+
if (iWidth + pDraw->iX > DISPLAY_WIDTH)
117+
iWidth = DISPLAY_WIDTH - pDraw->iX;
118+
usPalette = pDraw->pPalette;
119+
y = pDraw->iY + pDraw->y; // current line
120+
if (y >= DISPLAY_HEIGHT || pDraw->iX >= DISPLAY_WIDTH || iWidth < 1)
121+
return;
122+
s = pDraw->pPixels;
123+
if (pDraw->ucDisposalMethod == 2) // restore to background color
124+
{
125+
for (x=0; x<iWidth; x++)
126+
{
127+
if (s[x] == pDraw->ucTransparent)
128+
s[x] = pDraw->ucBackground;
129+
}
130+
pDraw->ucHasTransparency = 0;
131+
}
132+
133+
// Apply the new pixels to the main image
134+
if (pDraw->ucHasTransparency) // if transparency used
135+
{
136+
uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
137+
int x, iCount;
138+
pEnd = s + iWidth;
139+
x = 0;
140+
iCount = 0; // count non-transparent pixels
141+
while(x < iWidth)
142+
{
143+
c = ucTransparent-1;
144+
d = usTemp;
145+
while (c != ucTransparent && s < pEnd)
146+
{
147+
c = *s++;
148+
if (c == ucTransparent) // done, stop
149+
{
150+
s--; // back up to treat it like transparent
151+
}
152+
else // opaque
153+
{
154+
*d++ = usPalette[c];
155+
iCount++;
156+
}
157+
} // while looking for opaque pixels
158+
if (iCount) // any opaque pixels?
159+
{
160+
tft.startWrite();
161+
tft.setAddrWindow(pDraw->iX+x, y, iCount, 1);
162+
tft.writePixels(usTemp, iCount, false, false);
163+
tft.endWrite();
164+
x += iCount;
165+
iCount = 0;
166+
}
167+
// no, look for a run of transparent pixels
168+
c = ucTransparent;
169+
while (c == ucTransparent && s < pEnd)
170+
{
171+
c = *s++;
172+
if (c == ucTransparent)
173+
iCount++;
174+
else
175+
s--;
176+
}
177+
if (iCount)
178+
{
179+
x += iCount; // skip these
180+
iCount = 0;
181+
}
182+
}
183+
}
184+
else
185+
{
186+
s = pDraw->pPixels;
187+
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
188+
for (x=0; x<iWidth; x++)
189+
usTemp[x] = usPalette[*s++];
190+
tft.startWrite();
191+
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
192+
tft.writePixels(usTemp, iWidth, false, false);
193+
tft.endWrite();
194+
}
195+
} /* GIFDraw() */
196+
197+
198+
void setup() {
199+
Serial.begin(115200);
200+
while (!Serial);
201+
202+
Serial.println("Adafruit SPIFlash Animated GIF Example");
203+
204+
// Initialize flash library and check its chip ID.
205+
if (!flash.begin()) {
206+
Serial.println("Error, failed to initialize flash chip!");
207+
while(1);
208+
}
209+
Serial.print("Flash chip JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);
210+
211+
// First call begin to mount the filesystem. Check that it returns true
212+
// to make sure the filesystem was mounted.
213+
if (!fatfs.begin(&flash)) {
214+
Serial.println("Failed to mount filesystem!");
215+
Serial.println("Was CircuitPython loaded on the board first to create the filesystem?");
216+
while(1);
217+
}
218+
Serial.println("Mounted filesystem!");
219+
220+
if (!root.open(GIFDIRNAME)) {
221+
Serial.println("Open dir failed");
222+
}
223+
while (f.openNext(&root, O_RDONLY)) {
224+
f.printFileSize(&Serial);
225+
Serial.write(' ');
226+
f.printModifyDateTime(&Serial);
227+
Serial.write(' ');
228+
f.printName(&Serial);
229+
if (f.isDir()) {
230+
// Indicate a directory.
231+
Serial.write('/');
232+
}
233+
Serial.println();
234+
f.close();
235+
}
236+
root.close();
237+
238+
tft.init(DISPLAY_HEIGHT, DISPLAY_WIDTH);
239+
tft.fillScreen(ST77XX_BLUE);
240+
tft.setRotation(1);
241+
gif.begin(LITTLE_ENDIAN_PIXELS);
242+
}
243+
244+
void loop() {
245+
char thefilename[80];
246+
247+
if (!root.open(GIFDIRNAME)) {
248+
Serial.println("Open GIF directory failed");
249+
while (1);
250+
}
251+
while (f.openNext(&root, O_RDONLY)) {
252+
f.printFileSize(&Serial);
253+
Serial.write(' ');
254+
f.printModifyDateTime(&Serial);
255+
Serial.write(' ');
256+
f.printName(&Serial);
257+
if (f.isDir()) {
258+
// Indicate a directory.
259+
Serial.write('/');
260+
}
261+
Serial.println();
262+
f.getName(thefilename, sizeof(thefilename)-1);
263+
f.close();
264+
if (strstr(thefilename, ".gif") || strstr(thefilename, ".GIF")) {
265+
// found a gif mebe!
266+
if (gif.open(thefilename, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) {
267+
GIFINFO gi;
268+
Serial.printf("Successfully opened GIF %s; Canvas size = %d x %d\n", thefilename, gif.getCanvasWidth(), gif.getCanvasHeight());
269+
if (gif.getInfo(&gi)) {
270+
Serial.printf("frame count: %d\n", gi.iFrameCount);
271+
Serial.printf("duration: %d ms\n", gi.iDuration);
272+
Serial.printf("max delay: %d ms\n", gi.iMaxDelay);
273+
Serial.printf("min delay: %d ms\n", gi.iMinDelay);
274+
}
275+
// play thru n times
276+
for (int loops=0; loops<NUM_LOOPS; loops++) {
277+
while (gif.playFrame(true, NULL));
278+
gif.reset();
279+
}
280+
gif.close();
281+
} else {
282+
Serial.printf("Error opening file %s = %d\n", thefilename, gif.getLastError());
283+
}
284+
}
285+
}
286+
root.close();
287+
}
279 KB
Loading
260 KB
Loading
341 KB
Loading
323 KB
Loading
84.5 KB
Loading
136 KB
Loading
468 KB
Loading
139 KB
Loading

0 commit comments

Comments
 (0)