Is it possible to display a JPG Image with lvgl?

I want to display a JPG on my TFT, because BMP’s are to big for the microcontroller flash.

BMP is working fine with LVGL.

But if i try to display a JPG, nothing or waste will be displayed.

I convert the JPG to a sJPG, converted with the jpg_to_sjpg.py

The Online-Converter can’t convert the JPG to sJPG, because Color format = RAW isn’t present ( like explaned in the actual docu from TinyJepg: Tiny JPEG Decompressor (TJpgDec) - LVGL 9.4 documentation). He creates only a big BMP-format.

The original JPG is 8KB, the converted sJPG is 10KB (small image for testing)

I have try it with LVGL V8.3.0 and LVGL V9.3.0.

In lv_conf.h, i activate:

#define LV_USE_TJPGD 1
#define LV_USE_SJPG 1

Activation of LV_USE_LIBJPEG_TURBO is not possible, because important functions of the encoder are missing in the lvgl repro.

Code:

		lv_tjpgd_init();
		LV_IMAGE_DECLARE(Brot_S);  // Hintergrund-Bild Brot
		lv_obj_t * img1 = lv_image_create(lv_screen_active());
		lv_image_set_src(img1, &Brot_S);
		lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);

I tryed in the header.cf from lv_image_dsc_t of the small image:

  • LV_COLOR_FORMAT_RGB565 → waste
  • LV_COLOR_FORMAT_RAW → nothing
  • LV_COLOR_FORMAT_RGB888 → waste
  • LV_COLOR_FORMAT_RGB565A8 → nothing
  • LV_IMG_CF_RAW → unknown
lv_image_dsc_t Brot_S = {
	//.header.always_zero = 0,
	.header.cf = LV_COLOR_FORMAT_RGB565A8, // LV_COLOR_FORMAT_RGB565,
	.header.magic = LV_IMAGE_HEADER_MAGIC,
	.header.w = 128,
	.header.h = 93,
	.data_size = 9550,
//	.header.cf = LV_COLOR_FORMAT_RAW, // LV_IMG_CF_RAW,
	.data = Brot_S_map,
};

So my question:
Is it really possible to display a JPG-Image, or is it only planned in the future ??
How can i display a sJPG-Image (or JPG-Image) with LVGL ?

Environment

  • MCU/MPU/Board: Using a STM32H7B3I-DK @ STM32CubeIDE

  • LVGL version: 8.3 and 9.3

Hi, I don’t have much experience, but maybe my experience will help. I couldn’t use JPG directly for display. First, I need to decode it into a ROW, then convert the resulting array to an LVGL image variable:
uint8_t my_img_data = {0x00, 0x01, 0x02, …};

static lv_image_dsc_t my_img_dsc = {
.header.always_zero = 0,
.header.w = 80,
.header.h = 60,
.data_size = 80 * 60 * LV_COLOR_DEPTH / 8,
.header.cf = LV_COLOR_FORMAT_NATIVE, /* Set the color format */
.data = my_img_data,
};
and work as usual
lv_obj_t * icon = lv_image_create(lv_screen_active(), NULL);

/* From variable */
lv_image_set_src(icon, &my_icon_dsc);

/* From file */
lv_image_set_src(icon, “S:my_icon.bin”);

Or you can simply create a C file in the online converter (Image Converter — LVGL), include it in the project, and use it in lv_image_set_src.

Hello.

Thanks for the answer.

Converting to ROW is the problem.

The image converter for V9 creates an image array, with one word per pixel, no sjpg compression!
At 800*480 pixels, that’s 768,000 bytes per full-screen image, which would work without any problems.
With 7 images that I need to display, that’s 5.3MB just for the images, with 3MB of flash from the microcontroller.
With JPG encoding, it’s only a fraction of that and still fits.

Since I don’t have that much flash in my microcontroller, I need the jpg decode option, which the lvgl\src\libs\tjpgd should actually provide.

But unfortunately, no attempt to display a processed sjpg or a raw jpg in flash with the tjpgd decoder works.

