bitbank2 / AnimatedGIF

An optimized GIF decoder suitable for microcontrollers and PCs
Apache License 2.0
358 stars 50 forks source link

a example for tft_espi sd gif #68

Closed hannescam closed 1 year ago

hannescam commented 1 year ago

it would be nice if a example for tft_espi gif from sd example would be provided

tobozo commented 1 year ago

do you feel like creating it?

you can take this example as a template, and use the GIFDraw() function from this example.

bitbank2 commented 1 year ago

I think there are enough examples to get you going. The output of the GIFDRAW call is very simple - all you have to do is use the existing code to convert your pixels into RGB565/RGB888 and copy them to your output device one line at a time.

hannescam commented 1 year ago

but this example is for the m5 stack specific and was not meant for other boards mine is the esp32 capacitive touch camera from makerfabs

tobozo commented 1 year ago

The use of M5 specifics is just a layer over standard components so it'll be fine.

I assume you already have a working TFT_eSPI configuration for the makerfabs device, so basically you only need to rename a few variables in the template sketch and comment out the specifics.

1) comment out the first include line and add yours:

// #include <ESP32-Chimera-Core.h> // comment this out
#include <SD.h>
#include "YOUR_TFT_eSPI_Configuration.h"
#include <TFT_eSPI.h>

2) in the setup(), replace M5.begin() by Serial.begin( 115200 ); 3) search every occurence of M5STACK_SD and replace it by SD, it was just an alias so it'll be fine. 4) comment out the GIFDraw() and TFTDraw() functions 5) copy the GIFDraw function from the other sketch 6) optional: implement esp_deep_sleep() to replace M5.powerOFF();

hannescam commented 1 year ago

ok i will try that and i have allready a working tft_espi config (over hspi) and a working sd config (also over hspi)

hannescam commented 1 year ago

it doesnt work it doesnt find std::vector please provide me a functional sketch

tobozo commented 1 year ago

it doesnt find std::vector

adding #include <vector> or #include <vector.h> in your sketch should solve that

please provide me a functional sketch

I don't have your exact model of makerfabs board so I'm afraid the only help I can provide is theoritical.

hannescam commented 1 year ago

so for the functional example i mean just for tft_espi with loading from sd and i tried that with #include and it didnt work

tobozo commented 1 year ago

I'm sorry I can't help you more than that, good luck in your research!

hannescam commented 1 year ago

i managed to get it to compile and init both sd and TFT_eSPI successfully but it only displays a black screen with the serial output showing that it opens the file successfully (i combined the GIFDraw.ino from TFT_eSPI_memmory and the rest from adafruit_gfx_sdcard) the code:

#include <AnimatedGIF.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <SD.h>
#include <FS.h>

SPIClass spiSD(HSPI);

#define BUFFER_SIZE 256
#define BUILTIN_SDCARD 4

#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;

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

#define DISPLAY_WIDTH  tft.width()
#define DISPLAY_HEIGHT tft.height()

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)
{ 
  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() */

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() */

void setup() {
  Serial.begin(115200);
  while (!Serial);

// Note - some systems (ESP32?) require an SPI.begin() before calling SD.begin()
// this code was tested on a Teensy 4.1 board

  if(!SD.begin(BUILTIN_SDCARD, spiSD))
  {
    Serial.println("SD Card mount failed!");
    return;
  }
  else
  {
    Serial.println("SD Card mount succeeded!");
  }

  // put your setup code here, to run once:
  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(0);
  gif.begin(LITTLE_ENDIAN_PIXELS);
  Serial.print("SD Card Type: ");
      uint8_t cardType = SD.cardType();

    if(cardType == CARD_MMC){
        Serial.println("MMC");
    } else if(cardType == CARD_SD){
        Serial.println("SDSC");
    } else if(cardType == CARD_SDHC){
        Serial.println("SDHC");
    } else {
        Serial.println("UNKNOWN");
    }
    uint64_t cardSize = SD.cardSize() / (1024 * 1024);
    Serial.printf("SD Card Size: %lluMB\n", cardSize);
}

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
     file = root.openNextFile();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
//  Serial.println("About to call gif.open");
//listDir(SD, "/", 0);

  if (gif.open("/et.gif", GIFOpenFile, GIFCloseFile, GIFReadFile, GIFSeekFile, GIFDraw))
  {
    GIFINFO gi;
    Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n", 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);
    }
    while (gif.playFrame(true, NULL))
    {
    }
    gif.close();
  }
  else
  {
    Serial.printf("Error opening file = %d\n", gif.getLastError());
    while (1)
    {};
  }
}

and i use platformio my screen is a ili9488 with the resolution of 480x320

hannescam commented 1 year ago

and please reopen this issue

bitbank2 commented 1 year ago

This issue isn't a bug in AnimatedGIF and isn't really a feature request. I publish my own LCD library (bb_spi_lcd) that has plenty of examples in combination with this code. I don't use TFT_eSPI. You're free to add this to his examples if you think it's necessary.

hannescam commented 1 year ago

does bb_spi_lcd support the ILI9488 in hardware spi mode (the configuration of my board)

bitbank2 commented 1 year ago

Yes

hannescam commented 1 year ago

ok