Display jpg from a SD card

Sorry to bother you, but I had hoped for a simple function to display a jpg from a SD card.
Or should I dive into the lvl library offered by arduino.
Thanks.

Dear All,
I really had hoped that someone was able to help me out on this. Some pointers would be nice.
For example , m5stack offer a function drawJpg that use an argument that points to the source of the JPG file. I hope that this ( rather expensive ) screen also comes along with such a function. But maybe I should search better? Regards.

I suspect that points to the JPG data in memory. You will need to read the SD file and store it and when done call the function with a pointer to where you have stored the JPG.

1 Like

I don't have one of these shields. Thought about it but has been out of stock.

I do have Jpeg, BMP and PNG files displayed on an ILI9341 display on the GIGA.

There are a couple of example sketches in my display library including:
ILI9341_GIGA_n/examples/tft_picture_view_sd/tft_picture_view_sd.ino at main · KurtE/ILI9341_GIGA_n (github.com)

Note: the sketches have the code built into them to decode BMP file, as for JPEG I use the library
bitbank2/JPEGDEC: An optimized JPEG decoder for Arduino (github.com)

and for PNG I also use his library:
bitbank2/PNGdec: Arduino PNG image decoder library (github.com)

Both of these are in the Arduino Library Manager.

1 Like

Hello @MeandMrsJones,

There is an interesting forum post of JGordonJones displaying an image from a USB connected to the Giga. It should also work with an SD.

I don't have an SD card reader at this moment, but I tried the example using a USB and it worked for me.

Let me know if you need help.

1 Like

Thank you, that is interesting. I am going to try that one out. Maybe I'll get back to you or to JGordonJones.

1 Like

I've tried to display Jpg files by modifying a couple of libraries, Tiny Jpg and JPEGDEC to no avail. Problem is the structure of SD vs USB fatfilesytem commands, i.e. open.file vs fopen, read vs fread, etc. I looked into KurtE's wrapper function, but it's set up for the ILI9341 display. What is needed is a stripped down jpeg software decoder the only needs a file that has been opened by either method, or better yet, a USB file system that is compatible with SD or Sdfat commands.

OpenMV has a project, but you have to change the firmware and add an SD card module. They do not plan on adding support for the Giga's built-in USB port, so the mjpeg video is stored in flash mem. It will play 800x480 mjpeg video @ 24fps, maybe faster, using the H7's hardware jpg decoder. You will no longer be able to use Arduino IDE with this firmware, I suspect, so use OpenMV IDE instead (which I prefer not to do).

https://www.hackster.io/naveenbskumar/watch-movies-on-the-arduino-giga-r1-3e4761#toc-main-code-2

For the experts: Does Arduino Giga firmware have support for hardware jpg decode/encode? Maybe using lvgl, but outside of that. Using more of existing code of other projects without major modification is the best solution.

Thank you for trying out. Based on your guidelines I have also spend nearly 8 hours to get some basics working. To no avail. To be honest, really disappointment for such an expensive combi (display and Arduino), and this basic stuff seems not to be available. I really like the widgets, the touchscreen and the form factor, but this is a bummer. I will continue the project with M5AtomDisplay and Shareware HDMI screen. Works great. No touchscreen, but for the current project not needed.

1 Like

Yep, although it was just a simple FileWrapper setup... Where I could handle Files from SD library as well as from USB Host...

Another option you might try, is maybe simply open up the JPEG file, read all of it into memory and the call the method:
int openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
And see if that works for you or not.

That looks promising. I'll try storing jpg to SRAM first. If that works, will try storing to SDRAM, where there can be up to an 8mb buffer.

Should I store jpeg data as char, int, or other?

KurtE

Before I go down this rabbit hole, do you know if File class is consistent/compatible across different libraries i.e. SD, FATFileSytem, etc?

In other words, if I open a file with FATfilesytem, will SD or Sdfat commands recognize it as such?

Sorry not sure. The File class may be specific to having a file FS.h? I would have to look again.

