1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

refactor(snapshot): use draw buffer interface (#5487)

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
Co-authored-by: Benign X <1341398182@qq.com>
This commit is contained in:
Neo Xu 2024-01-30 16:18:54 +08:00 committed by GitHub
parent 396d7ae82b
commit 135ad49dce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 101 additions and 93 deletions

View File

@ -26,13 +26,13 @@ Free the Image
~~~~~~~~~~~~~~
The memory :cpp:func:`lv_snapshot_take` uses are dynamically allocated using
:cpp:func:`lv_malloc`. Use API :cpp:func:`lv_snapshot_free` to free the memory it
:cpp:func:`lv_draw_buf_create`. Use API :cpp:func:`lv_draw_buf_destroy` to free the memory it
takes. This will firstly free memory the image data takes, then the
image descriptor.
Take caution to free the snapshot but not delete the image object.
Before free the memory, be sure to firstly unlink it from image object,
using :cpp:expr:`lv_image_set_src(NULL)` and :cpp:expr:`lv_cache_invalidate(lv_cache_find(src, LV_CACHE_SRC_TYPE_PTR, 0, 0));`.
The snapshot image which is the draw buffer returned by :cpp:func:`lv_snapshot_take`
normally won't be added to cache because it can be drawn directly. So you don't need
to invalidate cache by :cpp:func:`lv_image_cache_drop` before destroy the draw buffer.
Below code snippet explains usage of this API.
@ -40,9 +40,9 @@ Below code snippet explains usage of this API.
void update_snapshot(lv_obj_t * obj, lv_obj_t * img_snapshot)
{
lv_image_dsc_t* snapshot = (void*)lv_image_get_src(img_snapshot);
lv_draw_buf_t* snapshot = (void*)lv_image_get_src(img_snapshot);
if(snapshot) {
lv_snapshot_free(snapshot);
lv_draw_buf_destroy(snapshot);
}
snapshot = lv_snapshot_take(obj, LV_COLOR_FORMAT_ARGB8888);
lv_image_set_src(img_snapshot, snapshot);
@ -52,16 +52,16 @@ Use Existing Buffer
~~~~~~~~~~~~~~~~~~~
If the snapshot needs update now and then, or simply caller provides memory, use API
``lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_image_dsc_t * dsc, void * buf, uint32_t buf_size);``
for this case. It's caller's responsibility to alloc/free the memory.
``lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * obj, lv_color_format_t cf, lv_draw_buf_t * draw_buf);``
for this case. It's caller's responsibility to create and destroy the draw buffer.
If snapshot is generated successfully, the image descriptor is updated
and image data will be stored to provided ``buf``.
Note that snapshot may fail if provided buffer is not enough, which may
happen when object size changes. It's recommended to use API
:cpp:func:`lv_snapshot_buf_size_needed` to check the needed buffer size in byte
firstly and resize the buffer accordingly.
:cpp:func:`lv_snapshot_reshape_draw_buf` to prepare the buffer firstly and if it
fails, destroy the existing draw buffer and call `lv_snapshot_take` directly.
.. _snapshot_example:

View File

