Display jpg from a SD card

Ofcourse.

const uint8_t panda[]

// Example for library:
// https://github.com/Bodmer/TJpg_Decoder

// This example renders a Jpeg file that is stored in an array within Flash (program) memory
// see panda.h tab.  The panda image file being ~13Kbytes.

// Include the array
#include "panda.h"

// Include the jpeg decoder library
#include <TJpg_Decoder.h>

// Include the TFT library https://github.com/Bodmer/TFT_eSPI
#include "SPI.h"
//#include <TFT_eSPI.h>              // Hardware-specific library
//TFT_eSPI tft = TFT_eSPI();         // Invoke custom library
#include "Arduino_GigaDisplay_GFX.h"

GigaDisplay_GFX tft;

#define GC9A01A_CYAN    0x07FF
#define GC9A01A_RED     0xf800
#define GC9A01A_BLUE    0x001F
#define GC9A01A_GREEN   0x07E0
#define GC9A01A_MAGENTA 0xF81F
#define GC9A01A_WHITE   0xffff
#define GC9A01A_BLACK   0x0000
#define GC9A01A_YELLOW  0xFFE0


// This next function will be called during decoding of the jpeg file to
// render each block to the TFT.  If you use a different TFT library
// you will need to adapt this function to suit.
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;

  // This function will clip the image block rendering automatically at the TFT boundaries
  //tft.pushImage(x, y, w, h, bitmap);
  //tft.drawRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
   tft.drawRGBBitmap(x, y, bitmap,w, h);
 
  // This might work instead if you adapt the sketch to use the Adafruit_GFX library
  // tft.drawRGBBitmap(x, y, bitmap, w, h);

  // Return 1 to decode next block
  return 1;
}

void setup()
{

  Serial.begin(115200);
  Serial.println("\n\n Testing TJpg_Decoder library");

  // Initialise the TFT
  tft.begin();
  tft.setTextColor(0xFFFF, 0x0000);
  tft.fillScreen(GC9A01A_YELLOW);

  delay(10000);
  // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
  TJpgDec.setJpgScale(1);

  // The byte order can be swapped (set true for TFT_eSPI)
  TJpgDec.setSwapBytes(false);

  // The decoder must be given the exact name of the rendering function above
  TJpgDec.setCallback(tft_output);
}

void loop()
{
  tft.fillScreen(GC9A01A_MAGENTA);

  // Time recorded for test purposes
  uint32_t t = millis();

  // Get the width and height in pixels of the jpeg if you wish
  uint16_t w = 0, h = 0;
  TJpgDec.getJpgSize(&w, &h, panda, sizeof(panda));
  Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);

  // Draw the image, top left at 0,0
  TJpgDec.drawJpg(100, 100, panda, sizeof(panda));

  // How much time did rendering take (ESP8266 80MHz 262ms, 160MHz 149ms, ESP32 SPI 111ms, 8bit parallel 90ms
  t = millis() - t;
  Serial.print(t); Serial.println(" ms");

  // Wait before drawing again
  delay(2000);
}

For reference, did you have to make any modifications to the TJpg_Decoder library?

That was not required because tft_output contains the only call to display pixels on the screen.
Now I am working on reading a jpg into a uint8_t array using ftp_client and then displaying the jog on the screen.
Regards

MeandMrsJones:

Good luck with your project. Meanwhile, I'm trying to work out a bug using Mjpeg code from the the moononournation/Arduino_GFX library which utilizes JPEGDEC as the engine. It worked fine with esp32 board, but getting an error with Giga, 'jpegDrawCallback' was not declared in this scope. Most likely due to changing code for SD functions to Mbed5 USB functions.

I tried your code with an 800x480 jpeg array. Worked fine. However, draw time is 421ms vs 369 using JPEGDEC with same jpeg array. Not a huge difference. Now on to get a working sketch with Mbed5 USB to draw jpegs from usb thumb drive.

I now have some working code. Download a JPG file using ftp and displaying it on the screen.
Rough code, proof of principle.

/******************************************************************************
  FTPClient_DownloadFile.ino

  FTP Client for Generic boards using SD, FS, etc.

  Based on and modified from

  1) esp32_ftpclient Library         https://github.com/ldab/ESP32_FTPClient

  Built by Khoi Hoang https://github.com/khoih-prog/FTPClient_Generic
******************************************************************************/

