Loading png image does not work with one file, but works just fine with another

Description

I can load a larger PNG file just fine, but if I used the preferred smaller resolution file it is all garbled. What’s even more strange is I am positive this was working at one point.

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

Custom hardware with custom Linux system

What do you experience?

The image loads, but it’s garbled. See attached photo.

Code to reproduce

png_decoder_init();

. . .

logo = lv_img_create(cont, NULL);
lv_img_set_src(logo, "/home/user/logo_220x82.png");
//lv_img_set_src(logo, "/home/user/logo_300x112.png"); // this displays properly

Looking at the file properties for both files:
# file logo_220x82.png
logo_220x82.png: PNG image data, 220 x 82, 8-bit/color RGBA, non-interlaced
# file logo_300x112.png
logo_300x112.png: PNG image data, 300 x 112, 8-bit/color RGBA, non-interlaced

I’ve also verified that both files look correct using gimp.

Screenshot and/or video


Sorry for the crummy quality. I’m trying to leave off identifying info just to be safe :slight_smile:

Please repost the contents of this topic; I can’t seem to recover it from the moderator inbox.

Sorry, I completely missed the undo button. My brain isn’t 100% today…

I spent some time trying to reproduce this on the simulator and I’ve seen it both work and crash. Needless to say, I am confused as to what’s going on as I never see a crash in my embedded app.

Anyway, here’s my code. Am I missing anything obvious that you can see when switching screens and using a different image? In my embedded app it doesn’t matter which file comes first or second, and the first will always display while the second is garbled.

static void cb_btn(lv_obj_t *obj, lv_event_t event);
static void second_screen();

static lv_style_t mystyle;

const lv_color_t MY_GRAY = {{5, 9, 5}}; // b, g, r

// Called from main() prior to the task loop
void menu_create(void)
{
    png_decoder_init();

    lv_style_copy(&mystyle, &lv_style_plain);
    mystyle.body.main_color = MY_GRAY;
    mystyle.body.grad_color = MY_GRAY;
    mystyle.body.border.width = 0;
    mystyle.body.border.opa = LV_OPA_TRANSP;
    mystyle.body.padding.left = 25;
    mystyle.body.padding.right = 25;

    lv_obj_t *cont = lv_cont_create(lv_scr_act(), NULL);
    lv_obj_set_style(cont, &mystyle);
    lv_obj_set_size(cont, 480, 272);
    lv_cont_set_fit(cont, LV_FIT_NONE); // Container is static
    lv_cont_set_layout(cont, LV_LAYOUT_COL_M);

    lv_obj_t *logo = lv_img_create(cont, NULL);
    lv_img_set_src(logo, "C:\\Projects\\test\\Strand_logo_220x82.png");

    lv_obj_t *btn = lv_btn_create(cont, NULL);
    lv_obj_set_event_cb(btn, cb_btn);
}

static void cb_btn(lv_obj_t *obj, lv_event_t event) {
    switch(event) {
    case LV_EVENT_CLICKED:
        second_screen();
        break;
    };
}

static void second_screen() {
    // Clean up all child-objs of the current screen
    lv_obj_clean(lv_scr_act());

    lv_obj_t *cont = lv_cont_create(lv_scr_act(), NULL);
    lv_obj_set_style(cont, &mystyle);
    lv_obj_set_size(cont, 480, 272); // 480x186 pixels
    lv_cont_set_fit(cont, LV_FIT_NONE); // Container is static
    lv_cont_set_layout(cont, LV_LAYOUT_COL_M);

    lv_obj_t *logo = lv_img_create(cont, NULL);
    lv_img_set_src(logo, "C:\\Projects\\test\\Strand_logo_300x112.png");
}

Screenshot from the simulator after pressing the button:
sim_crash

*edit* If I load the same image in both places I don’t have this issue at all. Not sure if that helps.

Are either of the images I see in your second post being drawn correctly? I assume not. Does it always work with just one image?

The first object is always drawn correctly, and the second is sometimes/usually/always scrambled. I say sometimes/usually/always, but in my embedded app it’s always and in the simulator I’ve seen inconsistent behavior depending on if I select ‘Run’ or ‘Debug/Continue.’

*edit* I want to clarify a little bit. If I draw the larger image file and then draw the smaller file on a new screen the smaller image is garbled. If I draw the smaller image first, and then the larger image on a new screen the larger image is garbled. It has nothing to do with the file itself, but the order in which they’ve been loaded/decoded/etc.

What’s more strange is I created a test application in the Simulator that drew both images, and then every second in a task I delete that screen and re-draw a new screen with the images in reverse order. This works perfectly fine. I tried it on my embedded application and it’s running perfectly fine as I write this.

I still feel like I have to be doing something wrong, but I can’t see it.

Can you try running lv_img_cache_set_size(0) before you load any images?

With this in place I see a white box that says “No data” for both images.

I’ve tired you example code and it worked fin for me. :frowning_face:

What happens with lv_img_cache_set_size(10)?

