Minimize RAM required for in-memory image


I have limited memory to show an image but need to maximize the height/width. Currently I am sending RBG bytes from a PNG to the device via UART over BLE with a 16bit/pixel, which I am storing in a uint8_t img_src[2049] on the device. It’s only 32x32 and I need it to be at least 100x100 but ideally larger (150x150 or 200x200).

My first thought was to use a PNG, but I was looking at the documentation on the PNG Decoder and saw that the RAM needed was a formula: image width x image height x 4, which for a 100x100 would be 40K bytes and I only have 64K.

I really only have a single color and a transparency. I was going to explore a custom decoder. I want to minimize transfer size and RAM to display or trying to print it line by line. I am looking for recommendations as I am a novice and this seems pretty complex.

What MCU/Processor/Board and compiler are you using?


What LVGL version are you using?


What do you want to achieve?

Minimize image memory size for transfer and display

Code to reproduce

This is my current code for showing the image:

uint8_t img_src[2049];

void screen_img_set_src() {


	lv_img_dsc_t img_dsc = { = LV_IMG_CF_TRUE_COLOR,
		.header.always_zero = 0,
		.header.reserved = 0,
		.header.w = 32,
		.header.h = 32,
		.data_size = 2049 * LV_COLOR_SIZE / 8,
		.data = img_src,

	lv_img_set_src(screen_image, &img_dsc);

Of the 2048 bytes I am sending over in the 32x32 rbg array, 1698 (83%) of them are 0. What is the most compressed rbg format for an image? The other option which somebody mentioned to me is printing it line by line. Do I use a canvas for this? Would this solve the problem of storing the whole image in a variable?

I just need the smallest memory size for a single color image. I get the data from a UIImage in swift pixelData32bitRGB() which I convert to 16bit color depth.

You mix here more as two thinks.

  1. source size before transfer
  2. transfer size and method
  3. decode direct to display or to RAM
    If your image is one color with alpha levels , you need transfer only alpha and one color info.
    For example lvgl support CF_ALPHA_4_BIT and for transfer is good use RLE compression
    After decompress ALPHA_4 100x100 = 5000bytes

Thanks for the response, @Marian_M. Whatever works on the lvgl side I will try to back into from the swift side.

  1. Right now I’m using RGB (565) but expect I can convert to other formats depending on what works for lvgl and the constraints of my MCU (nRF52832).
  2. I am using UART over BLE to transfer the bytes, the 32x32 is 2049 and I have +7 for some additional information I’m using. The speed at 2049 bytes works for me currently.

If LV_IMG_CF_ALPHA_4BIT or LV_IMG_CF_ALPHA_2BIT would work, I just need to figure out how to arrange the bytes to support. If, from there, I need to fold in compression, I will.

Writing to the screen line by line was another option if I could not get around the RAM constraint.

@Marian_M, what the size for a 200x200 LV_IMG_CF_INDEXED_1BIT?

5006 bytes

Thank you, @Marian_M. Is there a formula for each color format?

PNGs have to be decoded. so it is going to use a whole lot more ram than width x height x 32.

you need to store the PNG in memory one way or another, so whatever the file size is that memory is used right out of the box. Then you have to decode the PNG and store the decoded image in memory. That’s where the width x height x CD comes into play. Now PNG’s only have support for 32bit, 24bit, 8bit, 4bit, 2bit and 1bit color depths. There is no native support for RGB565 or RGBA5658. LVGL handles both 24 bit and 32 bit color in the same manner using 4 bytes of storage per pixel. Unless you drop to using 8 bit color you are not going to be able to save one byte of space. It’s basically a what you see is what you get scenario.

Since you are sending the image to the device have you considered decoding it before you send it? This would eliminate the memory use from having to store the PNG itself. You can dump the raw RGB data right into a pixel buffer as you receive it. That would use less memory that’s for sure.

This is pseudo code so you get an idea. this code is not going to work and is used for example purposes only.

lv_color_t buf[IMAGE_WIDTH * IMAGE_HEIGHT];
uint32_t pixel_num = 0;

// loop code for receive of pixel data
while (pixel_num + 1 != (IMAGE_WIDTH * IMAGE_HEIGHT) 
    buf[pixel_num] = (lv_color_t) recv(sizeof(lv_color_t));
    pixel_num ++;

lv_img_set_src(img_obj, buf);

if you have the color depth in LVGL set to 16 bit you will need to convert the pixel data to RGB565 before sending it.

Thank you, @kdschlosser. I have a process converting it from 32 to 15 (RGB565) and that’s what I’m sending down. I guess I shouldn’t explore PNG any further as this seems like the best way to do it. In my scenario I can use INDEXED 1 bit color depth because it would be the smallest I believe. I guess I need to figure out how to convert 32 bit RGB to INDEXED 1 bit. I see the online converter goes from PNG to the various formats, but I’m not even really sure of the file format specification.

@mmar22, I ran 50x50 (358) and 100x100 (1308) and 200x200 (5008). I see the 2 indexed colors are 4 each which accounts for the 8. I would love to have the file format.