JPEG image decoder library for TFT displays - for advanced TFT users

I have put a JPEG decoder library on Github that can be used with a Mega or Due to decode and display JPEG images on a TFT display. A commented example is included that could be adapted for any TFT graphics library.

At the moment this is for “Advanced users” as the library is still quite young and I have not yet produced any support documentation, however seasoned users of TFT displays should have no trouble adapting the example for their favorite display and graphics library.

A few JPEG images are included in the “extras” folder and header files containing images are attached to the example sketch.

Background
I have started a project where I wished to store control panel screens in program memory, this is to improve the visual appeal of the projects. To do this I needed to store compressed images in the program memory (FLASH).

After a search on the web I found a library here by Makoto Kurauchi which used picojpeg. Makoto’s library example only generated text output of the decoded file but provided an excellent starting point to add features to display the decoded JPEG images on a TFT screen. So bugs in the original have been fixed and the library is now working very well. It can display images pulled off SD Cards or images stored within arrays in program memory. This permits not only full screen images to be stored in program memory but also smaller graphics icons such as clouds, temperature gauges etc.

JPEG compression is a popular method of significantly reducing the size of image files to reduce storage space needs. So for example a 480 x 320 pixel image in 24 bit colour would occupy 461KBytes, in 16 bit raw format it would occupy 307KBytes. Thus it is impractical to store screen images in program memory and even a Due would only have room for 1 image.

Using JPEG compression can bring a file size down by 20 to 70 times, the included example image of the mouse is 480x320 pixels and has been JPEG compressed only 6.45KByes making it possible to store many startup images and control panel displays in program memory on a Due (or Mega). This means you can design your TFT screen panels on a PC and then just update key areas with information, for example a temperature or other sensor reading, with the Arduino.

There is a catch, the decoding of the compressed images involves a lot of maths so it takes a Due about 1 second to decode and display a 480 x 320 image (500ms for a 320x240 image), also the decoding process uses more RAM than a UNO provides. Thus the library will only work with a Mega or Due.

The example sketch just uses the TFT “setWindow” and “pushColor” member functions of a graphics library, so the example provided should be adaptable to any graphics library.

:astonished: I ever read that this was impossible with an Arduino (or Mega).

Great Work!

The JPEGDecoder library has been updated with a new abort member function. This allows the sketch to stop the decoder part way through decoding an image and end cleanly (e.g. close any open file and relinquish memory).

The function is useful to save time if the image does not fit the screen and runs off the bottom.

It is not possible to detect the image dimensions before decoding?

zoomx: It is not possible to detect the image dimensions before decoding?

Yes, the library supports that and makes the information available to the sketch to use as needed.

The example provided shows how to access this information, viz:

  // Print information extracted from the JPEG file
  Serial.println("JPEG image info");
  Serial.println("===============");
  Serial.print("Width      :");
  Serial.println(JpegDec.width);
  Serial.print("Height     :");
  Serial.println(JpegDec.height);
  Serial.print("Components :");
  Serial.println(JpegDec.comps);
  Serial.print("MCU / row  :");
  Serial.println(JpegDec.MCUSPerRow);
  Serial.print("MCU / col  :");
  Serial.println(JpegDec.MCUSPerCol);
  Serial.print("Scan type  :");
  Serial.println(JpegDec.scanType);
  Serial.print("MCU width  :");
  Serial.println(JpegDec.MCUWidth);
  Serial.print("MCU height :");
  Serial.println(JpegDec.MCUHeight);
  Serial.println("===============");
  Serial.println("");

Arduino Due users who are embedding JPEGs into arrays within program memory and have large sketch sizes will appreciate this thread which halves the sketch upload time.

Waiting 30s to download a sketch that another 30s to verify was getting tedious especially when you then immediately notice a new bug in the sketch!

I noticed that the TFT_draw_jpg_v4 example still use the SD library and so also the jpg decode library. I am not an expert, it seems that you use the FILE class from SD library when decoding embedded jpegs. It is possible to remove the SD library when using embedded jpegs?

zoomx: It is possible to remove the SD library when using embedded jpegs?

Yes, the lines that would use the SD library are commented out.

The library to use is selected at the start of the sketch, SD for Mega sdFat for Due. The Due uses the sdFat library because supports bit bashing the SPI lines. Sdfat also supports using DMA and faster SPI clock rates on the Due, which is nice and can give improved performance. You can also delete all the commented out lines in the example which refer to SD card files plus the SD image rendering function. The DMA capability of the sdFat library is good so I may pull out the relevant bits and create a new buffered Arduino SPI library so that anyone can use SPI DMA one the Due, but perhaps that has already been done :-)

That example is really a library test sketch hence bits are commented out. I am in the process of creating a simpler set of examples that will separate out the different features and possibilities.

I also have a new example to upload soon that will draw jpeg images half/quarter/eighth size.

Performance improvements have been made too, for example the 24 bit colour to 16 bit colour conversion is now done in the library to reduce RAM needs and make rendering the image to the TFT easier in a sketch.

