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