2019-06-20 18:43:03 +02:00
|
|
|
/**
|
|
|
|
* @file lv_img_cache.c
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* INCLUDES
|
|
|
|
*********************/
|
|
|
|
#include "lv_img_cache.h"
|
|
|
|
#include "../lv_hal/lv_hal_tick.h"
|
2019-06-20 23:14:17 +02:00
|
|
|
#include "../lv_misc/lv_gc.h"
|
2019-06-20 18:43:03 +02:00
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
#if defined(LV_GC_INCLUDE)
|
|
|
|
#include LV_GC_INCLUDE
|
|
|
|
#endif /* LV_ENABLE_GC */
|
2019-06-20 18:43:03 +02:00
|
|
|
/*********************
|
|
|
|
* DEFINES
|
|
|
|
*********************/
|
2019-06-20 23:14:17 +02:00
|
|
|
/*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
|
2019-06-20 18:43:03 +02:00
|
|
|
|
|
|
|
/**********************
|
|
|
|
* TYPEDEFS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC PROTOTYPES
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC VARIABLES
|
|
|
|
**********************/
|
|
|
|
static uint16_t slot_num;
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* MACROS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* GLOBAL FUNCTIONS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open an image using the image decoder interface and cache it.
|
|
|
|
* The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
|
|
|
|
* The image is closed if a new image is opened and the new image takes its place in the cache.
|
|
|
|
* @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
|
|
|
|
* @param style style of the image
|
|
|
|
* @return pointer to the cache entry or NULL if can open the image
|
|
|
|
*/
|
2019-06-20 23:14:17 +02:00
|
|
|
lv_img_cache_entry_t * lv_img_cache_open(const void * src, const lv_style_t * style)
|
2019-06-20 18:43:03 +02:00
|
|
|
{
|
|
|
|
if(slot_num == 0) {
|
|
|
|
LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
|
|
|
|
|
|
|
|
/*Decrement all lifes. Make the entries older*/
|
2019-06-20 18:43:03 +02:00
|
|
|
uint16_t i;
|
|
|
|
for(i = 0; i < slot_num; i++) {
|
2019-06-20 23:14:17 +02:00
|
|
|
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;
|
2019-06-20 18:43:03 +02:00
|
|
|
LV_LOG_TRACE("image draw: image found in the cache");
|
|
|
|
break;
|
|
|
|
}
|
2019-06-20 23:14:17 +02:00
|
|
|
}
|
2019-06-20 18:43:03 +02:00
|
|
|
|
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
/*The image is not cached then cache it now*/
|
|
|
|
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];
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*Close the slot to reuse if it was opened (has a valid source)*/
|
2019-06-20 23:14:17 +02:00
|
|
|
if(cached_src->dsc.src) {
|
|
|
|
lv_img_decoder_close(&cached_src->dsc);
|
2019-06-20 18:43:03 +02:00
|
|
|
LV_LOG_INFO("image draw: cache miss, close and reuse a slot");
|
|
|
|
} else {
|
|
|
|
LV_LOG_INFO("image draw: cache miss, cached in empty slot");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*Open the image and measure the time to open*/
|
|
|
|
uint32_t t_start;
|
|
|
|
t_start = lv_tick_get();
|
2019-06-24 05:41:49 +02:00
|
|
|
lv_res_t open_res = lv_img_decoder_open(&cached_src->dsc, src, style);
|
|
|
|
if(open_res ==LV_RES_INV) {
|
2019-06-20 18:43:03 +02:00
|
|
|
LV_LOG_WARN("Image draw cannot open the image resource");
|
2019-06-20 23:14:17 +02:00
|
|
|
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 */
|
2019-06-20 18:43:03 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
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;
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
return cached_src;
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the number of images to be cached.
|
|
|
|
* More cached images mean more opened image at same time which might mean more memory usage.
|
|
|
|
* E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
|
|
|
|
* @param new_slot_num number of image to cache
|
|
|
|
*/
|
|
|
|
void lv_img_cache_set_size(uint16_t new_slot_num)
|
|
|
|
{
|
2019-06-20 23:14:17 +02:00
|
|
|
if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
|
2019-06-20 18:43:03 +02:00
|
|
|
/*Clean the cache before free it*/
|
|
|
|
lv_img_cache_invalidate_src(NULL);
|
2019-06-20 23:14:17 +02:00
|
|
|
lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*Reallocate the cache*/
|
2019-06-20 23:14:17 +02:00
|
|
|
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) {
|
2019-06-20 18:43:03 +02:00
|
|
|
slot_num = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
slot_num = new_slot_num;
|
|
|
|
|
|
|
|
/*Clean the cache*/
|
|
|
|
uint16_t i;
|
|
|
|
for(i = 0; i < slot_num; i++) {
|
2019-06-20 23:14:17 +02:00
|
|
|
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));
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidate an image source in the cache.
|
|
|
|
* Useful if the image source is updated therefore it needs to be cached again.
|
|
|
|
* @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
|
|
|
|
*/
|
|
|
|
void lv_img_cache_invalidate_src(const void * src)
|
|
|
|
{
|
2019-06-20 23:14:17 +02:00
|
|
|
|
|
|
|
lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
|
|
|
|
|
2019-06-20 18:43:03 +02:00
|
|
|
uint16_t i;
|
|
|
|
for(i = 0; i < slot_num; i++) {
|
2019-06-20 23:14:17 +02:00
|
|
|
if(cache[i].dsc.src == src || src == NULL) {
|
|
|
|
if(cache[i].dsc.src != NULL) {
|
|
|
|
lv_img_decoder_close(&cache[i].dsc);
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
|
2019-06-20 23:14:17 +02:00
|
|
|
memset(&cache[i].dsc, 0, sizeof(lv_img_decoder_dsc_t));
|
|
|
|
memset(&cache[i], 0, sizeof(lv_img_cache_entry_t));
|
2019-06-20 18:43:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC FUNCTIONS
|
|
|
|
**********************/
|