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:
Gabor Kiss-Vamosi
2023-09-14 23:23:26 +02:00
parent 09c12d0f9c
commit 757877b1fc
25 changed files with 885 additions and 540 deletions

View File

@@ -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*/

View File

@@ -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

View File

@@ -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;

View File

@@ -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"

View File

@@ -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
**********************/

View File

@@ -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*/

View File

@@ -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

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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;
}

View 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*/

View File

@@ -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
View 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*/

View File

@@ -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*/

View File

@@ -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
}
/**********************

View File

@@ -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();
}