Since I don't own a DUE I am testing the library with a STM32 using STM32duino.

If I remove all SD related lines in the TFT_draw_jpg_v4 example i get an error createArray function

  File jpgFile;  error: 'File' was not declared in this scope

If I add SD library for STM32 I get this error

JPEGDecoder.cpp:234:17: error: 'SD' was not declared in this scope
     g_pInFile = SD.open(pFilename, FILE_READ);
                 ^

so I wrote that there is SD related code inside the library.

I am using IDE 1.6.5 since the newest versions doesn't work with the STM32 core.

I will test the library with a Mega in the next days using the last IDE! Thanks for your reply!

zoomx: ...so I wrote that there is SD related code inside the library.

Ah, yes. The way the original JPEGDecoder library worked was to pull data from SD Card and it uses call back so it looked tricky and inconvenient to move this out of the library. However now it handles arrays means that it should be possible to add some #defines for optional inclusion of the SD stuff.

I have some small STM32 boards but have not got around to trying them yet, so I can't provide support for that processor at the moment. :( The aim is to get them running so I can have a smaller ARM based processor for embedded projects that need a bit more computing power. You may get help on Roger Clarke's STM32 website.

I downloaded the last library version and there is still the

g_pInFile = SD.open(pFilename, FILE_READ);

line.
I commented quite all function in .cpp file except a return and now it compile. Unfortunately it takes 165.492 bytes so doesn’t fit my boards.
I have still the SD library but removing it I get again this error

File jpgFile;  error: 'File' was not declared in this scope

So I have to write an example without the SD or use an SD in next days.
I wrote about your library in the STM32duino forum but none seems interested or none wrote that have tested or ported it.

Thanks!

The JPEGDecoder library has been updated to simplify sketches. The library now sources 16 bit colour values instead of 8 bit Red+Green+Blue. This saves RAM and makes the rendering function in a sketch simpler and faster.

The library now includes 2 examples for rendering JPEGs, one for program memory files and one for files stored on SD Card.

Report bugs on GitHub or on this thread.

@zoomx

A “User_Config.h” file has been added in the library folder. This allows the library to be used for rendering images in program memory alone, by preventing the SD/sdFat libraries from being loaded by the Decoder library. Disabling the use of the SD Card libraries brings the sketch size down by ~2Kbytes and should improve portability to other processors.

I got compiled easily with simple changes the TFT_flash_jpg_v2 example

  1. Removing TFT_HX8357 library and using
#include <ILI9341_due_gText.h>
#include <ILI9341_due.h>

#define TFT_DC PA15
#define TFT_CS PB4
#define rst  PB3
ILI9341_due tft = ILI9341_due(TFT_CS, TFT_DC, rst);
  1. Changing every tft.setrotation(x) in
tft.setRotation((iliRotation)x)
  1. Changing every tft.setWindow in tft.setAdddrWindow
    To reduce code size I removed three images of the four. Removing the last 3 give a code of 116.140 bytes
    But it is still too much for my boards. Maybe I will try the SD version.

@zoomx Great to hear. The jpegs are 480 x 320 and the biggest is 23 Kbytes so for a 320 x 240 display the image can be cropped or resized to halve the byte count. I am guessing you have lots of fonts loaded as the jpeg library itself is quite small and hence something else must be using the Flash space.

I was so stupid that instead of using the smallest jpeg I was using the biggest! Using the smallest I get a code of only 96.872 bytes Now it fit a Maple Mini. You're probably right, something else must be using the Flash space. But this was a dirty work only to check if there was other problems. I have to check with a real hardware. I will post my results here.

A sketch on the Due with just one 6.5Kbyte jpeg image is about 36 Kbytes.

It looks like the JPEGDecoder is taking up about 10Kbytes of this, the rest (~19Kbytes) is the HX8357_Due graphics library with one small font loaded.

@zoomx, yes I would be interested in the your experience with the STM boards and the JPEGDecoder library. I bought some small cheap ones from China a few months back but have not powered them up yet, they also well made. My interest is in having a small cheap ARM board for projects where a $2 enhanced Pro Mini is not powerful enough (for 99% of my projects they are fine).

It works!

Tested on a Maple Mini I changed only one define for CS from PB4 to PA4 because this pin is close tho the SPI ones. So it will work also on cheap STM32 boards like the STM32F103C8T6 boards!

zoomx: It works!

Tested on a Maple Mini I changed only one define for CS from PB4 to PA4 because this pin is close tho the SPI ones. So it will work also on cheap STM32 boards like the STM32F103C8T6 boards!

Excellent. What is your IDE setup so I can try this? Is it the stuff from here?

Yes, I got the last core some days ago. I have chosen the Maple Mini with old bootloader becaure it has more flash but it seems that some 64k flash board have 128k instead. I posted this in the STM32duino forum here http://stm32duino.com/viewtopic.php?f=3&t=901&sid=f995472f2f400f5c4b0b24ed4bd69efc

You don't need any others library but I used an SPI ILI9341 TFT

@zoomx

The link is bad, I am guessing this is the link?