mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-22 23:37:43 +08:00
feat(cache): add empty_cb, add cache manager replace function, fix builtin cache init and tracing, update image overview documentation (#4604)
This commit is contained in:
+70
-50
@@ -17,7 +17,7 @@ Variables
|
||||
---------
|
||||
|
||||
Images stored internally in a variable are composed mainly of an
|
||||
:cpp:struct:`lv_img_dsc_t` structure with the following fields:
|
||||
:cpp:struct:`lv_image_dsc_t` structure with the following fields:
|
||||
|
||||
- **header**:
|
||||
|
||||
@@ -136,16 +136,16 @@ variable to display it using LVGL. For example:
|
||||
|
||||
uint8_t my_img_data[] = {0x00, 0x01, 0x02, ...};
|
||||
|
||||
static lv_img_dsc_t my_img_dsc = {
|
||||
static lv_image_dsc_t my_img_dsc = {
|
||||
.header.always_zero = 0,
|
||||
.header.w = 80,
|
||||
.header.h = 60,
|
||||
.data_size = 80 * 60 * LV_COLOR_DEPTH / 8,
|
||||
.header.cf = LV_IMG_CF_TRUE_COLOR, /*Set the color format*/
|
||||
.header.cf = LV_COLOR_FORMAT_NATIVE, /*Set the color format*/
|
||||
.data = my_img_data,
|
||||
};
|
||||
|
||||
If the color format is :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_ALPHA` you can set
|
||||
If the color format is :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_WITH_ALPHA` you can set
|
||||
``data_size`` like ``80 * 60 *`` :cpp:enumerator:`LV_IMG_PX_SIZE_ALPHA_BYTE`.
|
||||
|
||||
Another (possibly simpler) option to create and display an image at
|
||||
@@ -159,13 +159,13 @@ The simplest way to use an image in LVGL is to display it with an
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * icon = lv_img_create(lv_scr_act(), NULL);
|
||||
lv_obj_t * icon = lv_image_create(lv_scr_act(), NULL);
|
||||
|
||||
/*From variable*/
|
||||
lv_img_set_src(icon, &my_icon_dsc);
|
||||
lv_image_set_src(icon, &my_icon_dsc);
|
||||
|
||||
/*From file*/
|
||||
lv_img_set_src(icon, "S:my_icon.bin");
|
||||
lv_image_set_src(icon, "S:my_icon.bin");
|
||||
|
||||
If the image was converted with the online converter, you should use
|
||||
:cpp:expr:`LV_IMG_DECLARE(my_icon_dsc)` to declare the image in the file where
|
||||
@@ -242,10 +242,10 @@ open/close the PNG files. It should look like this:
|
||||
.. code:: c
|
||||
|
||||
/*Create a new decoder and register functions */
|
||||
lv_img_decoder_t * dec = lv_img_decoder_create();
|
||||
lv_img_decoder_set_info_cb(dec, decoder_info);
|
||||
lv_img_decoder_set_open_cb(dec, decoder_open);
|
||||
lv_img_decoder_set_close_cb(dec, decoder_close);
|
||||
lv_image_decoder_t * dec = lv_image_decoder_create();
|
||||
lv_image_decoder_set_info_cb(dec, decoder_info);
|
||||
lv_image_decoder_set_open_cb(dec, decoder_open);
|
||||
lv_image_decoder_set_close_cb(dec, decoder_close);
|
||||
|
||||
|
||||
/**
|
||||
@@ -255,7 +255,7 @@ open/close the PNG files. It should look like this:
|
||||
* @param header store the info here
|
||||
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
|
||||
*/
|
||||
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
|
||||
static lv_res_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header)
|
||||
{
|
||||
/*Check whether the type `src` is known by the decoder*/
|
||||
if(is_png(src) == false) return LV_RES_INV;
|
||||
@@ -263,7 +263,7 @@ open/close the PNG files. It should look like this:
|
||||
/* Read the PNG header and find `width` and `height` */
|
||||
...
|
||||
|
||||
header->cf = LV_IMG_CF_RAW_ALPHA;
|
||||
header->cf = LV_COLOR_FORMAT_RAW_ALPHA;
|
||||
header->w = width;
|
||||
header->h = height;
|
||||
}
|
||||
@@ -274,7 +274,7 @@ open/close the PNG files. It should look like this:
|
||||
* @param dsc pointer to a descriptor which describes this decoding session
|
||||
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
|
||||
*/
|
||||
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
|
||||
static lv_res_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
|
||||
/*Check whether the type `src` is known by the decoder*/
|
||||
@@ -284,10 +284,10 @@ open/close the PNG files. It should look like this:
|
||||
dsc->img_data = my_png_decoder(src);
|
||||
|
||||
/*Change the color format if required. For PNG usually 'Raw' is fine*/
|
||||
dsc->header.cf = LV_IMG_CF_...
|
||||
dsc->header.cf = LV_COLOR_FORMAT_...
|
||||
|
||||
/*Call a built in decoder function if required. It's not required if`my_png_decoder` opened the image in true color format.*/
|
||||
lv_res_t res = lv_img_decoder_built_in_open(decoder, dsc);
|
||||
lv_res_t res = lv_image_decoder_built_in_open(decoder, dsc);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -303,7 +303,7 @@ open/close the PNG files. It should look like this:
|
||||
* @param buf a buffer to store the decoded pixels
|
||||
* @return LV_RES_OK: ok; LV_RES_INV: failed
|
||||
*/
|
||||
lv_res_t decoder_built_in_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,
|
||||
lv_res_t decoder_built_in_read_line(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_coord_t x,
|
||||
lv_coord_t y, lv_coord_t len, uint8_t * buf)
|
||||
{
|
||||
/*With PNG it's usually not required*/
|
||||
@@ -317,12 +317,12 @@ open/close the PNG files. It should look like this:
|
||||
* @param decoder pointer to the decoder where this function belongs
|
||||
* @param dsc pointer to a descriptor which describes this decoding session
|
||||
*/
|
||||
static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
|
||||
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
/*Free all allocated data*/
|
||||
|
||||
/*Call the built-in close function if the built-in open/read_line was used*/
|
||||
lv_img_decoder_built_in_close(decoder, dsc);
|
||||
lv_image_decoder_built_in_close(decoder, dsc);
|
||||
|
||||
}
|
||||
|
||||
@@ -348,8 +348,8 @@ Manually use an image decoder
|
||||
|
||||
LVGL will use registered image decoders automatically if you try and
|
||||
draw a raw image (i.e. using the ``lv_img`` object) but you can use them
|
||||
manually too. Create an :cpp:type:`lv_img_decoder_dsc_t` variable to describe
|
||||
the decoding session and call :cpp:func:`lv_img_decoder_open`.
|
||||
manually too. Create an :cpp:type:`lv_image_decoder_dsc_t` variable to describe
|
||||
the decoding session and call :cpp:func:`lv_image_decoder_open`.
|
||||
|
||||
The ``color`` parameter is used only with ``LV_IMG_CF_ALPHA_1/2/4/8BIT``
|
||||
images to tell color of the image. ``frame_id`` can be used if the image
|
||||
@@ -359,12 +359,12 @@ to open is an animation.
|
||||
|
||||
|
||||
lv_res_t res;
|
||||
lv_img_decoder_dsc_t dsc;
|
||||
res = lv_img_decoder_open(&dsc, &my_img_dsc, color, frame_id);
|
||||
lv_image_decoder_dsc_t dsc;
|
||||
res = lv_image_decoder_open(&dsc, &my_img_dsc, color, frame_id);
|
||||
|
||||
if(res == LV_RES_OK) {
|
||||
/*Do something with `dsc->img_data`*/
|
||||
lv_img_decoder_close(&dsc);
|
||||
lv_image_decoder_close(&dsc);
|
||||
}
|
||||
|
||||
.. _image-caching:
|
||||
@@ -390,12 +390,13 @@ a relatively fast storage medium.
|
||||
Cache size
|
||||
----------
|
||||
|
||||
The number of cache entries can be defined with
|
||||
:c:macro:`LV_IMG_CACHE_DEF_SIZE` in *lv_conf.h*. The default value is 1 so only
|
||||
the most recently used image will be left open.
|
||||
The size of cache (in bytes) can be defined with
|
||||
:c:macro:`LV_CACHE_DEF_SIZE` in *lv_conf.h*. The default value is 0, so
|
||||
no image is cached.
|
||||
|
||||
The size of the cache can be changed at run-time with
|
||||
:cpp:expr:`lv_img_cache_set_size(entry_num)`.
|
||||
The size of cache can be changed at run-time with
|
||||
:cpp:expr:`lv_cache_set_max_size(size_t size)`,
|
||||
and get with :cpp:expr:`lv_cache_get_max_size()`.
|
||||
|
||||
Value of images
|
||||
---------------
|
||||
@@ -435,55 +436,74 @@ to cache even the largest images at the same time.
|
||||
Clean the cache
|
||||
---------------
|
||||
|
||||
Let's say you have loaded a PNG image into a :cpp:struct:`lv_img_dsc_t` ``my_png``
|
||||
variable and use it in an ``lv_img`` object. If the image is already
|
||||
Let's say you have loaded a PNG image into a :cpp:struct:`lv_image_dsc_t` ``my_png``
|
||||
variable and use it in an ``lv_image`` object. If the image is already
|
||||
cached and you then change the underlying PNG file, you need to notify
|
||||
LVGL to cache the image again. Otherwise, there is no easy way of
|
||||
detecting that the underlying file changed and LVGL will still draw the
|
||||
old image from cache.
|
||||
|
||||
To do this, use :cpp:expr:`lv_img_cache_invalidate_src(&my_png)`. If ``NULL`` is
|
||||
passed as a parameter, the whole cache will be cleaned.
|
||||
To do this, use :cpp:expr:`lv_cache_invalidate(lv_cache_find(&my_png, LV_CACHE_SRC_TYPE_PTR, 0, 0));`.
|
||||
|
||||
Custom cache algorithm
|
||||
----------------------
|
||||
|
||||
If you want to implement your own cache algorithm, you can refer to the
|
||||
following code to replace the LVGL built-in image cache manager:
|
||||
following code to replace the LVGL built-in cache manager:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static _lv_img_cache_entry_t * my_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
|
||||
static lv_cache_entry_t * my_cache_add_cb(size_t size)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_img_cache_set_size(uint16_t new_entry_cnt)
|
||||
static lv_cache_entry_t * my_cache_find_cb(const void * src, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_img_cache_invalidate_src(const void * src)
|
||||
static void my_cache_invalidate_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
void my_img_cache_init(void)
|
||||
static const void * my_cache_get_data_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
/* Before replacing the image cache manager,
|
||||
* you should ensure that all caches are cleared to prevent memory leaks.
|
||||
*/
|
||||
lv_img_cache_invalidate_src(NULL);
|
||||
...
|
||||
}
|
||||
|
||||
/*Initialize image cache manager.*/
|
||||
lv_img_cache_manager_t manager;
|
||||
lv_img_cache_manager_init(&manager);
|
||||
manager.open_cb = my_img_cache_open;
|
||||
manager.set_size_cb = my_img_cache_set_size;
|
||||
manager.invalidate_src_cb = my_img_cache_invalidate_src;
|
||||
static void my_cache_release_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
/*Apply image cache manager to LVGL.*/
|
||||
lv_img_cache_manager_apply(&manager);
|
||||
static void my_cache_set_max_size_cb(size_t new_size)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_cache_empty_cb(void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
void my_cache_init(void)
|
||||
{
|
||||
/*Initialize new cache manager.*/
|
||||
lv_cache_manager_t my_manager;
|
||||
my_manager.add_cb = my_cache_add_cb;
|
||||
my_manager.find_cb = my_cache_find_cb;
|
||||
my_manager.invalidate_cb = my_cache_invalidate_cb;
|
||||
my_manager.get_data_cb = my_cache_get_data_cb;
|
||||
my_manager.release_cb = my_cache_release_cb;
|
||||
my_manager.set_max_size_cb = my_cache_set_max_size_cb;
|
||||
my_manager.empty_cb = my_cache_empty_cb;
|
||||
|
||||
/*Replace existing cache manager with the new one.*/
|
||||
lv_cache_lock();
|
||||
lv_cache_replace_manager(&my_manager);
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
API
|
||||
|
||||
@@ -42,6 +42,25 @@ void _lv_cache_init(void)
|
||||
lv_mutex_init(&_cache_manager.mutex);
|
||||
}
|
||||
|
||||
void lv_cache_set_manager(lv_cache_manager_t * manager)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(manager == NULL) return;
|
||||
|
||||
if(_cache_manager.empty_cb != NULL) _cache_manager.empty_cb();
|
||||
else if(_cache_manager.set_max_size_cb != NULL) _cache_manager.set_max_size_cb(0);
|
||||
|
||||
_cache_manager.add_cb = manager->add_cb;
|
||||
_cache_manager.find_cb = manager->find_cb;
|
||||
_cache_manager.invalidate_cb = manager->invalidate_cb;
|
||||
_cache_manager.get_data_cb = manager->get_data_cb;
|
||||
_cache_manager.release_cb = manager->release_cb;
|
||||
_cache_manager.set_max_size_cb = manager->set_max_size_cb;
|
||||
_cache_manager.empty_cb = manager->empty_cb;
|
||||
|
||||
if(_cache_manager.set_max_size_cb != NULL) _cache_manager.set_max_size_cb(_cache_manager.max_size);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_add(size_t size)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
|
||||
@@ -121,6 +121,11 @@ typedef void (*lv_cache_release_cb)(lv_cache_entry_t * entry);
|
||||
*/
|
||||
typedef void (*lv_cache_set_max_size_cb)(size_t size);
|
||||
|
||||
/**
|
||||
* Empty the cache.
|
||||
*/
|
||||
typedef void (*lv_cache_empty_cb)(void);
|
||||
|
||||
typedef struct {
|
||||
lv_cache_add_cb add_cb;
|
||||
lv_cache_find_cb find_cb;
|
||||
@@ -128,6 +133,7 @@ typedef struct {
|
||||
lv_cache_get_data_cb get_data_cb;
|
||||
lv_cache_release_cb release_cb;
|
||||
lv_cache_set_max_size_cb set_max_size_cb;
|
||||
lv_cache_empty_cb empty_cb;
|
||||
|
||||
lv_mutex_t mutex;
|
||||
size_t max_size;
|
||||
@@ -143,6 +149,12 @@ typedef struct {
|
||||
*/
|
||||
void _lv_cache_init(void);
|
||||
|
||||
/**
|
||||
* Set new cache manager
|
||||
* @param manager the new cache manager with callback functions set
|
||||
*/
|
||||
void lv_cache_set_manager(lv_cache_manager_t * manager);
|
||||
|
||||
/**
|
||||
* Add a new entry to the cache with the given size.
|
||||
* It won't allocate any buffers just free enough space to be a new entry
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file lv_image_cache.c
|
||||
* @file lv_cache_builtin.c
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ static void invalidate_cb(lv_cache_entry_t * entry);
|
||||
static const void * get_data_cb(lv_cache_entry_t * entry);
|
||||
static void release_cb(lv_cache_entry_t * entry);
|
||||
static void set_max_size_cb(size_t new_size);
|
||||
static void empty_cb(void);
|
||||
static bool drop_youngest(void);
|
||||
|
||||
/**********************
|
||||
@@ -40,7 +41,7 @@ static bool drop_youngest(void);
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
#if LV_LOG_TRACE_CACHE
|
||||
#if LV_USE_LOG && LV_LOG_TRACE_CACHE
|
||||
#define LV_TRACE_CACHE(...) LV_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LV_TRACE_CACHE(...)
|
||||
@@ -52,15 +53,16 @@ static bool drop_youngest(void);
|
||||
|
||||
void _lv_cache_builtin_init(void)
|
||||
{
|
||||
lv_memzero(&_cache_manager, sizeof(lv_cache_manager_t));
|
||||
lv_memzero(&dsc, sizeof(lv_cache_builtin_dsc_t));
|
||||
_lv_ll_init(&dsc.entry_ll, sizeof(lv_cache_entry_t));
|
||||
|
||||
_cache_manager.add_cb = add_cb;
|
||||
_cache_manager.find_cb = find_cb;
|
||||
_cache_manager.invalidate_cb = invalidate_cb;
|
||||
_cache_manager.get_data_cb = get_data_cb;
|
||||
_cache_manager.release_cb = release_cb;
|
||||
_cache_manager.set_max_size_cb = set_max_size_cb;
|
||||
|
||||
_lv_ll_init(&dsc.entry_ll, sizeof(lv_cache_entry_t));
|
||||
_cache_manager.empty_cb = empty_cb;
|
||||
}
|
||||
|
||||
/**********************
|
||||
@@ -125,7 +127,7 @@ static void invalidate_cb(lv_cache_entry_t * entry)
|
||||
if(entry == NULL) return;
|
||||
|
||||
dsc.cur_size -= entry->data_size;
|
||||
LV_TRACE_CACHE("Drop cache: %lu bytes", (unsigned long)entry->data_size);
|
||||
LV_TRACE_CACHE("Drop cache: %u bytes", (uint32_t)entry->data_size);
|
||||
|
||||
if(entry->free_src) lv_free((void *)entry->src);
|
||||
if(entry->free_data) lv_draw_buf_free((void *)entry->data);
|
||||
@@ -174,6 +176,17 @@ static void set_max_size_cb(size_t new_size)
|
||||
}
|
||||
}
|
||||
|
||||
static void empty_cb(void)
|
||||
{
|
||||
lv_cache_entry_t * entry_to_drop = NULL;
|
||||
lv_cache_entry_t * entry = _lv_ll_get_head(&dsc.entry_ll);
|
||||
while(entry) {
|
||||
entry_to_drop = entry;
|
||||
entry = _lv_ll_get_next(&dsc.entry_ll, entry);
|
||||
invalidate_cb(entry_to_drop);
|
||||
}
|
||||
}
|
||||
|
||||
static bool drop_youngest(void)
|
||||
{
|
||||
int32_t life_min = INT32_MAX;
|
||||
|
||||
Reference in New Issue
Block a user