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 "../lv_misc/lv_log.h"
12 
13 /*********************
14  *      DEFINES
15  *********************/
16 
17 /**********************
18  *      TYPEDEFS
19  **********************/
20 
21 /**********************
22  *  STATIC PROTOTYPES
23  **********************/
24 static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask, const void * src,
25                                  const lv_style_t * style, lv_opa_t opa_scale);
26 
27 /**********************
28  *  STATIC VARIABLES
29  **********************/
30 
31 /**********************
32  *      MACROS
33  **********************/
34 
35 /**********************
36  *   GLOBAL FUNCTIONS
37  **********************/
38 
39 /**
40  * Draw an image
41  * @param coords the coordinates of the image
42  * @param mask the image will be drawn only in this area
43  * @param src pointer to a lv_color_t array which contains the pixels of the image
44  * @param style style of the image
45  * @param opa_scale scale down all opacities by the factor
46  */
lv_draw_img(const lv_area_t * coords,const lv_area_t * mask,const void * src,const lv_style_t * style,lv_opa_t opa_scale)47 void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask, const void * src, const lv_style_t * style,
48                  lv_opa_t opa_scale)
49 {
50     if(src == NULL) {
51         LV_LOG_WARN("Image draw: src is NULL");
52         lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
53         lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1, NULL);
54         return;
55     }
56 
57     lv_res_t res;
58     res = lv_img_draw_core(coords, mask, src, style, opa_scale);
59 
60     if(res == LV_RES_INV) {
61         LV_LOG_WARN("Image draw error");
62         lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
63         lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL, -1, -1, NULL);
64         return;
65     }
66 }
67 
68 /**
69  * Get the color of an image's pixel
70  * @param dsc an image descriptor
71  * @param x x coordinate of the point to get
72  * @param y x coordinate of the point to get
73  * @param style style of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` `style->image.color` shows
74  * the color. Can be `NULL` but for `ALPHA` images black will be returned. In other cases it is not
75  * used.
76  * @return color of the point
77  */
lv_img_buf_get_px_color(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,const lv_style_t * style)78 lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, const lv_style_t * style)
79 {
80     lv_color_t p_color = LV_COLOR_BLACK;
81     if(x >= dsc->header.w) {
82         x = dsc->header.w - 1;
83         LV_LOG_WARN("lv_canvas_get_px: x is too large (out of canvas)");
84     } else if(x < 0) {
85         x = 0;
86         LV_LOG_WARN("lv_canvas_get_px: x is < 0 (out of canvas)");
87     }
88 
89     if(y >= dsc->header.h) {
90         y = dsc->header.h - 1;
91         LV_LOG_WARN("lv_canvas_get_px: y is too large (out of canvas)");
92     } else if(y < 0) {
93         y = 0;
94         LV_LOG_WARN("lv_canvas_get_px: y is < 0 (out of canvas)");
95     }
96 
97     uint8_t * buf_u8 = (uint8_t *)dsc->data;
98 
99     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
100        dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
101         uint8_t px_size = lv_img_color_format_get_px_size(dsc->header.cf) >> 3;
102         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
103         memcpy(&p_color, &buf_u8[px], sizeof(lv_color_t));
104 #if LV_COLOR_SIZE == 32
105         p_color.ch.alpha = 0xFF; /*Only the color should be get so use a deafult alpha value*/
106 #endif
107     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
108         buf_u8 += 4 * 2;
109         uint8_t bit = x & 0x7;
110         x           = x >> 3;
111 
112         /* Get the current pixel.
113          * dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
114          * so the possible real width are 8, 16, 24 ...*/
115         uint32_t px  = ((dsc->header.w + 7) >> 3) * y + x;
116         p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
117     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
118         buf_u8 += 4 * 4;
119         uint8_t bit = (x & 0x3) * 2;
120         x           = x >> 2;
121 
122         /* Get the current pixel.
123          * dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
124          * so the possible real width are 4, 8, 12 ...*/
125         uint32_t px  = ((dsc->header.w + 3) >> 2) * y + x;
126         p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
127     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
128         buf_u8 += 4 * 16;
129         uint8_t bit = (x & 0x1) * 4;
130         x           = x >> 1;
131 
132         /* Get the current pixel.
133          * dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
134          * so the possible real width are 2, 4, 6 ...*/
135         uint32_t px  = ((dsc->header.w + 1) >> 1) * y + x;
136         p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
137     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
138         buf_u8 += 4 * 256;
139         uint32_t px  = dsc->header.w * y + x;
140         p_color.full = buf_u8[px];
141     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
142               dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
143         if(style)
144             p_color = style->image.color;
145         else
146             p_color = LV_COLOR_BLACK;
147     }
148     return p_color;
149 }
150 
151 /**
152  * Get the alpha value of an image's pixel
153  * @param dsc pointer to an image descriptor
154  * @param x x coordinate of the point to set
155  * @param y x coordinate of the point to set
156  * @return alpha value of the point
157  */
lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y)158 lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
159 {
160     if(x >= dsc->header.w) {
161         x = dsc->header.w - 1;
162         LV_LOG_WARN("lv_canvas_get_px: x is too large (out of canvas)");
163     } else if(x < 0) {
164         x = 0;
165         LV_LOG_WARN("lv_canvas_get_px: x is < 0 (out of canvas)");
166     }
167 
168     if(y >= dsc->header.h) {
169         y = dsc->header.h - 1;
170         LV_LOG_WARN("lv_canvas_get_px: y is too large (out of canvas)");
171     } else if(y < 0) {
172         y = 0;
173         LV_LOG_WARN("lv_canvas_get_px: y is < 0 (out of canvas)");
174     }
175 
176     uint8_t * buf_u8 = (uint8_t *)dsc->data;
177 
178     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
179         uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
180         return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
181     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
182         uint8_t bit = x & 0x7;
183         x           = x >> 3;
184 
185         /* Get the current pixel.
186          * dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
187          * so the possible real width are 8 ,16, 24 ...*/
188         uint32_t px    = ((dsc->header.w + 7) >> 3) * y + x;
189         uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
190         return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
191     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
192         const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
193 
194         uint8_t bit = (x & 0x3) * 2;
195         x           = x >> 2;
196 
197         /* Get the current pixel.
198          * dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
199          * so the possible real width are 4 ,8, 12 ...*/
200         uint32_t px    = ((dsc->header.w + 3) >> 2) * y + x;
201         uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
202         return opa_table[px_opa];
203     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
204         const uint8_t opa_table[16] = {0,  17, 34,  51, /*Opacity mapping with bpp = 4*/
205                                        68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255};
206 
207         uint8_t bit = (x & 0x1) * 4;
208         x           = x >> 1;
209 
210         /* Get the current pixel.
211          * dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
212          * so the possible real width are 2 ,4, 6 ...*/
213         uint32_t px    = ((dsc->header.w + 1) >> 1) * y + x;
214         uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
215         return opa_table[px_opa];
216     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
217         uint32_t px = dsc->header.w * y + x;
218         return buf_u8[px];
219     }
220 
221     return LV_OPA_COVER;
222 }
223 
224 /**
225  * Set the color of a pixel of an image. The alpha channel won't be affected.
226  * @param dsc pointer to an image descriptor
227  * @param x x coordinate of the point to set
228  * @param y x coordinate of the point to set
229  * @param c color of the point
230  */
lv_img_buf_set_px_color(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_color_t c)231 void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
232 {
233     uint8_t * buf_u8 = (uint8_t *)dsc->data;
234 
235     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
236         uint8_t px_size = lv_img_color_format_get_px_size(dsc->header.cf) >> 3;
237         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
238         memcpy(&buf_u8[px], &c, px_size);
239     } else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
240         uint8_t px_size = lv_img_color_format_get_px_size(dsc->header.cf) >> 3;
241         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
242         memcpy(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
243     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
244         buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
245 
246         uint8_t bit = x & 0x7;
247         x           = x >> 3;
248 
249         /* Get the current pixel.
250          * dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
251          * so the possible real width are 8 ,16, 24 ...*/
252         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
253         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
254         buf_u8[px]  = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
255     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
256         buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
257         uint8_t bit = (x & 0x3) * 2;
258         x           = x >> 2;
259 
260         /* Get the current pixel.
261          * dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
262          * so the possible real width are 4, 8 ,12 ...*/
263         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
264 
265         buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
266         buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
267     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
268         buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
269         uint8_t bit = (x & 0x1) * 4;
270         x           = x >> 1;
271 
272         /* Get the current pixel.
273          * dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
274          * so the possible real width are 2 ,4, 6 ...*/
275         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
276         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
277         buf_u8[px]  = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
278     } else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
279         buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
280         uint32_t px = dsc->header.w * y + x;
281         buf_u8[px]  = c.full;
282     }
283 }
284 
285 /**
286  * Set the alpha value of a pixel of an image. The color won't be affected
287  * @param dsc pointer to an image descriptor
288  * @param x x coordinate of the point to set
289  * @param y x coordinate of the point to set
290  * @param opa the desired opacity
291  */
lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_opa_t opa)292 void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
293 {
294     uint8_t * buf_u8 = (uint8_t *)dsc->data;
295 
296     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
297         uint8_t px_size          = lv_img_color_format_get_px_size(dsc->header.cf) >> 3;
298         uint32_t px              = dsc->header.w * y * px_size + x * px_size;
299         buf_u8[px + px_size - 1] = opa;
300     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
301         opa         = opa >> 7; /*opa -> [0,1]*/
302         uint8_t bit = x & 0x7;
303         x           = x >> 3;
304 
305         /* Get the current pixel.
306          * dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
307          * so the possible real width are 8 ,16, 24 ...*/
308         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
309         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
310         buf_u8[px]  = buf_u8[px] | ((opa & 0x1) << (7 - bit));
311     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
312         opa         = opa >> 6; /*opa -> [0,3]*/
313         uint8_t bit = (x & 0x3) * 2;
314         x           = x >> 2;
315 
316         /* Get the current pixel.
317          * dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
318          * so the possible real width are 4 ,8, 12 ...*/
319         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
320         buf_u8[px]  = buf_u8[px] & ~(3 << (6 - bit));
321         buf_u8[px]  = buf_u8[px] | ((opa & 0x3) << (6 - bit));
322     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
323         opa         = opa >> 4; /*opa -> [0,15]*/
324         uint8_t bit = (x & 0x1) * 4;
325         x           = x >> 1;
326 
327         /* Get the current pixel.
328          * dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
329          * so the possible real width are 2 ,4, 6 ...*/
330         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
331         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
332         buf_u8[px]  = buf_u8[px] | ((opa & 0xF) << (4 - bit));
333     } else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
334         uint32_t px = dsc->header.w * y + x;
335         buf_u8[px]  = opa;
336     }
337 }
338 
339 /**
340  * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
341  * @param dsc pointer to an image descriptor
342  * @param id the palette color to set:
343  *   - for `LV_IMG_CF_INDEXED1`: 0..1
344  *   - for `LV_IMG_CF_INDEXED2`: 0..3
345  *   - for `LV_IMG_CF_INDEXED4`: 0..15
346  *   - for `LV_IMG_CF_INDEXED8`: 0..255
347  * @param c the color to set
348  */
lv_img_buf_set_palette(lv_img_dsc_t * dsc,uint8_t id,lv_color_t c)349 void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
350 {
351     if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
352        (dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
353         LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
354         return;
355     }
356 
357     lv_color32_t c32;
358     c32.full      = lv_color_to32(c);
359     uint8_t * buf = (uint8_t *)dsc->data;
360     memcpy(&buf[id * sizeof(c32)], &c32, sizeof(c32));
361 }
362 
363 /**
364  * Get the pixel size of a color format in bits
365  * @param cf a color format (`LV_IMG_CF_...`)
366  * @return the pixel size in bits
367  */
lv_img_color_format_get_px_size(lv_img_cf_t cf)368 uint8_t lv_img_color_format_get_px_size(lv_img_cf_t cf)
369 {
370     uint8_t px_size = 0;
371 
372     switch(cf) {
373         case LV_IMG_CF_UNKNOWN:
374         case LV_IMG_CF_RAW: px_size = 0; break;
375         case LV_IMG_CF_TRUE_COLOR:
376         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: px_size = LV_COLOR_SIZE; break;
377         case LV_IMG_CF_TRUE_COLOR_ALPHA: px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3; break;
378         case LV_IMG_CF_INDEXED_1BIT:
379         case LV_IMG_CF_ALPHA_1BIT: px_size = 1; break;
380         case LV_IMG_CF_INDEXED_2BIT:
381         case LV_IMG_CF_ALPHA_2BIT: px_size = 2; break;
382         case LV_IMG_CF_INDEXED_4BIT:
383         case LV_IMG_CF_ALPHA_4BIT: px_size = 4; break;
384         case LV_IMG_CF_INDEXED_8BIT:
385         case LV_IMG_CF_ALPHA_8BIT: px_size = 8; break;
386         default: px_size = 0; break;
387     }
388 
389     return px_size;
390 }
391 
392 /**
393  * Check if a color format is chroma keyed or not
394  * @param cf a color format (`LV_IMG_CF_...`)
395  * @return true: chroma keyed; false: not chroma keyed
396  */
lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf)397 bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf)
398 {
399     bool is_chroma_keyed = false;
400 
401     switch(cf) {
402         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
403         case LV_IMG_CF_RAW_CHROMA_KEYED:
404         case LV_IMG_CF_INDEXED_1BIT:
405         case LV_IMG_CF_INDEXED_2BIT:
406         case LV_IMG_CF_INDEXED_4BIT:
407         case LV_IMG_CF_INDEXED_8BIT: is_chroma_keyed = true; break;
408         default: is_chroma_keyed = false; break;
409     }
410 
411     return is_chroma_keyed;
412 }
413 
414 /**
415  * Check if a color format has alpha channel or not
416  * @param cf a color format (`LV_IMG_CF_...`)
417  * @return true: has alpha channel; false: doesn't have alpha channel
418  */
lv_img_color_format_has_alpha(lv_img_cf_t cf)419 bool lv_img_color_format_has_alpha(lv_img_cf_t cf)
420 {
421     bool has_alpha = false;
422 
423     switch(cf) {
424         case LV_IMG_CF_TRUE_COLOR_ALPHA:
425         case LV_IMG_CF_RAW_ALPHA:
426         case LV_IMG_CF_ALPHA_1BIT:
427         case LV_IMG_CF_ALPHA_2BIT:
428         case LV_IMG_CF_ALPHA_4BIT:
429         case LV_IMG_CF_ALPHA_8BIT: has_alpha = true; break;
430         default: has_alpha = false; break;
431     }
432 
433     return has_alpha;
434 }
435 
436 /**
437  * Get the type of an image source
438  * @param src pointer to an image source:
439  *  - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
440  *  - a path to a file (e.g. "S:/folder/image.bin")
441  *  - or a symbol (e.g. LV_SYMBOL_CLOSE)
442  * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
443  */
lv_img_src_get_type(const void * src)444 lv_img_src_t lv_img_src_get_type(const void * src)
445 {
446     lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
447 
448     if(src == NULL) return img_src_type;
449     const uint8_t * u8_p = src;
450 
451     /*The first byte shows the type of the image source*/
452     if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
453         img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
454     } else if(u8_p[0] >= 0x80) {
455         img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
456     } else {
457         img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is design to the first byte < 0x20*/
458     }
459 
460     if(LV_IMG_SRC_UNKNOWN == img_src_type) {
461         LV_LOG_WARN("lv_img_src_get_type: unknown image type");
462     }
463 
464     return img_src_type;
465 }
466 
467 /**********************
468  *   STATIC FUNCTIONS
469  **********************/
470 
lv_img_draw_core(const lv_area_t * coords,const lv_area_t * mask,const void * src,const lv_style_t * style,lv_opa_t opa_scale)471 static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask, const void * src,
472                                  const lv_style_t * style, lv_opa_t opa_scale)
473 {
474 
475     lv_area_t mask_com; /*Common area of mask and coords*/
476     bool union_ok;
477     union_ok = lv_area_intersect(&mask_com, mask, coords);
478     if(union_ok == false) {
479         return LV_RES_OK; /*Out of mask. There is nothing to draw so the image is drawn
480                              successfully.*/
481     }
482 
483     lv_opa_t opa =
484         opa_scale == LV_OPA_COVER ? style->image.opa : (uint16_t)((uint16_t)style->image.opa * opa_scale) >> 8;
485 
486     lv_img_cache_entry_t * cdsc = lv_img_cache_open(src, style);
487 
488     if(cdsc == NULL) return LV_RES_INV;
489 
490     bool chroma_keyed = lv_img_color_format_is_chroma_keyed(cdsc->dec_dsc.header.cf);
491     bool alpha_byte   = lv_img_color_format_has_alpha(cdsc->dec_dsc.header.cf);
492 
493     if(cdsc->dec_dsc.error_msg != NULL) {
494         LV_LOG_WARN("Image draw error");
495         lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
496         lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, cdsc->dec_dsc.error_msg, LV_TXT_FLAG_NONE, NULL, -1,
497                       -1, NULL);
498     }
499     /* The decoder open could open the image and gave the entire uncompressed image.
500      * Just draw it!*/
501     else if(cdsc->dec_dsc.img_data) {
502         lv_draw_map(coords, mask, cdsc->dec_dsc.img_data, opa, chroma_keyed, alpha_byte, style->image.color,
503                     style->image.intense);
504     }
505     /* The whole uncompressed image is not available. Try to read it line-by-line*/
506     else {
507         lv_coord_t width = lv_area_get_width(&mask_com);
508 
509         uint8_t  * buf = lv_draw_get_buf(lv_area_get_width(&mask_com) * ((LV_COLOR_DEPTH >> 3) + 1));  /*+1 because of the possible alpha byte*/
510 
511         lv_area_t line;
512         lv_area_copy(&line, &mask_com);
513         lv_area_set_height(&line, 1);
514         lv_coord_t x = mask_com.x1 - coords->x1;
515         lv_coord_t y = mask_com.y1 - coords->y1;
516         lv_coord_t row;
517         lv_res_t read_res;
518         for(row = mask_com.y1; row <= mask_com.y2; row++) {
519             read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
520             if(read_res != LV_RES_OK) {
521                 lv_img_decoder_close(&cdsc->dec_dsc);
522                 LV_LOG_WARN("Image draw can't read the line");
523                 return LV_RES_INV;
524             }
525             lv_draw_map(&line, mask, buf, opa, chroma_keyed, alpha_byte, style->image.color, style->image.intense);
526             line.y1++;
527             line.y2++;
528             y++;
529         }
530     }
531 
532     return LV_RES_OK;
533 }
534