// iets met JPG : https://github.com/lvgl/lv_lib_split_jpg

// esp32-cam PIXFORMAT_RGB565,    // 2BPP/RGB565

// over RGB565 format : https://rgbcolorpicker.com/565#:~:text=RGB565%20(also%20known%20as%2016,bits%20for%20the%20blue%20channel.



#include "Arduino.h"
#include <TJpg_Decoder.h>
#include "panda.h"
#include "SPI.h"
//#include "Arduino_H7_Video.h"
//#include "lvgl.h"

//Arduino_H7_Video          Display(800, 480, GigaDisplayShield);
//#include "defines.h"

#include "WiFi.h"

#include <FTPClient_Generic.h>

#include "Arduino_GigaDisplay_GFX.h"

GigaDisplay_GFX tft;

#define GC9A01A_CYAN    0x07FF
#define GC9A01A_RED     0xf800
#define GC9A01A_BLUE    0x001F
#define GC9A01A_GREEN   0x07E0
#define GC9A01A_MAGENTA 0xF81F
#define GC9A01A_WHITE   0xffff
#define GC9A01A_BLACK   0x0000
#define GC9A01A_YELLOW  0xFFE0

#define WHITE 0xffff
	#define BLACK 0x0000


#define WIFI_SSID      ""
#define WIFI_PASS      ""

const  int max_File_size=32000;
uint8_t file_buffer[max_File_size]; // moet dit niet 16 bit zijn ? maar panda is ook uint8_t
int Nr_Bytes_Read;

// To use `true` with the following PASV mode asnswer from server, such as `VSFTP`
// 227 Entering Passive Mode (192,168,2,112,157,218)
// Using `false` with old style PASV answer, such as `FTP_Server_Teensy41` library
// 227 Entering Passive Mode (4043483328, port 55600)
#define USING_VSFTP_SERVER      true


// Change according to your FTP server
char ftp_server[] = "";  // port 21 ?

char ftp_user[] = "";
char ftp_pass[] = "";

char dirName[] = "";           //"/home/ftp_test";
char newDirName[] = "/Test1";  //"/home/ftp_test/NewDir";


// FTPClient_Generic(char* _serverAdress, char* _userName, char* _passWord, uint16_t _timeout = 10000);
FTPClient_Generic ftp (ftp_server, ftp_user, ftp_pass, 60000);

char fileName[] = "helloworld.txt";



// This next function will be called during decoding of the jpeg file to
// render each block to the TFT.  If you use a different TFT library
// you will need to adapt this function to suit.
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;

  // This function will clip the image block rendering automatically at the TFT boundaries
  //tft.pushImage(x, y, w, h, bitmap);
  //tft.drawRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
   tft.drawRGBBitmap(x, y, bitmap,w, h);
 
  // This might work instead if you adapt the sketch to use the Adafruit_GFX library
  // tft.drawRGBBitmap(x, y, bitmap, w, h);

  // Return 1 to decode next block
  return 1;
}


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

  tft.begin();
  tft.setTextColor(0xFFFF, 0x0000);

  tft.fillScreen(GC9A01A_GREEN);

  WiFi.begin( WIFI_SSID, WIFI_PASS );

  Serial.println("Connecting WiFi, SSID = ");
  Serial.println(WIFI_SSID);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  Serial.print("\nIP address: ");
  Serial.println(WiFi.localIP());


  Serial.println("Downloading file");
  ftp.OpenConnection();
  ftp.ChangeWorkDir(dirName);
  ftp.InitFile(COMMAND_XFER_TYPE_BINARY);
  ftp.DownloadFile("WiFi.jpg", file_buffer, max_File_size, false);


  Serial.println("Download ready");

// check the content of the file
int Cnt=0;
Nr_Bytes_Read=0;
while (Nr_Bytes_Read<max_File_size)
  {
    Serial.print(file_buffer[Nr_Bytes_Read],HEX);
    Serial.print("  ");
    Nr_Bytes_Read++;
    Cnt++;
    if (Cnt>=16) {Serial.println();Cnt=0;}
  }

Serial.println();
Serial.println("Gettin filesize");

uint8_t index_1=0xFF;
uint8_t index_2=0xD9;


// Now trying to find 0xFF 0xD9 so that you know the length of the file
bool NietKlaar;

Nr_Bytes_Read=0;
NietKlaar=true;

