You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
Connect an SD card and GC9A01A display to an ESP32-WROOM32 Dev board.
Utilize TFT_eSPI and the included GC9A01A driver with AnimatedGIF
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++];
There doesn't appear to be anything wrong with the code you sent, but a few clarifications -
There can be no memory leaks within AnimatedGIF because it doesn't allocate or free any memory
You state that there is an error, but don't say what it is.
There are various things that can go wrong with SD card access
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.
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:
Image files
Screenshots
Serial output
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;
#ifdef USE_DMA // 71.6 fps (ST7796 84.5 fps)
#else // 57.0 fps
tft.setCursor(56,56);
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixels(&usTemp[0][0], iCount);
#endif
#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();
}
}
`
The text was updated successfully, but these errors were encountered: