Using the image decoder


From the blog:

After this, if you can set PNG images from file or C array as the source of image object’s of LittlevGL

So, after registering PNG image decoder I tried calling lv_img_set_src with a pointer to the raw PNG data.
This doesn’t work, because lv_img_src_get_type seems to expect lv_img_dsc_t and not the raw PNG image.

  • Am I supposed to create lv_img_dsc_t struct and fill up its data field?
  • If so, am I supposed to fill data_size and header fields as well?
  • Is there some example of image decoder usage?

Another question -
In v6.0 when implementing open_cb, how do we get the data size?
We can access the data by dsc.src but there is no src_size field anywhere.


There are two options:

  1. Create an lv_img_dsc_t and initialize its header (including size) and data with the raw PNG data. You can also use Online converter with Color format = Raw with alpha
  2. Pass the filename to lv_img_set_src(img, "D:image.png"). The path should start with an upper cased driver letter. (LittlevGL checks the first byte to decide if it’s a path or a pointer to lv_img_dsc_t)

LittlevGL uses the info_cb to get info about the image. If the source is an initialized lv_img_dsc_t variable then it already has all the required info. If the source was path to the file then the file needs to be opened to get the required info. But the way to get the info depends on the implementation of the decoder.

Unfortunately, there is no more examples then the mentioned blog post. After the release of v6.0 we will work on adding more examples. And the image decoder interface definitely required more examples.

Anyway if you have more questions feel free to ask!

The path should start with an upper cased letter?? What if that’s unix like path, that starts with a slash? /mnt/... for example?

I noticed that using the decoder slows things down significantly, vs. converting the image to raw img and using it directly, even when that conversion (PNG decoding) is done by the MCU.
I understand that the decoder needs to decode the image every time it is redrawn, but even when it’s drawn only once, it’s still much slower.
It seems that every time the image is redrawn, the image is decoded multiple times. Does it makes sense?

Yes, just trim the letter if you handle the path as a unix-like path: D/mnt/... -> /mnt/....
The lettes are required to be able to use multiple drivers and differentiate them. For example you can create

  • S: SD card
  • U: USB
  • F: FTP

It seems that every time the image is redrawn, the image is decoded multiple times. Does it makes sense?

If you have a 300x300 image but only 300x10 display buffer (VDB) then LittlevGL will call open and close 30 times. (once for each chunk) Probably it would be reasonable to keep the last image opened. Or free it if it was not used for 1 sec or so.

Do you suggest that the image decoder caches the last image opened?

I’m not sure how this can be done efficiently. How would it know it’s the same image? When it receives a pointer to data in the RAM, that data might represent the same image or a different image.


Not really because the lv_img_dsc_t stored in the lv_img object remains the same between the calls of open and close. So the src should be the for the same image.

So you guarantee that src remains the same between a call to open_cb and a call to close_cb.
But suppose my open_cb returns the whole image (and not NULL) so read_line_cb is not called. I’m seeing multiple calls to open_cb for the same image (I didn’t check but probably a close_cb as well for each open_cb call).
How can I know then that it’s the same image between calls to open_cb?

@kisvegabor one more question - Can I use more than one decoder on the same time?
When I register the PNG decoder, it seems that native (“raw”) images can no longer be displayed (I use pointer to data, not filename)

Yes, I checked it and always the src directly from the image object is used.

What about something like this in open_cb