I know that SDFat and FATFileSystem used for USB and the Portenta SD(SDIO) I believe end up using File handles and the like and use STDIO calls, where SDFat has at least three configurations, SdFat(don't remember exact spelling for case) only supported FAT32 and Fat16.
ExFat supported only ExFat
And SdFS was a wrapper class that supported both.

1 Like

Dear all, I just found out that ESP32 CAM is also able to provide RGB565.
So in that case I don’t have to post process the images at all. Why haven’t I checked that option in the beginning instead of starting with the final display step ha ha ha.

config.pixel_format = PIXFORMAT_JPEG; //YUV422,GRAYSCALE,RGB565,JPEG

KurtE:
Tried your suggestion using these two functions on the Giga and the JPEGDEC lib. No image drawn on screen.

int JPEGDraw(JPEGDRAW *pDraw) {

  tft.drawRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
  return 1;
} 

void drawJpeg(char* filename) {    //, int x, int y, int rawWidth, int rawHeight
  uint8_t            *jbuf;                                           
  int          freadReturn;
  long drawTime = micros();            // Keep track of speed

  #if defined(ARM_MATH_CM4) || defined(ARM_MATH_CM7)
    Serial.println("MCU has SIMD");
  #else
  Serial.println("MCU has no SIMD");
  #endif

   // Check file exists and open it
  FILE* jpgFILE = fopen(filename, "rb+");
  if (jpgFILE == NULL) {Serial.print("File not found...");return;}
  else {Serial.print("Opened "); Serial.println(filename); Serial.println();} 
  
  fseek(jpgFILE, 0 , SEEK_END);
  long file_size = ftell(jpgFILE);
  Serial.print("file_size = "); Serial.println(file_size);

  //freadReturn = fread(&jbuf, sizeof(uint8_t), jpeg.getWidth() * jpeg.getHeight(), jpgFILE);
  freadReturn = fread(&jbuf, sizeof(uint8_t), file_size, jpgFILE);

  tft.startWrite();

  if (jpeg.openRAM((uint8_t *)jbuf, sizeof(jbuf), JPEGDraw)) {
    //openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
       
      if (jpeg.decode(0, 0, JPEG_AUTO_ROTATE | 0))  {
        tft.endWrite();
        //Serial.print (f("%d x %d image, decode time = %d us\n", jpeg.getWidth() >> i, jpeg.getHeight() >> i, (int)lTime));
       }
    }
  else Serial.println("Error: jpeg buffer not decoded");

  jpeg.close();
  drawTime = micros() - drawTime;
}

I will have another look at the code later on. I can draw an array with jpeg.openFLASH, though. Draw time for 800x480 jpeg is 369ms. Half size using same array is 201ms, or about 5 fps. Theoretically, if array is 400x240, draw time would be 92ms, or 10fps. Not great speed for playing mjpeg files.

DSI commands for the GigaDisplay use DMA access to the framebuffer, but don't think Arduino Giga core supports hardware jpeg onboard the stm32h7 (yet). Hope you get a GigaDisplay soon, I need some help :slight_smile:

I havent pursued this one yet, I am too stubborn to let go of the jpg approach.
For now I am working on this case:
The TPJG decoder example (GitHub - Bodmer/TJpg_Decoder: Jpeg decoder library based on Tiny JPEG Decompressor) contains an example Flash_JPG. It is meant to display a JPG array on a tft screen. If I understand the documentation correctly, you only have to modify this function :bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
Inside that function there is a function tft.pushImage(x, y, w, h, bitmap);

So, simple as I am, I suppose that if I create my own pushImage function using LVGL functions, that should do the trick.
I am working in that now.
Btw, if the is a route that you all have tried before and no avail, please let me know. Thanks. I will keep you posted.

MeandMrsJones:

You don't need to pursue that approach. Use this instead;

#include <Arduino_GigaDisplay_GFX.h>

tft.drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h)  

The drawRGBBitmap() function uses one command, rather than two, using setAddrWindow() and pushColors() found in other libraries. Substitute the buffer holding the decoded jpeg data (in rgb565) to'uint16_t *bitmap'.

Look at my example for drawing raw images on the Giga for more info on usage of drawRGBBitmap().

Yes, I have seen your example, and that was the inspiration for my solution.
The thing is, I want to make use of the LVGL functionality. And I assume you can't have both, meaning GFX and LVGL simultaneous). Or maybe I should give that a try.
Thanks again.

LVGL is a large library and is not needed unless you want more than just it's jpeg drawing functions.

Wow, I switched to the #include "Arduino_GigaDisplay_GFX.h" lib.
using this decoder : GitHub - Bodmer/TJpg_Decoder: Jpeg decoder library based on Tiny JPEG Decompressor
Set : // The byte order can be swapped (set true for TFT_eSPI)
TJpgDec.setSwapBytes(false);
Used : tft.drawRGBBitmap(x, y, bitmap,w, h);
And there is my Panda on the screen.
Wonderfull.
Still a pity that Arduino does not come up with an easy solution for lvgl.
Thanks

1 Like

Would you mind sharing all your code, so others can avoid lots of wasted time and dead ends.