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