LVGL: Images color formats for Embedded systems
So you want to include images into your LVGL project, running on a nRF5340. You prepare your images, convert them using LVGL online tool, integrate them into your application, and.... end up with 167% of your flash used!
Optimizing your images is crucial in low resources system such as MCUs. As a quick example, if you want to use a full size background for your application, using a 480x320 display, using ARGB8888 you end up with:
480 * 320 * 4 = 614400 B = 614.4 kBWhich, in some system corresponds to more than half the flash size.
So what can we do to optimize it? The following sections will show solutions to reduce flash usage.
Using RLE compression
RLE is a simple compression format which group contiguous symbols together.
Here is a simple example:
AAABBCCCCD (10B) -> RLE compression -> 3A2B4C1D (8B)It is very useful when your image have a lot of contiguous same color pixels. It becomes a bit less interesting with images using a lot of colors and blurring as there won't be that much contiguous pixels.
To convert an image to RLE, LVGL offers an utility script in scripts/LVGLImage.py:
python3 lvgl/scripts/LVGLImage.py --ofmt C --cf RGB565 --compress RLE -o my_image.c my_image.pngThis will convert my_image.png to a .c file in RGB565 format, compressed using RLE. To be able to use this compressed image in LVGL, you must activate these configs:
LV_USE_RLE 1: Enable RLE processing.
LV_BIN_DECODER_RAM_LOAD 1: Enable decoding the binary images in RAM.
Note that RLE decompression can use quite a lot of RAM, as it will need decompression buffers while loading the image.
Using indexed color format
The indexed color format allow to represent each pixel as an index, pointing to a color in a palette,which is encoded next to the pixels data. It means that if your image has <= 256 colors, the palette index can be represented as 1B (index 0 to 255). The palettes are written as ARGB8888, meaning that a 256 colors palette = 256 * 4 = 1024B = 1kB.
To convert an image to indexed color, you can also use LVGL scripts/LVGLImage.py:
python3 lvgl/LVGLImage.py --ofmt C --cf I8 -o my_image.c my_image.pngThe script internally convert your base PNG to an indexed PNG if needed, and then convert it to a .c.
One can use these color format (parameter --cf):
I1: 1 color palette
I2: 4 color palette
I4: 16 colors palette
I8: 256 colors palette
Using A* format (for grayscale images)
The last format(s) we will talk about are the A* formats, where * can be 1, 2, 4, 8. This format allows to only encode the transparency. Like this, you end up with a grayscale image, which can the be recolored using the style.recolor property.
To convert an image to A8, you can also use LVGL scripts/LVGLImage.py:
python3 lvgl/LVGLImage.py --ofmt C --cf A8 -o my_image.c my_image.pngConclusion
We saw that multiple options are available when you need optimizing your images size. Depending on your base image, it may be better to use one or the other method, so my advice would be to try playing a bit with these color formats and see what works best for your images.
If you need any assistance in optimizing your images for your project, don't hesitate to contact us!
Links:
RLE compression: https://en.wikipedia.org/wiki/Run-length_encoding
Indexed color format: https://en.wikipedia.org/wiki/Indexed_color#:~:text=In%20computing%2C%20indexed%20color%20is,2%2Dbit%20indexed%20color%20image
LVGL Image doc: https://docs.lvgl.io/9.2/overview/image.html#overview-image


