Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GIF won't load and seemingly crashes #93

Open
plain opened this issue Sep 12, 2024 · 1 comment
Open

GIF won't load and seemingly crashes #93

plain opened this issue Sep 12, 2024 · 1 comment

Comments

@plain
Copy link

plain commented Sep 12, 2024

Description:
I am attempting to use the library to display a GIF from an SD card on a GC9A01A display on an ESP32-WROOM32.

When a GIF is loaded, there seems to be a memory leak as the Arduino IDE slows to a crawl and random characters are displayed in the serial monitor. I can confirm that the SD card works and detects the file system and selected file, but the crash appears to occur when opening the file (confirmed by receiving an error when loading a non-existent GIF, but crashing when loading a real GIF). Additionally, displaying a GIF from memory on the TfT display is working, but reading a GIF from the SD does not.

To Reproduce
Steps to reproduce the behavior:

  1. Connect an SD card and GC9A01A display to an ESP32-WROOM32 Dev board.
  2. Utilize TFT_eSPI and the included GC9A01A driver with AnimatedGIF
  3. Attempt to open a GIF from SD

Image files
d
1
homer-worried

Screenshots
Serial output
Screenshot 2024-09-12 at 7 34 50 PM

Code
`
#include <AnimatedGIF.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <SD.h>

TFT_eSPI tft = TFT_eSPI();
AnimatedGIF gif;
File f;

void * GIFOpenFile(const char *fname, int32_t *pSize){
f = SD.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){
File *f = static_cast<File >(pFile->fHandle);
f->seek(iPosition);
pFile->iPos = (int32_t)f->position();
return pFile->iPos;
} /
GIFSeekFile() */

// GIFDraw is called by AnimatedGIF library frame to screen

#define DISPLAY_WIDTH tft.width()
#define DISPLAY_HEIGHT tft.height()
#define BUFFER_SIZE 256 // Optimum is >= GIF width or integral division of width

#ifdef USE_DMA
uint16_t usTemp[2][BUFFER_SIZE]; // Global to support DMA use
#else
uint16_t usTemp[1][BUFFER_SIZE]; // Global to support DMA use
#endif
bool dmaBuf = 0;

void GIFDraw(GIFDRAW *pDraw) {
uint8_t *s;
uint16_t *d, *usPalette;
int x, y, iWidth, iCount;

// Display bounds check and cropping
iWidth = pDraw->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;

// Old image disposal
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;
pEnd = s + iWidth;
x = 0;
iCount = 0; // count non-transparent pixels
while (x < iWidth)
{
c = ucTransparent - 1;
d = &usTemp[0][0];
while (c != ucTransparent && s < pEnd && iCount < BUFFER_SIZE )
{
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?
{
// DMA would degrtade performance here due to short line segments
tft.setCursor(56,56);
tft.setAddrWindow(pDraw->iX + x, y, iCount, 1);
tft.pushPixels(usTemp, iCount);
x += iCount;
iCount = 0;
}
// no, look for a run of transparent pixels
c = ucTransparent;
while (c == ucTransparent && s < pEnd)
{
c = *s++;
if (c == ucTransparent)
x++;
else
s--;
}
}
}
else
{
s = pDraw->pPixels;

// Unroll the first pass to boost DMA performance
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
if (iWidth <= BUFFER_SIZE)
  for (iCount = 0; iCount < iWidth; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
else
  for (iCount = 0; iCount < BUFFER_SIZE; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];

#ifdef USE_DMA // 71.6 fps (ST7796 84.5 fps)

tft.setCursor(56,56);
tft.dmaWait();
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;

#else // 57.0 fps
tft.setCursor(56,56);
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixels(&usTemp[0][0], iCount);
#endif

iWidth -= iCount;
// Loop if pixel buffer smaller than width
while (iWidth > 0)
{
  // Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
  if (iWidth <= BUFFER_SIZE)
    for (iCount = 0; iCount < iWidth; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];
  else
    for (iCount = 0; iCount < BUFFER_SIZE; iCount++) usTemp[dmaBuf][iCount] = usPalette[*s++];

#ifdef USE_DMA
tft.setCursor(56,56);
tft.dmaWait();
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;
#else
tft.setCursor(56,56);
tft.pushPixels(&usTemp[0][0], iCount);
#endif
iWidth -= iCount;
}
}
}

void setup() {
Serial.begin(115200);
if(!SD.begin()){
Serial.println("Card Mount Failed");
return;
}
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.fillRectHGradient(0, 0, 240, 240, TFT_MAGENTA, TFT_BLUE);
tft.setCursor(56,56);
gif.begin(BIG_ENDIAN_PIXELS);
tft.setCursor(56,56);
}

void loop(){
if (gif.open("/d.gif", GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw)) {
Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", gif.getCanvasWidth(), gif.getCanvasHeight());
tft.setCursor(56,56);
tft.startWrite();
while (gif.playFrame(true, NULL)){ yield();}
gif.close();
tft.endWrite();
}
}
`

@bitbank2
Copy link
Owner

There doesn't appear to be anything wrong with the code you sent, but a few clarifications -

  1. There can be no memory leaks within AnimatedGIF because it doesn't allocate or free any memory
  2. You state that there is an error, but don't say what it is.
  3. There are various things that can go wrong with SD card access
  4. TFT_eSPI was not written by me, so I can't confirm that it's not a source of problems

If the GIF decodes correctly from memory, then I would like to verify that the SD card logic is working correctly. If you have the RAM, I would use my new "COOKED" option to render the frames. You would be able to eliminate a lot of the code from your sketch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants