mirror of
https://github.com/lvgl/lvgl.git
synced 2025-01-14 06:42:58 +08:00
update lv_img_cache
This commit is contained in:
parent
a9c818a6ef
commit
ca1f21ad04
@ -165,17 +165,13 @@ typedef void * lv_fs_drv_user_data_t;
|
||||
#define LV_IMG_CF_ALPHA 1
|
||||
|
||||
/* Default image cache size. Image caching keeps the images opened.
|
||||
* If only built-in images are used there is no real advantage of caching.
|
||||
* If only the built-in image formats are used there is no real advantage of caching.
|
||||
* (I.e. no new image decoder is added)
|
||||
* With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
|
||||
* However the opened images might consume additional RAM.
|
||||
* LV_IMG_CACHE_DEF_SIZE must be >= 1 */
|
||||
#define LV_IMG_CACHE_DEF_SIZE 1
|
||||
|
||||
/* If an image wasn't used for this time consider it unused.
|
||||
* It's more provable that "unused" images will be replaced by other in the cache
|
||||
* The unit is [ms]*/
|
||||
#define LV_IMG_CACHE_DEF_LIFE_TIME 10000
|
||||
|
||||
/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_img_decoder_user_data_t;
|
||||
|
||||
|
@ -174,6 +174,9 @@
|
||||
#define LV_USE_ANIMATION 1
|
||||
#endif
|
||||
#if LV_USE_ANIMATION
|
||||
|
||||
/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/
|
||||
|
||||
#endif
|
||||
|
||||
/* 1: Enable shadow drawing*/
|
||||
@ -201,6 +204,15 @@
|
||||
/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/
|
||||
#endif
|
||||
|
||||
/*1: Add a `user_data` to drivers and objects*/
|
||||
#ifndef LV_USE_USER_DATA
|
||||
#define LV_USE_USER_DATA 1
|
||||
#endif
|
||||
|
||||
/*========================
|
||||
* Image decoder and cache
|
||||
*========================*/
|
||||
|
||||
/* 1: Enable indexed (palette) images */
|
||||
#ifndef LV_IMG_CF_INDEXED
|
||||
#define LV_IMG_CF_INDEXED 1
|
||||
@ -212,7 +224,8 @@
|
||||
#endif
|
||||
|
||||
/* Default image cache size. Image caching keeps the images opened.
|
||||
* If only built-in images are used there is no real advantage of caching.
|
||||
* If only the built-in image formats are used there is no real advantage of caching.
|
||||
* (I.e. no new image decoder is added)
|
||||
* With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
|
||||
* However the opened images might consume additional RAM.
|
||||
* LV_IMG_CACHE_DEF_SIZE must be >= 1 */
|
||||
@ -220,20 +233,8 @@
|
||||
#define LV_IMG_CACHE_DEF_SIZE 1
|
||||
#endif
|
||||
|
||||
/* If an image wasn't used for this time consider it unused.
|
||||
* It's more provable that "unused" images will be replaced by other in the cache
|
||||
* The unit is [ms]*/
|
||||
#ifndef LV_IMG_CACHE_DEF_LIFE_TIME
|
||||
#define LV_IMG_CACHE_DEF_LIFE_TIME 10000
|
||||
#endif
|
||||
|
||||
/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/
|
||||
|
||||
/*1: Add a `user_data` to drivers and objects*/
|
||||
#ifndef LV_USE_USER_DATA
|
||||
#define LV_USE_USER_DATA 1
|
||||
#endif
|
||||
|
||||
/*=====================
|
||||
* Compiler settings
|
||||
*====================*/
|
||||
|
@ -447,7 +447,7 @@ static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mas
|
||||
opa_scale == LV_OPA_COVER ? style->image.opa : (uint16_t)((uint16_t)style->image.opa * opa_scale) >> 8;
|
||||
|
||||
|
||||
lv_img_cache_t * cdsc = lv_img_cache_open(src, style);
|
||||
lv_img_cache_entry_t * cdsc = lv_img_cache_open(src, style);
|
||||
|
||||
if(cdsc == NULL) return LV_RES_INV;
|
||||
|
||||
|
@ -8,10 +8,27 @@
|
||||
*********************/
|
||||
#include "lv_img_cache.h"
|
||||
#include "../lv_hal/lv_hal_tick.h"
|
||||
#include "../lv_misc/lv_gc.h"
|
||||
|
||||
#if defined(LV_GC_INCLUDE)
|
||||
#include LV_GC_INCLUDE
|
||||
#endif /* LV_ENABLE_GC */
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
/*Decrement life with this value in every open*/
|
||||
#define LV_IMG_CACHE_AGING 1
|
||||
|
||||
/*Boost life by this factor (multiply time_to_open with this value)*/
|
||||
#define LV_IMG_CACHE_LIFE_GAIN 1
|
||||
|
||||
/*Don't let life to be greater than this limit because it would require a lot of time to
|
||||
* "die" from very high values */
|
||||
#define LV_IMG_CACHE_LIFE_LIMIT 1000
|
||||
|
||||
#if LV_IMG_CACHE_DEF_SIZE < 1
|
||||
#error "LV_IMG_CACHE_DEF_SIZE must be >= 1. See lv_conf.h"
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
@ -24,8 +41,6 @@
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
static lv_img_cache_t * img_cache;
|
||||
static uint32_t life_time = LV_IMG_CACHE_DEF_LIFE_TIME;
|
||||
static uint16_t slot_num;
|
||||
|
||||
/**********************
|
||||
@ -44,54 +59,52 @@ static uint16_t slot_num;
|
||||
* @param style style of the image
|
||||
* @return pointer to the cache entry or NULL if can open the image
|
||||
*/
|
||||
lv_img_cache_t * lv_img_cache_open(const void * src, const lv_style_t * style)
|
||||
lv_img_cache_entry_t * lv_img_cache_open(const void * src, const lv_style_t * style)
|
||||
{
|
||||
if(slot_num == 0) {
|
||||
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*Check if the image is already cached*/
|
||||
lv_img_cache_t * cached_dsc = NULL;
|
||||
lv_img_cache_t * cache_dsc_reuse = NULL;
|
||||
lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
|
||||
|
||||
/*Decrement all lifes. Make the entries older*/
|
||||
uint16_t i;
|
||||
for(i = 0; i < slot_num; i++) {
|
||||
if(img_cache[i].dsc.src == src) {
|
||||
cached_dsc = &img_cache[i];
|
||||
LV_LOG_TRACE("image draw: image found in the cache");
|
||||
break;
|
||||
}
|
||||
/*Meanwhile check for a reusable slot in the cache. It will be required if `src` is not cached*/
|
||||
else {
|
||||
|
||||
/*If there is no idea for `cache_dsc_reuse` then let's use this*/
|
||||
if(cache_dsc_reuse == NULL) {
|
||||
cache_dsc_reuse = &img_cache[i];
|
||||
}
|
||||
|
||||
/*There won't be better option then an empty slot so keep it.*/
|
||||
if(cache_dsc_reuse->dsc.src == NULL) continue;
|
||||
|
||||
/*If its an empty slot then its the best choice to use*/
|
||||
if(img_cache[i].dsc.src == NULL) {
|
||||
cache_dsc_reuse = &img_cache[i];
|
||||
}
|
||||
/*If this image wasn't used for while then reuse it*/
|
||||
else if(lv_tick_elaps(img_cache[i].timestamp) > life_time) {
|
||||
cache_dsc_reuse = &img_cache[i];
|
||||
}
|
||||
/* Drop the image which is the fastest to reopen */
|
||||
else if(img_cache[i].open_duration < cache_dsc_reuse->open_duration) {
|
||||
cache_dsc_reuse = &img_cache[i];
|
||||
}
|
||||
if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
|
||||
cache[i].life -= LV_IMG_CACHE_AGING;
|
||||
}
|
||||
}
|
||||
|
||||
/*Is the image cached?*/
|
||||
lv_img_cache_entry_t * cached_src = NULL;
|
||||
for(i = 0; i < slot_num; i++) {
|
||||
if(cache[i].dsc.src == src) {
|
||||
/* If opened increment its life.
|
||||
* Image difficult to open should live longer to keep avoid frequent their recaching.
|
||||
* Therefore increase `life` with `time_to_open`*/
|
||||
cached_src = &cache[i];
|
||||
cached_src->life += cached_src->time_to_open * LV_IMG_CACHE_LIFE_GAIN ;
|
||||
if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
|
||||
LV_LOG_TRACE("image draw: image found in the cache");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*The image is not cached then cache it now*/
|
||||
if(cached_dsc == NULL) {
|
||||
if(cached_src == NULL) {
|
||||
/*Find an entry to reuse. Select the entry with the least life*/
|
||||
cached_src = &cache[0];
|
||||
for(i = 1; i < slot_num; i++) {
|
||||
if(cache[i].life < cached_src->life) {
|
||||
cached_src = &cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*Close the slot to reuse if it was opened (has a valid source)*/
|
||||
if(cache_dsc_reuse->dsc.src) {
|
||||
lv_img_decoder_close(&cache_dsc_reuse->dsc);
|
||||
if(cached_src->dsc.src) {
|
||||
lv_img_decoder_close(&cached_src->dsc);
|
||||
LV_LOG_INFO("image draw: cache miss, close and reuse a slot");
|
||||
} else {
|
||||
LV_LOG_INFO("image draw: cache miss, cached in empty slot");
|
||||
@ -100,23 +113,23 @@ lv_img_cache_t * lv_img_cache_open(const void * src, const lv_style_t * style)
|
||||
/*Open the image and measure the time to open*/
|
||||
uint32_t t_start;
|
||||
t_start = lv_tick_get();
|
||||
const uint8_t * img_data = lv_img_decoder_open(&cache_dsc_reuse->dsc, src, style);
|
||||
const uint8_t * img_data = lv_img_decoder_open(&cached_src->dsc, src, style);
|
||||
if(img_data == LV_IMG_DECODER_OPEN_FAIL) {
|
||||
LV_LOG_WARN("Image draw cannot open the image resource");
|
||||
lv_img_decoder_close(&cache_dsc_reuse->dsc);
|
||||
memset(&cache_dsc_reuse->dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&cache_dsc_reuse, 0, sizeof(lv_img_cache_t));
|
||||
lv_img_decoder_close(&cached_src->dsc);
|
||||
memset(&cached_src->dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&cached_src, 0, sizeof(lv_img_cache_entry_t));
|
||||
cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its use */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cached_dsc = cache_dsc_reuse;
|
||||
cached_dsc->img_data = img_data;
|
||||
cached_dsc->open_duration = lv_tick_elaps(t_start);
|
||||
cached_src->img_data = img_data;
|
||||
cached_src->life = 0;
|
||||
cached_src->time_to_open = lv_tick_elaps(t_start);
|
||||
if(cached_src->time_to_open == 0) cached_src->time_to_open = 1;
|
||||
}
|
||||
|
||||
cached_dsc->timestamp = lv_tick_get();
|
||||
|
||||
return cached_dsc;
|
||||
return cached_src;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,16 +140,16 @@ lv_img_cache_t * lv_img_cache_open(const void * src, const lv_style_t * style)
|
||||
*/
|
||||
void lv_img_cache_set_size(uint16_t new_slot_num)
|
||||
{
|
||||
if(img_cache != NULL) {
|
||||
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
|
||||
/*Clean the cache before free it*/
|
||||
lv_img_cache_invalidate_src(NULL);
|
||||
lv_mem_free(img_cache);
|
||||
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
|
||||
}
|
||||
|
||||
/*Reallocate the cache*/
|
||||
img_cache = lv_mem_alloc(sizeof(lv_img_cache_t) * new_slot_num);
|
||||
lv_mem_assert(img_cache);
|
||||
if(img_cache == NULL) {
|
||||
LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(lv_img_cache_entry_t) * new_slot_num);
|
||||
lv_mem_assert(LV_GC_ROOT(_lv_img_cache_array));
|
||||
if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
|
||||
slot_num = 0;
|
||||
return;
|
||||
}
|
||||
@ -145,22 +158,11 @@ void lv_img_cache_set_size(uint16_t new_slot_num)
|
||||
/*Clean the cache*/
|
||||
uint16_t i;
|
||||
for(i = 0; i < slot_num; i++) {
|
||||
memset(&img_cache[i].dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&img_cache[i], 0, sizeof(lv_img_cache_t));
|
||||
memset(&LV_GC_ROOT(_lv_img_cache_array)[i].dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&LV_GC_ROOT(_lv_img_cache_array)[i], 0, sizeof(lv_img_cache_entry_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a life time for the cache entries.
|
||||
* After this time a cached image is considered unused and it's more probable the it will be replaced by a new image.
|
||||
* @param new_life_time the new life time in milliseconds
|
||||
*/
|
||||
void lv_img_cache_set_life_time(uint32_t new_life_time)
|
||||
{
|
||||
life_time = new_life_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate an image source in the cache.
|
||||
* Useful if the image source is updated therefore it needs to be cached again.
|
||||
@ -168,15 +170,18 @@ void lv_img_cache_set_life_time(uint32_t new_life_time)
|
||||
*/
|
||||
void lv_img_cache_invalidate_src(const void * src)
|
||||
{
|
||||
|
||||
lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
|
||||
|
||||
uint16_t i;
|
||||
for(i = 0; i < slot_num; i++) {
|
||||
if(img_cache[i].dsc.src == src || src == NULL) {
|
||||
if(img_cache[i].dsc.src != NULL) {
|
||||
lv_img_decoder_close(&img_cache[i].dsc);
|
||||
if(cache[i].dsc.src == src || src == NULL) {
|
||||
if(cache[i].dsc.src != NULL) {
|
||||
lv_img_decoder_close(&cache[i].dsc);
|
||||
}
|
||||
|
||||
memset(&img_cache[i].dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&img_cache[i], 0, sizeof(lv_img_cache_t));
|
||||
memset(&cache[i].dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
||||
memset(&cache[i], 0, sizeof(lv_img_cache_entry_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CAHCE_H
|
||||
#define LV_CACHE_H
|
||||
#ifndef LV_IMG_CACHE_H
|
||||
#define LV_IMG_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -27,13 +27,14 @@ typedef struct
|
||||
lv_img_decoder_dsc_t dsc;
|
||||
const uint8_t * img_data;
|
||||
|
||||
/* How much time did it take to open the image?
|
||||
* When out of cache close the images which are faster to reopen*/
|
||||
uint32_t open_duration;
|
||||
/* How much time did it take to open the image.*/
|
||||
uint32_t time_to_open;
|
||||
|
||||
/*Time stamp when the image was last used*/
|
||||
uint32_t timestamp;
|
||||
}lv_img_cache_t;
|
||||
/* Count the cache entries's life. Add `time_tio_open` to `life` when the entry is used.
|
||||
* Decrement all lifes by one every in every `lv_img_cache_open`.
|
||||
* If life == 0 the entry can be reused,*/
|
||||
int32_t life;
|
||||
}lv_img_cache_entry_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
@ -47,7 +48,7 @@ typedef struct
|
||||
* @param style style of the image
|
||||
* @return pointer to the cache entry or NULL if can open the image
|
||||
*/
|
||||
lv_img_cache_t * lv_img_cache_open(const void * src, const lv_style_t * style);
|
||||
lv_img_cache_entry_t * lv_img_cache_open(const void * src, const lv_style_t * style);
|
||||
|
||||
/**
|
||||
* Set the number of images to be cached.
|
||||
@ -57,13 +58,6 @@ lv_img_cache_t * lv_img_cache_open(const void * src, const lv_style_t * style);
|
||||
*/
|
||||
void lv_img_cache_set_size(uint16_t new_slot_num);
|
||||
|
||||
/**
|
||||
* Set a life time for the cache entries.
|
||||
* After this time a cached image is considered unused and it's more probable the it will be replaced by a new image.
|
||||
* @param new_life_time the new life time in milliseconds
|
||||
*/
|
||||
void lv_img_cache_set_life_time(uint32_t new_life_time);
|
||||
|
||||
/**
|
||||
* Invalidate an image source in the cache.
|
||||
* Useful if the image source is updated therefore it needs to be cached again.
|
||||
@ -79,4 +73,4 @@ void lv_img_cache_invalidate_src(const void * src);
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_H*/
|
||||
#endif /*LV_IMG_CACHE_H*/
|
||||
|
@ -331,6 +331,7 @@ static const uint8_t * lv_img_decoder_built_in_open(lv_img_decoder_t * decoder,
|
||||
* So simply give its pointer*/
|
||||
return ((lv_img_dsc_t *)dsc->src)->data;
|
||||
} else {
|
||||
|
||||
/*If it's a file it need to be read line by line later*/
|
||||
return NULL;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ extern "C" {
|
||||
#include <stdbool.h>
|
||||
#include "lv_mem.h"
|
||||
#include "lv_ll.h"
|
||||
#include "../lv_draw/lv_img_cache.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@ -38,6 +39,7 @@ extern "C" {
|
||||
prefix lv_ll_t _lv_anim_ll; \
|
||||
prefix lv_ll_t _lv_group_ll; \
|
||||
prefix lv_ll_t _lv_img_defoder_ll; \
|
||||
prefix lv_img_cache_entry_t * _lv_img_cache_array; \
|
||||
prefix void * _lv_task_act;
|
||||
|
||||
#define LV_NO_PREFIX
|
||||
|
Loading…
x
Reference in New Issue
Block a user