adding code and gifs
Adding code and gifs for the mini gif players guide
BIN
Mini_GIF_Players/adafruit_gifs/172/Arduino-Logo-172.gif
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/BrainCraft-HAT-172.gif
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/CPX-172.gif
Normal file
|
After Width: | Height: | Size: 341 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/LED-Glasses-172.gif
Normal file
|
After Width: | Height: | Size: 323 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/badger-172.gif
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/blinka-172.gif
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/172/doom-guy-JP-172.gif
Normal file
|
After Width: | Height: | Size: 468 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/Arduino-Logo-320.gif
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/BrainCraft-HAT-320.gif
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/LED-Glasses-320.gif
Normal file
|
After Width: | Height: | Size: 323 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/badger-320.gif
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/blinka-320.gif
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/cpx-320.gif
Normal file
|
After Width: | Height: | Size: 357 KiB |
BIN
Mini_GIF_Players/adafruit_gifs/320/doom-guy-JP-320.gif
Normal file
|
After Width: | Height: | Size: 379 KiB |
287
Mini_GIF_Players/adafruit_mini_gif_players_gfx_spiflash.ino
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Limor Fried for Adafruit Industries
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include <AnimatedGIF.h>
|
||||||
|
#include <SdFat.h>
|
||||||
|
#include <Adafruit_SPIFlash.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
|
||||||
|
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
|
||||||
|
|
||||||
|
#define TFT_CS 5
|
||||||
|
#define TFT_DC 6
|
||||||
|
#define TFT_RST 9
|
||||||
|
|
||||||
|
#define DISPLAY_WIDTH 320
|
||||||
|
#define DISPLAY_HEIGHT 174
|
||||||
|
|
||||||
|
#define GIFDIRNAME "/"
|
||||||
|
#define NUM_LOOPS 5
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
// ESP32 use same flash device that store code.
|
||||||
|
// Therefore there is no need to specify the SPI and SS
|
||||||
|
Adafruit_FlashTransport_ESP32 flashTransport;
|
||||||
|
|
||||||
|
#elif defined(ARDUINO_ARCH_RP2040)
|
||||||
|
// RP2040 use same flash device that store code.
|
||||||
|
// Therefore there is no need to specify the SPI and SS
|
||||||
|
// Use default (no-args) constructor to be compatible with CircuitPython partition scheme
|
||||||
|
Adafruit_FlashTransport_RP2040 flashTransport;
|
||||||
|
|
||||||
|
// For generic usage: Adafruit_FlashTransport_RP2040(start_address, size)
|
||||||
|
// If start_address and size are both 0, value that match filesystem setting in
|
||||||
|
// 'Tools->Flash Size' menu selection will be used
|
||||||
|
|
||||||
|
#else
|
||||||
|
// On-board external flash (QSPI or SPI) macros should already
|
||||||
|
// defined in your board variant if supported
|
||||||
|
// - EXTERNAL_FLASH_USE_QSPI
|
||||||
|
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
|
||||||
|
#if defined(EXTERNAL_FLASH_USE_QSPI)
|
||||||
|
Adafruit_FlashTransport_QSPI flashTransport;
|
||||||
|
|
||||||
|
#elif defined(EXTERNAL_FLASH_USE_SPI)
|
||||||
|
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error No QSPI/SPI flash are defined on your board variant.h !
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Adafruit_SPIFlash flash(&flashTransport);
|
||||||
|
|
||||||
|
// file system object from SdFat
|
||||||
|
FatFileSystem fatfs;
|
||||||
|
|
||||||
|
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
|
||||||
|
AnimatedGIF gif;
|
||||||
|
File f, root;
|
||||||
|
|
||||||
|
void * GIFOpenFile(const char *fname, int32_t *pSize)
|
||||||
|
{
|
||||||
|
f = fatfs.open(fname);
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
*pSize = f.size();
|
||||||
|
return (void *)&f;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
} /* GIFOpenFile() */
|
||||||
|
|
||||||
|
void GIFCloseFile(void *pHandle)
|
||||||
|
{
|
||||||
|
File *f = static_cast<File *>(pHandle);
|
||||||
|
if (f != NULL)
|
||||||
|
f->close();
|
||||||
|
} /* GIFCloseFile() */
|
||||||
|
|
||||||
|
int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen)
|
||||||
|
{
|
||||||
|
int32_t iBytesRead;
|
||||||
|
iBytesRead = iLen;
|
||||||
|
File *f = static_cast<File *>(pFile->fHandle);
|
||||||
|
// Note: If you read a file all the way to the last byte, seek() stops working
|
||||||
|
if ((pFile->iSize - pFile->iPos) < iLen)
|
||||||
|
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
|
||||||
|
if (iBytesRead <= 0)
|
||||||
|
return 0;
|
||||||
|
iBytesRead = (int32_t)f->read(pBuf, iBytesRead);
|
||||||
|
pFile->iPos = f->position();
|
||||||
|
return iBytesRead;
|
||||||
|
} /* GIFReadFile() */
|
||||||
|
|
||||||
|
int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition)
|
||||||
|
{
|
||||||
|
int i = micros();
|
||||||
|
File *f = static_cast<File *>(pFile->fHandle);
|
||||||
|
f->seek(iPosition);
|
||||||
|
pFile->iPos = (int32_t)f->position();
|
||||||
|
i = micros() - i;
|
||||||
|
// Serial.printf("Seek time = %d us\n", i);
|
||||||
|
return pFile->iPos;
|
||||||
|
} /* GIFSeekFile() */
|
||||||
|
|
||||||
|
// Draw a line of image directly on the LCD
|
||||||
|
void GIFDraw(GIFDRAW *pDraw)
|
||||||
|
{
|
||||||
|
uint8_t *s;
|
||||||
|
uint16_t *d, *usPalette, usTemp[320];
|
||||||
|
int x, y, iWidth;
|
||||||
|
|
||||||
|
iWidth = pDraw->iWidth;
|
||||||
|
// Serial.printf("Drawing %d pixels\n", iWidth);
|
||||||
|
|
||||||
|
if (iWidth + pDraw->iX > DISPLAY_WIDTH)
|
||||||
|
iWidth = DISPLAY_WIDTH - pDraw->iX;
|
||||||
|
usPalette = pDraw->pPalette;
|
||||||
|
y = pDraw->iY + pDraw->y; // current line
|
||||||
|
if (y >= DISPLAY_HEIGHT || pDraw->iX >= DISPLAY_WIDTH || iWidth < 1)
|
||||||
|
return;
|
||||||
|
s = pDraw->pPixels;
|
||||||
|
if (pDraw->ucDisposalMethod == 2) // restore to background color
|
||||||
|
{
|
||||||
|
for (x=0; x<iWidth; x++)
|
||||||
|
{
|
||||||
|
if (s[x] == pDraw->ucTransparent)
|
||||||
|
s[x] = pDraw->ucBackground;
|
||||||
|
}
|
||||||
|
pDraw->ucHasTransparency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new pixels to the main image
|
||||||
|
if (pDraw->ucHasTransparency) // if transparency used
|
||||||
|
{
|
||||||
|
uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
|
||||||
|
int x, iCount;
|
||||||
|
pEnd = s + iWidth;
|
||||||
|
x = 0;
|
||||||
|
iCount = 0; // count non-transparent pixels
|
||||||
|
while(x < iWidth)
|
||||||
|
{
|
||||||
|
c = ucTransparent-1;
|
||||||
|
d = usTemp;
|
||||||
|
while (c != ucTransparent && s < pEnd)
|
||||||
|
{
|
||||||
|
c = *s++;
|
||||||
|
if (c == ucTransparent) // done, stop
|
||||||
|
{
|
||||||
|
s--; // back up to treat it like transparent
|
||||||
|
}
|
||||||
|
else // opaque
|
||||||
|
{
|
||||||
|
*d++ = usPalette[c];
|
||||||
|
iCount++;
|
||||||
|
}
|
||||||
|
} // while looking for opaque pixels
|
||||||
|
if (iCount) // any opaque pixels?
|
||||||
|
{
|
||||||
|
tft.startWrite();
|
||||||
|
tft.setAddrWindow(pDraw->iX+x, y, iCount, 1);
|
||||||
|
tft.writePixels(usTemp, iCount, false, false);
|
||||||
|
tft.endWrite();
|
||||||
|
x += iCount;
|
||||||
|
iCount = 0;
|
||||||
|
}
|
||||||
|
// no, look for a run of transparent pixels
|
||||||
|
c = ucTransparent;
|
||||||
|
while (c == ucTransparent && s < pEnd)
|
||||||
|
{
|
||||||
|
c = *s++;
|
||||||
|
if (c == ucTransparent)
|
||||||
|
iCount++;
|
||||||
|
else
|
||||||
|
s--;
|
||||||
|
}
|
||||||
|
if (iCount)
|
||||||
|
{
|
||||||
|
x += iCount; // skip these
|
||||||
|
iCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s = pDraw->pPixels;
|
||||||
|
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
|
||||||
|
for (x=0; x<iWidth; x++)
|
||||||
|
usTemp[x] = usPalette[*s++];
|
||||||
|
tft.startWrite();
|
||||||
|
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
|
||||||
|
tft.writePixels(usTemp, iWidth, false, false);
|
||||||
|
tft.endWrite();
|
||||||
|
}
|
||||||
|
} /* GIFDraw() */
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial);
|
||||||
|
|
||||||
|
Serial.println("Adafruit SPIFlash Animated GIF Example");
|
||||||
|
|
||||||
|
// Initialize flash library and check its chip ID.
|
||||||
|
if (!flash.begin()) {
|
||||||
|
Serial.println("Error, failed to initialize flash chip!");
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.print("Flash chip JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);
|
||||||
|
|
||||||
|
// First call begin to mount the filesystem. Check that it returns true
|
||||||
|
// to make sure the filesystem was mounted.
|
||||||
|
if (!fatfs.begin(&flash)) {
|
||||||
|
Serial.println("Failed to mount filesystem!");
|
||||||
|
Serial.println("Was CircuitPython loaded on the board first to create the filesystem?");
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
Serial.println("Mounted filesystem!");
|
||||||
|
|
||||||
|
if (!root.open(GIFDIRNAME)) {
|
||||||
|
Serial.println("Open dir failed");
|
||||||
|
}
|
||||||
|
while (f.openNext(&root, O_RDONLY)) {
|
||||||
|
f.printFileSize(&Serial);
|
||||||
|
Serial.write(' ');
|
||||||
|
f.printModifyDateTime(&Serial);
|
||||||
|
Serial.write(' ');
|
||||||
|
f.printName(&Serial);
|
||||||
|
if (f.isDir()) {
|
||||||
|
// Indicate a directory.
|
||||||
|
Serial.write('/');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
root.close();
|
||||||
|
|
||||||
|
tft.init(DISPLAY_HEIGHT, DISPLAY_WIDTH);
|
||||||
|
tft.fillScreen(ST77XX_BLUE);
|
||||||
|
tft.setRotation(1);
|
||||||
|
gif.begin(LITTLE_ENDIAN_PIXELS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
char thefilename[80];
|
||||||
|
|
||||||
|
if (!root.open(GIFDIRNAME)) {
|
||||||
|
Serial.println("Open GIF directory failed");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
while (f.openNext(&root, O_RDONLY)) {
|
||||||
|
f.printFileSize(&Serial);
|
||||||
|
Serial.write(' ');
|
||||||
|
f.printModifyDateTime(&Serial);
|
||||||
|
Serial.write(' ');
|
||||||
|
f.printName(&Serial);
|
||||||
|
if (f.isDir()) {
|
||||||
|
// Indicate a directory.
|
||||||
|
Serial.write('/');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
f.getName(thefilename, sizeof(thefilename)-1);
|
||||||
|
f.close();
|
||||||
|
if (strstr(thefilename, ".gif") || strstr(thefilename, ".GIF")) {
|
||||||
|
// found a gif mebe!
|
||||||
|
if (gif.open(thefilename, GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) {
|
||||||
|
GIFINFO gi;
|
||||||
|
Serial.printf("Successfully opened GIF %s; Canvas size = %d x %d\n", thefilename, gif.getCanvasWidth(), gif.getCanvasHeight());
|
||||||
|
if (gif.getInfo(&gi)) {
|
||||||
|
Serial.printf("frame count: %d\n", gi.iFrameCount);
|
||||||
|
Serial.printf("duration: %d ms\n", gi.iDuration);
|
||||||
|
Serial.printf("max delay: %d ms\n", gi.iMaxDelay);
|
||||||
|
Serial.printf("min delay: %d ms\n", gi.iMinDelay);
|
||||||
|
}
|
||||||
|
// play thru n times
|
||||||
|
for (int loops=0; loops<NUM_LOOPS; loops++) {
|
||||||
|
while (gif.playFrame(true, NULL));
|
||||||
|
gif.reset();
|
||||||
|
}
|
||||||
|
gif.close();
|
||||||
|
} else {
|
||||||
|
Serial.printf("Error opening file %s = %d\n", thefilename, gif.getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.close();
|
||||||
|
}
|
||||||