animated gif sdcard spiffs


/* ESP32 Dev Module
 * Upload speed: 921600
 * CPU fr: 240Mhz
 * Flash fr: QIO
 * Flash size "4MB (32MB)
 * Partition: Defult 4MB with spiffs (1.2MB APP/1.5 SB SPIFFS)
 * Debug: none
 * PSRAM: Disabled
 * Arduin Runs on "Core 1"
 * Events Run on: Core 1"
 * Erase All Flash: Disabled
 * JTAG: Disabled
 * Zigbee : Disabled
 */
#include <SD.h>
// GPIO26 = pin 7 = button
// GPIO27 = pin 6 = switch (down = 3.3v vcc, up = gnd)

#include <SPI.h>
#include <FS.h>
#include <SPIFFS.h>
#include <TFT_eSPI.h>      // Install this library with the Arduino Library Manager
                           // Don't forget to configure the driver for the display!

#include <AnimatedGIF.h>   // Install this library with the Arduino Library Manager

#define SD_CS_PIN 5 // changed by MSC 12 // Chip Select Pin (CS) for SD card Reader

AnimatedGIF gif;
File gifFile;              // Global File object for the GIF file
TFT_eSPI tft = TFT_eSPI(); 

const char *filename[2][7] = {
  { "/one.gif", "/two.gif", "/three.gif", "/four.gif", "/five.gif", "/six.gif", "/seven.gif" },   // Change to load other gif files in images/GIF
  { "/sub/one.gif", "/sub/two.gif", "/sub/three.gif", "/sub/four.gif", "/sub/five.gif", "/sub/six.gif", "/sub/seven.gif" }
};

char file_index=0;
int no_gifs = 7;
const uint8_t buttonPin = 26;
const uint8_t switchPin = 27;
volatile bool not_pressed = true;

// interrupt routine
void ARDUINO_ISR_ATTR buttonISR() {
  not_pressed = false;
}


void setup() {
#ifdef DEBUG_MODE
  Serial.begin(115200);
#endif

  pinMode(switchPin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  attachInterrupt(buttonPin, buttonISR, RISING);

  tft.begin();
  tft.setRotation(2); // Adjust Rotation of your screen (0-3)
  tft.fillScreen(TFT_BLACK);

  // Initialize SD card
  if (!SD.begin(SD_CS_PIN,tft.getSPIinstance())) {
#ifdef DEBUG_MODE
    Serial.println("SD card initialization failed!");
#endif
    return;
  }

  // Initialize SPIFFS
  if (!SPIFFS.begin(true)) {
#ifdef DEBUG_MODE
      Serial.println("SPIFFS initialization failed!");
#endif
  }

  // Reformmating the SPIFFS to have space if a large GIF is loaded
  // You could run out of SPIFFS storage space if you load more than one image or a large GIF
  // You can also increase the SPIFFS storage space by changing the partition of the ESP32
  //
#ifdef DEBUG_MODE
   Serial.println("Formatting SPIFFS...");
#endif  
 
  SPIFFS.format(); // This will erase all the files, change as needed, SPIFFs is non-volatile memory
#ifdef DEBUG_MODE
   Serial.println("SPIFFS formatted successfully.");
#endif

   if (digitalRead(switchPin) == HIGH)  file_index = 1;
   else  file_index = 0;

}  //  setup

void loop() {
  // Open GIF file from SD card

  for (int i=0; i < no_gifs; i++) {
     File sdFile = SD.open(filename[file_index][i]);
     if (!sdFile) {
#ifdef DEBUG_MODE
   Serial.println("Failed to open GIF file from SD card!");
#endif          
         return;  // restarts loop()
    }

       // Create a file in SPIFFS to store the GIF
     File spiffsFile = SPIFFS.open(filename[file_index][i], FILE_WRITE, true);
     if (!spiffsFile) {
#ifdef DEBUG_MODE
  Serial.println("Failed to copy GIF in SPIFFS!");
#endif           
           return;
     }

     // Read the GIF from SD card and write to SPIFFS
#ifdef DEBUG_MODE
  Serial.println("Copy GIF in SPIFFS...");
#endif     
     byte buffer[4096];  // block size sd card
     while (sdFile.available()) {
         int bytesRead = sdFile.read(buffer, sizeof(buffer));
         spiffsFile.write(buffer, bytesRead);
     }

     spiffsFile.close();
     sdFile.close();

     // Initialize the GIF
#ifdef DEBUG_MODE
  Serial.println("Starting animation...");
#endif     

     gif.begin(BIG_ENDIAN_PIXELS);
  // this is the gif display loop
  //   int times = 0;
     while (not_pressed) {
       if (gif.open(filename[file_index][i], fileOpen, fileClose, fileRead, fileSeek, GIFDraw)) {
           tft.startWrite(); // The TFT chip select is locked low
           while (gif.playFrame(true, NULL)) {       
           }
           gif.close();
           tft.endWrite(); // Release TFT chip select for the SD Card Reader
       }
    //   times++;
     }
     
     tft.fillScreen(TFT_BLACK);    
     SPIFFS.remove(filename[file_index][i]);
     not_pressed = true;

  } // for filename[] loop
} // loop

// Callback for file operations for the Animated GIF Lobrary
void *fileOpen(const char *filename, int32_t *pFileSize) {
  gifFile = SPIFFS.open(filename, FILE_READ);
  *pFileSize = gifFile.size();
  if (!gifFile)  {
#ifdef DEBUG_MODE
  Serial.println("Failed to open GIF file from SPIFFS!");
#endif    
  }
  return &gifFile;
}

// Callback for file operations for the Animated GIF Library
void fileClose(void *pHandle){
  gifFile.close();
}

// Callback for file operations for the Animated GIF Library
int32_t fileRead(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) {
  int32_t iBytesRead;
  iBytesRead = iLen;
  if ((pFile->iSize - pFile->iPos) < iLen)
    iBytesRead = pFile->iSize - pFile->iPos;
  if (iBytesRead <= 0)
    return 0;

  gifFile.seek(pFile->iPos);
  int32_t bytesRead = gifFile.read(pBuf, iLen);
  pFile->iPos += iBytesRead;

  return bytesRead;
}

// Callback for file operations for the Animated GIF Library
int32_t fileSeek(GIFFILE *pFile, int32_t iPosition) {
  if (iPosition < 0)
    iPosition = 0;
  else if (iPosition >= pFile->iSize)
    iPosition = pFile->iSize - 1;
  pFile->iPos = iPosition;
  gifFile.seek(pFile->iPos);
  return iPosition;
}

---------------GIFDraw.ino-------------------------------
// 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;
  
// Draw a line of image directly on the LCD
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.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.dmaWait();
    tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
    tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
    dmaBuf = !dmaBuf;
#else // 57.0 fps
     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.dmaWait();
      tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
      dmaBuf = !dmaBuf;
#else
      tft.pushPixels(&usTemp[0][0], iCount);
#endif
      iWidth -= iCount;
    }
  }
} /* GIFDraw() */