Yeah, this has been inconsistent for me in the simulator. I was showing a colleague yesterday, and the very first time I tried it the second image was scrambled. The next 7 or 8 times it was fine. We chatted for a while, and I tried it again and it was scrambled - it feels like some sort of marginal condition (maybe race?), but this is pure speculation at this point.

Same thing as if I leave it at default or set it to 1 (or 2 or 5 or 17 or 33 or …). The first image is fine, and the second is garbled.

Let’s try this:

    lv_obj_t * img1 = lv_img_create(lv_scr_act(), NULL);
    lv_img_set_src(img1, &icon1);

    lv_obj_t * img2 = lv_img_create(lv_scr_act(), NULL);
    lv_img_set_src(img2, &icon2);
    lv_obj_set_y(img2, 150);

    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_exec_cb(&a, img1, lv_obj_set_x);
    lv_anim_set_repeat(&a, 100);
    lv_anim_create(&a);

    lv_anim_set_exec_cb(&a, img2, lv_obj_set_x);
    lv_anim_create(&a);
  1. Does an image scrambled?
  2. Is it always scrambled or are there good frames?

Nope.

With this example I’ve only seen good frames on the simulator and in my embedded environment.

My one comment is that I never show two images on the screen at once in my normal application, and they are always on separate screens. If I change the code to the following on my embedded system then the second image is scrambled (I’ve yet to see it scramble in the Simulator).

static void cb_btn(lv_obj_t *obj, lv_event_t event);
static void second_screen();
static lv_anim_t a;
static lv_style_t mystyle;

. . .

lv_obj_t * img1 = lv_img_create(lv_scr_act(), NULL);
lv_img_set_src(img1, "big.png");

lv_anim_init(&a);
lv_anim_set_exec_cb(&a, img1, lv_obj_set_x);
lv_anim_set_repeat(&a, 100);
lv_anim_create(&a);

lv_obj_t *btn = lv_btn_create(cont, NULL);
lv_obj_set_event_cb(btn, cb_btn);

. . .

static void cb_btn(lv_obj_t *obj, lv_event_t event) {
    switch(event) {
    case LV_EVENT_CLICKED:
        second_screen();
        break;
    };
}

static void second_screen() {
    // Clean up all child-objs of the current screen
    lv_obj_clean(lv_scr_act());

    lv_obj_t *cont = lv_cont_create(lv_scr_act(), NULL);
    lv_obj_set_style(cont, &mystyle);
    lv_obj_set_size(cont, 480, 272); // 480x186 pixels
    lv_cont_set_fit(cont, LV_FIT_NONE); // Container is static
    lv_cont_set_layout(cont, LV_LAYOUT_COL_L);

    lv_obj_t *logo = lv_img_create(lv_scr_act(), NULL);
    lv_img_set_src(logo, "small.png");
    lv_anim_set_exec_cb(&a, logo, lv_obj_set_x);
    lv_anim_create(&a);
}

It’s even a bit funny watching a scrambled image floating across the screen :wink:

I have not had success converting the images into C arrays yet which is why I’m loading them from the file system. Do you think there is merit in trying this? I don’t mind taking the time to try this out especially if it might fix something.

It will probably work, simply because I think more people use bitmap C arrays than real PNG files on a disk. But there is obviously something wrong somewhere with the handling of PNG files.

1 Like

Fair enough. I’ll see if I can figure out why I have been unsuccessful loading the C array.

I tried to make it fail on the Simulator about a dozen or so times before I finally caught it scrambled, so it’s indeed not limited to my embedded system. I can’t upload the video file but sure enough it looks just like the static photo I sent earlier only animated.

An other idea: what if you set LV_MEM_JUNK 1 in lv_mem.c?

Do you mean LV_MEM_ADD_JUNK? This is already defined as a 1.

/*********************
 *      DEFINES
 *********************/
/*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/
#define LV_MEM_ADD_JUNK 1

I’ve converted my PNG files to C Arrays, and when I launch my application in the embedded system they both appear to load properly now!

I guess this definitely has something to do with opening the file and decoding on-the-fly. The fact that the behavior is so consistent on my embedded platform and so hard to reproduce on the simulator makes me think that it’s some time-related issue, but I don’t know that I will have the opportunity to dig any further than that. For now I consider this a valid workaround.

Thanks for all your help! The issue I had with loading the C arrays previously was something silly as usual (I was loading the actual pixel map array and not the const lv_img_dsc_t object). If I may make a suggestion, this would have helped me if that object was at the top of the converted file rather than down below the array. Out of sight, out of mind and all that.

And for the sake of posterity, I did remove the lv_img_cache_set_size() statement so it is back at the default value.

Thanks for your help!

I would like to thank you for providing me with such a website
I’m very confused about the difference between lv_disp_get_scr_act(NULL) function and lv_scr_act() function。
I’m even can’t distinguish screen and display.

This documentation page explains the difference.

As long as you only have one display, they are identical (the latter was mainly kept for backwards compatibility with 5.x, but stuck around as a useful shortcut).

The difference comes when you have multiple displays. In this case, lv_scr_act will return the screen for the “default” display (which might not be the one you want). You would want to use lv_disp_get_scr_act so that you can pass in a specific display and get its screen.

Thank you very much I get it