1 /**
2  * @file lv_draw_img.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_img.h"
10 #include "lv_img_cache.h"
11 #include "../hal/lv_hal_disp.h"
12 #include "../misc/lv_log.h"
13 #include "../core/lv_refr.h"
14 #include "../misc/lv_mem.h"
15 #include "../misc/lv_math.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 /**********************
22  *      TYPEDEFS
23  **********************/
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 static lv_res_t /* LV_ATTRIBUTE_FAST_MEM */ decode_and_draw(lv_draw_ctx_t * draw_ctx,
29                                                             const lv_draw_img_dsc_t * draw_dsc,
30                                                             const lv_area_t * coords, const void * src);
31 
32 static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg);
33 static void draw_cleanup(_lv_img_cache_entry_t * cache);
34 
35 /**********************
36  *  STATIC VARIABLES
37  **********************/
38 
39 /**********************
40  *      MACROS
41  **********************/
42 
43 /**********************
44  *   GLOBAL FUNCTIONS
45  **********************/
46 
lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)47 void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
48 {
49     lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
50     dsc->recolor = lv_color_black();
51     dsc->opa = LV_OPA_COVER;
52     dsc->zoom = LV_IMG_ZOOM_NONE;
53     dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
54 }
55 
56 /**
57  * Draw an image
58  * @param coords the coordinates of the image
59  * @param mask the image will be drawn only in this area
60  * @param src pointer to a lv_color_t array which contains the pixels of the image
61  * @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
62  */
lv_draw_img(lv_draw_ctx_t * draw_ctx,const lv_draw_img_dsc_t * dsc,const lv_area_t * coords,const void * src)63 void lv_draw_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const void * src)
64 {
65     if(src == NULL) {
66         LV_LOG_WARN("Image draw: src is NULL");
67         show_error(draw_ctx, coords, "No\ndata");
68         return;
69     }
70 
71     if(dsc->opa <= LV_OPA_MIN) return;
72 
73     lv_res_t res = LV_RES_INV;
74 
75     if(draw_ctx->draw_img) {
76         res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
77     }
78 
79     if(res != LV_RES_OK) {
80         res = decode_and_draw(draw_ctx, dsc, coords, src);
81     }
82 
83     if(res != LV_RES_OK) {
84         LV_LOG_WARN("Image draw error");
85         show_error(draw_ctx, coords, "No\ndata");
86     }
87 }
88 
89 /**
90  * Get the pixel size of a color format in bits
91  * @param cf a color format (`LV_IMG_CF_...`)
92  * @return the pixel size in bits
93  */
lv_img_cf_get_px_size(lv_img_cf_t cf)94 uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
95 {
96     uint8_t px_size = 0;
97 
98     switch(cf) {
99         case LV_IMG_CF_UNKNOWN:
100         case LV_IMG_CF_RAW:
101             px_size = 0;
102             break;
103         case LV_IMG_CF_TRUE_COLOR:
104         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
105             px_size = LV_COLOR_SIZE;
106             break;
107         case LV_IMG_CF_TRUE_COLOR_ALPHA:
108             px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
109             break;
110         case LV_IMG_CF_INDEXED_1BIT:
111         case LV_IMG_CF_ALPHA_1BIT:
112             px_size = 1;
113             break;
114         case LV_IMG_CF_INDEXED_2BIT:
115         case LV_IMG_CF_ALPHA_2BIT:
116             px_size = 2;
117             break;
118         case LV_IMG_CF_INDEXED_4BIT:
119         case LV_IMG_CF_ALPHA_4BIT:
120             px_size = 4;
121             break;
122         case LV_IMG_CF_INDEXED_8BIT:
123         case LV_IMG_CF_ALPHA_8BIT:
124             px_size = 8;
125             break;
126         default:
127             px_size = 0;
128             break;
129     }
130 
131     return px_size;
132 }
133 
134 /**
135  * Check if a color format is chroma keyed or not
136  * @param cf a color format (`LV_IMG_CF_...`)
137  * @return true: chroma keyed; false: not chroma keyed
138  */
lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)139 bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
140 {
141     bool is_chroma_keyed = false;
142 
143     switch(cf) {
144         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
145         case LV_IMG_CF_RAW_CHROMA_KEYED:
146             is_chroma_keyed = true;
147             break;
148 
149         default:
150             is_chroma_keyed = false;
151             break;
152     }
153 
154     return is_chroma_keyed;
155 }
156 
157 /**
158  * Check if a color format has alpha channel or not
159  * @param cf a color format (`LV_IMG_CF_...`)
160  * @return true: has alpha channel; false: doesn't have alpha channel
161  */
lv_img_cf_has_alpha(lv_img_cf_t cf)162 bool lv_img_cf_has_alpha(lv_img_cf_t cf)
163 {
164     bool has_alpha = false;
165 
166     switch(cf) {
167         case LV_IMG_CF_TRUE_COLOR_ALPHA:
168         case LV_IMG_CF_RAW_ALPHA:
169         case LV_IMG_CF_INDEXED_1BIT:
170         case LV_IMG_CF_INDEXED_2BIT:
171         case LV_IMG_CF_INDEXED_4BIT:
172         case LV_IMG_CF_INDEXED_8BIT:
173         case LV_IMG_CF_ALPHA_1BIT:
174         case LV_IMG_CF_ALPHA_2BIT:
175         case LV_IMG_CF_ALPHA_4BIT:
176         case LV_IMG_CF_ALPHA_8BIT:
177             has_alpha = true;
178             break;
179         default:
180             has_alpha = false;
181             break;
182     }
183 
184     return has_alpha;
185 }
186 
187 /**
188  * Get the type of an image source
189  * @param src pointer to an image source:
190  *  - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
191  *  - a path to a file (e.g. "S:/folder/image.bin")
192  *  - or a symbol (e.g. LV_SYMBOL_CLOSE)
193  * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
194  */
lv_img_src_get_type(const void * src)195 lv_img_src_t lv_img_src_get_type(const void * src)
196 {
197     lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
198 
199     if(src == NULL) return img_src_type;
200     const uint8_t * u8_p = src;
201 
202     /*The first or fourth byte depending on platform endianess shows the type of the image source*/
203 #if LV_BIG_ENDIAN_SYSTEM
204     if(u8_p[3] >= 0x20 && u8_p[3] <= 0x7F) {
205 #else
206     if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
207 #endif
208         img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
209     }
210 #if LV_BIG_ENDIAN_SYSTEM
211     else if(u8_p[3] >= 0x80) {
212 #else
213     else if(u8_p[0] >= 0x80) {
214 #endif
215         img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
216     }
217     else {
218         img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
219     }
220 
221     if(LV_IMG_SRC_UNKNOWN == img_src_type) {
222         LV_LOG_WARN("lv_img_src_get_type: unknown image type");
223     }
224 
225     return img_src_type;
226 }
227 
228 void lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
229                          const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
230 {
231     if(draw_ctx->draw_img_decoded == NULL) return;
232 
233     draw_ctx->draw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
234 }
235 
236 /**********************
237  *   STATIC FUNCTIONS
238  **********************/
239 
240 static lv_res_t LV_ATTRIBUTE_FAST_MEM decode_and_draw(lv_draw_ctx_t * draw_ctx,
241                                                       const lv_draw_img_dsc_t * draw_dsc,
242                                                       const lv_area_t * coords, const void * src)
243 {
244     if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
245 
246     _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
247 
248     if(cdsc == NULL) return LV_RES_INV;
249 
250     lv_img_cf_t cf;
251     if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
252     else if(LV_IMG_CF_ALPHA_8BIT == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_ALPHA_8BIT;
253     else if(LV_IMG_CF_RGB565A8 == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_RGB565A8;
254     else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
255     else cf = LV_IMG_CF_TRUE_COLOR;
256 
257     if(cf == LV_IMG_CF_ALPHA_8BIT) {
258         if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
259             /* resume normal method */
260             cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
261             cdsc->dec_dsc.img_data = NULL;
262         }
263     }
264 
265     if(cdsc->dec_dsc.error_msg != NULL) {
266         LV_LOG_WARN("Image draw error");
267 
268         show_error(draw_ctx, coords, cdsc->dec_dsc.error_msg);
269     }
270     /*The decoder could open the image and gave the entire uncompressed image.
271      *Just draw it!*/
272     else if(cdsc->dec_dsc.img_data) {
273         lv_area_t map_area_rot;
274         lv_area_copy(&map_area_rot, coords);
275         if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
276             int32_t w = lv_area_get_width(coords);
277             int32_t h = lv_area_get_height(coords);
278 
279             _lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
280 
281             map_area_rot.x1 += coords->x1;
282             map_area_rot.y1 += coords->y1;
283             map_area_rot.x2 += coords->x1;
284             map_area_rot.y2 += coords->y1;
285         }
286 
287         lv_area_t clip_com; /*Common area of mask and coords*/
288         bool union_ok;
289         union_ok = _lv_area_intersect(&clip_com, draw_ctx->clip_area, &map_area_rot);
290         /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
291         if(union_ok == false) {
292             draw_cleanup(cdsc);
293             return LV_RES_OK;
294         }
295 
296         const lv_area_t * clip_area_ori = draw_ctx->clip_area;
297         draw_ctx->clip_area = &clip_com;
298         lv_draw_img_decoded(draw_ctx, draw_dsc, coords, cdsc->dec_dsc.img_data, cf);
299         draw_ctx->clip_area = clip_area_ori;
300     }
301     /*The whole uncompressed image is not available. Try to read it line-by-line*/
302     else {
303         lv_area_t mask_com; /*Common area of mask and coords*/
304         bool union_ok;
305         union_ok = _lv_area_intersect(&mask_com, draw_ctx->clip_area, coords);
306         /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
307         if(union_ok == false) {
308             draw_cleanup(cdsc);
309             return LV_RES_OK;
310         }
311 
312         int32_t width = lv_area_get_width(&mask_com);
313 
314         uint8_t  * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
315                                         LV_IMG_PX_SIZE_ALPHA_BYTE);  /*+1 because of the possible alpha byte*/
316 
317         const lv_area_t * clip_area_ori = draw_ctx->clip_area;
318         lv_area_t line;
319         lv_area_copy(&line, &mask_com);
320         lv_area_set_height(&line, 1);
321         int32_t x = mask_com.x1 - coords->x1;
322         int32_t y = mask_com.y1 - coords->y1;
323         int32_t row;
324         lv_res_t read_res;
325         for(row = mask_com.y1; row <= mask_com.y2; row++) {
326             lv_area_t mask_line;
327             union_ok = _lv_area_intersect(&mask_line, clip_area_ori, &line);
328             if(union_ok == false) continue;
329 
330             read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
331             if(read_res != LV_RES_OK) {
332                 lv_img_decoder_close(&cdsc->dec_dsc);
333                 LV_LOG_WARN("Image draw can't read the line");
334                 lv_mem_buf_release(buf);
335                 draw_cleanup(cdsc);
336                 draw_ctx->clip_area = clip_area_ori;
337                 return LV_RES_INV;
338             }
339 
340             draw_ctx->clip_area = &mask_line;
341             lv_draw_img_decoded(draw_ctx, draw_dsc, &line, buf, cf);
342             line.y1++;
343             line.y2++;
344             y++;
345         }
346         draw_ctx->clip_area = clip_area_ori;
347         lv_mem_buf_release(buf);
348     }
349 
350     draw_cleanup(cdsc);
351     return LV_RES_OK;
352 }
353 
354 static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg)
355 {
356     lv_draw_rect_dsc_t rect_dsc;
357     lv_draw_rect_dsc_init(&rect_dsc);
358     rect_dsc.bg_color = lv_color_white();
359     lv_draw_rect(draw_ctx, &rect_dsc, coords);
360 
361     lv_draw_label_dsc_t label_dsc;
362     lv_draw_label_dsc_init(&label_dsc);
363     lv_draw_label(draw_ctx, &label_dsc, coords, msg, NULL);
364 }
365 
366 static void draw_cleanup(_lv_img_cache_entry_t * cache)
367 {
368     /*Automatically close images with no caching*/
369 #if LV_IMG_CACHE_DEF_SIZE == 0
370     lv_img_decoder_close(&cache->dec_dsc);
371 #else
372     LV_UNUSED(cache);
373 #endif
374 }
375