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