feat(cache): rework cache
- based in cache size instead of entry count - not only for images - needs to be called manually in decoders (and not the cache manages the decoders)
This commit is contained in:
@@ -247,13 +247,11 @@
|
||||
#define LV_GLOBAL_CUSTOM_INCLUDE <stdint.h>
|
||||
#endif
|
||||
|
||||
/*Default image cache size. Image caching keeps some images opened.
|
||||
*If only the built-in image formats are used there is no real advantage of caching.
|
||||
*With other image decoders (e.g. PNG or JPG) caching save the continuous open/decode of images.
|
||||
*However the opened images consume additional RAM.
|
||||
*0: to disable caching*/
|
||||
#define LV_IMAGE_CACHE_DEF_SIZE 0
|
||||
|
||||
/*Default cache size in bytes.
|
||||
*Used by image decoders such as `lv_png` to keep the decoded image in the memory.
|
||||
*Data larger than the size of the cache also can be allocated but
|
||||
*will be dropped immediately after usage.*/
|
||||
#define LV_CACHE_DEF_SIZE 0
|
||||
|
||||
/*Number of stops allowed per gradient. Increase this to allow more stops.
|
||||
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
|
||||
|
||||
@@ -17,7 +17,8 @@ extern "C" {
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../draw/lv_image_cache.h"
|
||||
#include "../misc/lv_cache.h"
|
||||
#include "../misc/lv_cache_builtin.h"
|
||||
#include "../draw/lv_draw.h"
|
||||
#if LV_USE_DRAW_SW
|
||||
#include "../draw/sw/lv_draw_sw.h"
|
||||
@@ -101,13 +102,9 @@ typedef struct _lv_global_t {
|
||||
lv_draw_buf_handlers_t draw_buf_handlers;
|
||||
|
||||
lv_ll_t img_decoder_ll;
|
||||
lv_image_cache_manager_t img_cache_mgr;
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
uint16_t img_cache_entry_cnt;
|
||||
_lv_image_cache_entry_t * img_cache_array;
|
||||
#else
|
||||
_lv_image_cache_entry_t img_cache_single;
|
||||
#endif
|
||||
lv_cache_manager_t cache_manager;
|
||||
lv_cache_builtin_dsc_t cache_builtin_dsc;
|
||||
size_t cache_builtin_max_size;
|
||||
|
||||
lv_draw_global_info_t draw_info;
|
||||
#if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0
|
||||
|
||||
@@ -18,8 +18,8 @@ extern "C" {
|
||||
#include "../misc/lv_style.h"
|
||||
#include "../misc/lv_txt.h"
|
||||
#include "../misc/lv_profiler.h"
|
||||
#include "../misc/lv_cache.h"
|
||||
#include "lv_image_decoder.h"
|
||||
#include "lv_image_cache.h"
|
||||
#include "../osal/lv_os.h"
|
||||
#include "lv_draw_buf.h"
|
||||
|
||||
@@ -170,10 +170,10 @@ typedef struct {
|
||||
uint32_t used_memory_for_layers_kb;
|
||||
#if LV_USE_OS
|
||||
lv_thread_sync_t sync;
|
||||
lv_mutex_t circle_cache_mutex;
|
||||
#else
|
||||
int dispatch_req;
|
||||
#endif
|
||||
lv_mutex_t circle_cache_mutex;
|
||||
bool task_running;
|
||||
} lv_draw_global_info_t;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_draw_image.h"
|
||||
#include "lv_image_cache.h"
|
||||
#include "../disp/lv_disp.h"
|
||||
#include "../misc/lv_log.h"
|
||||
#include "../misc/lv_math.h"
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* @file lv_image_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_image_cache.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define img_cache_manager LV_GLOBAL_DEFAULT()->img_cache_mgr
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_image_cache_manager_init(lv_image_cache_manager_t * manager)
|
||||
{
|
||||
LV_ASSERT_NULL(manager);
|
||||
lv_memzero(manager, sizeof(lv_image_cache_manager_t));
|
||||
}
|
||||
|
||||
void lv_image_cache_manager_apply(const lv_image_cache_manager_t * manager)
|
||||
{
|
||||
LV_ASSERT_NULL(manager);
|
||||
lv_memcpy(&img_cache_manager, manager, sizeof(lv_image_cache_manager_t));
|
||||
}
|
||||
|
||||
_lv_image_cache_entry_t * _lv_image_cache_open(const void * src, lv_color_t color, int32_t frame_id)
|
||||
{
|
||||
LV_ASSERT_NULL(img_cache_manager.open_cb);
|
||||
return img_cache_manager.open_cb(src, color, frame_id);
|
||||
}
|
||||
|
||||
void lv_image_cache_set_size(uint16_t new_entry_cnt)
|
||||
{
|
||||
LV_ASSERT_NULL(img_cache_manager.set_size_cb);
|
||||
img_cache_manager.set_size_cb(new_entry_cnt);
|
||||
}
|
||||
|
||||
void lv_image_cache_invalidate_src(const void * src)
|
||||
{
|
||||
LV_ASSERT_NULL(img_cache_manager.invalidate_src_cb);
|
||||
img_cache_manager.invalidate_src_cb(src);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* @file lv_image_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_IMAGE_CACHE_H
|
||||
#define LV_IMAGE_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_image_decoder.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* When loading images from the network it can take a long time to download and decode the image.
|
||||
*
|
||||
* To avoid repeating this heavy load images can be cached.
|
||||
*/
|
||||
typedef struct {
|
||||
lv_image_decoder_dsc_t dec_dsc; /**< Image information*/
|
||||
void * user_data; /**< Image cache entry user data*/
|
||||
} _lv_image_cache_entry_t;
|
||||
|
||||
typedef struct {
|
||||
_lv_image_cache_entry_t * (*open_cb)(const void * src, lv_color_t color, int32_t frame_id);
|
||||
void (*set_size_cb)(uint16_t new_entry_cnt);
|
||||
void (*invalidate_src_cb)(const void * src);
|
||||
} lv_image_cache_manager_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the img cache manager
|
||||
* @param manager Pointer to the img cache manager
|
||||
*/
|
||||
void lv_image_cache_manager_init(lv_image_cache_manager_t * manager);
|
||||
|
||||
/**
|
||||
* Apply the img cache manager
|
||||
* @param manager Pointer to the img cache manager
|
||||
*/
|
||||
void lv_image_cache_manager_apply(const lv_image_cache_manager_t * manager);
|
||||
|
||||
/**
|
||||
* 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_image_dsc_t` variable
|
||||
* @param color The color of the image with `LV_IMAGE_CF_ALPHA_...`
|
||||
* @param frame_id the index of the frame. Used only with animated images, set 0 for normal images
|
||||
* @return pointer to the cache entry or NULL if can open the image
|
||||
*/
|
||||
_lv_image_cache_entry_t * _lv_image_cache_open(const void * src, lv_color_t color, int32_t frame_id);
|
||||
|
||||
/**
|
||||
* 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_entry_cnt number of image to cache
|
||||
*/
|
||||
void lv_image_cache_set_size(uint16_t new_entry_cnt);
|
||||
|
||||
/**
|
||||
* 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_image_dsc_t` variable.
|
||||
*/
|
||||
void lv_image_cache_invalidate_src(const void * src);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_IMAGE_CACHE_H*/
|
||||
@@ -1,249 +0,0 @@
|
||||
/**
|
||||
* @file lv_image_cache_builtin.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_image_cache.h"
|
||||
#include "lv_image_cache_builtin.h"
|
||||
#include "lv_image_decoder.h"
|
||||
#include "lv_draw_image.h"
|
||||
#include "../tick/lv_tick.h"
|
||||
#include "../misc/lv_assert.h"
|
||||
#include "../core/lv_global.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
#define entry_cnt LV_GLOBAL_DEFAULT()->img_cache_entry_cnt
|
||||
#define _image_cache_array LV_GLOBAL_DEFAULT()->img_cache_array
|
||||
#else
|
||||
#define _image_cache_single LV_GLOBAL_DEFAULT()->img_cache_single
|
||||
#endif
|
||||
|
||||
|
||||
/** Count the cache entries's life. Add `time_to_open` to `life` when the entry is used.
|
||||
* Decrement all lifes by one every in every ::lv_image_cache_open.
|
||||
* If life == 0 the entry can be reused*/
|
||||
#define CACHE_GET_LIFE(entry) ((lv_intptr_t)((entry)->user_data))
|
||||
#define CACHE_SET_LIFE(entry, v) ((entry)->user_data = (void*)((lv_intptr_t)(v)))
|
||||
|
||||
/*Decrement life with this value on every open*/
|
||||
#define LV_IMAGE_CACHE_AGING 1
|
||||
|
||||
/*Boost life by this factor (multiply time_to_open with this value)*/
|
||||
#define LV_IMAGE_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_IMAGE_CACHE_LIFE_LIMIT 1000
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static _lv_image_cache_entry_t * _lv_image_cache_open_builtin(const void * src, lv_color_t color, int32_t frame_id);
|
||||
static void lv_image_cache_set_size_builtin(uint16_t new_entry_cnt);
|
||||
static void lv_image_cache_invalidate_src_builtin(const void * src);
|
||||
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
static bool lv_image_cache_match(const void * src1, const void * src2);
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void _lv_image_cache_builtin_init(void)
|
||||
{
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
lv_image_cache_set_size_builtin(LV_IMAGE_CACHE_DEF_SIZE);
|
||||
#endif
|
||||
|
||||
lv_image_cache_manager_t manager;
|
||||
lv_image_cache_manager_init(&manager);
|
||||
manager.open_cb = _lv_image_cache_open_builtin;
|
||||
manager.set_size_cb = lv_image_cache_set_size_builtin;
|
||||
manager.invalidate_src_cb = lv_image_cache_invalidate_src_builtin;
|
||||
lv_image_cache_manager_apply(&manager);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC 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_image_dsc_t` variable
|
||||
* @param color color The color of the image with `LV_IMAGE_CF_ALPHA_...`
|
||||
* @return pointer to the cache entry or NULL if can open the image
|
||||
*/
|
||||
static _lv_image_cache_entry_t * _lv_image_cache_open_builtin(const void * src, lv_color_t color, int32_t frame_id)
|
||||
{
|
||||
/*Is the image cached?*/
|
||||
_lv_image_cache_entry_t * cached_src = NULL;
|
||||
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
if(entry_cnt == 0) {
|
||||
LV_LOG_WARN("the cache size is 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_lv_image_cache_entry_t * cache = _image_cache_array;
|
||||
|
||||
/*Decrement all lifes. Make the entries older*/
|
||||
uint16_t i;
|
||||
for(i = 0; i < entry_cnt; i++) {
|
||||
int32_t life = CACHE_GET_LIFE(&cache[i]);
|
||||
if(life > INT32_MIN + LV_IMAGE_CACHE_AGING) {
|
||||
CACHE_SET_LIFE(&cache[i], life - LV_IMAGE_CACHE_AGING);
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < entry_cnt; i++) {
|
||||
if(lv_color_eq(color, cache[i].dec_dsc.color) &&
|
||||
frame_id == cache[i].dec_dsc.frame_id &&
|
||||
lv_image_cache_match(src, cache[i].dec_dsc.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];
|
||||
int32_t life = CACHE_GET_LIFE(cached_src);
|
||||
CACHE_SET_LIFE(cached_src, life + cached_src->dec_dsc.time_to_open * LV_IMAGE_CACHE_LIFE_GAIN);
|
||||
if(CACHE_GET_LIFE(cached_src) > LV_IMAGE_CACHE_LIFE_LIMIT) CACHE_SET_LIFE(cached_src, LV_IMAGE_CACHE_LIFE_LIMIT);
|
||||
LV_LOG_TRACE("image source found in the cache");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*The image is not cached then cache it now*/
|
||||
if(cached_src) return cached_src;
|
||||
|
||||
/*Find an entry to reuse. Select the entry with the least life*/
|
||||
cached_src = &cache[0];
|
||||
for(i = 1; i < entry_cnt; i++) {
|
||||
if(CACHE_GET_LIFE(&cache[i]) < CACHE_GET_LIFE(cached_src)) {
|
||||
cached_src = &cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*Close the decoder to reuse if it was opened (has a valid source)*/
|
||||
if(cached_src->dec_dsc.src) {
|
||||
lv_image_decoder_close(&cached_src->dec_dsc);
|
||||
LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
|
||||
}
|
||||
else {
|
||||
LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
|
||||
}
|
||||
#else
|
||||
cached_src = &(_image_cache_single);
|
||||
#endif
|
||||
/*Open the image and measure the time to open*/
|
||||
uint32_t t_start = lv_tick_get();
|
||||
lv_res_t open_res = lv_image_decoder_open(&cached_src->dec_dsc, src, color, frame_id);
|
||||
if(open_res == LV_RES_INV) {
|
||||
LV_LOG_WARN("Image draw cannot open the image resource");
|
||||
lv_memzero(cached_src, sizeof(_lv_image_cache_entry_t));
|
||||
CACHE_SET_LIFE(cached_src, INT32_MIN); /*Make the empty entry very "weak" to force its us*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CACHE_SET_LIFE(cached_src, 0);
|
||||
|
||||
/*If `time_to_open` was not set in the open function set it here*/
|
||||
if(cached_src->dec_dsc.time_to_open == 0) {
|
||||
cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
|
||||
}
|
||||
|
||||
if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
|
||||
|
||||
return cached_src;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_entry_cnt number of image to cache
|
||||
*/
|
||||
static void lv_image_cache_set_size_builtin(uint16_t new_entry_cnt)
|
||||
{
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE == 0
|
||||
LV_UNUSED(new_entry_cnt);
|
||||
LV_LOG_WARN("Can't change cache size because it's disabled by LV_IMAGE_CACHE_DEF_SIZE = 0");
|
||||
#else
|
||||
if(_image_cache_array != NULL) {
|
||||
/*Clean the cache before free it*/
|
||||
lv_image_cache_invalidate_src_builtin(NULL);
|
||||
lv_free(_image_cache_array);
|
||||
}
|
||||
|
||||
/*Reallocate the cache*/
|
||||
_image_cache_array = lv_malloc(sizeof(_lv_image_cache_entry_t) * new_entry_cnt);
|
||||
LV_ASSERT_MALLOC(_image_cache_array);
|
||||
if(_image_cache_array == NULL) {
|
||||
entry_cnt = 0;
|
||||
return;
|
||||
}
|
||||
entry_cnt = new_entry_cnt;
|
||||
|
||||
/*Clean the cache*/
|
||||
lv_memzero(_image_cache_array, entry_cnt * sizeof(_lv_image_cache_entry_t));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_image_dsc_t` variable.
|
||||
*/
|
||||
static void lv_image_cache_invalidate_src_builtin(const void * src)
|
||||
{
|
||||
LV_UNUSED(src);
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
_lv_image_cache_entry_t * cache = _image_cache_array;
|
||||
|
||||
uint16_t i;
|
||||
for(i = 0; i < entry_cnt; i++) {
|
||||
if(src == NULL || lv_image_cache_match(src, cache[i].dec_dsc.src)) {
|
||||
if(cache[i].dec_dsc.src != NULL) {
|
||||
lv_image_decoder_close(&cache[i].dec_dsc);
|
||||
}
|
||||
|
||||
lv_memzero(&cache[i], sizeof(_lv_image_cache_entry_t));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LV_IMAGE_CACHE_DEF_SIZE
|
||||
static bool lv_image_cache_match(const void * src1, const void * src2)
|
||||
{
|
||||
lv_image_src_t src_type = lv_image_src_get_type(src1);
|
||||
if(src_type == LV_IMAGE_SRC_VARIABLE)
|
||||
return src1 == src2;
|
||||
if(src_type != LV_IMAGE_SRC_FILE)
|
||||
return false;
|
||||
if(lv_image_src_get_type(src2) != LV_IMAGE_SRC_FILE)
|
||||
return false;
|
||||
return strcmp(src1, src2) == 0;
|
||||
}
|
||||
#endif
|
||||
@@ -9,12 +9,12 @@
|
||||
#include "lv_draw_sw.h"
|
||||
#if LV_USE_DRAW_SW
|
||||
|
||||
#include "../lv_image_cache.h"
|
||||
#include "../../disp/lv_disp.h"
|
||||
#include "../../disp/lv_disp_private.h"
|
||||
#include "../../misc/lv_log.h"
|
||||
#include "../../core/lv_refr.h"
|
||||
#include "../../stdlib/lv_mem.h"
|
||||
#include "../../misc/lv_cache.h"
|
||||
#include "../../misc/lv_math.h"
|
||||
#include "../../misc/lv_color.h"
|
||||
#include "../../stdlib/lv_string.h"
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define CIRCLE_CACHE_LIFE_MAX 1000
|
||||
#define CIRCLE_CACHE_AGING(life, r) life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
|
||||
#if LV_USE_OS
|
||||
#define circle_cache_mutex LV_GLOBAL_DEFAULT()->draw_info.circle_cache_mutex
|
||||
#endif
|
||||
#define _circle_cache LV_GLOBAL_DEFAULT()->sw_circle_cache
|
||||
#define CIRCLE_CACHE_LIFE_MAX 1000
|
||||
#define CIRCLE_CACHE_AGING(life, r) life = LV_MIN(life + (r < 16 ? 1 : (r >> 4)), 1000)
|
||||
#define circle_cache_mutex LV_GLOBAL_DEFAULT()->draw_info.circle_cache_mutex
|
||||
#define _circle_cache LV_GLOBAL_DEFAULT()->sw_circle_cache
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
@@ -81,9 +79,7 @@ LV_ATTRIBUTE_FAST_MEM static inline lv_opa_t mask_mix(lv_opa_t mask_act, lv_opa_
|
||||
|
||||
void lv_draw_sw_mask_init(void)
|
||||
{
|
||||
#if LV_USE_OS
|
||||
lv_mutex_init(&circle_cache_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -115,10 +111,7 @@ LV_ATTRIBUTE_FAST_MEM lv_draw_sw_mask_res_t lv_draw_sw_mask_apply(void * masks[]
|
||||
*/
|
||||
void lv_draw_sw_mask_free_param(void * p)
|
||||
{
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_mutex_lock(&circle_cache_mutex);
|
||||
#endif
|
||||
_lv_draw_sw_mask_common_dsc_t * pdsc = p;
|
||||
if(pdsc->type == LV_DRAW_SW_MASK_TYPE_RADIUS) {
|
||||
lv_draw_sw_mask_radius_param_t * radius_p = (lv_draw_sw_mask_radius_param_t *) p;
|
||||
@@ -133,9 +126,7 @@ void lv_draw_sw_mask_free_param(void * p)
|
||||
}
|
||||
}
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_mutex_unlock(&circle_cache_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void _lv_draw_sw_mask_cleanup(void)
|
||||
@@ -362,9 +353,7 @@ void lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param, const l
|
||||
return;
|
||||
}
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_mutex_lock(&circle_cache_mutex);
|
||||
#endif
|
||||
|
||||
uint32_t i;
|
||||
|
||||
@@ -374,9 +363,7 @@ void lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param, const l
|
||||
_circle_cache[i].used_cnt++;
|
||||
CIRCLE_CACHE_AGING(_circle_cache[i].life, radius);
|
||||
param->circle = &(_circle_cache[i]);
|
||||
#if LV_USE_OS
|
||||
lv_mutex_unlock(&circle_cache_mutex);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -406,9 +393,7 @@ void lv_draw_sw_mask_radius_init(lv_draw_sw_mask_radius_param_t * param, const l
|
||||
param->circle = entry;
|
||||
|
||||
circ_calc_aa4(param->circle, radius);
|
||||
#if LV_USE_OS
|
||||
lv_mutex_unlock(&circle_cache_mutex);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,9 @@ static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj
|
||||
LV_UNUSED(class_p);
|
||||
|
||||
lv_image_dsc_t * img = lv_canvas_get_image(obj);
|
||||
lv_image_cache_invalidate_src(img);
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(img, LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
|
||||
if(!img->data) {
|
||||
LV_LOG_INFO("canvas buffer is NULL");
|
||||
|
||||
@@ -61,7 +61,10 @@ void lv_gif_set_src(lv_obj_t * obj, const void * src)
|
||||
|
||||
/*Close previous gif if any*/
|
||||
if(gifobj->gif) {
|
||||
lv_image_cache_invalidate_src(&gifobj->imgdsc);
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(lv_img_get_src(obj), LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
|
||||
gd_close_gif(gifobj->gif);
|
||||
gifobj->gif = NULL;
|
||||
gifobj->imgdsc.data = NULL;
|
||||
@@ -122,7 +125,11 @@ static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||||
{
|
||||
LV_UNUSED(class_p);
|
||||
lv_gif_t * gifobj = (lv_gif_t *) obj;
|
||||
lv_image_cache_invalidate_src(&gifobj->imgdsc);
|
||||
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(lv_img_get_src(obj), LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
|
||||
if(gifobj->gif)
|
||||
gd_close_gif(gifobj->gif);
|
||||
lv_timer_del(gifobj->timer);
|
||||
@@ -147,7 +154,9 @@ static void next_frame_task_cb(lv_timer_t * t)
|
||||
|
||||
gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data);
|
||||
|
||||
lv_image_cache_invalidate_src(lv_image_get_src(obj));
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(lv_img_get_src(obj), LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
lv_obj_invalidate(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -5308,7 +5308,7 @@ static void decodeGeneric(unsigned char ** out, unsigned * w, unsigned * h,
|
||||
|
||||
if(!state->error) {
|
||||
outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
|
||||
*out = (unsigned char *)lodepng_malloc(outsize);
|
||||
*out = (unsigned char *)lv_draw_buf_malloc(outsize, LV_COLOR_FORMAT_ARGB8888);
|
||||
if(!*out) state->error = 83; /*alloc fail*/
|
||||
}
|
||||
if(!state->error) {
|
||||
|
||||
@@ -28,6 +28,8 @@ static lv_res_t decoder_info(struct _lv_image_decoder_t * decoder, const void *
|
||||
static lv_res_t decoder_open(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
|
||||
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
|
||||
static void convert_color_depth(uint8_t * img_p, uint32_t px_cnt);
|
||||
static const void * decode_png_data(const void * png_data, size_t png_data_size);
|
||||
static lv_res_t try_cache(lv_image_decoder_dsc_t * dsc);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
@@ -142,87 +144,129 @@ static lv_res_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_
|
||||
{
|
||||
|
||||
(void) decoder; /*Unused*/
|
||||
uint32_t error; /*For the return values of PNG decoder functions*/
|
||||
|
||||
uint8_t * img_data = NULL;
|
||||
/*Check the cache first*/
|
||||
if(try_cache(dsc) == LV_RES_OK) return LV_RES_OK;
|
||||
|
||||
/*If it's a PNG file...*/
|
||||
const uint8_t * png_data = NULL;
|
||||
size_t png_data_size = 0;
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
const char * fn = dsc->src;
|
||||
if(strcmp(lv_fs_get_ext(fn), "png") == 0) { /*Check the extension*/
|
||||
|
||||
/*Load the PNG file into buffer. It's still compressed (not decoded)*/
|
||||
unsigned char * png_data = NULL; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/
|
||||
size_t png_data_size; /*Size of `png_data` in bytes*/
|
||||
|
||||
error = lodepng_load_file(&png_data, &png_data_size, fn); /*Load the file*/
|
||||
unsigned error;
|
||||
error = lodepng_load_file((void *)&png_data, &png_data_size, fn); /*Load the file*/
|
||||
if(error) {
|
||||
if(png_data != NULL) {
|
||||
lv_free(png_data);
|
||||
lv_free((void *)png_data);
|
||||
}
|
||||
LV_LOG_WARN("error %" LV_PRIu32 ": %s\n", error, lodepng_error_text(error));
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
/*Decode the PNG image*/
|
||||
unsigned png_width; /*Will be the width of the decoded image*/
|
||||
unsigned png_height; /*Will be the width of the decoded image*/
|
||||
|
||||
/*Decode the loaded image in ARGB8888 */
|
||||
error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
|
||||
lv_free(png_data); /*Free the loaded file*/
|
||||
if(error) {
|
||||
if(img_data != NULL) {
|
||||
lv_free(img_data);
|
||||
}
|
||||
LV_LOG_WARN("error %" LV_PRIu32 ": %s\n", error, lodepng_error_text(error));
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
/*Convert the image to the system's color depth*/
|
||||
convert_color_depth(img_data, png_width * png_height);
|
||||
dsc->img_data = img_data;
|
||||
return LV_RES_OK; /*The image is fully decoded. Return with its pointer*/
|
||||
}
|
||||
}
|
||||
/*If it's a PNG file in a C array...*/
|
||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
const lv_image_dsc_t * img_dsc = dsc->src;
|
||||
unsigned png_width; /*Not used, just required by the decoder*/
|
||||
unsigned png_height; /*Not used, just required by the decoder*/
|
||||
|
||||
/*Decode the image in ARGB8888 */
|
||||
error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size);
|
||||
|
||||
if(error) {
|
||||
if(img_data != NULL) {
|
||||
lv_free(img_data);
|
||||
}
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
/*Convert the image to the system's color depth*/
|
||||
convert_color_depth(img_data, png_width * png_height);
|
||||
|
||||
dsc->img_data = img_data;
|
||||
return LV_RES_OK; /*Return with its pointer*/
|
||||
const lv_img_dsc_t * img_dsc = dsc->src;
|
||||
png_data = img_dsc->data;
|
||||
png_data_size = img_dsc->data_size;
|
||||
}
|
||||
else {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
return LV_RES_INV; /*If not returned earlier then it failed*/
|
||||
lv_cache_lock();
|
||||
lv_cache_entry_t * cache = lv_cache_add(dsc->header.w * dsc->header.h * 4);
|
||||
if(cache == NULL) {
|
||||
lv_cache_unlock();
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
uint32_t t = lv_tick_get();
|
||||
const void * decoded_img = decode_png_data(png_data, png_data_size);
|
||||
t = lv_tick_elaps(t);
|
||||
cache->weight = t;
|
||||
cache->data = decoded_img;
|
||||
cache->free_data = 1;
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
cache->src = lv_strdup(dsc->src);
|
||||
cache->src_type = LV_CACHE_SRC_TYPE_STR;
|
||||
cache->free_src = 1;
|
||||
lv_free((void *)png_data);
|
||||
}
|
||||
else {
|
||||
cache->src_type = LV_CACHE_SRC_TYPE_PTR;
|
||||
cache->src = dsc->src;
|
||||
}
|
||||
|
||||
dsc->img_data = lv_cache_get_data(cache);
|
||||
dsc->user_data = cache;
|
||||
|
||||
lv_cache_unlock();
|
||||
return LV_RES_OK; /*If not returned earlier then it failed*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the allocated resources
|
||||
*/
|
||||
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
LV_UNUSED(decoder); /*Unused*/
|
||||
if(dsc->img_data) {
|
||||
lv_free((uint8_t *)dsc->img_data);
|
||||
dsc->img_data = NULL;
|
||||
}
|
||||
LV_UNUSED(dec);
|
||||
|
||||
lv_cache_lock();
|
||||
lv_cache_release(dsc->user_data);
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
|
||||
static lv_res_t try_cache(lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
lv_cache_lock();
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
const char * fn = dsc->src;
|
||||
|
||||
lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0);
|
||||
if(cache) {
|
||||
dsc->img_data = lv_cache_get_data(cache);
|
||||
dsc->user_data = cache; /*Save the cache to release it in decoder_close*/
|
||||
lv_cache_unlock();
|
||||
return LV_RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
const lv_img_dsc_t * img_dsc = dsc->src;
|
||||
|
||||
lv_cache_entry_t * cache = lv_cache_find(img_dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0);
|
||||
if(cache) {
|
||||
dsc->img_data = lv_cache_get_data(cache);
|
||||
dsc->user_data = cache; /*Save the cache to release it in decoder_close*/
|
||||
lv_cache_unlock();
|
||||
return LV_RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
lv_cache_unlock();
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
static const void * decode_png_data(const void * png_data, size_t png_data_size)
|
||||
{
|
||||
unsigned png_width; /*Not used, just required by the decoder*/
|
||||
unsigned png_height; /*Not used, just required by the decoder*/
|
||||
uint8_t * img_data = NULL;
|
||||
|
||||
/*Decode the image in ARGB8888 */
|
||||
unsigned error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
|
||||
|
||||
if(error) {
|
||||
if(img_data != NULL) lv_free(img_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*Convert the image to the system's color depth*/
|
||||
convert_color_depth(img_data, png_width * png_height);
|
||||
|
||||
return img_data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the display is not in 32 bit format (ARGB888) then convert the image to the current color depth
|
||||
* @param img the ARGB888 image
|
||||
|
||||
@@ -224,7 +224,9 @@ static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||||
LV_UNUSED(class_p);
|
||||
|
||||
lv_image_dsc_t * img_dsc = lv_canvas_get_image(obj);
|
||||
lv_image_cache_invalidate_src(img_dsc);
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(img_dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
|
||||
if(!img_dsc->data) {
|
||||
return;
|
||||
|
||||
@@ -689,20 +689,18 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*Default image cache size. Image caching keeps some images opened.
|
||||
*If only the built-in image formats are used there is no real advantage of caching.
|
||||
*With other image decoders (e.g. PNG or JPG) caching save the continuous open/decode of images.
|
||||
*However the opened images consume additional RAM.
|
||||
*0: to disable caching*/
|
||||
#ifndef LV_IMAGE_CACHE_DEF_SIZE
|
||||
#ifdef CONFIG_LV_IMAGE_CACHE_DEF_SIZE
|
||||
#define LV_IMAGE_CACHE_DEF_SIZE CONFIG_LV_IMAGE_CACHE_DEF_SIZE
|
||||
/*Default cache size in bytes.
|
||||
*Used by image decoders such as `lv_png` to keep the decoded image in the memory.
|
||||
*Data larger than the size of the cache also can be allocated but
|
||||
*will be dropped immediately after usage.*/
|
||||
#ifndef LV_CACHE_DEF_SIZE
|
||||
#ifdef CONFIG_LV_CACHE_DEF_SIZE
|
||||
#define LV_CACHE_DEF_SIZE CONFIG_LV_CACHE_DEF_SIZE
|
||||
#else
|
||||
#define LV_IMAGE_CACHE_DEF_SIZE 0
|
||||
#define LV_CACHE_DEF_SIZE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*Number of stops allowed per gradient. Increase this to allow more stops.
|
||||
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
|
||||
#ifndef LV_GRADIENT_MAX_STOPS
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
#include "libs/png/lv_png.h"
|
||||
#include "libs/sjpg/lv_sjpg.h"
|
||||
#include "draw/lv_draw.h"
|
||||
#include "draw/lv_image_cache_builtin.h"
|
||||
#include "misc/lv_cache.h"
|
||||
#include "misc/lv_cache_builtin.h"
|
||||
#include "misc/lv_async.h"
|
||||
#include "misc/lv_fs.h"
|
||||
|
||||
@@ -147,7 +148,11 @@ void lv_init(void)
|
||||
|
||||
_lv_image_decoder_init();
|
||||
|
||||
_lv_image_cache_builtin_init();
|
||||
_lv_cache_init();
|
||||
_lv_cache_builtin_init();
|
||||
lv_cache_lock();
|
||||
lv_cache_set_max_size(LV_CACHE_DEF_SIZE);
|
||||
lv_cache_unlock();
|
||||
|
||||
/*Test if the IDE has UTF-8 encoding*/
|
||||
const char * txt = "Á";
|
||||
|
||||
115
src/misc/lv_cache.c
Normal file
115
src/misc/lv_cache.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @file lv_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_cache.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "../osal/lv_os.h"
|
||||
#include "../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define _cache_manager LV_GLOBAL_DEFAULT()->cache_manager
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void _lv_cache_init(void)
|
||||
{
|
||||
lv_memzero(&_cache_manager, sizeof(lv_cache_manager_t));
|
||||
lv_mutex_init(&_cache_manager.mutex);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_add(size_t size)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.add_cb == NULL) return NULL;
|
||||
|
||||
return _cache_manager.add_cb(size);
|
||||
}
|
||||
|
||||
lv_cache_entry_t * lv_cache_find(const void * src_ptr, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.find_cb == NULL) return NULL;
|
||||
|
||||
return _cache_manager.find_cb(src_ptr, src_type, param1, param2);
|
||||
}
|
||||
|
||||
|
||||
void lv_cache_invalidate(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.invalidate_cb == NULL) return;
|
||||
|
||||
_cache_manager.invalidate_cb(entry);
|
||||
}
|
||||
|
||||
const void * lv_cache_get_data(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.get_data_cb == NULL) return NULL;
|
||||
|
||||
const void * data = _cache_manager.get_data_cb(entry);
|
||||
return data;
|
||||
}
|
||||
|
||||
void lv_cache_release(lv_cache_entry_t * entry)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.release_cb == NULL) return;
|
||||
|
||||
_cache_manager.release_cb(entry);
|
||||
}
|
||||
|
||||
void lv_cache_set_max_size(size_t size)
|
||||
{
|
||||
LV_ASSERT(_cache_manager.locked);
|
||||
if(_cache_manager.set_max_size_cb == NULL) return;
|
||||
|
||||
_cache_manager.set_max_size_cb(size);
|
||||
_cache_manager.max_size = size;
|
||||
}
|
||||
|
||||
size_t lv_cache_get_max_size(void)
|
||||
{
|
||||
return _cache_manager.max_size;
|
||||
}
|
||||
|
||||
void lv_cache_lock(void)
|
||||
{
|
||||
lv_mutex_lock(&_cache_manager.mutex);
|
||||
_cache_manager.locked = 1;
|
||||
}
|
||||
|
||||
void lv_cache_unlock(void)
|
||||
{
|
||||
_cache_manager.locked = 0;
|
||||
lv_mutex_unlock(&_cache_manager.mutex);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
219
src/misc/lv_cache.h
Normal file
219
src/misc/lv_cache.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* @file lv_cache.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_H
|
||||
#define LV_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../osal/lv_os.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef enum {
|
||||
LV_CACHE_SRC_TYPE_PTR,
|
||||
LV_CACHE_SRC_TYPE_STR,
|
||||
_LV_CACHE_SRC_TYPE_LAST,
|
||||
} lv_cache_src_type_t;
|
||||
|
||||
typedef struct {
|
||||
/**The image source or other source related to the cache content.*/
|
||||
const void * src;
|
||||
|
||||
lv_cache_src_type_t src_type;
|
||||
|
||||
/** Some extra parameters to describe the source. E.g. the current frame of an animation*/
|
||||
uint32_t param1;
|
||||
uint32_t param2;
|
||||
|
||||
/** The data to cache*/
|
||||
const void * data;
|
||||
|
||||
/** Size of data in bytes*/
|
||||
uint32_t data_size;
|
||||
|
||||
/** On access to any cache entry, `life` of each cache entry will be incremented by their own `weight` to keep the entry alive longer*/
|
||||
uint32_t weight;
|
||||
|
||||
/** The current `life`. Entries with the smallest life will be purged from the cache if a new entry needs to be cached*/
|
||||
int32_t life;
|
||||
|
||||
/** Count how many times the cached data is being used.
|
||||
* It will be incremented in lv_cache_get_data and decremented in lv_cache_release.
|
||||
* A data will dropped from the cache only if its usage_count is zero */
|
||||
uint32_t usage_count;
|
||||
|
||||
/** Call `lv_free` on `src` when the entry is removed from the cache */
|
||||
uint32_t free_src : 1;
|
||||
|
||||
/** Call `lv_draw_buf_free` on `data` when the entry is removed from the cache */
|
||||
uint32_t free_data : 1;
|
||||
|
||||
/** The cache entry was larger then the max cache size so only a temporary entry was allocated
|
||||
* The entry will be closed and freed in `lv_cache_release` automatically*/
|
||||
uint32_t temporary : 1;
|
||||
|
||||
/**Any user data if needed*/
|
||||
void * user_data;
|
||||
} lv_cache_entry_t;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* with `size` bytes fits.
|
||||
* @param size the size of the new entry in bytes
|
||||
* @return a handler for the new cache entry
|
||||
*/
|
||||
typedef lv_cache_entry_t * (*lv_cache_add_cb)(size_t size);
|
||||
|
||||
/**
|
||||
* Find a cache entry
|
||||
* @param src_ptr pointer to the source data
|
||||
* @param src_type source type (`LV_CACHE_SRC_TYPE_PTR` or `LV_CACHE_SRC_TYPE_STR`)
|
||||
* @param param1 param1, which was set when the cache was added
|
||||
* @param param2 param2, which was set when the cache was added
|
||||
* @return the cache entry with given source and parameters or NULL if not found
|
||||
*/
|
||||
typedef lv_cache_entry_t * (*lv_cache_find_cb)(const void * src_ptr, lv_cache_src_type_t src_type, uint32_t param1,
|
||||
uint32_t param2);
|
||||
|
||||
/**
|
||||
* Invalidate (drop) a cache entry
|
||||
* @param entry the entry to invalidate. (can be retrieved by `lv_cache_find()`)
|
||||
*/
|
||||
typedef void (*lv_cache_invalidate_cb)(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the data of a cache entry.
|
||||
* It is considered a cached data access so the cache manager can count that
|
||||
* this entry was used on more times, and therefore it's more relevant.
|
||||
* It also increments entry->usage_count to indicate that the data is being used
|
||||
* and cannot be dropped.
|
||||
* @param entry the cache entry whose data should be retrieved
|
||||
*/
|
||||
typedef const void * (*lv_cache_get_data_cb)(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Mark the cache entry as unused. It decrements entry->usage_count.
|
||||
* @param entry the cache entry to invalidate
|
||||
*/
|
||||
typedef void (*lv_cache_release_cb)(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Set maximum cache size in bytes.
|
||||
* @param size the max size in byes
|
||||
*/
|
||||
typedef void (*lv_cache_set_max_size_cb)(size_t size);
|
||||
|
||||
typedef struct {
|
||||
lv_cache_add_cb add_cb;
|
||||
lv_cache_find_cb find_cb;
|
||||
lv_cache_invalidate_cb invalidate_cb;
|
||||
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_mutex_t mutex;
|
||||
size_t max_size;
|
||||
uint32_t locked : 1; /**< Show the mutex state, used to log unlocked cache access*/
|
||||
} lv_cache_manager_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the cache module
|
||||
*/
|
||||
void _lv_cache_init(void);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* with `size` bytes fits.
|
||||
* @param size the size of the new entry in bytes
|
||||
* @return a handler for the new cache entry
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_add(size_t size);
|
||||
|
||||
/**
|
||||
* Find a cache entry with pointer source type
|
||||
* @param src_ptr pointer to the source data
|
||||
* @param src_type source type (`LV_CACHE_SRC_TYPE_PTR` or `LV_CACHE_SRC_TYPE_STR`)
|
||||
* @param param1 param1, which was set when the cache was added
|
||||
* @param param2 param2, which was set when the cache was added
|
||||
* @return the cache entry with given source and parameters or NULL if not found
|
||||
*/
|
||||
lv_cache_entry_t * lv_cache_find(const void * src, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2);
|
||||
|
||||
/**
|
||||
* Invalidate (drop) a cache entry
|
||||
* @param entry the entry to invalidate. (can be retrieved by `lv_cache_find()`)
|
||||
*/
|
||||
void lv_cache_invalidate(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Get the data of a cache entry.
|
||||
* It is considered a cached data access so the cache manager can count that
|
||||
* this entry was used on more times, and therefore it's more relevant.
|
||||
* It also increments entry->usage_count to indicate that the data is being used
|
||||
* and cannot be dropped.
|
||||
* @param entry the cache entry whose data should be retrieved
|
||||
*/
|
||||
const void * lv_cache_get_data(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Mark the cache entry as unused. It decrements entry->usage_count.
|
||||
* @param entry
|
||||
*/
|
||||
void lv_cache_release(lv_cache_entry_t * entry);
|
||||
|
||||
/**
|
||||
* Set maximum cache size in bytes.
|
||||
* @param size the max size in byes
|
||||
*/
|
||||
void lv_cache_set_max_size(size_t size);
|
||||
|
||||
/**
|
||||
* Get the max size of the cache
|
||||
* @return the max size in bytes
|
||||
*/
|
||||
size_t lv_cache_get_max_size(void);
|
||||
|
||||
/**
|
||||
* Lock the mutex of the cache.
|
||||
* Needs to be called manually before any cache operation,
|
||||
*/
|
||||
void lv_cache_lock(void);
|
||||
|
||||
/**
|
||||
* Unlock the mutex of the cache.
|
||||
* Needs to be called manually after any cache operation,
|
||||
*/
|
||||
void lv_cache_unlock(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_H*/
|
||||
194
src/misc/lv_cache_builtin.c
Normal file
194
src/misc/lv_cache_builtin.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @file lv_image_cache.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_cache.h"
|
||||
#include "../stdlib/lv_string.h"
|
||||
#include "../core/lv_global.h"
|
||||
#include "../misc/lv_ll.h"
|
||||
#include "../osal/lv_os.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define _cache_manager LV_GLOBAL_DEFAULT()->cache_manager
|
||||
#define dsc LV_GLOBAL_DEFAULT()->cache_builtin_dsc
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
static lv_cache_entry_t * add_cb(size_t size);
|
||||
static lv_cache_entry_t * find_cb(const void * src, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2);
|
||||
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 bool drop_yougest(void);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void _lv_cache_builtin_init(void)
|
||||
{
|
||||
lv_memzero(&_cache_manager, sizeof(lv_cache_manager_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));
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static lv_cache_entry_t * add_cb(size_t size)
|
||||
{
|
||||
size_t max_size = lv_cache_get_max_size();
|
||||
/*Can't cache data larger than max size*/
|
||||
|
||||
bool temporary = size > max_size ? true : false;
|
||||
if(!temporary) {
|
||||
/*Keep dropping items until there is enough space*/
|
||||
while(dsc.cur_size + size > _cache_manager.max_size) {
|
||||
bool ret = drop_yougest();
|
||||
|
||||
/*No item could be dropped.
|
||||
*It can happen because the usage_count of the remaining items are not zero.*/
|
||||
if(ret == false) {
|
||||
temporary = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lv_cache_entry_t * e = _lv_ll_ins_head(&dsc.entry_ll);
|
||||
lv_memzero(e, sizeof(lv_cache_entry_t));
|
||||
e->data_size = size;
|
||||
e->weight = 1;
|
||||
e->temporary = temporary;
|
||||
|
||||
if(temporary) {
|
||||
LV_LOG_USER("cache add temporary: %"LV_PRIu32, (uint32_t)size);
|
||||
}
|
||||
else {
|
||||
LV_LOG_USER("cache add: %"LV_PRIu32, (uint32_t)size);
|
||||
dsc.cur_size += size;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * find_cb(const void * src, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2)
|
||||
{
|
||||
lv_cache_entry_t * e = _lv_ll_get_head(&dsc.entry_ll);
|
||||
while(e) {
|
||||
if(param1 == e->param1 && param2 == e->param2 && src_type == e->src_type &&
|
||||
((src_type == LV_CACHE_SRC_TYPE_PTR && src == e->src) ||
|
||||
(src_type == LV_CACHE_SRC_TYPE_STR && strcmp(src, e->src) == 0))) {
|
||||
return e;
|
||||
}
|
||||
|
||||
e = _lv_ll_get_next(&dsc.entry_ll, e);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void invalidate_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
if(entry == NULL) return;
|
||||
|
||||
dsc.cur_size -= entry->data_size;
|
||||
LV_LOG_USER("cache drop %"LV_PRIu32, (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);
|
||||
|
||||
_lv_ll_remove(&dsc.entry_ll, entry);
|
||||
lv_free(entry);
|
||||
}
|
||||
|
||||
static const void * get_data_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
lv_cache_entry_t * e = _lv_ll_get_head(&dsc.entry_ll);
|
||||
while(e) {
|
||||
e->life += e->weight;
|
||||
e = _lv_ll_get_next(&dsc.entry_ll, e);
|
||||
}
|
||||
|
||||
entry->usage_count++;
|
||||
|
||||
return entry->data;
|
||||
}
|
||||
|
||||
static void release_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
if(entry == NULL) return;
|
||||
|
||||
if(entry->temporary) {
|
||||
invalidate_cb(entry);
|
||||
}
|
||||
else {
|
||||
if(entry->usage_count == 0) {
|
||||
LV_LOG_ERROR("More lv_cache_release than lv_cache_get_data");
|
||||
return;
|
||||
}
|
||||
entry->usage_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_max_size_cb(size_t new_size)
|
||||
{
|
||||
while(dsc.cur_size > new_size) {
|
||||
bool ret = drop_yougest();
|
||||
|
||||
/*No item could be dropped.
|
||||
*It can happen because the usage_count of the remaining items are not zero.*/
|
||||
if(ret == false) return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool drop_yougest(void)
|
||||
{
|
||||
|
||||
int32_t life_min = INT32_MAX;
|
||||
lv_cache_entry_t * e_min = NULL;
|
||||
|
||||
lv_cache_entry_t * e = _lv_ll_get_head(&dsc.entry_ll);
|
||||
while(e) {
|
||||
if(e->life < life_min && e->usage_count == 0) e_min = e;
|
||||
e = _lv_ll_get_next(&dsc.entry_ll, e);
|
||||
}
|
||||
|
||||
if(e_min == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
invalidate_cb(e_min);
|
||||
return true;
|
||||
|
||||
}
|
||||
45
src/misc/lv_cache_builtin.h
Normal file
45
src/misc/lv_cache_builtin.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @file lv_cache_builtin.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_CACHE_BUILTIN_H
|
||||
#define LV_CACHE_BUILTIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_ll.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t cur_size;
|
||||
lv_ll_t entry_ll;
|
||||
} lv_cache_builtin_dsc_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
void _lv_cache_builtin_init(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_CACHE_BUILTIN_H*/
|
||||
@@ -19,11 +19,12 @@ extern "C" {
|
||||
*********************/
|
||||
#include "../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_OS
|
||||
|
||||
#include "../misc/lv_types.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#if LV_USE_OS == LV_OS_PTHREAD
|
||||
#if LV_USE_OS == LV_OS_NONE
|
||||
#include "lv_os_none.h"
|
||||
#elif LV_USE_OS == LV_OS_PTHREAD
|
||||
#include "lv_pthread.h"
|
||||
#elif LV_USE_OS == LV_OS_FREERTOS
|
||||
#include "lv_freertos.h"
|
||||
@@ -143,8 +144,6 @@ lv_res_t lv_thread_sync_delete(lv_thread_sync_t * sync);
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_OS*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
122
src/osal/lv_os_none.c
Normal file
122
src/osal/lv_os_none.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @file lv_os_none.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_os.h"
|
||||
|
||||
#if LV_USE_OS == LV_OS_NONE
|
||||
#include "../misc/lv_types.h"
|
||||
#include "../misc/lv_assert.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
|
||||
lv_res_t lv_thread_init(lv_thread_t * thread, lv_thread_prio_t prio, void (*callback)(void *), size_t stack_size,
|
||||
void * user_data)
|
||||
{
|
||||
LV_UNUSED(thread);
|
||||
LV_UNUSED(callback);
|
||||
LV_UNUSED(prio);
|
||||
LV_UNUSED(stack_size);
|
||||
LV_UNUSED(user_data);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
lv_res_t lv_thread_delete(lv_thread_t * thread)
|
||||
{
|
||||
LV_UNUSED(thread);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
|
||||
lv_res_t lv_mutex_init(lv_mutex_t * mutex)
|
||||
{
|
||||
LV_UNUSED(mutex);
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
lv_res_t lv_mutex_lock(lv_mutex_t * mutex)
|
||||
{
|
||||
LV_UNUSED(mutex);
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
lv_res_t lv_mutex_lock_isr(lv_mutex_t * mutex)
|
||||
{
|
||||
LV_UNUSED(mutex);
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
lv_res_t lv_mutex_unlock(lv_mutex_t * mutex)
|
||||
{
|
||||
LV_UNUSED(mutex);
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
lv_res_t lv_mutex_delete(lv_mutex_t * mutex)
|
||||
{
|
||||
LV_UNUSED(mutex);
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
lv_res_t lv_thread_sync_init(lv_thread_sync_t * sync)
|
||||
{
|
||||
LV_UNUSED(sync);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
lv_res_t lv_thread_sync_wait(lv_thread_sync_t * sync)
|
||||
{
|
||||
LV_UNUSED(sync);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
lv_res_t lv_thread_sync_signal(lv_thread_sync_t * sync)
|
||||
{
|
||||
LV_UNUSED(sync);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
lv_res_t lv_thread_sync_delete(lv_thread_sync_t * sync)
|
||||
{
|
||||
LV_UNUSED(sync);
|
||||
LV_ASSERT(0);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_OS == LV_OS_NONE*/
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file lv_image_cache_builtin.h
|
||||
* @file lv_os_none.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_IMAGE_CACHE_BUILTIN_H
|
||||
#define LV_IMAGE_CACHE_BUILTIN_H
|
||||
#ifndef LV_OS_NONE_H
|
||||
#define LV_OS_NONE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -13,6 +13,7 @@ extern "C" {
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#if LV_USE_OS == LV_OS_NONE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@@ -21,19 +22,22 @@ extern "C" {
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef int lv_mutex_t;
|
||||
typedef int lv_thread_t;
|
||||
typedef int lv_thread_sync_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
void _lv_image_cache_builtin_init(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_OS == LV_OS_NONE*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_IMAGE_CACHE_BUILTIN_H*/
|
||||
#endif /*LV_OS_NONE_H*/
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "../lv_string.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
|
||||
#include "../lv_mem.h"
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
@@ -66,7 +70,17 @@ char * lv_strcpy(char * dst, const char * src)
|
||||
|
||||
char * lv_strdup(const char * src)
|
||||
{
|
||||
/*strdup uses malloc, so use the built in malloc if it's enabled */
|
||||
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
|
||||
size_t len = lv_strlen(src) + 1;
|
||||
char * dst = lv_malloc(len);
|
||||
if(dst == NULL) return NULL;
|
||||
|
||||
lv_memcpy(dst, src, len); /*do memcpy is faster than strncpy when length is known*/
|
||||
return dst;
|
||||
#else
|
||||
return strdup(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**********************
|
||||
|
||||
@@ -76,7 +76,9 @@ void lv_canvas_set_buffer(lv_obj_t * obj, void * buf, lv_coord_t w, lv_coord_t h
|
||||
canvas->dsc.data_size = w * h * lv_color_format_get_size(cf);
|
||||
|
||||
lv_image_set_src(obj, &canvas->dsc);
|
||||
lv_image_cache_invalidate_src(&canvas->dsc);
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(&canvas->dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
void lv_canvas_set_px(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
|
||||
@@ -336,7 +338,10 @@ static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||||
LV_TRACE_OBJ_CREATE("begin");
|
||||
|
||||
lv_canvas_t * canvas = (lv_canvas_t *)obj;
|
||||
lv_image_cache_invalidate_src(&canvas->dsc);
|
||||
|
||||
lv_cache_lock();
|
||||
lv_cache_invalidate(lv_cache_find(&canvas->dsc, LV_CACHE_SRC_TYPE_PTR, 0, 0));
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user