while (Nr_Bytes_Read<max_File_size-2 and NietKlaar)
 {
    if (file_buffer[Nr_Bytes_Read]==index_1)
      {
        if (file_buffer[Nr_Bytes_Read+1]==index_2) {NietKlaar=false; break;}
      } 
    Nr_Bytes_Read++;
 }


Serial.print("File size : ");
Serial.println(Nr_Bytes_Read);

  Serial.println("CloseConnection");

  ftp.CloseConnection();

  // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
  TJpgDec.setJpgScale(1);

  // The byte order can be swapped (set true for TFT_eSPI)
  TJpgDec.setSwapBytes(false);

  // The decoder must be given the exact name of the rendering function above
  TJpgDec.setCallback(tft_output);


}

void loop()
{
  tft.fillScreen(GC9A01A_MAGENTA);

  // Time recorded for test purposes
  uint32_t t = millis();

  // Get the width and height in pixels of the jpeg if you wish
  uint16_t w = 0, h = 0;
 // TJpgDec.getJpgSize(&w, &h, file_buffer, sizeof(file_buffer)); // volgens mij moet je dit handmatig doen
  w=240;
  h=300;
  Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);

  // Draw the image, top left at 0,0
  TJpgDec.drawJpg(100, 100, file_buffer, sizeof(file_buffer));

  // How much time did rendering take (ESP8266 80MHz 262ms, 160MHz 149ms, ESP32 SPI 111ms, 8bit parallel 90ms
  t = millis() - t;
  Serial.print(t); Serial.println(" ms");

  // Wait before drawing again

  while(1) {delay(300);}
  delay(2000);

}


unsigned long testFillScreen() {
  unsigned long start = micros();
  tft.fillScreen(BLACK);
  yield();
  Serial.println("1");
  delay(10000);
  tft.fillScreen(WHITE);
  yield();
  Serial.println("2");
  delay(10000);
  tft.fillScreen(BLACK);
  yield();
  Serial.println("3");
  delay(10000);
  tft.fillScreen(WHITE);
  yield();
  Serial.println("4");
  delay(10000);
  tft.fillScreen(BLACK);
  yield();
  Serial.println("5");
  delay(10000);
  return micros() - start;
}

Nice, wireless transfer! How long did it take to draw the jpeg on screen? Approximately, including upload time from PC.

FTP is clearly limiting. In my case download takes at least 10 sec but that will depend on your ftp server provider and network. Drawing only 500 millisecond at max.

I have managed to get Mjpeg files to draw on the GigaDisplay. Added an SD card module, because the USB Mbed5 File structure was giving much trouble. Using the SDFat lib for it's obvious advantages, and the JPEGDEC lib. Draw time averages around 8.47 fps for a 208x320 mjpeg file. Only problem is a red tint on screen, having trouble tracking it down. Can post the code if you'd like to help.

Reminder: Be sure to use an SD card Module that has a 3.3v pin, or could damage boards that are not 5v tolerant, such as the Giga and Due.

Interesting progress. Do you mean a red tint on the whole screen or just on the JPG image you are displaying. In the latter case, could it be that something is going wrong in converting the unit_8 jpg byte into Red Green Blue? If you dive into TJpg_Decoder.h you might stumble on this function : pgm_read_word. I believe that is the one that turns the byte color into RBG colours. And I also remember that R ,G and Blue are treated differently. Forgot what it was. Let me know how I can be of further help.

Mjpeg has heavy red tint, rest of screen is normal. Tjpg has a swap bytes function, as I recall.

I believe there is an issue with the decoder.

I am seeing it as well,

I saw this also on Teensy...

Same red hue mine had. I looked into the JPECDEC lib files but couldn't figure out what to change. Maybe you could see where the problem lies. It seems to be the fastest jpeg decoder around.

I used JPECDEC lib with an ESP32 WeMos board and ILI display and had no problems.

Created new issue:
RED tint on image files on Arduino GIGA and I believe Teensy boards as well · Issue #70 · bitbank2/JPEGDEC (github.com)

The temporary fix Bitbank2 posted at git works for Giga if you change jpeg.inl (JPEGDEC lib) to

#if !defined(NO_SIMD) && (defined(ARM_MATH_CM4) || defined(ARM_MATH_CM7))
//#define HAS_SIMD
#endif

Thanks for raising the issue.

Thanks, that is working better now!