diff --git a/.devcontainer/__lv_conf.h__ b/.devcontainer/__lv_conf.h__ index b022d40206..eba6b2ec45 100644 --- a/.devcontainer/__lv_conf.h__ +++ b/.devcontainer/__lv_conf.h__ @@ -271,6 +271,10 @@ *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ #define LV_CACHE_DEF_SIZE 0 +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ #define LV_GRADIENT_MAX_STOPS 2 diff --git a/Kconfig b/Kconfig index 54d06693a8..e17e7b72e6 100644 --- a/Kconfig +++ b/Kconfig @@ -269,6 +269,18 @@ menu "LVGL configuration" save the continuous open/decode of images. However the opened images might consume additional RAM. + config LV_IMAGE_HEADER_CACHE_DEF_CNT + int "Default image header cache count. 0 to disable caching." + default 0 + depends on LV_USE_DRAW_SW + help + If only the built-in image formats are used there is no real advantage of caching. + (I.e. no new image decoder is added). + + With complex image decoders (e.g. PNG or JPG) caching can + save the continuous getting header information of images. + However the records of opened images headers might consume additional RAM. + config LV_GRADIENT_MAX_STOPS int "Number of stops allowed per gradient." default 2 diff --git a/env_support/cmsis-pack/lv_conf_cmsis.h b/env_support/cmsis-pack/lv_conf_cmsis.h index f638fe88f5..08f085b30c 100644 --- a/env_support/cmsis-pack/lv_conf_cmsis.h +++ b/env_support/cmsis-pack/lv_conf_cmsis.h @@ -261,6 +261,10 @@ *will be dropped immediately after usage.*/ #define LV_CACHE_DEF_SIZE 0 +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ #define LV_GRADIENT_MAX_STOPS 2 diff --git a/lv_conf_template.h b/lv_conf_template.h index f20e840e52..8405cc734c 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -271,6 +271,10 @@ *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ #define LV_CACHE_DEF_SIZE 0 +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ #define LV_GRADIENT_MAX_STOPS 2 diff --git a/src/core/lv_global.h b/src/core/lv_global.h index fdbf7394dd..ed006224c3 100644 --- a/src/core/lv_global.h +++ b/src/core/lv_global.h @@ -101,6 +101,10 @@ typedef struct _lv_global_t { lv_cache_t * img_cache; #endif +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 + lv_cache_t * img_header_cache; +#endif + lv_draw_global_info_t draw_info; #if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0 lv_draw_sw_shadow_cache_t sw_shadow_cache; diff --git a/src/draw/lv_image_decoder.c b/src/draw/lv_image_decoder.c index 75aea0a26b..204edf9942 100644 --- a/src/draw/lv_image_decoder.c +++ b/src/draw/lv_image_decoder.c @@ -18,6 +18,7 @@ *********************/ #define img_decoder_ll_p &(LV_GLOBAL_DEFAULT()->img_decoder_ll) #define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache) +#define img_header_cache_p (LV_GLOBAL_DEFAULT()->img_header_cache) /********************** * TYPEDEFS @@ -37,6 +38,12 @@ static uint32_t img_width_to_stride(lv_image_header_t * header); */ static lv_image_decoder_t * image_decoder_get_info(const void * src, lv_image_header_t * header); +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 +static lv_cache_compare_res_t image_decoder_header_cache_compare_cb(const lv_image_header_cache_data_t * lhs, + const lv_image_header_cache_data_t * rhs); +static void image_decoder_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data); +#endif + #if LV_CACHE_DEF_SIZE > 0 static lv_cache_compare_res_t image_decoder_cache_compare_cb(const lv_image_cache_data_t * lhs, const lv_image_cache_data_t * rhs); @@ -70,6 +77,16 @@ void _lv_image_decoder_init(void) .create_cb = NULL, .free_cb = (lv_cache_free_cb_t)image_decoder_cache_free_cb, }); + +#endif + +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 + img_header_cache_p = lv_cache_create(&lv_cache_class_lru_rb_count, + sizeof(lv_image_header_cache_data_t), LV_IMAGE_HEADER_CACHE_DEF_CNT, (lv_cache_ops_t) { + .compare_cb = (lv_cache_compare_cb_t)image_decoder_header_cache_compare_cb, + .create_cb = NULL, + .free_cb = (lv_cache_free_cb_t)image_decoder_header_cache_free_cb + }); #endif } @@ -81,6 +98,10 @@ void _lv_image_decoder_deinit(void) #if LV_CACHE_DEF_SIZE > 0 lv_cache_destroy(img_cache_p, NULL); #endif + +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 + lv_cache_destroy(img_header_cache_p, NULL); +#endif _lv_ll_clear(img_decoder_ll_p); } @@ -295,17 +316,51 @@ static lv_image_decoder_t * image_decoder_get_info(const void * src, lv_image_he } lv_image_decoder_t * decoder; + +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 + lv_image_header_cache_data_t search_key; + search_key.src_type = src_type; + search_key.src = src; + + lv_cache_entry_t * entry = lv_cache_acquire(img_header_cache_p, &search_key, NULL); + + if(entry) { + lv_image_header_cache_data_t * cached_data = lv_cache_entry_get_data(entry); + *header = cached_data->header; + decoder = cached_data->decoder; + lv_cache_release(img_header_cache_p, entry, NULL); + return decoder; + } +#endif + _LV_LL_READ(img_decoder_ll_p, decoder) { /*Info and Open callbacks are required*/ if(decoder->info_cb && decoder->open_cb) { lv_result_t res = decoder->info_cb(decoder, src, header); if(res == LV_RESULT_OK) { if(header->stride == 0) header->stride = img_width_to_stride(header); - return decoder; + break; } } } - return NULL; + +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 + if(decoder) { + if(src_type == LV_IMAGE_SRC_FILE) search_key.src = lv_strdup(src); + search_key.decoder = decoder; + search_key.header = *header; + entry = lv_cache_add(img_header_cache_p, &search_key, NULL); + + if(entry == NULL) { + if(src_type == LV_IMAGE_SRC_FILE) lv_free((void *)search_key.src); + return NULL; + } + + lv_cache_release(img_header_cache_p, entry, NULL); + } +#endif + + return decoder; } static uint32_t img_width_to_stride(lv_image_header_t * header) @@ -318,26 +373,50 @@ static uint32_t img_width_to_stride(lv_image_header_t * header) } } +#if LV_CACHE_DEF_SIZE > 0 || LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 +inline static lv_cache_compare_res_t image_decoder_common_compare(const void * lhs_src, lv_image_src_t lhs_src_type, + const void * rhs_src, lv_image_src_t rhs_src_type) +{ + if(lhs_src_type == rhs_src_type) { + if(lhs_src_type == LV_IMAGE_SRC_FILE) { + int32_t cmp_res = lv_strcmp(lhs_src, rhs_src); + if(cmp_res != 0) { + return cmp_res > 0 ? 1 : -1; + } + } + else if(lhs_src_type == LV_IMAGE_SRC_VARIABLE) { + if(lhs_src != rhs_src) { + return lhs_src > rhs_src ? 1 : -1; + } + } + return 0; + } + return lhs_src_type > rhs_src_type ? 1 : -1; +} +#endif + +#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0 +static lv_cache_compare_res_t image_decoder_header_cache_compare_cb( + const lv_image_header_cache_data_t * lhs, + const lv_image_header_cache_data_t * rhs) +{ + return image_decoder_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type); +} + +static void image_decoder_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data) +{ + LV_UNUSED(user_data); /*Unused*/ + + if(entry->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)entry->src); +} +#endif + #if LV_CACHE_DEF_SIZE > 0 static lv_cache_compare_res_t image_decoder_cache_compare_cb( const lv_image_cache_data_t * lhs, const lv_image_cache_data_t * rhs) { - if(lhs->src_type == rhs->src_type) { - if(lhs->src_type == LV_IMAGE_SRC_FILE) { - int32_t cmp_res = lv_strcmp(lhs->src, rhs->src); - if(cmp_res != 0) { - return cmp_res > 0 ? 1 : -1; - } - } - else if(lhs->src_type == LV_IMAGE_SRC_VARIABLE) { - if(lhs->src != rhs->src) { - return lhs->src > rhs->src ? 1 : -1; - } - } - return 0; - } - return lhs->src_type > rhs->src_type ? 1 : -1; + return image_decoder_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type); } static void image_decoder_cache_free_cb(lv_image_cache_data_t * entry, void * user_data) diff --git a/src/draw/lv_image_decoder.h b/src/draw/lv_image_decoder.h index 57ddb5a91e..2979f08fcf 100644 --- a/src/draw/lv_image_decoder.h +++ b/src/draw/lv_image_decoder.h @@ -125,6 +125,14 @@ typedef struct _lv_image_decoder_cache_data_t { void * user_data; } lv_image_cache_data_t; +typedef struct _lv_image_decoder_header_cache_data_t { + const void * src; + lv_image_src_t src_type; + + lv_image_header_t header; + lv_image_decoder_t * decoder; +} lv_image_header_cache_data_t; + /**Describe an image decoding session. Stores data about the decoding*/ struct _lv_image_decoder_dsc_t { /**The decoder which was able to open the image source*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 47ed197bc8..98dff9594e 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -771,6 +771,16 @@ #endif #endif +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#ifndef LV_IMAGE_HEADER_CACHE_DEF_CNT + #ifdef CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT + #define LV_IMAGE_HEADER_CACHE_DEF_CNT CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT + #else + #define LV_IMAGE_HEADER_CACHE_DEF_CNT 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