Hello LVGL community,
I’ve encountered a strange issue with image rendering in LVGL v9.3.0 (using the Zephyr RTOS). When displaying two separate image objects that share the same Y-coordinate but different X-coordinates, the second image overwrites the first one but not totally. It is a bit garbled (see screenshot) - even though they don’t overlap at all.
I am sending the image data over serial and then creating the image from the buffer.
Description of the issue:
- Send image 1 data over serial
- Call display_raw_image() with (X1, Y) (source below)
- Send image 2 data over serial
- Call display_raw_image() with (X2, Y) (source below)
- X2 is far enough away from X1 that the images should not overlap
- The second image displays correctly, but it also partially overwrites/garbles the first image
Important observation:
- This issue ONLY occurs when both images have the same Y-coordinate
- When I place the second image at a different Y-coordinate, the problem doesn’t happen
- Both images display properly individually, it’s only when they’re placed at the same Y-coordinate that the overwriting occurs
I’m using a monochrome 1bpp display (Sharp LS027B7DH01) with LVGL in Zephyr. My rendering code follows this pattern:
I’ve tried pretty much everything I can think of including cache invalidation lv_draw_buf_invalidate_cache()
I am probably missing something very trivial / fundament. Has anyone else encountered this issue, or does anyone have insight into why LVGL might be overwriting content when images share the same Y-coordinate?
Screen Shots
Below a couple screenshots of what the screen looks like while observing the issue.
First image drawn and displayed correctly at X1, Y.
Second image drawn and displayed correctly at X2, Y but 1st image garbled.
Code
/**
* @brief Display a raw 1bpp image on the screen
*
* @param image_data Pointer to raw 1bpp image data
* @param width Image width in pixels
* @param height Image height in pixels
* @param x X position for display
* @param y Y position for display
* @param clear Whether to clear the display before drawing
* @return int 0 if successful, negative error code otherwise
*/
static int display_raw_image(const uint8_t* image_data, uint16_t width,
uint16_t height, uint16_t x, uint16_t y,
bool clear) {
LOG_INF("Displaying raw image: %ux%u pixels at (%d,%d)", width, height, x,
y);
/* Create LVGL image descriptor */
lv_img_dsc_t img_dsc;
lv_color32_t palette[2];
/* Calculate the byte-aligned width for 1bpp data */
uint32_t w_bytes = (width + 7) / 8;
uint32_t buf_size = w_bytes * height;
palette[0] = lv_color_to_32(lv_color_white(), LV_OPA_MAX);
palette[1] = lv_color_to_32(lv_color_black(), LV_OPA_MAX);
buf_size += sizeof(palette);
/* Allocate buffer for LVGL format */
uint8_t* lvgl_buf = lv_malloc(buf_size);
if (!lvgl_buf) {
LOG_ERR("Failed to allocate memory for LVGL image");
return -ENOMEM;
}
memset(lvgl_buf, 0xff, buf_size);
/* Copy the palette data to the LVGL buffer */
memcpy(lvgl_buf, palette, sizeof(palette));
/* Copy the raw 1bpp image data to the LVGL buffer */
memcpy(lvgl_buf + sizeof(palette), image_data, buf_size - sizeof(palette));
// memset(lvgl_buf, 0xff, buf_size / 2);
/* Setup the image descriptor */
img_dsc.header.cf = LV_COLOR_FORMAT_NATIVE; /* 1-bit per pixel monochrome */
img_dsc.header.w = width;
img_dsc.header.h = height;
img_dsc.data_size = buf_size;
img_dsc.header.stride =
LV_STRIDE_AUTO; /* Set stride to byte-aligned row width */
img_dsc.data = lvgl_buf;
img_dsc.header.flags = LV_IMAGE_FLAGS_ALLOCATED; /* Uncompressed image */
/* Create an LVGL image object */
lv_obj_t* img_obj = lv_img_create(lv_scr_act());
lv_img_set_src(img_obj, &img_dsc);
/* Position the image */
lv_obj_set_pos(img_obj, x, y);
/* If clear flag is set, remove all other objects first */
if (clear) {
/* First, get the screen and save our image */
lv_obj_t* scr = lv_scr_act();
/* Find and delete all other objects */
uint32_t i = 0; /* First child index */
lv_obj_t* child = lv_obj_get_child(scr, i);
while (child) {
if (child != img_obj) {
lv_obj_del(child);
/* After deletion, the next child moves into this position, so
* don't increment i */
child = lv_obj_get_child(scr, i);
} else {
/* Skip our image object */
i++;
child = lv_obj_get_child(scr, i);
}
}
}
/* Force screen update */
lv_timer_handler();
lv_free(lvgl_buf); /* Free the allocated buffer */
return 0;
}
Thank you for any help or suggestions!