@ -7,9 +7,9 @@ static void event_cb(lv_event_t * e)
lv_obj_t * img = lv_event_get_target(e);
if(snapshot_obj) {
lv_image_dsc_t * snapshot = (void *)lv_image_get_src(snapshot_obj);
lv_draw_buf_t * snapshot = (lv_draw_buf_t *)lv_image_get_src(snapshot_obj);
if(snapshot) {
lv_snapshot_free(snapshot);
lv_draw_buf_destroy(snapshot);
}
/*Update the snapshot, we know parent of object is the container.*/

View File

@ -39,38 +39,41 @@
* GLOBAL FUNCTIONS
**********************/
uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_color_format_t cf)
/**
* Create a draw buffer for object to store the snapshot image.
*/
lv_draw_buf_t * lv_snapshot_create_draw_buf(lv_obj_t * obj, lv_color_format_t cf)
{
LV_ASSERT_NULL(obj);
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 0;
}
lv_obj_update_layout(obj);
/*Width and height determine snapshot image size.*/
int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);
int32_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
if(w == 0 || h == 0) return NULL;
return lv_draw_buf_width_to_stride(w, cf) * h;
return lv_draw_buf_create(w, h, cf, LV_STRIDE_AUTO);
}
lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_image_dsc_t * dsc, void * buf,
uint32_t buf_size)
lv_result_t lv_snapshot_reshape_draw_buf(lv_obj_t * obj, lv_draw_buf_t * draw_buf)
{
lv_obj_update_layout(obj);
int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);
int32_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
if(w == 0 || h == 0) return LV_RESULT_INVALID;
draw_buf = lv_draw_buf_reshape(draw_buf, LV_COLOR_FORMAT_UNKNOWN, w, h, LV_STRIDE_AUTO);
return draw_buf == NULL ? LV_RESULT_INVALID : LV_RESULT_OK;
}
lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * obj, lv_color_format_t cf, lv_draw_buf_t * draw_buf)
{
LV_ASSERT_NULL(obj);
LV_ASSERT_NULL(dsc);
LV_ASSERT_NULL(buf);
LV_ASSERT_NULL(draw_buf);
lv_result_t res;
switch(cf) {
case LV_COLOR_FORMAT_RGB565:
@ -83,38 +86,23 @@ lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_ima
return LV_RESULT_INVALID;
}
uint32_t buf_size_needed = lv_snapshot_buf_size_needed(obj, cf);
if(buf_size_needed == 0 || buf_size < buf_size_needed) return LV_RESULT_INVALID;
res = lv_snapshot_reshape_draw_buf(obj, draw_buf);
if(res != LV_RESULT_OK) return res;
LV_ASSERT_MSG(buf == lv_draw_buf_align(buf, cf), "Buffer is not aligned");
/*Width and height determine snapshot image size.*/
int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);
int32_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
/* 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(obj);
lv_obj_get_coords(obj, &snapshot_area);
lv_area_increase(&snapshot_area, ext_size, ext_size);
lv_memzero(buf, buf_size);
dsc->data = buf;
dsc->data_size = buf_size_needed;
/*Keep header flags unchanged, because we don't know if it's allocated or not.*/
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
dsc->header.magic = LV_IMAGE_HEADER_MAGIC;
lv_layer_t layer;
lv_memzero(&layer, sizeof(layer));
lv_draw_buf_t draw_buf;
lv_draw_buf_from_image(&draw_buf, dsc);
layer.draw_buf = &draw_buf;
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;
@ -141,34 +129,18 @@ lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_ima
return LV_RESULT_OK;
}
lv_image_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf)
lv_draw_buf_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf)
{
LV_ASSERT_NULL(obj);
lv_obj_update_layout(obj);
int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);
int32_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
if(w == 0 || h == 0) return NULL;
lv_draw_buf_t * draw_buf = lv_draw_buf_create(w, h, cf, LV_STRIDE_AUTO);
lv_draw_buf_t * draw_buf = lv_snapshot_create_draw_buf(obj, cf);
if(draw_buf == NULL) return NULL;
if(lv_snapshot_take_to_buf(obj, cf, (lv_image_dsc_t *)draw_buf, draw_buf->data, draw_buf->data_size) != LV_RESULT_OK) {
if(lv_snapshot_take_to_draw_buf(obj, cf, draw_buf) != LV_RESULT_OK) {
lv_draw_buf_destroy(draw_buf);
return NULL;
}
return (lv_image_dsc_t *)draw_buf;
}
void lv_snapshot_free(lv_image_dsc_t * dsc)
{
if(!dsc)
return;
lv_draw_buf_destroy((lv_draw_buf_t *)dsc);
return draw_buf;
}
/**********************

View File

@ -32,29 +32,54 @@ extern "C" {
**********************/
/**
* Take snapshot for object with its children, alloc the memory needed.
* Take snapshot for object with its children, create the draw buffer as needed.
* @param obj the object to generate snapshot.
* @param cf color format for generated image.
* @return a pointer to an image descriptor, or NULL if failed.
* @return a pointer to an draw buffer containing snapshot image, or NULL if failed.
*/
lv_image_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf);
lv_draw_buf_t * lv_snapshot_take(lv_obj_t * obj, lv_color_format_t cf);
/**
* Create a draw buffer to store the snapshot image for object.
* @param obj the object to generate snapshot.
* @param cf color format for generated image.
* @return a pointer to an draw buffer ready for taking snapshot, or NULL if failed.
*/
lv_draw_buf_t * lv_snapshot_create_draw_buf(lv_obj_t * obj, lv_color_format_t cf);
/**
* Reshape the draw buffer to prepare for taking snapshot for obj.
* This is usually used to check if the existing draw buffer is enough for
* obj snapshot. If return LV_RESULT_INVALID, you should create a new one.
* @param draw_buf the draw buffer to reshape.
* @param obj the object to generate snapshot.
*/
lv_result_t lv_snapshot_reshape_draw_buf(lv_obj_t * obj, lv_draw_buf_t * draw_buf);
/**
* Take snapshot for object with its children, save image info to provided buffer.
* @param obj the object to generate snapshot.
* @param cf color format for new snapshot image.
* It could differ with cf of `draw_buf` as long as the new cf will fit in.
* @param draw_buf the draw buffer to store the image result. It's reshaped automatically.
* @return LV_RESULT_OK on success, LV_RESULT_INVALID on error.
*/
lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * obj, lv_color_format_t cf, lv_draw_buf_t * draw_buf);
/**
* Legacy API, use `lv_draw_buf_destroy` instead.
*
* Free the snapshot image returned by @ref lv_snapshot_take
* It will firstly free the data image takes, then the image descriptor.
* @param dsc the image descriptor generated by lv_snapshot_take.
*/
void lv_snapshot_free(lv_image_dsc_t * dsc);
/**
* Get the buffer needed for object snapshot image.
* @param obj the object to generate snapshot.
* @param cf color format for generated image.
* @return the buffer size needed in bytes
*/
uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_color_format_t cf);
static inline void lv_snapshot_free(lv_image_dsc_t * dsc)
{
LV_LOG_WARN("Legacy API, use lv_draw_buf_destroy instead.");
lv_draw_buf_destroy((lv_draw_buf_t *)dsc);
}
/**
* Legacy API, use lv_snapshot_take_to_draw_buf instead.
* Take snapshot for object with its children, save image info to provided buffer.
* @param obj the object to generate snapshot.
* @param cf color format for generated image.
@ -63,8 +88,19 @@ uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_color_format_t cf);
* @param buf_size provided buffer size in bytes.
* @return LV_RESULT_OK on success, LV_RESULT_INVALID on error.
*/
lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_image_dsc_t * dsc, void * buf,
uint32_t buf_size);
static inline lv_result_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_color_format_t cf, lv_image_dsc_t * dsc,
void * buf,
uint32_t buf_size)
{
lv_draw_buf_t draw_buf;
LV_LOG_WARN("Legacy API, use lv_snapshot_take_to_draw_buf instead.");
lv_draw_buf_init(&draw_buf, 1, 1, cf, buf_size, buf, buf_size);
lv_result_t res = lv_snapshot_take_to_draw_buf(obj, cf, &draw_buf);
if(res == LV_RESULT_OK) {
lv_memcpy((void *)dsc, &draw_buf, sizeof(lv_image_dsc_t));
}
return res;
}
/**********************
* MACROS

View File

@ -14,7 +14,7 @@ void test_snapshot_should_not_leak_memory(void)
uint32_t final_available_memory = 0;
lv_mem_monitor_t monitor;
lv_image_dsc_t * snapshots[NUM_SNAPSHOTS] = {NULL};
lv_draw_buf_t * snapshots[NUM_SNAPSHOTS] = {NULL};
lv_mem_monitor(&monitor);
initial_available_memory = monitor.free_size;
@ -25,7 +25,7 @@ void test_snapshot_should_not_leak_memory(void)
}
for(idx = 0; idx < NUM_SNAPSHOTS; idx++) {
lv_snapshot_free(snapshots[idx]);
lv_draw_buf_destroy(snapshots[idx]);
}
lv_mem_monitor(&monitor);
@ -40,7 +40,7 @@ void test_snapshot_take_snapshot_immidiately_after_obj_create(void)
lv_obj_set_style_text_font(label, &lv_font_montserrat_28, 0);
lv_label_set_text(label, "Wubba lubba dub dub!");
lv_image_dsc_t * draw_dsc = lv_snapshot_take(label, LV_COLOR_FORMAT_ARGB8888);
lv_draw_buf_t * draw_dsc = lv_snapshot_take(label, LV_COLOR_FORMAT_ARGB8888);
lv_obj_t * img_obj = lv_image_create(lv_screen_active());
lv_image_set_src(img_obj, draw_dsc);