LVGL screenshot

lv_100ask_screenshot_demo

Video: https://youtu.be/zR_niKRp0sI

Introduction

lv_100ask_screenshot is a screenshot tool based on LVGL.

lv_100ask_screenshot features:

  • Save LVGL screen objects (full screen) as image files: lv_scr_act(),layer_sys(),layer_top()
  • Capture and save the specified LVGL object and its children as an image file
  • Supported save as: BMP, PNG, JPG
  • more todo…

lv_100ask_screenshot is very simple to use, and the subsequent custom expansion functions are also very convenient, so stay tuned for more functions.

Usage

Refer to the example in lv_lib_100ask/test/lv_100ask_screenshot_test.

Get lv_100ask_screenshot

Try lv_100ask_screenshot

You can try lv_100ask_screenshot functionality through these two files, please feel free to use them.

The path for saving images is “D:/” and cannot be modified:

2 Likes

Very nice! I’m sure it will be useful for many people!

Thank you! I hope so too!

Hey, this looks generally useful, but after quickly trying out I’d prefer to be able to get a single actual screenshot of full display output for an actual real screenshot, is there a simple way of doing this without hooking flush_cb? As snapshot only seems to be able to get a layer at a time, but I want all visible layers in capture.

Hi

lv_100ask_screenshot based on snapshot.

If you want to obtain all the screenshots, the best way I can think of at the moment is to process them in flush_cb.

I actually now made a small modified copy of snapshot functions that takes a display object and renders scr/top/sys layers to do the job for now, this appears at a quick test to get me the result I wanted whilst allowing rendering in RGB8888 despite using 16bit RGB output.

Sounds great! Looking forward to your sharing of code and upgrading snapshot.

Sure, it’s pretty much just a quick hack, and I didn’t check if the output size is necessarily correct, as it worked as expected in my situation:

lv_result_t lv_screenshot_take_to_draw_buf(lv_display_t * disp, lv_color_format_t cf, lv_draw_buf_t * draw_buf)
{
LV_ASSERT_NULL(disp);
LV_ASSERT_NULL(draw_buf);
lv_result_t res;

switch(cf) {
    case LV_COLOR_FORMAT_RGB565:
    case LV_COLOR_FORMAT_RGB888:
    case LV_COLOR_FORMAT_XRGB8888:
    case LV_COLOR_FORMAT_ARGB8888:
        break;
    default:
        LV_LOG_WARN("Not supported color format");
        return LV_RESULT_INVALID;
}

res = lv_snapshot_reshape_draw_buf(disp->act_scr, draw_buf);
if(res != LV_RESULT_OK) return res;

/* clear draw buffer*/
lv_draw_buf_clear(draw_buf, NULL);

lv_area_t snapshot_area;
int32_t w = draw_buf->header.w;
int32_t h = draw_buf->header.h;
int32_t ext_size = _lv_obj_get_ext_draw_size(disp->act_scr);
lv_obj_get_coords(disp->act_scr, &snapshot_area);
lv_area_increase(&snapshot_area, ext_size, ext_size);

lv_layer_t layer;
lv_memzero(&layer, sizeof(layer));

layer.draw_buf = draw_buf;
layer.buf_area.x1 = snapshot_area.x1;
layer.buf_area.y1 = snapshot_area.y1;
layer.buf_area.x2 = snapshot_area.x1 + w - 1;
layer.buf_area.y2 = snapshot_area.y1 + h - 1;
layer.color_format = cf;
layer._clip_area = snapshot_area;

lv_display_t * disp_old = _lv_refr_get_disp_refreshing();
lv_display_t * disp_new = disp;//lv_obj_get_display(obj);
lv_layer_t * layer_old = disp_new->layer_head;
disp_new->layer_head = &layer;

_lv_refr_set_disp_refreshing(disp_new);

lv_obj_redraw(&layer, disp_new->bottom_layer);
lv_obj_redraw(&layer, disp_new->act_scr);
lv_obj_redraw(&layer, disp_new->top_layer);
lv_obj_redraw(&layer, disp_new->sys_layer);

while(layer.draw_task_head) {
    lv_draw_dispatch_wait_for_request();
    lv_draw_dispatch_layer(NULL, &layer);
}

disp_new->layer_head = layer_old;
_lv_refr_set_disp_refreshing(disp_old);

return LV_RESULT_OK;

}

lv_draw_buf_t * lv_screenshot_take(lv_display_t * disp, lv_color_format_t cf)
{
LV_ASSERT_NULL(disp);
lv_draw_buf_t * draw_buf = lv_snapshot_create_draw_buf(disp->act_scr, cf);
if(draw_buf == NULL) return NULL;

if(lv_screenshot_take_to_draw_buf(disp, cf, draw_buf) != LV_RESULT_OK) {
    lv_draw_buf_destroy(draw_buf);
    return NULL;
}

return draw_buf;

}

1 Like

Can be ported to embedded systems?
I have STM32H757 that supports LvGL and SD-Card.

Embedded RAM scarce systems will definitely be a challenge due to it allocating a full frame buffer to capture into, I think they such would have to be captured via a flush_cb hook with a hook to write out partial frame writes out to sd card.

My board has enough ram to store the entire buffer (externally).