summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/misc/cache
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-06-12 17:54:40 +1000
committerjacqueline <me@jacqueline.id.au>2024-06-12 17:54:40 +1000
commit64bd9053a25297f7a442ca831c7da5b44bd33f84 (patch)
treea90c6cad25a12028302ab1a5334510fba0229bae /lib/lvgl/src/misc/cache
parent611176ed667c4ed7ee9f609e958f9404f4aee91d (diff)
downloadtangara-fw-64bd9053a25297f7a442ca831c7da5b44bd33f84.tar.gz
Update LVGL to v9.1.0
Diffstat (limited to 'lib/lvgl/src/misc/cache')
-rw-r--r--lib/lvgl/src/misc/cache/_lv_cache_lru_rb.c464
-rw-r--r--lib/lvgl/src/misc/cache/_lv_cache_lru_rb.h44
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache.c275
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache.h206
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache_entry.c167
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache_entry.h116
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache_entry_private.h51
-rw-r--r--lib/lvgl/src/misc/cache/lv_cache_private.h190
-rw-r--r--lib/lvgl/src/misc/cache/lv_image_cache.c109
-rw-r--r--lib/lvgl/src/misc/cache/lv_image_cache.h69
10 files changed, 1691 insertions, 0 deletions
diff --git a/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.c b/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.c
new file mode 100644
index 00000000..097f764f
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.c
@@ -0,0 +1,464 @@
+/**
+* @file _lv_cache_lru_rb.c
+*
+*/
+
+/***************************************************************************\
+* *
+* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
+* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┌ ─ ─ ─ ┐ *
+* ┌ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ Cache insert ┃ *
+* ┃ RB Tree │ │Hitting│ head *
+* └ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ ─ ─ ─ ─ ┃ *
+* ┃ ┌─┬─┬─┬─┐ ┌─────┐ *
+* ┌──│◄│B│►│ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┃─ ─ ╋ ─ ─▶│ B │ ┃ *
+* ┃ │ └─┴─┴─┴─┘ └──▲──┘ *
+* │ │ ┃ ┃ │ ┃ *
+* ┃ │ │ ┌──┴──┐ *
+* │ └──────┐ ┌ ─┃─ ─ ╋ ─ ─▶│ E │ ┃ *
+* ┃ ▼ ┌ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └──▲──┘ *
+* ┌─┬─┬─┬─┐ ▼ │ ┃ ┃ │ ┃ *
+* ┃│◄│A│►│ │─ ─ ┘ ┌─┬─┬─┬─┐ │ ┌──┴──┐ *
+* └─┴─┴─┴─┘ ┌───│◄│D│►│ │─ ─ ─ ─ ─ ─│─ ╋ ┐ ┃ ─ ▶│ A │ ┌ ─ ─ ─ ─ ─ ┐ ┃ *
+* ┃ │ └─┴─┴─┴─┘ └──▲──┘ LRU *
+* │ │ │ ┃ │ ┃ │ │ Cache │ ┃ *
+* ┃ ▼ └──────┐ ┌──┴──┐ ─ ─ ─ ─ ─ ─ *
+* ┌─┬─┬─┬─┐ ▼ │ ┃ └ ─┃─ ─ ▶│ D │ ┃ *
+* ┃ │◄│C│►│ │─ ─ ┌─┬─┬─┬─┐ └──▲──┘ *
+* └─┴─┴─┴─┘ │ │◄│E│►│ │─ ┘ ┃ ┃ │ ┃ *
+* ┃ └─┴─┴─┴─┘ ┌──┴──┐ *
+* │ │ ─ ╋ ─ ─┃─ ─ ▶│ C │ ┃ *
+* ┃ ─ ─ ─ ─ ┼ ─ ─ ┘ └──▲──┘ *
+* ▼ ┃ ┃ ┌ ─ ─│─ ─ ┐ ┃ *
+* ┃ ┌─┬─┬─┬─┐ ┌──┴──┐ *
+* │◄│F│►│ │─ ─┃─ ─ ╋ ─ ┼▶│ F │ │ ┃ *
+* ┃ └─┴─┴─┴─┘ └─────┘ *
+* ┃ ┃ └ ─ ─ ─ ─ ┘ ┃ *
+* ┃ remove *
+* ┃ ┃ tail ┃ *
+* ┗ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ *
+* *
+\***************************************************************************/
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "_lv_cache_lru_rb.h"
+#include "../../stdlib/lv_sprintf.h"
+#include "../../stdlib/lv_string.h"
+#include "../lv_ll.h"
+#include "../lv_rb.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef uint32_t (get_data_size_cb_t)(const void * data);
+
+struct _lv_lru_rb_t {
+ lv_cache_t cache;
+
+ lv_rb_t rb;
+ lv_ll_t ll;
+
+ get_data_size_cb_t * get_data_size_cb;
+};
+typedef struct _lv_lru_rb_t lv_lru_rb_t_;
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void * alloc_cb(void);
+static bool init_cnt_cb(lv_cache_t * cache);
+static bool init_size_cb(lv_cache_t * cache);
+static void destroy_cb(lv_cache_t * cache, void * user_data);
+
+static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
+static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
+static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
+static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
+static void drop_all_cb(lv_cache_t * cache, void * user_data);
+static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data);
+static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
+ void * user_data);
+
+static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data);
+inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node);
+
+static uint32_t cnt_get_data_size_cb(const void * data);
+static uint32_t size_get_data_size_cb(const void * data);
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+const lv_cache_class_t lv_cache_class_lru_rb_count = {
+ .alloc_cb = alloc_cb,
+ .init_cb = init_cnt_cb,
+ .destroy_cb = destroy_cb,
+
+ .get_cb = get_cb,
+ .add_cb = add_cb,
+ .remove_cb = remove_cb,
+ .drop_cb = drop_cb,
+ .drop_all_cb = drop_all_cb,
+ .get_victim_cb = get_victim_cb,
+ .reserve_cond_cb = reserve_cond_cb
+};
+
+const lv_cache_class_t lv_cache_class_lru_rb_size = {
+ .alloc_cb = alloc_cb,
+ .init_cb = init_size_cb,
+ .destroy_cb = destroy_cb,
+
+ .get_cb = get_cb,
+ .add_cb = add_cb,
+ .remove_cb = remove_cb,
+ .drop_cb = drop_cb,
+ .drop_all_cb = drop_all_cb,
+ .get_victim_cb = get_victim_cb,
+ .reserve_cond_cb = reserve_cond_cb
+};
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ LV_ASSERT_NULL(lru);
+ LV_ASSERT_NULL(key);
+
+ if(lru == NULL || key == NULL) {
+ return NULL;
+ }
+
+ lv_rb_node_t * node = lv_rb_insert(&lru->rb, key);
+ if(node == NULL)
+ goto FAILED_HANDLER2;
+
+ void * data = node->data;
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, lru->cache.node_size);
+ lv_memcpy(data, key, lru->cache.node_size);
+
+ void * lru_node = _lv_ll_ins_head(&lru->ll);
+ if(lru_node == NULL)
+ goto FAILED_HANDLER1;
+
+ lv_memcpy(lru_node, &node, sizeof(void *));
+ lv_memcpy(get_lru_node(lru, node), &lru_node, sizeof(void *));
+
+ lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
+ goto FAILED_HANDLER2;
+
+FAILED_HANDLER1:
+ lv_rb_drop_node(&lru->rb, node);
+ node = NULL;
+FAILED_HANDLER2:
+ return node;
+}
+
+inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node)
+{
+ return (void **)((char *)node->data + lru->rb.size - sizeof(void *));
+}
+
+static void * alloc_cb(void)
+{
+ void * res = lv_malloc(sizeof(lv_lru_rb_t_));
+ LV_ASSERT_MALLOC(res);
+ if(res == NULL) {
+ LV_LOG_ERROR("malloc failed");
+ return NULL;
+ }
+
+ lv_memzero(res, sizeof(lv_lru_rb_t_));
+ return res;
+}
+
+static bool init_cnt_cb(lv_cache_t * cache)
+{
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru->cache.ops.compare_cb);
+ LV_ASSERT_NULL(lru->cache.ops.free_cb);
+ LV_ASSERT(lru->cache.node_size > 0);
+
+ if(lru->cache.node_size <= 0 || lru->cache.max_size <= 0
+ || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
+ return false;
+ }
+
+ /*add void* to store the ll node pointer*/
+ if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
+ return false;
+ }
+ _lv_ll_init(&lru->ll, sizeof(void *));
+
+ lru->get_data_size_cb = cnt_get_data_size_cb;
+
+ return true;
+}
+
+static bool init_size_cb(lv_cache_t * cache)
+{
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru->cache.ops.compare_cb);
+ LV_ASSERT_NULL(lru->cache.ops.free_cb);
+ LV_ASSERT(lru->cache.node_size > 0);
+
+ if(lru->cache.node_size <= 0 || lru->cache.max_size <= 0
+ || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
+ return false;
+ }
+
+ /*add void* to store the ll node pointer*/
+ if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
+ return false;
+ }
+ _lv_ll_init(&lru->ll, sizeof(void *));
+
+ lru->get_data_size_cb = size_get_data_size_cb;
+
+ return true;
+}
+
+static void destroy_cb(lv_cache_t * cache, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+
+ if(lru == NULL) {
+ return;
+ }
+
+ cache->clz->drop_all_cb(cache, user_data);
+}
+
+static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+ LV_ASSERT_NULL(key);
+
+ if(lru == NULL || key == NULL) {
+ return NULL;
+ }
+
+ /*try the first ll node first*/
+ void * head = _lv_ll_get_head(&lru->ll);
+ if(head) {
+ lv_rb_node_t * node = *(lv_rb_node_t **)head;
+ void * data = node->data;
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
+ if(lru->cache.ops.compare_cb(data, key) == 0) {
+ return entry;
+ }
+ }
+
+ lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
+ /*cache hit*/
+ if(node) {
+ void * lru_node = *get_lru_node(lru, node);
+ head = _lv_ll_get_head(&lru->ll);
+ _lv_ll_move_before(&lru->ll, lru_node, head);
+
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(node->data, cache->node_size);
+ return entry;
+ }
+ return NULL;
+}
+
+static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+ LV_ASSERT_NULL(key);
+
+ if(lru == NULL || key == NULL) {
+ return NULL;
+ }
+
+ lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data);
+ if(new_node == NULL) {
+ return NULL;
+ }
+
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node->data, cache->node_size);
+
+ cache->size += lru->get_data_size_cb(key);
+
+ return entry;
+}
+
+static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+ LV_ASSERT_NULL(entry);
+
+ if(lru == NULL || entry == NULL) {
+ return;
+ }
+
+ void * data = lv_cache_entry_get_data(entry);
+ lv_rb_node_t * node = lv_rb_find(&lru->rb, data);
+ if(node == NULL) {
+ return;
+ }
+
+ void * lru_node = *get_lru_node(lru, node);
+ lv_rb_remove_node(&lru->rb, node);
+ _lv_ll_remove(&lru->ll, lru_node);
+ lv_free(lru_node);
+
+ cache->size -= lru->get_data_size_cb(data);
+}
+
+static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
+{
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+ LV_ASSERT_NULL(key);
+
+ if(lru == NULL || key == NULL) {
+ return;
+ }
+
+ lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
+ if(node == NULL) {
+ return;
+ }
+
+ void * data = node->data;
+
+ lru->cache.ops.free_cb(data, user_data);
+ cache->size -= lru->get_data_size_cb(data);
+
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
+ void * lru_node = *get_lru_node(lru, node);
+
+ lv_rb_remove_node(&lru->rb, node);
+ lv_cache_entry_delete(entry);
+
+ _lv_ll_remove(&lru->ll, lru_node);
+ lv_free(lru_node);
+}
+
+static void drop_all_cb(lv_cache_t * cache, void * user_data)
+{
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+
+ if(lru == NULL) {
+ return;
+ }
+
+ uint32_t used_cnt = 0;
+ lv_rb_node_t ** node;
+ _LV_LL_READ(&lru->ll, node) {
+ /*free user handled data and do other clean up*/
+ void * search_key = (*node)->data;
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(search_key, cache->node_size);
+ if(lv_cache_entry_get_ref(entry) == 0) {
+ lru->cache.ops.free_cb(search_key, user_data);
+ }
+ else {
+ LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", (void *)entry, lv_cache_entry_get_ref(entry));
+ used_cnt++;
+ }
+ }
+ if(used_cnt > 0) {
+ LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
+ }
+
+ lv_rb_destroy(&lru->rb);
+ _lv_ll_clear(&lru->ll);
+
+ cache->size = 0;
+}
+
+static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+
+ lv_rb_node_t ** tail;
+ _LV_LL_READ_BACK(&lru->ll, tail) {
+ lv_rb_node_t * tail_node = *tail;
+ lv_cache_entry_t * entry = lv_cache_entry_get_entry(tail_node->data, cache->node_size);
+ if(lv_cache_entry_get_ref(entry) == 0) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
+ void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
+
+ LV_ASSERT_NULL(lru);
+
+ if(lru == NULL) {
+ return LV_CACHE_RESERVE_COND_ERROR;
+ }
+
+ uint32_t data_size = key ? lru->get_data_size_cb(key) : 0;
+ if(data_size > lru->cache.max_size) {
+ LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size, lru->cache.max_size);
+ return LV_CACHE_RESERVE_COND_TOO_LARGE;
+ }
+
+ return cache->size + reserved_size + data_size > lru->cache.max_size
+ ? LV_CACHE_RESERVE_COND_NEED_VICTIM
+ : LV_CACHE_RESERVE_COND_OK;
+}
+
+static uint32_t cnt_get_data_size_cb(const void * data)
+{
+ LV_UNUSED(data);
+ return 1;
+}
+
+static uint32_t size_get_data_size_cb(const void * data)
+{
+ lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
+ return slot->size;
+}
diff --git a/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.h b/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.h
new file mode 100644
index 00000000..a27110b9
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/_lv_cache_lru_rb.h
@@ -0,0 +1,44 @@
+/**
+* @file _lv_cache_lru_rb.h
+*
+*/
+
+#ifndef LV_CACHE_LRU_RB_H
+#define LV_CACHE_LRU_RB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_cache_entry.h"
+#include "lv_cache_private.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_count;
+LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_size;
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_CACHE_LRU_RB_H*/
diff --git a/lib/lvgl/src/misc/cache/lv_cache.c b/lib/lvgl/src/misc/cache/lv_cache.c
new file mode 100644
index 00000000..e22cd9e4
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache.c
@@ -0,0 +1,275 @@
+/**
+* @file lv_cache.c
+*
+*/
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_cache.h"
+#include "../../stdlib/lv_sprintf.h"
+#include "../lv_assert.h"
+#include "lv_cache_entry_private.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
+static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data);
+static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
+ size_t node_size, size_t max_size,
+ lv_cache_ops_t ops)
+{
+ lv_cache_t * cache = cache_class->alloc_cb();
+ LV_ASSERT_MALLOC(cache);
+
+ cache->clz = cache_class;
+ cache->node_size = node_size;
+ cache->max_size = max_size;
+ cache->size = 0;
+ cache->ops = ops;
+
+ if(cache->clz->init_cb(cache) == false) {
+ LV_LOG_ERROR("Cache init failed");
+ lv_free(cache);
+ return NULL;
+ }
+
+ lv_mutex_init(&cache->lock);
+
+ return cache;
+}
+
+void lv_cache_destroy(lv_cache_t * cache, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+
+ lv_mutex_lock(&cache->lock);
+ cache->clz->destroy_cb(cache, user_data);
+ lv_mutex_unlock(&cache->lock);
+ lv_mutex_delete(&cache->lock);
+ lv_free(cache);
+}
+
+lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+ LV_ASSERT_NULL(key);
+
+ lv_mutex_lock(&cache->lock);
+ lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
+ if(entry != NULL) {
+ lv_cache_entry_acquire_data(entry);
+ }
+ lv_mutex_unlock(&cache->lock);
+ return entry;
+}
+void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
+{
+ LV_ASSERT_NULL(entry);
+
+ lv_mutex_lock(&cache->lock);
+ lv_cache_entry_release_data(entry, user_data);
+
+ if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) {
+ cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
+ lv_cache_entry_delete(entry);
+ }
+ lv_mutex_unlock(&cache->lock);
+}
+lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+ LV_ASSERT_NULL(key);
+
+ lv_mutex_lock(&cache->lock);
+ lv_cache_entry_t * entry = cache_add_internal_no_lock(cache, key, user_data);
+ if(entry != NULL) {
+ lv_cache_entry_acquire_data(entry);
+ }
+ lv_mutex_unlock(&cache->lock);
+
+ return entry;
+}
+lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+ LV_ASSERT_NULL(key);
+
+ lv_mutex_lock(&cache->lock);
+ lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
+ if(entry != NULL) {
+ lv_cache_entry_acquire_data(entry);
+ lv_mutex_unlock(&cache->lock);
+ return entry;
+ }
+ entry = cache_add_internal_no_lock(cache, key, user_data);
+ if(entry == NULL) {
+ lv_mutex_unlock(&cache->lock);
+ return NULL;
+ }
+ bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data);
+ if(create_res == false) {
+ cache->clz->remove_cb(cache, entry, user_data);
+ lv_cache_entry_delete(entry);
+ entry = NULL;
+ }
+ else {
+ lv_cache_entry_acquire_data(entry);
+ }
+ lv_mutex_unlock(&cache->lock);
+ return entry;
+}
+void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+
+ for(lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data);
+ reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
+ reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data))
+ cache_evict_one_internal_no_lock(cache, user_data);
+
+}
+void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+ LV_ASSERT_NULL(key);
+
+ lv_mutex_lock(&cache->lock);
+ cache_drop_internal_no_lock(cache, key, user_data);
+ lv_mutex_unlock(&cache->lock);
+}
+bool lv_cache_evict_one(lv_cache_t * cache, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+
+ lv_mutex_lock(&cache->lock);
+ bool res = cache_evict_one_internal_no_lock(cache, user_data);
+ lv_mutex_unlock(&cache->lock);
+
+ return res;
+}
+void lv_cache_drop_all(lv_cache_t * cache, void * user_data)
+{
+ LV_ASSERT_NULL(cache);
+
+ lv_mutex_lock(&cache->lock);
+ cache->clz->drop_all_cb(cache, user_data);
+ lv_mutex_unlock(&cache->lock);
+}
+
+void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data)
+{
+ LV_UNUSED(user_data);
+ cache->max_size = max_size;
+}
+size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data)
+{
+ LV_UNUSED(user_data);
+ return cache->max_size;
+}
+size_t lv_cache_get_size(lv_cache_t * cache, void * user_data)
+{
+ LV_UNUSED(user_data);
+ return cache->size;
+}
+size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data)
+{
+ LV_UNUSED(user_data);
+ return cache->max_size - cache->size;
+}
+void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data)
+{
+ LV_UNUSED(user_data);
+ cache->ops.compare_cb = compare_cb;
+}
+void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data)
+{
+ LV_UNUSED(user_data);
+ cache->ops.create_cb = alloc_cb;
+}
+void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data)
+{
+ LV_UNUSED(user_data);
+ cache->ops.free_cb = free_cb;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
+{
+ lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
+ if(entry == NULL) {
+ return;
+ }
+
+ if(lv_cache_entry_get_ref(entry) == 0) {
+ cache->clz->remove_cb(cache, entry, user_data);
+ cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
+ lv_cache_entry_delete(entry);
+ }
+ else {
+ lv_cache_entry_set_invalid(entry, true);
+ cache->clz->remove_cb(cache, entry, user_data);
+ }
+}
+
+static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data)
+{
+ lv_cache_entry_t * victim = cache->clz->get_victim_cb(cache, user_data);
+
+ if(victim == NULL) {
+ LV_LOG_ERROR("No victim found");
+ return false;
+ }
+
+ cache->clz->remove_cb(cache, victim, user_data);
+ cache->ops.free_cb(lv_cache_entry_get_data(victim), user_data);
+ lv_cache_entry_delete(victim);
+ return true;
+}
+
+static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
+{
+ lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data);
+ if(reserve_cond_res == LV_CACHE_RESERVE_COND_TOO_LARGE) {
+ LV_LOG_ERROR("data %p is too large that exceeds max size (%" LV_PRIu32 ")", key, cache->max_size);
+ return NULL;
+ }
+
+ for(; reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
+ reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data))
+ if(cache_evict_one_internal_no_lock(cache, user_data) == false)
+ return NULL;
+
+ lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data);
+
+ return entry;
+}
diff --git a/lib/lvgl/src/misc/cache/lv_cache.h b/lib/lvgl/src/misc/cache/lv_cache.h
new file mode 100644
index 00000000..25db6fce
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache.h
@@ -0,0 +1,206 @@
+/**
+* @file lv_cache.h
+*
+*/
+
+#ifndef LV_CACHE1_H
+#define LV_CACHE1_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_cache_entry.h"
+#include "lv_cache_private.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "_lv_cache_lru_rb.h"
+
+#include "lv_image_cache.h"
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create a cache object with the given parameters.
+ * @param cache_class The class of the cache. Currently only support one two builtin classes:
+ * @lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
+ * @lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
+ * @param node_size The node size is the size of the data stored in the cache..
+ * @param max_size The max size is the maximum amount of memory or count that the cache can hold.
+ * @lv_cache_class_lru_rb_count: max_size is the maximum count of nodes in the cache.
+ * @lv_cache_class_lru_rb_size: max_size is the maximum size of the cache in bytes.
+ * @param ops A set of operations that can be performed on the cache. See @lv_cache_ops_t for details.
+ * @return Returns a pointer to the created cache object on success, @NULL on error.
+ */
+lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
+ size_t node_size, size_t max_size,
+ lv_cache_ops_t ops);
+
+/**
+ * Destroy a cache object.
+ * @param cache The cache object pointer to destroy.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_destroy(lv_cache_t * cache, void * user_data);
+
+/**
+ * Acquire a cache entry with the given key. If the entry is not in the cache, it will return @NULL as it is not found.
+ * If the entry is found, it's priority will be changed by the cache's policy. And the @lv_entry_t::ref count will be incremented.
+ * @param cache The cache object pointer to acquire the entry.
+ * @param key The key of the entry to acquire.
+ * @param user_data A user data pointer that will be passed to the create callback.
+ * @return Returns a pointer to the acquired cache entry on success with @lv_entry_t::ref count incremented, @NULL on error.
+ */
+lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * Acquire a cache entry with the given key. If the entry is not in the cache, it will create a new entry with the given key.
+ * If the entry is found, it's priority will be changed by the cache's policy. And the @lv_entry_t::ref count will be incremented.
+ * If you want to use this API to simplify the code, you should provide a @lv_cache_ops_t::create_cb that creates a new entry with the given key.
+ * This API is a combination of @lv_cache_acquire and @lv_cache_add. The effect is the same as calling @lv_cache_acquire and @lv_cache_add separately.
+ * And the internal impact on cache is also consistent with these two APIs.
+ * @param cache The cache object pointer to acquire the entry.
+ * @param key The key of the entry to acquire or create.
+ * @param user_data A user data pointer that will be passed to the create callback.
+ * @return Returns a pointer to the acquired or created cache entry on success with @lv_entry_t::ref count incremented, @NULL on error.
+ */
+lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * Add a new cache entry with the given key and data. If the cache is full, the cache's policy will be used to evict an entry.
+ * @param cache The cache object pointer to add the entry.
+ * @param key The key of the entry to add.
+ * @param user_data A user data pointer that will be passed to the create callback.
+ * @return Returns a pointer to the added cache entry on success with @lv_entry_t::ref count incremented, @NULL on error.
+ */
+lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * Release a cache entry. The @lv_entry_t::ref count will be decremented. If the @lv_entry_t::ref count is zero, it will issue an error.
+ * If the entry passed to this function is the last reference to the data and the entry is marked as invalid, the cache's policy will be used to evict the entry.
+ * @param cache The cache object pointer to release the entry.
+ * @param entry The cache entry pointer to release.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
+
+/**
+ * Reserve a certain amount of memory/count in the cache. This function is useful when you want to reserve a certain amount of memory/count in advance,
+ * for example, when you know that you will need it later.
+ * When the current cache size is max than the reserved size, the function will evict entries until the reserved size is reached.
+ * @param cache The cache object pointer to reserve.
+ * @param reserved_size The amount of memory/count to reserve.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data);
+
+/**
+ * Drop a cache entry with the given key. If the entry is not in the cache, nothing will happen to it.
+ * If the entry is found, it will be removed from the cache and its data will be freed when the last reference to it is released.
+ * @note The data will not be freed immediately but when the last reference to it is released. But this entry will not be found by @lv_cache_acquire.
+ * If you want cache a same key again, you should use @lv_cache_add or @lv_cache_acquire_or_create.
+ * @param cache The cache object pointer to drop the entry.
+ * @param key The key of the entry to drop.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * Drop all cache entries. All entries will be removed from the cache and their data will be freed when the last reference to them is released.
+ * @note If some entries are still referenced by other objects, it will issue an error. And this case shouldn't happen in normal cases..
+ * @param cache The cache object pointer to drop all entries.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_drop_all(lv_cache_t * cache, void * user_data);
+
+/**
+ * Evict one entry from the cache. The eviction policy will be used to select the entry to evict.
+ * @param cache The cache object pointer to evict an entry.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ * @return Returns true if an entry is evicted, false if no entry is evicted.
+ */
+bool lv_cache_evict_one(lv_cache_t * cache, void * user_data);
+
+/**
+ * Set the maximum size of the cache.
+ * If the current cache size is greater than the new maximum size, the cache's policy will be used to evict entries until the new maximum size is reached.
+ * @note But this behavior will happen only new entries are added to the cache.
+ * @param cache The cache object pointer to set the maximum size.
+ * @param max_size The new maximum size of the cache.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ */
+void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data);
+
+/**
+ * Get the maximum size of the cache.
+ * @param cache The cache object pointer to get the maximum size.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ * @return Returns the maximum size of the cache.
+ */
+size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data);
+
+/**
+ * Get the current size of the cache.
+ * @param cache The cache object pointer to get the current size.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ * @return Returns the current size of the cache.
+ */
+size_t lv_cache_get_size(lv_cache_t * cache, void * user_data);
+
+/**
+ * Get the free size of the cache.
+ * @param cache The cache object pointer to get the free size.
+ * @param user_data A user data pointer that will be passed to the free callback.
+ * @return Returns the free size of the cache.
+ */
+size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data);
+
+/**
+ * Set the compare callback of the cache.
+ * @param cache The cache object pointer to set the compare callback.
+ * @param compare_cb The compare callback to set.
+ * @param user_data A user data pointer.
+ */
+void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data);
+
+/**
+ * Set the create callback of the cache.
+ * @param cache The cache object pointer to set the create callback.
+ * @param alloc_cb The create callback to set.
+ * @param user_data A user data pointer.
+ */
+void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data);
+
+/**
+ * Set the free callback of the cache.
+ * @param cache The cache object pointer to set the free callback.
+ * @param free_cb The free callback to set.
+ * @param user_data A user data pointer.
+ */
+void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data);
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_CACHE_H*/
diff --git a/lib/lvgl/src/misc/cache/lv_cache_entry.c b/lib/lvgl/src/misc/cache/lv_cache_entry.c
new file mode 100644
index 00000000..242e1a0f
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache_entry.c
@@ -0,0 +1,167 @@
+/**
+* @file lv_cache_entry.c
+*
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_cache_entry.h"
+#include "../../stdlib/lv_sprintf.h"
+#include "../lv_assert.h"
+#include "lv_cache.h"
+#include "lv_cache_entry_private.h"
+#include "lv_cache_private.h"
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_cache_entry_t {
+ const lv_cache_t * cache;
+ int32_t ref_cnt;
+ uint32_t node_size;
+
+ bool is_invalid;
+};
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_cache_entry_reset_ref(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ entry->ref_cnt = 0;
+}
+void lv_cache_entry_inc_ref(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ entry->ref_cnt++;
+}
+void lv_cache_entry_dec_ref(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ entry->ref_cnt--;
+ if(entry->ref_cnt < 0) {
+ LV_LOG_WARN("ref_cnt(%" LV_PRIu32 ") < 0", entry->ref_cnt);
+ entry->ref_cnt = 0;
+ }
+}
+int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ return entry->ref_cnt;
+}
+uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry)
+{
+ return entry->node_size;
+}
+void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size)
+{
+ LV_ASSERT_NULL(entry);
+ entry->node_size = node_size;
+}
+void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid)
+{
+ LV_ASSERT_NULL(entry);
+ entry->is_invalid = is_invalid;
+}
+bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ return entry->is_invalid;
+}
+void * lv_cache_entry_get_data(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ return (uint8_t *)entry - entry->node_size;
+}
+void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+
+ lv_cache_entry_inc_ref(entry);
+ return lv_cache_entry_get_data(entry);
+}
+void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data)
+{
+ LV_UNUSED(user_data);
+
+ LV_ASSERT_NULL(entry);
+ if(lv_cache_entry_get_ref(entry) == 0) {
+ LV_LOG_ERROR("ref_cnt(%" LV_PRIu32 ") == 0", entry->ref_cnt);
+ return;
+ }
+
+ lv_cache_entry_dec_ref(entry);
+}
+lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size)
+{
+ LV_ASSERT_NULL(data);
+ return (lv_cache_entry_t *)((uint8_t *)data + node_size);
+}
+void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache)
+{
+ LV_ASSERT_NULL(entry);
+ entry->cache = cache;
+}
+const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+ return entry->cache;
+}
+
+uint32_t lv_cache_entry_get_size(const uint32_t node_size)
+{
+ return node_size + sizeof(lv_cache_entry_t);
+}
+lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache)
+{
+ void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size));
+ LV_ASSERT_MALLOC(res)
+ if(res == NULL) {
+ LV_LOG_ERROR("malloc failed");
+ return NULL;
+ }
+ lv_cache_entry_t * entry = (lv_cache_entry_t *)res;
+ lv_cache_entry_init(entry, cache, node_size);
+ return (lv_cache_entry_t *)((uint8_t *)entry + node_size);
+}
+void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size)
+{
+ LV_ASSERT_NULL(entry);
+ LV_ASSERT_NULL(cache);
+
+ entry->cache = cache;
+ entry->node_size = node_size;
+ entry->ref_cnt = 0;
+ entry->is_invalid = false;
+}
+void lv_cache_entry_delete(lv_cache_entry_t * entry)
+{
+ LV_ASSERT_NULL(entry);
+
+ void * data = lv_cache_entry_get_data(entry);
+ lv_free(data);
+}
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/cache/lv_cache_entry.h b/lib/lvgl/src/misc/cache/lv_cache_entry.h
new file mode 100644
index 00000000..0cd2fb2e
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache_entry.h
@@ -0,0 +1,116 @@
+/**
+* @file lv_cache_entry.h
+*
+ */
+
+#ifndef LV_CACHE_ENTRY_H
+#define LV_CACHE_ENTRY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../osal/lv_os.h"
+#include "../lv_types.h"
+#include "lv_cache_private.h"
+#include <stdbool.h>
+#include <stdlib.h>
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Get the size of a cache entry.
+ * @param node_size The size of the node in the cache.
+ * @return The size of the cache entry.
+ */
+uint32_t lv_cache_entry_get_size(const uint32_t node_size);
+
+/**
+ * Get the reference count of a cache entry.
+ * @param entry The cache entry to get the reference count of.
+ * @return The reference count of the cache entry.
+ */
+int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry);
+
+/**
+ * Get the node size of a cache entry. Which is the same size with @lv_cache_entry_get_size's node_size parameter.
+ * @param entry The cache entry to get the node size of.
+ * @return The node size of the cache entry.
+ */
+uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry);
+
+/**
+ * Check if a cache entry is invalid.
+ * @param entry The cache entry to check.
+ * @return True: the cache entry is invalid. False: the cache entry is valid.
+ */
+bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry);
+
+/**
+ * Get the data of a cache entry.
+ * @param entry The cache entry to get the data of.
+ * @return The pointer to the data of the cache entry.
+ */
+void * lv_cache_entry_get_data(lv_cache_entry_t * entry);
+
+/**
+ * Get the cache instance of a cache entry.
+ * @param entry The cache entry to get the cache instance of.
+ * @return The pointer to the cache instance of the cache entry.
+ */
+const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry);
+
+/**
+ * Get the cache entry of a data. The data should be allocated by the cache instance.
+ * @param data The data to get the cache entry of.
+ * @param node_size The size of the node in the cache.
+ * @return The pointer to the cache entry of the data.
+ */
+lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size);
+
+/**
+ * Allocate a cache entry.
+ * @param node_size The size of the node in the cache.
+ * @param cache The cache instance to allocate the cache entry from.
+ * @return The pointer to the allocated cache entry.
+ */
+lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache);
+
+/**
+ * Initialize a cache entry.
+ * @param entry The cache entry to initialize.
+ * @param cache The cache instance to allocate the cache entry from.
+ * @param node_size The size of the node in the cache.
+ */
+void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size);
+
+/**
+ * Deallocate a cache entry. And the data of the cache entry will be freed.
+ * @param entry The cache entry to deallocate.
+ */
+void lv_cache_entry_delete(lv_cache_entry_t * entry);
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_CACHE_ENTRY_H*/
diff --git a/lib/lvgl/src/misc/cache/lv_cache_entry_private.h b/lib/lvgl/src/misc/cache/lv_cache_entry_private.h
new file mode 100644
index 00000000..227585e1
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache_entry_private.h
@@ -0,0 +1,51 @@
+/**
+* @file lv_cache_entry_private.h
+*
+ */
+
+#ifndef LV_CACHE_ENTRY_PRIVATE
+#define LV_CACHE_ENTRY_PRIVATE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_types.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include "../../osal/lv_os.h"
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+void lv_cache_entry_reset_ref(lv_cache_entry_t * entry);
+void lv_cache_entry_inc_ref(lv_cache_entry_t * entry);
+void lv_cache_entry_dec_ref(lv_cache_entry_t * entry);
+void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size);
+void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid);
+void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache);
+void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry);
+void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data);
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_CACHE_ENTRY_PRIVATE*/
diff --git a/lib/lvgl/src/misc/cache/lv_cache_private.h b/lib/lvgl/src/misc/cache/lv_cache_private.h
new file mode 100644
index 00000000..4dbafd17
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_cache_private.h
@@ -0,0 +1,190 @@
+/**
+* @file lv_cache_private.h
+*
+*/
+
+#ifndef LV_CACHE_PRIVATE_H
+#define LV_CACHE_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_types.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include "../../osal/lv_os.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * The result of the cache reserve condition callback
+ */
+typedef enum {
+ LV_CACHE_RESERVE_COND_OK, /**< The condition is met and no entries need to be evicted */
+ LV_CACHE_RESERVE_COND_TOO_LARGE, /**< The condition is not met and the reserve size is too large */
+ LV_CACHE_RESERVE_COND_NEED_VICTIM, /**< The condition is not met and a victim is needed to be evicted */
+ LV_CACHE_RESERVE_COND_ERROR /**< An error occurred while checking the condition */
+} lv_cache_reserve_cond_res_t;
+
+struct _lv_cache_ops_t;
+struct _lv_cache_t;
+struct _lv_cache_class_t;
+struct _lv_cache_entry_t;
+
+typedef struct _lv_cache_ops_t lv_cache_ops_t;
+typedef struct _lv_cache_t lv_cache_t;
+typedef struct _lv_cache_class_t lv_cache_class_t;
+typedef struct _lv_cache_entry_t lv_cache_entry_t;
+
+typedef int8_t lv_cache_compare_res_t;
+typedef bool (*lv_cache_create_cb_t)(void * node, void * user_data);
+typedef void (*lv_cache_free_cb_t)(void * node, void * user_data);
+typedef lv_cache_compare_res_t (*lv_cache_compare_cb_t)(const void * a, const void * b);
+
+/**
+ * The cache instance allocation function, used by the cache class to allocate memory for cache instances.
+ * @return It should return a pointer to the allocated instance.
+ */
+typedef void * (*lv_cache_alloc_cb_t)(void);
+
+/**
+ * The cache instance initialization function, used by the cache class to initialize the cache instance.
+ * @return It should return true if the initialization is successful, false otherwise.
+ */
+typedef bool (*lv_cache_init_cb_t)(lv_cache_t * cache);
+
+/**
+ * The cache instance destruction function, used by the cache class to destroy the cache instance.
+ */
+typedef void (*lv_cache_destroy_cb_t)(lv_cache_t * cache, void * user_data);
+
+/**
+ * The cache get function, used by the cache class to get a cache entry by its key.
+ * @return @NULL if the key is not found.
+ */
+typedef lv_cache_entry_t * (*lv_cache_get_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * The cache add function, used by the cache class to add a cache entry with a given key.
+ * This function only cares about how to add the entry, it doesn't check if the entry already exists and doesn't care about is it a victim or not.
+ * @return the added cache entry, or NULL if the entry is not added.
+ */
+typedef lv_cache_entry_t * (*lv_cache_add_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * The cache remove function, used by the cache class to remove a cache entry from the cache but doesn't free the memory..
+ * This function only cares about how to remove the entry, it doesn't care about is it a victim or not.
+ */
+typedef void (*lv_cache_remove_cb_t)(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
+
+/**
+ * The cache drop function, used by the cache class to remove a cache entry from the cache and free the memory.
+ */
+typedef void (*lv_cache_drop_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
+
+/**
+ * The cache drop all function, used by the cache class to remove all cache entries from the cache and free the memory.
+ */
+typedef void (*lv_cache_drop_all_cb_t)(lv_cache_t * cache, void * user_data);
+
+/**
+ * The cache get victim function, used by the cache class to get a victim entry to be evicted.
+ */
+typedef lv_cache_entry_t * (*lv_cache_get_victim_cb)(lv_cache_t * cache, void * user_data);
+
+/**
+ * The cache reserve condition function, used by the cache class to check if a new entry can be added to the cache without exceeding its maximum size.
+ * See @lv_cache_reserve_cond_res_t for the possible results.
+ */
+typedef lv_cache_reserve_cond_res_t (*lv_cache_reserve_cond_cb)(lv_cache_t * cache, const void * key, size_t size,
+ void * user_data);
+
+/**
+ * The cache operations struct
+ */
+struct _lv_cache_ops_t {
+ lv_cache_compare_cb_t compare_cb; /**< Compare function for keys */
+ lv_cache_create_cb_t create_cb; /**< Create function for nodes */
+ lv_cache_free_cb_t free_cb; /**< Free function for nodes */
+};
+
+/**
+ * The cache entry struct
+ */
+struct _lv_cache_t {
+ const lv_cache_class_t * clz; /**< The cache class. There are two built-in classes:
+ * @lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
+ * @lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy. */
+
+ uint32_t node_size; /**< The size of a node */
+
+ uint32_t max_size; /**< The maximum size of the cache */
+ uint32_t size; /**< The current size of the cache */
+
+ lv_cache_ops_t ops; /**< The cache operations struct @lv_cache_ops_t */
+
+ lv_mutex_t lock; /**< The cache lock used to protect the cache in multithreading environments */
+};
+
+/**
+ * The cache class struct for building custom cache classes, and there are two built-in classes for examples:
+ * @lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
+ * @lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
+ */
+struct _lv_cache_class_t {
+ lv_cache_alloc_cb_t alloc_cb; /**< The allocation function for cache entries */
+ lv_cache_init_cb_t init_cb; /**< The initialization function for cache entries */
+ lv_cache_destroy_cb_t destroy_cb; /**< The destruction function for cache entries */
+
+ lv_cache_get_cb_t get_cb; /**< The get function for cache entries */
+ lv_cache_add_cb_t add_cb; /**< The add function for cache entries */
+ lv_cache_remove_cb_t remove_cb; /**< The remove function for cache entries */
+ lv_cache_drop_cb_t drop_cb; /**< The drop function for cache entries */
+ lv_cache_drop_all_cb_t drop_all_cb; /**< The drop all function for cache entries */
+ lv_cache_get_victim_cb get_victim_cb; /**< The get victim function for cache entries */
+ lv_cache_reserve_cond_cb reserve_cond_cb; /**< The reserve condition function for cache entries */
+};
+
+/*-----------------
+ * Cache entry slot
+ *----------------*/
+
+struct _lv_cache_slot_size_t;
+
+typedef struct _lv_cache_slot_size_t lv_cache_slot_size_t;
+
+/**
+ * The cache entry slot struct
+ * To add new fields to the cache entry, add them to a new struct and add it to the first field of the cache data struct.
+ * And this one is a size slot for the cache entry.
+ */
+struct _lv_cache_slot_size_t {
+ size_t size;
+};
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_CACHE_PRIVATE_H*/
diff --git a/lib/lvgl/src/misc/cache/lv_image_cache.c b/lib/lvgl/src/misc/cache/lv_image_cache.c
new file mode 100644
index 00000000..28f0aec7
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_image_cache.c
@@ -0,0 +1,109 @@
+/**
+* @file lv_image_cache.c
+*
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_assert.h"
+#include "lv_image_cache.h"
+#include "../../core/lv_global.h"
+/*********************
+ * DEFINES
+ *********************/
+#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
+#define img_header_cache_p (LV_GLOBAL_DEFAULT()->img_header_cache)
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+void lv_image_cache_drop(const void * src)
+{
+ /*If user invalidate image, the header cache should be invalidated too.*/
+ lv_image_header_cache_drop(src);
+
+#if LV_CACHE_DEF_SIZE > 0
+ if(src == NULL) {
+ lv_cache_drop_all(img_cache_p, NULL);
+ return;
+ }
+
+ lv_image_cache_data_t search_key = {
+ .src = src,
+ .src_type = lv_image_src_get_type(src),
+ };
+
+ lv_cache_drop(img_cache_p, &search_key, NULL);
+#else
+ LV_UNUSED(src);
+#endif
+}
+
+void lv_image_cache_resize(uint32_t new_size, bool evict_now)
+{
+#if LV_CACHE_DEF_SIZE > 0
+ lv_cache_set_max_size(img_cache_p, new_size, NULL);
+ if(evict_now) {
+ lv_cache_reserve(img_cache_p, new_size, NULL);
+ }
+#else
+ LV_UNUSED(new_size);
+ LV_UNUSED(evict_now);
+#endif
+}
+
+void lv_image_header_cache_drop(const void * src)
+{
+#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0
+ if(src == NULL) {
+ lv_cache_drop_all(img_header_cache_p, NULL);
+ return;
+ }
+
+ lv_image_header_cache_data_t search_key = {
+ .src = src,
+ .src_type = lv_image_src_get_type(src),
+ };
+
+ lv_cache_drop(img_header_cache_p, &search_key, NULL);
+#else
+ LV_UNUSED(src);
+#endif
+}
+
+void lv_image_header_cache_resize(uint32_t new_size, bool evict_now)
+{
+#if LV_IMAGE_HEADER_CACHE_DEF_CNT > 0
+ lv_cache_set_max_size(img_header_cache_p, new_size, NULL);
+ if(evict_now) {
+ lv_cache_reserve(img_header_cache_p, new_size, NULL);
+ }
+#else
+ LV_UNUSED(new_size);
+ LV_UNUSED(evict_now);
+#endif
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/misc/cache/lv_image_cache.h b/lib/lvgl/src/misc/cache/lv_image_cache.h
new file mode 100644
index 00000000..f8a59034
--- /dev/null
+++ b/lib/lvgl/src/misc/cache/lv_image_cache.h
@@ -0,0 +1,69 @@
+/**
+* @file lv_image_cache.h
+*
+ */
+
+#ifndef LV_IMAGE_CACHE_H
+#define LV_IMAGE_CACHE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_cache_private.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Invalidate image cache. Use NULL to invalidate all images.
+ * @param src pointer to an image source.
+ */
+void lv_image_cache_drop(const void * src);
+
+/**
+ * Resize image cache.
+ * @param new_size new size of the cache in bytes.
+ * @param evict_now true: evict the images should be removed by the eviction policy, false: wait for the next cache cleanup.
+ */
+void lv_image_cache_resize(uint32_t new_size, bool evict_now);
+
+/**
+ * Invalidate image header cache. Use NULL to invalidate all image headers.
+ * It's also automatically called when an image is invalidated.
+ * @param src pointer to an image source.
+ */
+void lv_image_header_cache_drop(const void * src);
+
+/**
+ * Resize image header cache.
+ * @param new_size new size of the cache in count of image headers.
+ * @param evict_now true: evict the image headers should be removed by the eviction policy, false: wait for the next cache cleanup.
+ */
+void lv_image_header_cache_resize(uint32_t new_size, bool evict_now);
+
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_IMAGE_CACHE_H*/