I’ve already tried to get libjpeg_turbo running, but all the functions are missing:

  • src/libs/libjpeg_turbo/lv_libjpeg_turbo.c:492:(.text.get_jpeg_direction.isra.0+0x2c): undefined reference to `jpeg_CreateDecompress’
  • src/libs/libjpeg_turbo/lv_libjpeg_turbo.c:494:(.text.get_jpeg_direction.isra.0+0x36): undefined reference to `jpeg_mem_src’
  • src/libs/libjpeg_turbo/lv_libjpeg_turbo.c:496:(.text.get_jpeg_direction.isra.0+0x42): undefined reference to `jpeg_save_markers’
    … and so on…

When I try to get the missing functions from the git repro of libjpeg-turbo-main, I’m still missing functions that aren’t even present in the repro.

I’m starting to get desperate.

I’ve tried it with V9.3, V9.4 (main branch), and V8.3. No success.
Has anyone managed to decode a split JPG on the microcontroller and display it on the screen (not a BMP array)?

And if so, how do you do it?

the only things you need to populate in the image descriptor is the data and data size. I am not sure if the height and width need to be supplied they may or may not because that information is embedded in the JPEG file. Let the decoder hammer out the rest of the information that is needed.

Simple reply is yes jpg is usable , but some condition : primary

  • Decoding normal JPG consumes RAM with the size fo the whole uncompressed image (recommended only for devices with more RAM)

JPG decoder — LVGL documentation

But i preffer png …

FYI if image WxH is decoded oversized for RAM = not showed

@Marian_M

It is also a pig of an image format. Very CPU heavy to do the decoding if the CPU doesn’t have special instructions for handling JPG. The ESP32 does have those instructions but I do not believe the JPG decoder in LVGL is written to use that feature.

I do also prefer PNG over JPG but even using that is going to be RAM intensive. Also loading from a file VS loading from in memory data is also going to be slower due to the constant reading of the chunked data from the file. I personally will read the file and then decode the file drawing the decoded data to a buffer using the canvas widget and then create a new image descriptor with that data and pass that to whatever lvgl widget is going to display the image. I do that at startup so this way the image data is loaded and done with right from the get go so any time that part of the image needs to be redrawn the decoding doesn’t need to take place. You gotta have the memory for it.In most cases when using PNG data the bit depth doesn’t need to be set to 32 bit. It can be lowered by quite a bit. What effects a PNG files size is the color pallet. So what I do is I will take a PNG file and open it up in an editor and then save it as a bitmap where I have more flexibility with the bit depths.Then load that saved file so the reduced color pallet loads and then save it as a PNG file set to 32 bit color. It really shrinks down the file size by doing that. Some images have such a tiny variation in colors that it makes the pallet very large in size but your eyes are not able to tell the difference.

here is an example…

This is RGB888 (24 bit)
image

and here is RGB565 (16 bit)

310770006-baa49d9c-1489-4103-83f2-d095847398f8

The RGB888 image being saved as a 24bit PNG file has a size of 461,866 bytes where as saving the second image which has a reduced color pallet down to RGB565 and still saving it as a 24bit PNG file has a size of 382,279 bytes. That’s a 17.23% reduction in file size!!!

Now you might be able to tell the difference between the 2 images on a desktop PC with a high DPI high resolution display, but when you display that image in a 5" or a 7" display you cannot tell any difference between the 2 images.

There are tricks that can be used to shrink the size of a PNG file at the cost of some additional processor time. A great example of that is with icons that have transparent backgrounds where the color portion is fully opaque. Remove the alpha channel from the PNG completely and set that background to a color that is not being used in the image. Then you can mask out the background and convert it over to a fully transparent background. Doing that will reduce the file size by a minimum of 25%

hi, just so you know, we’re working on a feature with the new GLTF viewer portion of LVGL, and it might help you out here when it’s ready.

First, quick explanation what GLTF’s are, etc. They’re 3D files, that store the model and all of it’s textures in one big ‘glb’ file. So it embeds all the images into the file. Those images can be PNG, JPG, or WEBP. WebP is particularly notable here because the compression is really amazing, way better than JPG and it supports alpha channels.

But how does that help here? Well this feature that I added, but we have not yet rolled into the public implementation, is a way to extract the embedded textures back out into standard LVGL image arrays. So you could take all your images and use them as textures of a placeholder 3D model, then load that 3D model and load the images from it, like it was a filestore. The GLB itself can be converted into hexbytes and included, like images are currently. So that seems pretty helpful for what you’re trying to do.

Challenges:

  • This feature is not there yet, don’t bother looking. I have a copy, and that works great, but we decided it wasn’t a core feature and put it aside for a minute to focus on other stuff.

  • It would require that you build with support for at least loading the GLTF files, even if you can’t actually display them.

  • I would need to add some code to allow people to load GLTF files even if they can not actually display them with a 3D gpu, but that’s not super complicated.

Does this sound like functionality that would help?

1 Like