acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_img_cache.c (6911B)

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