if(last_src == src) {
  return last_decoded;
} else {
  last_src = src;
  ... decode ...

and leave the close_cb empty.

Yes, you can register more than one decoder. E.g. for png and jpg too.

How do you mean it? If none of the registered decoders can open an image with raw format the “No data” label will be displayed.

@kisvegabor Consider this scenario:

  • The user allocates a buffer in ram for PNG image, and void *p points to this buffer.
  • The user decodes p and displays an image.
  • The user overwrites *p with a different image, or the user frees p and allocates a new buffer that happens to be located exactly where p pointed at.
  • The user decodes his new data (pointed by the same p) and expects to see his new image (but is surprised to see the old one).

With your suggestion, the image decoder would assume the data is the same just because the pointer to the data is the same, but this doesn’t have to be true.

If I don’t register any decoder I can still call lv_img_set_src and display the image, as long as it’s a “raw” image in the format the lvgl expects.
But once I register a decoder I “lose the ability” to display “raw” images.
I would expect that if PNG decoding fails, lvgl would default to raw image decoding, but it doens’t.

I really didn’t consider this scenario. I can imagine two solutions:

  1. Provide a png_refresh() function which will make last_src = NULL. The user needs to call it manually if the image is refreshed this way.
  2. Add a user_data field to lv_img_dsc_t which can be refreshed by the user if a new image data is set. It can be set to user_data = lv_tick_get(). However, it doesn’t solve the problem if files are used and the content of the image is silently changed.

Then it should be a bug in the library. LV_IMG_CF_RAW_... images shouldn’t be drawn by the built-in decoder functions. Can you send an code snippet to reproduce it?
For “normal” images LV_IMG_CF_TRUE_COLOR_... should be used. LittlevGL loves them the most. :slight_smile:

I would like to suggest a different solution.

I suggest adding an “ID” field to lv_img_decoder_dsc_t struct.
When lvgl prepares lv_img_decoder_dsc_t before it sends it to the decoder callbacks, it would set this ID to a unique value (unique per image data).
Every time the user calls lv_img_set_src, lvgl will change the ID to a new unique value.

This would work under the assumption that the user calls lv_img_set_src whenever he wants to set a new image, even if src parameter is the same between calls.

What do you think?

I mistankenly called it “raw” but actually I was referring to LV_IMG_CF_TRUE_COLOR , sorry for that.
So rephrasing this again:

Once I register a decoder I “lose the ability” to display LV_IMG_CF_TRUE_COLOR images.
Is that expected?

If I understood correctly the ID could be a counter which tells how many times lv_img_set_src() were called, right?

It’s not expected. You should be able to register multiple decoders. However, if a decoder can’t handle the received format it should return LV_IMG_DECODER_OPEN_FAIL to tell LittlevGL “I can’t do anything with this image, try an other decoder”.

It looks like this for me using the demo app and the png decoder attached in this post.


Yes, but it should be a global counter, counting the total times lv_img_set_src() was called for all images.

Yes, I return LV_IMG_DECODER_OPEN_FAIL from my decoder when it fails.
Then I noticed that when I call lv_img_set_src with “regular” (LV_IMG_CF_TRUE_COLOR) images, nothing it displayed.

But I’ll test this again in the evening and see if I can reproduce this problem.

I see. I could work just a good “high level” need should be found for this ID. BTW, it don’t needs to be passed in lv_img_decoder_dsc_t but we could have functions like lv_img_decoder_report_update() and lv_img_decoder_is_update(). What do you think?

The image decoder was fully rewritten so it can easily happen that there are bugs in it.

I’m not sure how you meant lv_img_decoder_report_update() and lv_img_decoder_is_update() would be used. Could you give an example? Is that simpler than a counter?

The “high level” need I see is caching images in the decoder.
My experience is that without this the decoder is very slow (It decodes the same image again and again). So caching would make sense for every non trivial encoding (PNG, JPG, etc.) on every platform that has enough ram to cache an image.

There is another option, btw. You could implement the caching in lvgl instead of in the decoder, and prevent lvgl from decoding the same image again and again. I think it would make sense to make the caching logic “generic” and not specific to the decoder.

A flush function can be provided to clear the cache in that case.

@kisvegabor What do you think about the idea of adding an image caching logic into lvgl in order to improve decoders performance?
If it makes sense, I think it’s better continuing the discussion on a github issue instead of the forum.

Sure, let’s continue on GitHub.
I’ll answer the above comments there, ok?

Sure, I’ve opened an issue:

why the png decoder source code run on the esp32_ili9341 project display wrong?

would you like provide an example of using png decoder.

ref: [png decoder] Using the png decoder demo but display an error picture