1 /**
2 * @file lv_img_cache.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../lv_misc/lv_debug.h"
10 #include "lv_img_cache.h"
11 #include "lv_img_decoder.h"
12 #include "lv_draw_img.h"
13 #include "../lv_hal/lv_hal_tick.h"
14 #include "../lv_misc/lv_gc.h"
15
16 #if defined(LV_GC_INCLUDE)
17 #include LV_GC_INCLUDE
18 #endif /* LV_ENABLE_GC */
19 /*********************
20 * DEFINES
21 *********************/
22 /*Decrement life with this value in every open*/
23 #define LV_IMG_CACHE_AGING 1
24
25 /*Boost life by this factor (multiply time_to_open with this value)*/
26 #define LV_IMG_CACHE_LIFE_GAIN 1
27
28 /*Don't let life to be greater than this limit because it would require a lot of time to
29 * "die" from very high values */
30 #define LV_IMG_CACHE_LIFE_LIMIT 1000
31
32 #if LV_IMG_CACHE_DEF_SIZE < 1
33 #error "LV_IMG_CACHE_DEF_SIZE must be >= 1. See lv_conf.h"
34 #endif
35
36 /**********************
37 * TYPEDEFS
38 **********************/
39
40 /**********************
41 * STATIC PROTOTYPES
42 **********************/
43
44 /**********************
45 * STATIC VARIABLES
46 **********************/
47 static uint16_t entry_cnt;
48
49 /**********************
50 * MACROS
51 **********************/
52
53 /**********************
54 * GLOBAL FUNCTIONS
55 **********************/
56
57 /**
58 * Open an image using the image decoder interface and cache it.
59 * The image will be left open meaning if the image decoder open callback allocated memory then it will remain.
60 * The image is closed if a new image is opened and the new image takes its place in the cache.
61 * @param src source of the image. Path to file or pointer to an `lv_img_dsc_t` variable
62 * @param color color The color of the image with `LV_IMG_CF_ALPHA_...`
63 * @return pointer to the cache entry or NULL if can open the image
64 */
_lv_img_cache_open(const void * src,lv_color_t color)65 lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color)
66 {
67 if(entry_cnt == 0) {
68 LV_LOG_WARN("lv_img_cache_open: the cache size is 0");
69 return NULL;
70 }
71
72 lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
73
74 /*Decrement all lifes. Make the entries older*/
75 uint16_t i;
76 for(i = 0; i < entry_cnt; i++) {
77 if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {
78 cache[i].life -= LV_IMG_CACHE_AGING;
79 }
80 }
81
82 /*Is the image cached?*/
83 lv_img_cache_entry_t * cached_src = NULL;
84 for(i = 0; i < entry_cnt; i++) {
85 bool match = false;
86 lv_img_src_t src_type = lv_img_src_get_type(cache[i].dec_dsc.src);
87 if(src_type == LV_IMG_SRC_VARIABLE) {
88 if(cache[i].dec_dsc.src == src && cache[i].dec_dsc.color.full == color.full) match = true;
89 }
90 else if(src_type == LV_IMG_SRC_FILE) {
91 if(strcmp(cache[i].dec_dsc.src, src) == 0) match = true;
92 }
93
94 if(match) {
95 /* If opened increment its life.
96 * Image difficult to open should live longer to keep avoid frequent their recaching.
97 * Therefore increase `life` with `time_to_open`*/
98 cached_src = &cache[i];
99 cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;
100 if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;
101 LV_LOG_TRACE("image draw: image found in the cache");
102 break;
103 }
104 }
105
106 /*The image is not cached then cache it now*/
107 if(cached_src == NULL) {
108 /*Find an entry to reuse. Select the entry with the least life*/
109 cached_src = &cache[0];
110 for(i = 1; i < entry_cnt; i++) {
111 if(cache[i].life < cached_src->life) {
112 cached_src = &cache[i];
113 }
114 }
115
116 /*Close the decoder to reuse if it was opened (has a valid source)*/
117 if(cached_src->dec_dsc.src) {
118 lv_img_decoder_close(&cached_src->dec_dsc);
119 LV_LOG_INFO("image draw: cache miss, close and reuse an entry");
120 }
121 else {
122 LV_LOG_INFO("image draw: cache miss, cached to an empty entry");
123 }
124
125 /*Open the image and measure the time to open*/
126 uint32_t t_start;
127 t_start = lv_tick_get();
128 cached_src->dec_dsc.time_to_open = 0;
129 lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color);
130 if(open_res == LV_RES_INV) {
131 LV_LOG_WARN("Image draw cannot open the image resource");
132 lv_img_decoder_close(&cached_src->dec_dsc);
133 _lv_memset_00(&cached_src->dec_dsc, sizeof(lv_img_decoder_dsc_t));
134 _lv_memset_00(cached_src, sizeof(lv_img_cache_entry_t));
135 cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its use */
136 return NULL;
137 }
138
139 cached_src->life = 0;
140
141 /*If `time_to_open` was not set in the open function set it here*/
142 if(cached_src->dec_dsc.time_to_open == 0) {
143 cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);
144 }
145
146 if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;
147 }
148
149 return cached_src;
150 }
151
152 /**
153 * Set the number of images to be cached.
154 * More cached images mean more opened image at same time which might mean more memory usage.
155 * E.g. if 20 PNG or JPG images are open in the RAM they consume memory while opened in the cache.
156 * @param new_entry_cnt number of image to cache
157 */
lv_img_cache_set_size(uint16_t new_entry_cnt)158 void lv_img_cache_set_size(uint16_t new_entry_cnt)
159 {
160 if(LV_GC_ROOT(_lv_img_cache_array) != NULL) {
161 /*Clean the cache before free it*/
162 lv_img_cache_invalidate_src(NULL);
163 lv_mem_free(LV_GC_ROOT(_lv_img_cache_array));
164 }
165
166 /*Reallocate the cache*/
167 LV_GC_ROOT(_lv_img_cache_array) = lv_mem_alloc(sizeof(lv_img_cache_entry_t) * new_entry_cnt);
168 LV_ASSERT_MEM(LV_GC_ROOT(_lv_img_cache_array));
169 if(LV_GC_ROOT(_lv_img_cache_array) == NULL) {
170 entry_cnt = 0;
171 return;
172 }
173 entry_cnt = new_entry_cnt;
174
175 /*Clean the cache*/
176 uint16_t i;
177 for(i = 0; i < entry_cnt; i++) {
178 _lv_memset_00(&LV_GC_ROOT(_lv_img_cache_array)[i].dec_dsc, sizeof(lv_img_decoder_dsc_t));
179 _lv_memset_00(&LV_GC_ROOT(_lv_img_cache_array)[i], sizeof(lv_img_cache_entry_t));
180 }
181 }
182
183 /**
184 * Invalidate an image source in the cache.
185 * Useful if the image source is updated therefore it needs to be cached again.
186 * @param src an image source path to a file or pointer to an `lv_img_dsc_t` variable.
187 */
lv_img_cache_invalidate_src(const void * src)188 void lv_img_cache_invalidate_src(const void * src)
189 {
190
191 lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);
192
193 uint16_t i;
194 for(i = 0; i < entry_cnt; i++) {
195 if(cache[i].dec_dsc.src == src || src == NULL) {
196 if(cache[i].dec_dsc.src != NULL) {
197 lv_img_decoder_close(&cache[i].dec_dsc);
198 }
199
200 _lv_memset_00(&cache[i].dec_dsc, sizeof(lv_img_decoder_dsc_t));
201 _lv_memset_00(&cache[i], sizeof(lv_img_cache_entry_t));
202 }
203 }
204 }
205
206 /**********************
207 * STATIC FUNCTIONS
208 **********************/
209