1 /**
2  * @file lv_draw_sw_img.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../misc/lv_area_private.h"
10 #include "blend/lv_draw_sw_blend_private.h"
11 #include "../lv_image_decoder_private.h"
12 #include "../lv_draw_image_private.h"
13 #include "../lv_draw_private.h"
14 #include "lv_draw_sw.h"
15 #if LV_USE_DRAW_SW
16 
17 #include "../../display/lv_display.h"
18 #include "../../display/lv_display_private.h"
19 #include "../../misc/lv_log.h"
20 #include "../../core/lv_refr_private.h"
21 #include "../../stdlib/lv_mem.h"
22 #include "../../misc/lv_math.h"
23 #include "../../misc/lv_color.h"
24 #include "../../stdlib/lv_string.h"
25 #include "../../core/lv_global.h"
26 
27 #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
28     #include "arm2d/lv_draw_sw_helium.h"
29 #elif LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM
30     #include LV_DRAW_SW_ASM_CUSTOM_INCLUDE
31 #endif
32 
33 /*********************
34  *      DEFINES
35  *********************/
36 #define MAX_BUF_SIZE (uint32_t) (4 * lv_display_get_horizontal_resolution(lv_refr_get_disp_refreshing()) * lv_color_format_get_size(lv_display_get_color_format(lv_refr_get_disp_refreshing())))
37 
38 #ifndef LV_DRAW_SW_IMAGE
39     #define LV_DRAW_SW_IMAGE(...)   LV_RESULT_INVALID
40 #endif
41 
42 #ifndef LV_DRAW_SW_RGB565_RECOLOR
43     #define LV_DRAW_SW_RGB565_RECOLOR(...)  LV_RESULT_INVALID
44 #endif
45 
46 #ifndef LV_DRAW_SW_RGB888_RECOLOR
47     #define LV_DRAW_SW_RGB888_RECOLOR(...)  LV_RESULT_INVALID
48 #endif
49 
50 /**********************
51  *      TYPEDEFS
52  **********************/
53 
54 /**********************
55  *  STATIC PROTOTYPES
56  **********************/
57 
58 static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
59                           const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
60                           const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
61 
62 
63 static void radius_only(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
64                         const lv_image_decoder_dsc_t * decoder_dsc,
65                         const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
66 
67 static void recolor_only(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
68                          const lv_image_decoder_dsc_t * decoder_dsc,
69                          const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
70 
71 static void transform_and_recolor(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
72                                   const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
73                                   const lv_area_t * img_coords, const lv_area_t * clipped_img_area);
74 
75 
76 static void recolor(lv_area_t relative_area, uint8_t * src_buf, uint8_t * dest_buf, int32_t src_stride,
77                     lv_color_format_t cf, const lv_draw_image_dsc_t * draw_dsc);
78 
79 static bool apply_mask(const lv_draw_image_dsc_t * draw_dsc);
80 
81 /**********************
82  *  STATIC VARIABLES
83  **********************/
84 #define _draw_info LV_GLOBAL_DEFAULT()->draw_info
85 
86 /**********************
87  *      MACROS
88  **********************/
89 
90 /**********************
91  *   GLOBAL FUNCTIONS
92  **********************/
93 
lv_draw_sw_layer(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_area_t * coords)94 void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords)
95 {
96     lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
97 
98     /*It can happen that nothing was draw on a layer and therefore its buffer is not allocated.
99      *In this case just return. */
100     if(layer_to_draw->draw_buf == NULL) return;
101 
102     if(draw_dsc->bitmap_mask_src) {
103         bool visible = apply_mask(draw_dsc);
104         if(!visible) return;
105     }
106 
107     /*The source should be a draw_buf, not a layer*/
108     lv_draw_image_dsc_t new_draw_dsc = *draw_dsc;
109     new_draw_dsc.src = layer_to_draw->draw_buf;
110 
111     lv_draw_sw_image(draw_unit, &new_draw_dsc, coords);
112 #if LV_USE_LAYER_DEBUG || LV_USE_PARALLEL_DRAW_DEBUG
113     lv_area_t area_rot;
114     lv_area_copy(&area_rot, coords);
115     if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
116         int32_t w = lv_area_get_width(coords);
117         int32_t h = lv_area_get_height(coords);
118 
119         lv_image_buf_get_transformed_area(&area_rot, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y,
120                                           &draw_dsc->pivot);
121 
122         area_rot.x1 += coords->x1;
123         area_rot.y1 += coords->y1;
124         area_rot.x2 += coords->x1;
125         area_rot.y2 += coords->y1;
126     }
127     lv_area_t draw_area;
128     if(!lv_area_intersect(&draw_area, &area_rot, draw_unit->clip_area)) return;
129 #endif
130 
131 #if LV_USE_LAYER_DEBUG
132     lv_draw_fill_dsc_t fill_dsc;
133     lv_draw_fill_dsc_init(&fill_dsc);
134     fill_dsc.color = lv_color_hex(layer_to_draw->color_format == LV_COLOR_FORMAT_ARGB8888 ? 0xff0000 : 0x00ff00);
135     fill_dsc.opa = LV_OPA_20;
136     lv_draw_sw_fill(draw_unit, &fill_dsc, &area_rot);
137 
138     lv_draw_border_dsc_t border_dsc;
139     lv_draw_border_dsc_init(&border_dsc);
140     border_dsc.color = fill_dsc.color;
141     border_dsc.opa = LV_OPA_60;
142     border_dsc.width = 2;
143     lv_draw_sw_border(draw_unit, &border_dsc, &area_rot);
144 
145 #endif
146 
147 #if LV_USE_PARALLEL_DRAW_DEBUG
148     uint32_t idx = 0;
149     lv_draw_unit_t * draw_unit_tmp = _draw_info.unit_head;
150     while(draw_unit_tmp != draw_unit) {
151         draw_unit_tmp = draw_unit_tmp->next;
152         idx++;
153     }
154 
155     lv_draw_fill_dsc_t fill_dsc;
156     lv_draw_fill_dsc_init(&fill_dsc);
157     fill_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
158     fill_dsc.opa = LV_OPA_10;
159     lv_draw_sw_fill(draw_unit, &fill_dsc, &area_rot);
160 
161     lv_draw_border_dsc_t border_dsc;
162     lv_draw_border_dsc_init(&border_dsc);
163     border_dsc.color = lv_palette_main(idx % LV_PALETTE_LAST);
164     border_dsc.opa = LV_OPA_60;
165     border_dsc.width = 1;
166     lv_draw_sw_border(draw_unit, &border_dsc, &area_rot);
167 
168     lv_point_t txt_size;
169     lv_text_get_size(&txt_size, "W", LV_FONT_DEFAULT, 0, 0, 100, LV_TEXT_FLAG_NONE);
170 
171     lv_area_t txt_area;
172     txt_area.x1 = draw_area.x1;
173     txt_area.x2 = draw_area.x1 + txt_size.x - 1;
174     txt_area.y2 = draw_area.y2;
175     txt_area.y1 = draw_area.y2 - txt_size.y + 1;
176 
177     lv_draw_fill_dsc_init(&fill_dsc);
178     fill_dsc.color = lv_color_black();
179     lv_draw_sw_fill(draw_unit, &fill_dsc, &txt_area);
180 
181     char buf[8];
182     lv_snprintf(buf, sizeof(buf), "%d", idx);
183     lv_draw_label_dsc_t label_dsc;
184     lv_draw_label_dsc_init(&label_dsc);
185     label_dsc.color = lv_color_white();
186     label_dsc.text = buf;
187     lv_draw_sw_label(draw_unit, &label_dsc, &txt_area);
188 #endif
189 }
190 
lv_draw_sw_image(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_area_t * coords)191 void lv_draw_sw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
192                       const lv_area_t * coords)
193 {
194     if(!draw_dsc->tile) {
195         lv_draw_image_normal_helper(draw_unit, draw_dsc, coords, img_draw_core);
196     }
197     else {
198         lv_draw_image_tiled_helper(draw_unit, draw_dsc, coords, img_draw_core);
199     }
200 }
201 
202 /**********************
203  *   STATIC FUNCTIONS
204  **********************/
205 
img_draw_core(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_image_decoder_dsc_t * decoder_dsc,lv_draw_image_sup_t * sup,const lv_area_t * img_coords,const lv_area_t * clipped_img_area)206 static void img_draw_core(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
207                           const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
208                           const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
209 {
210     bool transformed = draw_dsc->rotation != 0 || draw_dsc->scale_x != LV_SCALE_NONE ||
211                        draw_dsc->scale_y != LV_SCALE_NONE ? true : false;
212 
213     bool radius = draw_dsc->clip_radius > 0;
214 
215     const lv_draw_buf_t * decoded = decoder_dsc->decoded;
216     const uint8_t * src_buf = decoded->data;
217     const lv_image_header_t * header = &decoded->header;
218     uint32_t img_stride = decoded->header.stride;
219     lv_color_format_t cf = decoded->header.cf;
220 
221     lv_draw_sw_blend_dsc_t blend_dsc;
222     lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
223     blend_dsc.opa = draw_dsc->opa;
224     blend_dsc.blend_mode = draw_dsc->blend_mode;
225     blend_dsc.src_stride = img_stride;
226 
227     if(!transformed && !radius && cf == LV_COLOR_FORMAT_A8) {
228         lv_area_t clipped_coords;
229         if(!lv_area_intersect(&clipped_coords, img_coords, draw_unit->clip_area)) return;
230 
231         blend_dsc.mask_buf = (lv_opa_t *)src_buf;
232         blend_dsc.mask_area = img_coords;
233         blend_dsc.mask_stride = img_stride;
234         blend_dsc.src_buf = NULL;
235         blend_dsc.color = draw_dsc->recolor;
236         blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
237 
238         blend_dsc.blend_area = img_coords;
239         lv_draw_sw_blend(draw_unit, &blend_dsc);
240     }
241     else if(!transformed && !radius && cf == LV_COLOR_FORMAT_RGB565A8 && draw_dsc->recolor_opa <= LV_OPA_MIN) {
242         int32_t src_h = lv_area_get_height(img_coords);
243         int32_t src_w = lv_area_get_width(img_coords);
244         blend_dsc.src_area = img_coords;
245         blend_dsc.src_buf = src_buf;
246         blend_dsc.mask_buf = (lv_opa_t *)src_buf;
247         blend_dsc.mask_buf += img_stride * src_w / header->w * src_h;
248         /**
249          * Note, for RGB565A8, lacking of stride parameter, we always use
250          * always half of RGB map stride as alpha map stride. The image should
251          * be generated in this way too.
252          */
253         blend_dsc.mask_stride = img_stride / 2;
254         blend_dsc.blend_area = img_coords;
255         blend_dsc.mask_area = img_coords;
256         blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
257         blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB565;
258         lv_draw_sw_blend(draw_unit, &blend_dsc);
259     }
260     else if(!transformed && !radius && (cf == LV_COLOR_FORMAT_L8 || cf == LV_COLOR_FORMAT_AL88)) {
261         blend_dsc.src_area = img_coords;
262         blend_dsc.src_buf = src_buf;
263         blend_dsc.blend_area = img_coords;
264         blend_dsc.src_color_format = cf;
265         lv_draw_sw_blend(draw_unit, &blend_dsc);
266     }
267     /*The simplest case just copy the pixels into the draw_buf. Blending will convert the colors if needed*/
268     else if(!transformed && !radius && draw_dsc->recolor_opa <= LV_OPA_MIN) {
269         blend_dsc.src_area = img_coords;
270         blend_dsc.src_buf = src_buf;
271         blend_dsc.blend_area = img_coords;
272         blend_dsc.src_color_format = cf;
273         lv_draw_sw_blend(draw_unit, &blend_dsc);
274     }
275     else if(!transformed && !radius && draw_dsc->recolor_opa > LV_OPA_MIN) {
276         recolor_only(draw_unit, draw_dsc, decoder_dsc, img_coords,  clipped_img_area);
277     }
278     /*Handle masked RGB565, RGB888, XRGB888, or ARGB8888 images*/
279     else if(!transformed && radius && draw_dsc->recolor_opa <= LV_OPA_MIN) {
280         radius_only(draw_unit, draw_dsc, decoder_dsc, img_coords,  clipped_img_area);
281     }
282     /* check whether it is possible to accelerate the operation in synchronous mode */
283     else if(LV_RESULT_INVALID == LV_DRAW_SW_IMAGE(transformed,      /* whether require transform */
284                                                   cf,               /* image format */
285                                                   src_buf,          /* image buffer */
286                                                   img_coords,       /* src_h, src_w, src_x1, src_y1 */
287                                                   img_stride,       /* image stride */
288                                                   clipped_img_area, /* blend area */
289                                                   draw_unit,        /* target buffer, buffer width, buffer height, buffer stride */
290                                                   draw_dsc)) {      /* opa, recolour_opa and colour */
291         /*In the other cases every pixel need to be checked one-by-one*/
292         transform_and_recolor(draw_unit, draw_dsc, decoder_dsc, sup, img_coords, clipped_img_area);
293 
294     }
295 }
radius_only(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_image_decoder_dsc_t * decoder_dsc,const lv_area_t * img_coords,const lv_area_t * clipped_img_area)296 static void radius_only(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
297                         const lv_image_decoder_dsc_t * decoder_dsc,
298                         const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
299 {
300 
301     const lv_draw_buf_t * decoded = decoder_dsc->decoded;
302     uint32_t img_stride = decoded->header.stride;
303     lv_color_format_t cf = decoded->header.cf;
304     lv_color_format_t cf_ori = cf;
305     if(cf == LV_COLOR_FORMAT_RGB565A8) {
306         cf = LV_COLOR_FORMAT_RGB565;
307     }
308 
309 
310     lv_draw_sw_blend_dsc_t blend_dsc;
311     lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
312     blend_dsc.opa = draw_dsc->opa;
313     blend_dsc.blend_mode = draw_dsc->blend_mode;
314     blend_dsc.src_stride = img_stride;
315     blend_dsc.src_area = img_coords;
316     blend_dsc.src_buf = decoded->data;
317     blend_dsc.src_color_format = cf;
318 
319     lv_area_t blend_area = *clipped_img_area;
320     blend_dsc.blend_area = &blend_area;
321     int32_t y_last = blend_area.y2;
322     blend_area.y2 = blend_area.y1;
323 
324     int32_t blend_w = lv_area_get_width(&blend_area);
325     uint8_t * mask_buf = lv_malloc(blend_w);
326     blend_dsc.mask_buf = mask_buf;
327     blend_dsc.mask_area = &blend_area;
328     blend_dsc.mask_stride = blend_w;
329 
330     if(cf == LV_COLOR_FORMAT_A8) {
331         blend_dsc.src_buf = NULL;
332         blend_dsc.color = draw_dsc->recolor;
333     }
334 
335     lv_draw_sw_mask_radius_param_t mask_param;
336     lv_draw_sw_mask_radius_init(&mask_param, &draw_dsc->image_area, draw_dsc->clip_radius, false);
337 
338     void * masks[2] = {0};
339     masks[0] = &mask_param;
340 
341     int32_t image_h = lv_area_get_height(img_coords);
342     while(blend_area.y1 <= y_last) {
343         if(cf_ori == LV_COLOR_FORMAT_RGB565A8) {
344             const uint8_t * mask_start = decoded->data + img_stride * image_h;
345             int32_t y_ofs = blend_area.y1 - img_coords->y1;
346             int32_t x_ofs = blend_area.x1 - img_coords->x1;
347             lv_memcpy(mask_buf, mask_start + y_ofs * img_stride / 2  + x_ofs, blend_w);
348         }
349         else if(cf_ori == LV_COLOR_FORMAT_A8) {
350             int32_t y_ofs = blend_area.y1 - img_coords->y1;
351             int32_t x_ofs = blend_area.x1 - img_coords->x1;
352             lv_memcpy(mask_buf, decoded->data + y_ofs * img_stride + x_ofs, blend_w);
353         }
354         else {
355             lv_memset(mask_buf, 0xff, blend_w);
356 
357         }
358 
359         blend_dsc.mask_res = lv_draw_sw_mask_apply(masks, mask_buf, blend_area.x1, blend_area.y1, blend_w);
360 
361         if(cf_ori == LV_COLOR_FORMAT_RGB565A8 || cf_ori == LV_COLOR_FORMAT_A8) {
362             blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
363         }
364 
365         /*Blend*/
366         lv_draw_sw_blend(draw_unit, &blend_dsc);
367 
368         /*Go to the next area*/
369         blend_area.y1 ++;
370         blend_area.y2 ++;
371     }
372     lv_free(mask_buf);
373 
374 }
recolor_only(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_image_decoder_dsc_t * decoder_dsc,const lv_area_t * img_coords,const lv_area_t * clipped_img_area)375 static void recolor_only(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
376                          const lv_image_decoder_dsc_t * decoder_dsc,
377                          const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
378 {
379     lv_area_t blend_area = *clipped_img_area;
380 
381     const lv_draw_buf_t * decoded = decoder_dsc->decoded;
382     uint32_t img_stride = decoded->header.stride;
383     lv_color_format_t cf = decoded->header.cf;
384     uint32_t px_size = lv_color_format_get_size(cf);
385     int32_t src_h = lv_area_get_height(img_coords);
386     int32_t blend_w = lv_area_get_width(&blend_area);
387     int32_t blend_h = lv_area_get_height(&blend_area);
388     uint8_t * tmp_buf;
389     int32_t buf_h;
390     uint32_t buf_stride = blend_w * px_size;
391     if(buf_stride == 0) {
392         buf_stride = 1;
393     }
394     buf_h = MAX_BUF_SIZE / buf_stride;
395     if(buf_h > blend_h) buf_h = blend_h;
396     tmp_buf = lv_malloc(buf_stride * buf_h);
397 
398     lv_draw_sw_blend_dsc_t blend_dsc;
399     lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
400     blend_dsc.opa = draw_dsc->opa;
401     blend_dsc.blend_mode = draw_dsc->blend_mode;
402     blend_dsc.src_stride = blend_w * px_size;
403     blend_dsc.src_area = &blend_area;
404     blend_dsc.blend_area = &blend_area;
405     blend_dsc.src_buf = tmp_buf;
406     blend_dsc.src_color_format = cf;
407     if(cf == LV_COLOR_FORMAT_RGB565A8) {
408         blend_dsc.mask_area = img_coords;
409         blend_dsc.mask_buf = decoded->data + img_stride * src_h;
410         blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
411         blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB565;
412         blend_dsc.mask_stride = img_stride / 2;
413     }
414 
415     int32_t y_last = blend_area.y2;
416     blend_area.y2 = blend_area.y1 + buf_h - 1;
417     while(blend_area.y1 <= y_last) {
418         lv_area_t relative_area;
419         lv_area_copy(&relative_area, &blend_area);
420         lv_area_move(&relative_area, -img_coords->x1, -img_coords->y1);
421         recolor(relative_area, decoded->data, tmp_buf, img_stride, blend_dsc.src_color_format, draw_dsc);
422 
423         lv_draw_sw_blend(draw_unit, &blend_dsc);
424 
425         /*Go to the next area*/
426         blend_area.y1 = blend_area.y2 + 1;
427         blend_area.y2 = blend_area.y1 + buf_h - 1;
428         if(blend_area.y2 > y_last) {
429             blend_area.y2 = y_last;
430         }
431     }
432 
433     lv_free(tmp_buf);
434 
435 
436 }
437 
transform_and_recolor(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_image_decoder_dsc_t * decoder_dsc,lv_draw_image_sup_t * sup,const lv_area_t * img_coords,const lv_area_t * clipped_img_area)438 static void transform_and_recolor(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
439                                   const lv_image_decoder_dsc_t * decoder_dsc, lv_draw_image_sup_t * sup,
440                                   const lv_area_t * img_coords, const lv_area_t * clipped_img_area)
441 
442 {
443     const lv_draw_buf_t * decoded = decoder_dsc->decoded;
444     uint32_t img_stride = decoded->header.stride;
445     lv_color_format_t cf = decoded->header.cf;
446     lv_draw_sw_blend_dsc_t blend_dsc;
447     lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
448     blend_dsc.opa = draw_dsc->opa;
449     blend_dsc.blend_mode = draw_dsc->blend_mode;
450 
451     lv_area_t blend_area = *clipped_img_area;
452     blend_dsc.blend_area = &blend_area;
453 
454     int32_t src_w = lv_area_get_width(img_coords);
455     int32_t src_h = lv_area_get_height(img_coords);
456     int32_t blend_w = lv_area_get_width(&blend_area);
457     int32_t blend_h = lv_area_get_height(&blend_area);
458 
459     bool do_recolor = draw_dsc->recolor_opa > LV_OPA_MIN;
460     if(cf == LV_COLOR_FORMAT_L8 || cf == LV_COLOR_FORMAT_A8) {
461         blend_dsc.color = draw_dsc->recolor;
462         do_recolor = false;
463     }
464 
465     lv_color_format_t cf_final = cf;
466     if(cf_final == LV_COLOR_FORMAT_RGB888 || cf_final == LV_COLOR_FORMAT_XRGB8888) cf_final = LV_COLOR_FORMAT_ARGB8888;
467     else if(cf_final == LV_COLOR_FORMAT_RGB565) cf_final = LV_COLOR_FORMAT_RGB565A8;
468     else if(cf_final == LV_COLOR_FORMAT_L8) cf_final = LV_COLOR_FORMAT_AL88;
469 
470     uint8_t * transformed_buf;
471     int32_t buf_h;
472     if(cf_final == LV_COLOR_FORMAT_RGB565A8) {
473         uint32_t buf_stride = blend_w * 3;
474         buf_h = MAX_BUF_SIZE / buf_stride;
475         if(buf_h > blend_h) buf_h = blend_h;
476         transformed_buf = lv_malloc(buf_stride * buf_h);
477     }
478     else {
479         uint32_t buf_stride = blend_w * lv_color_format_get_size(cf_final);
480         buf_h = MAX_BUF_SIZE / buf_stride;
481         if(buf_h > blend_h) buf_h = blend_h;
482         transformed_buf = lv_malloc(buf_stride * buf_h);
483     }
484     LV_ASSERT_MALLOC(transformed_buf);
485 
486     blend_dsc.src_buf = transformed_buf;
487     blend_dsc.src_color_format = cf_final;
488     int32_t y_last = blend_area.y2;
489     blend_area.y2 = blend_area.y1 + buf_h - 1;
490 
491     blend_dsc.src_area = &blend_area;
492     const uint8_t * src_buf = decoded->data;
493     if(cf_final == LV_COLOR_FORMAT_RGB565A8) {
494         /*RGB565A8 images will blended as RGB565 + mask
495          *Therefore the stride can be different. */
496         blend_dsc.src_stride = blend_w * 2;
497         blend_dsc.mask_area = &blend_area;
498         blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
499         blend_dsc.mask_buf = transformed_buf + blend_w * 2 * buf_h;
500         blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB565;
501     }
502     else if(cf_final == LV_COLOR_FORMAT_A8) {
503         blend_dsc.mask_buf = transformed_buf;
504         blend_dsc.mask_stride = blend_w;
505         blend_dsc.mask_area = &blend_area;
506         blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
507         blend_dsc.src_buf = NULL;
508     }
509     else {
510         blend_dsc.src_stride = blend_w * lv_color_format_get_size(cf_final);
511     }
512 
513     while(blend_area.y1 <= y_last) {
514         /*Apply transformations if any or separate the channels*/
515         lv_area_t relative_area;
516         lv_area_copy(&relative_area, &blend_area);
517         lv_area_move(&relative_area, -img_coords->x1, -img_coords->y1);
518         lv_draw_sw_transform(draw_unit, &relative_area, src_buf, src_w, src_h, img_stride,
519                              draw_dsc, sup, cf, transformed_buf);
520 
521         if(do_recolor) {
522             lv_area_t relative_area2;
523             lv_area_copy(&relative_area2, &blend_area);
524             lv_area_move(&relative_area2, -blend_area.x1, -blend_area.y1);
525             recolor(relative_area2, transformed_buf, transformed_buf, blend_dsc.src_stride, cf_final, draw_dsc);
526         }
527 
528         /*Blend*/
529         lv_draw_sw_blend(draw_unit, &blend_dsc);
530 
531         /*Go to the next area*/
532         blend_area.y1 = blend_area.y2 + 1;
533         blend_area.y2 = blend_area.y1 + buf_h - 1;
534         if(blend_area.y2 > y_last) {
535             blend_area.y2 = y_last;
536             if(cf_final == LV_COLOR_FORMAT_RGB565A8) {
537                 blend_dsc.mask_buf =  transformed_buf + blend_w * 2 * lv_area_get_height(&blend_area);
538             }
539         }
540     }
541 
542     lv_free(transformed_buf);
543 }
544 
recolor(lv_area_t relative_area,uint8_t * src_buf,uint8_t * dest_buf,int32_t src_stride,lv_color_format_t cf,const lv_draw_image_dsc_t * draw_dsc)545 static void recolor(lv_area_t relative_area, uint8_t * src_buf, uint8_t * dest_buf, int32_t src_stride,
546                     lv_color_format_t cf, const lv_draw_image_dsc_t * draw_dsc)
547 {
548     int32_t w = lv_area_get_width(&relative_area);
549     int32_t h = lv_area_get_height(&relative_area);
550 
551     /*Apply recolor*/
552     lv_color_t color = draw_dsc->recolor;
553     lv_opa_t mix = draw_dsc->recolor_opa;
554     lv_opa_t mix_inv = 255 - mix;
555 
556     if(cf == LV_COLOR_FORMAT_RGB565A8 || cf == LV_COLOR_FORMAT_RGB565) {
557         if(LV_RESULT_INVALID == LV_DRAW_SW_RGB565_RECOLOR(dest_buf, blend_area, color, mix)) {
558             const uint8_t * src_buf_tmp = src_buf + src_stride * relative_area.y1 + relative_area.x1 * 2;
559             int32_t img_stride_px = src_stride / 2;
560 
561             uint16_t c_mult[3];
562             c_mult[0] = (color.blue >> 3) * mix;
563             c_mult[1] = (color.green >> 2) * mix;
564             c_mult[2] = (color.red >> 3) * mix;
565             uint16_t * buf16_src = (uint16_t *)src_buf_tmp;
566             uint16_t * buf16_dest = (uint16_t *)dest_buf;
567 
568             int32_t y;
569             for(y = 0; y < h; y++) {
570                 int32_t x;
571                 for(x = 0; x < w; x++) {
572                     *buf16_dest = (((c_mult[2] + ((buf16_src[x] >> 11) & 0x1F) * mix_inv) << 3) & 0xF800) +
573                                   (((c_mult[1] + ((buf16_src[x] >> 5) & 0x3F) * mix_inv) >> 3) & 0x07E0) +
574                                   ((c_mult[0] + (buf16_src[x] & 0x1F) * mix_inv) >> 8);
575                     buf16_dest++;
576                 }
577                 buf16_src += img_stride_px;
578             }
579         }
580     }
581     else if(cf == LV_COLOR_FORMAT_RGB888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_ARGB8888) {
582         if(LV_RESULT_INVALID == LV_DRAW_SW_RGB888_RECOLOR(dest_buf, blend_area, color, mix, cf_final)) {
583             uint32_t px_size = lv_color_format_get_size(cf);
584             src_buf += src_stride * relative_area.y1 + relative_area.x1 * px_size;
585             uint16_t c_mult[3];
586             c_mult[0] = color.blue * mix;
587             c_mult[1] = color.green * mix;
588             c_mult[2] = color.red * mix;
589             int32_t y;
590             for(y = 0; y < h; y++) {
591                 int32_t x;
592                 for(x = 0; x < w; x++) {
593                     dest_buf[0] = (c_mult[0] + (src_buf[0] * mix_inv)) >> 8;
594                     dest_buf[1] = (c_mult[1] + (src_buf[1] * mix_inv)) >> 8;
595                     dest_buf[2] = (c_mult[2] + (src_buf[2] * mix_inv)) >> 8;
596                     if(cf == LV_COLOR_FORMAT_ARGB8888) dest_buf[3] = src_buf[3];
597                     src_buf += px_size;
598                     dest_buf += px_size;
599                 }
600                 src_buf += src_stride - w * px_size;
601             }
602         }
603     }
604 }
605 
apply_mask(const lv_draw_image_dsc_t * draw_dsc)606 static bool apply_mask(const lv_draw_image_dsc_t * draw_dsc)
607 {
608     lv_layer_t * layer_to_draw = (lv_layer_t *)draw_dsc->src;
609     lv_draw_buf_t * image_draw_buf = layer_to_draw->draw_buf;
610     lv_image_decoder_dsc_t mask_decoder_dsc;
611     lv_area_t mask_area;
612     uint32_t mask_stride;
613 
614     lv_result_t decoder_res = lv_image_decoder_open(&mask_decoder_dsc, draw_dsc->bitmap_mask_src, NULL);
615     if(decoder_res != LV_RESULT_OK || mask_decoder_dsc.decoded == NULL) {
616         LV_LOG_WARN("Could open the mask. The mask is not applied.");
617         return true;
618     }
619 
620     if(mask_decoder_dsc.decoded->header.cf != LV_COLOR_FORMAT_A8 &&
621        mask_decoder_dsc.decoded->header.cf != LV_COLOR_FORMAT_L8) {
622         LV_LOG_WARN("The mask image is not A8/L8 format. The mask is not applied.");
623         return true;
624 
625     }
626 
627     const lv_draw_buf_t * mask_draw_buf = mask_decoder_dsc.decoded;
628     mask_stride = mask_draw_buf->header.stride;
629 
630     /*Align the mask to the center*/
631     lv_area_t image_area;
632     image_area = draw_dsc->image_area;  /*Use the whole image area for the alignment*/
633     lv_area_set(&mask_area, 0, 0, mask_draw_buf->header.w - 1, mask_draw_buf->header.h - 1);
634     lv_area_align(&image_area, &mask_area, LV_ALIGN_CENTER, 0, 0);
635 
636     image_area =
637         layer_to_draw->buf_area; /*The image can be smaller if only a part was rendered. Use this are during rendering*/
638 
639     /*Only the intersection of the mask and image needs to be rendered
640      *If if there is no intersection there is nothing to render as the image is out of the mask.*/
641     lv_area_t masked_area;
642     if(!lv_area_intersect(&masked_area, &mask_area, &image_area)) return false;
643 
644     /*Clear the sides if any*/
645     lv_area_t side_area = {0};
646     /*Top*/
647     side_area.x2 = layer_to_draw->draw_buf->header.w - 1;
648     side_area.y2 = masked_area.y1 - 1 - image_area.y1;
649     lv_draw_buf_clear(layer_to_draw->draw_buf, &side_area);
650 
651     /*Bottom*/
652     side_area.y1 = masked_area.y2 + 1 - image_area.y1;
653     side_area.y2 = layer_to_draw->draw_buf->header.h - 1;
654     lv_draw_buf_clear(layer_to_draw->draw_buf, &side_area);
655 
656     /*Left*/
657     side_area.y1 = 0;
658     side_area.x1 = 0;
659     side_area.x2 = masked_area.x1 - 1 - image_area.x1;
660     lv_draw_buf_clear(layer_to_draw->draw_buf, &side_area);
661 
662     /*Right*/
663     side_area.x1 = masked_area.x2 + 1 - image_area.x1;
664     side_area.x2 = layer_to_draw->draw_buf->header.w - 1;
665     lv_draw_buf_clear(layer_to_draw->draw_buf, &side_area);
666 
667     /*Seek to the first of the image and mask on the masked area*/
668     uint8_t * img_start = lv_draw_buf_goto_xy(image_draw_buf,
669                                               masked_area.x1 - image_area.x1,
670                                               masked_area.y1 - image_area.y1);
671     uint8_t * mask_start = lv_draw_buf_goto_xy(mask_decoder_dsc.decoded,
672                                                masked_area.x1 - mask_area.x1,
673                                                masked_area.y1 - mask_area.y1);
674 
675     int32_t h = lv_area_get_height(&masked_area);
676     int32_t w = lv_area_get_width(&masked_area);
677 
678     int32_t y;
679     for(y = 0; y < h; y++) {
680         int32_t x;
681         for(x = 0; x < w; x++) {
682             img_start[x * 4 + 3] = LV_OPA_MIX2(mask_start[x], img_start[x * 4 + 3]);
683         }
684         img_start += layer_to_draw->draw_buf->header.stride;
685         mask_start += mask_stride;
686     }
687 
688     return true;
689 }
690 
691 #endif /*LV_USE_DRAW_SW*/
692