1 /**
2  * @file lv_draw_rect.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_sw.h"
10 #include "../../misc/lv_math.h"
11 #include "../../misc/lv_txt_ap.h"
12 #include "../../core/lv_refr.h"
13 #include "../../misc/lv_assert.h"
14 #include "lv_draw_sw_dither.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define SHADOW_UPSCALE_SHIFT    6
20 #define SHADOW_ENHANCE          1
21 #define SPLIT_LIMIT             50
22 
23 /**********************
24  *      TYPEDEFS
25  **********************/
26 
27 /**********************
28  *  STATIC PROTOTYPES
29  **********************/
30 static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
31 static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
32 static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
33 
34 static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
35 
36 #if LV_DRAW_COMPLEX
37 static void /* LV_ATTRIBUTE_FAST_MEM */ draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
38                                                     const lv_area_t * coords);
39 static void /* LV_ATTRIBUTE_FAST_MEM */ shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf,
40                                                                lv_coord_t s, lv_coord_t r);
41 static void /* LV_ATTRIBUTE_FAST_MEM */ shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf);
42 #endif
43 
44 void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
45                          lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
46 
47 static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
48                                lv_color_t color, lv_opa_t opa);
49 
50 /**********************
51  *  STATIC VARIABLES
52  **********************/
53 #if defined(LV_SHADOW_CACHE_SIZE) && LV_SHADOW_CACHE_SIZE > 0
54     static uint8_t sh_cache[LV_SHADOW_CACHE_SIZE * LV_SHADOW_CACHE_SIZE];
55     static int32_t sh_cache_size = -1;
56     static int32_t sh_cache_r = -1;
57 #endif
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)67 void lv_draw_sw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
68 {
69 #if LV_DRAW_COMPLEX
70     draw_shadow(draw_ctx, dsc, coords);
71 #endif
72 
73     draw_bg(draw_ctx, dsc, coords);
74     draw_bg_img(draw_ctx, dsc, coords);
75 
76     draw_border(draw_ctx, dsc, coords);
77 
78     draw_outline(draw_ctx, dsc, coords);
79 
80     LV_ASSERT_MEM_INTEGRITY();
81 }
82 
lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)83 void lv_draw_sw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
84 {
85 #if LV_COLOR_SCREEN_TRANSP && LV_COLOR_DEPTH == 32
86     lv_memset_00(draw_ctx->buf, lv_area_get_size(draw_ctx->buf_area) * sizeof(lv_color_t));
87 #endif
88 
89     draw_bg(draw_ctx, dsc, coords);
90     draw_bg_img(draw_ctx, dsc, coords);
91 }
92 
93 /**********************
94  *   STATIC FUNCTIONS
95  **********************/
96 
draw_bg(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)97 static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
98 {
99     if(dsc->bg_opa <= LV_OPA_MIN) return;
100 
101     lv_area_t bg_coords;
102     lv_area_copy(&bg_coords, coords);
103 
104     /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/
105     if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) {
106         bg_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0;
107         bg_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0;
108         bg_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0;
109         bg_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0;
110     }
111 
112     lv_area_t clipped_coords;
113     if(!_lv_area_intersect(&clipped_coords, &bg_coords, draw_ctx->clip_area)) return;
114 
115     lv_grad_dir_t grad_dir = dsc->bg_grad.dir;
116     lv_color_t bg_color    = grad_dir == LV_GRAD_DIR_NONE ? dsc->bg_color : dsc->bg_grad.stops[0].color;
117     if(bg_color.full == dsc->bg_grad.stops[1].color.full) grad_dir = LV_GRAD_DIR_NONE;
118 
119     bool mask_any = lv_draw_mask_is_any(&bg_coords);
120     lv_draw_sw_blend_dsc_t blend_dsc = {0};
121     blend_dsc.blend_mode = dsc->blend_mode;
122     blend_dsc.color = bg_color;
123 
124     /*Most simple case: just a plain rectangle*/
125     if(!mask_any && dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) {
126         blend_dsc.blend_area = &bg_coords;
127         blend_dsc.opa = dsc->bg_opa;
128         lv_draw_sw_blend(draw_ctx, &blend_dsc);
129         return;
130     }
131 
132     /*Complex case: there is gradient, mask, or radius*/
133 #if LV_DRAW_COMPLEX == 0
134     LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_COMPLEX = 0");
135 #else
136     lv_opa_t opa = dsc->bg_opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->bg_opa;
137 
138     /*Get the real radius. Can't be larger than the half of the shortest side */
139     lv_coord_t coords_bg_w = lv_area_get_width(&bg_coords);
140     lv_coord_t coords_bg_h = lv_area_get_height(&bg_coords);
141     int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h);
142     int32_t rout = LV_MIN(dsc->radius, short_side >> 1);
143 
144     /*Add a radius mask if there is radius*/
145     int32_t clipped_w = lv_area_get_width(&clipped_coords);
146     int16_t mask_rout_id = LV_MASK_ID_INV;
147     lv_opa_t * mask_buf = NULL;
148     lv_draw_mask_radius_param_t mask_rout_param;
149     if(rout > 0 || mask_any) {
150         mask_buf = lv_mem_buf_get(clipped_w);
151         lv_draw_mask_radius_init(&mask_rout_param, &bg_coords, rout, false);
152         mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
153     }
154 
155     int32_t h;
156 
157     lv_area_t blend_area;
158     blend_area.x1 = clipped_coords.x1;
159     blend_area.x2 = clipped_coords.x2;
160 
161     blend_dsc.mask_buf = mask_buf;
162     blend_dsc.blend_area = &blend_area;
163     blend_dsc.mask_area = &blend_area;
164     blend_dsc.opa = LV_OPA_COVER;
165 
166     /*Get gradient if appropriate*/
167     lv_grad_t * grad = lv_gradient_get(&dsc->bg_grad, coords_bg_w, coords_bg_h);
168     if(grad && grad_dir == LV_GRAD_DIR_HOR) {
169         blend_dsc.src_buf = grad->map + clipped_coords.x1 - bg_coords.x1;
170     }
171 
172 #if _DITHER_GRADIENT
173     lv_dither_mode_t dither_mode = dsc->bg_grad.dither;
174     lv_dither_func_t dither_func = &lv_dither_none;
175     lv_coord_t grad_size = coords_bg_w;
176     if(grad_dir == LV_GRAD_DIR_VER && dither_mode != LV_DITHER_NONE) {
177         /* When dithering, we are still using a map that's changing from line to line*/
178         blend_dsc.src_buf = grad->map;
179     }
180 
181     if(grad && dither_mode == LV_DITHER_NONE) {
182         grad->filled = 0; /*Should we force refilling it each draw call ?*/
183         if(grad_dir == LV_GRAD_DIR_VER)
184             grad_size = coords_bg_h;
185     }
186     else
187 #if LV_DITHER_ERROR_DIFFUSION
188         if(dither_mode == LV_DITHER_ORDERED)
189 #endif
190             switch(grad_dir) {
191                 case LV_GRAD_DIR_HOR:
192                     dither_func = lv_dither_ordered_hor;
193                     break;
194                 case LV_GRAD_DIR_VER:
195                     dither_func = lv_dither_ordered_ver;
196                     break;
197                 default:
198                     dither_func = NULL;
199             }
200 
201 #if LV_DITHER_ERROR_DIFFUSION
202         else if(dither_mode == LV_DITHER_ERR_DIFF)
203             switch(grad_dir) {
204                 case LV_GRAD_DIR_HOR:
205                     dither_func = lv_dither_err_diff_hor;
206                     break;
207                 case LV_GRAD_DIR_VER:
208                     dither_func = lv_dither_err_diff_ver;
209                     break;
210                 default:
211                     dither_func = NULL;
212             }
213 #endif
214 #endif
215 
216     /*There is another mask too. Draw line by line. */
217     if(mask_any) {
218         for(h = clipped_coords.y1; h <= clipped_coords.y2; h++) {
219             blend_area.y1 = h;
220             blend_area.y2 = h;
221 
222             /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER.
223              * It saves calculating the final opa in lv_draw_sw_blend*/
224             lv_memset(mask_buf, opa, clipped_w);
225             blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w);
226             if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
227 
228 #if _DITHER_GRADIENT
229             if(dither_func) dither_func(grad, blend_area.x1,  h - bg_coords.y1, grad_size);
230 #endif
231             if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1];
232             lv_draw_sw_blend(draw_ctx, &blend_dsc);
233         }
234         goto bg_clean_up;
235     }
236 
237     /* Draw the top of the rectangle line by line and mirror it to the bottom. */
238     for(h = 0; h < rout; h++) {
239         lv_coord_t top_y = bg_coords.y1 + h;
240         lv_coord_t bottom_y = bg_coords.y2 - h;
241         if(top_y < clipped_coords.y1 && bottom_y > clipped_coords.y2) continue;   /*This line is clipped now*/
242 
243         /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER.
244          * It saves calculating the final opa in lv_draw_sw_blend*/
245         lv_memset(mask_buf, opa, clipped_w);
246         blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, blend_area.x1, top_y, clipped_w);
247         if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
248 
249         if(top_y >= clipped_coords.y1) {
250             blend_area.y1 = top_y;
251             blend_area.y2 = top_y;
252 
253 #if _DITHER_GRADIENT
254             if(dither_func) dither_func(grad, blend_area.x1,  top_y - bg_coords.y1, grad_size);
255 #endif
256             if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[top_y - bg_coords.y1];
257             lv_draw_sw_blend(draw_ctx, &blend_dsc);
258         }
259 
260         if(bottom_y <= clipped_coords.y2) {
261             blend_area.y1 = bottom_y;
262             blend_area.y2 = bottom_y;
263 
264 #if _DITHER_GRADIENT
265             if(dither_func) dither_func(grad, blend_area.x1,  bottom_y - bg_coords.y1, grad_size);
266 #endif
267             if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[bottom_y - bg_coords.y1];
268             lv_draw_sw_blend(draw_ctx, &blend_dsc);
269         }
270     }
271 
272     /* Draw the center of the rectangle.*/
273 
274     /*If no other masks and no gradient, the center is a simple rectangle*/
275     lv_area_t center_coords;
276     center_coords.x1 = bg_coords.x1;
277     center_coords.x2 = bg_coords.x2;
278     center_coords.y1 = bg_coords.y1 + rout;
279     center_coords.y2 = bg_coords.y2 - rout;
280     bool mask_any_center = lv_draw_mask_is_any(&center_coords);
281     if(!mask_any_center && grad_dir == LV_GRAD_DIR_NONE) {
282         blend_area.y1 = bg_coords.y1 + rout;
283         blend_area.y2 = bg_coords.y2 - rout;
284         blend_dsc.opa = opa;
285         blend_dsc.mask_buf = NULL;
286         lv_draw_sw_blend(draw_ctx, &blend_dsc);
287     }
288     /*With gradient and/or mask draw line by line*/
289     else {
290         blend_dsc.opa = opa;
291         blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER;
292         int32_t h_end = bg_coords.y2 - rout;
293         for(h = bg_coords.y1 + rout; h <= h_end; h++) {
294             /*If there is no other mask do not apply mask as in the center there is no radius to mask*/
295             if(mask_any_center) {
296                 lv_memset(mask_buf, opa, clipped_w);
297                 blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clipped_coords.x1, h, clipped_w);
298             }
299 
300             blend_area.y1 = h;
301             blend_area.y2 = h;
302 
303 #if _DITHER_GRADIENT
304             if(dither_func) dither_func(grad, blend_area.x1,  h - bg_coords.y1, grad_size);
305 #endif
306             if(grad_dir == LV_GRAD_DIR_VER) blend_dsc.color = grad->map[h - bg_coords.y1];
307             lv_draw_sw_blend(draw_ctx, &blend_dsc);
308         }
309     }
310 
311 bg_clean_up:
312     if(mask_buf) lv_mem_buf_release(mask_buf);
313     if(mask_rout_id != LV_MASK_ID_INV) {
314         lv_draw_mask_remove_id(mask_rout_id);
315         lv_draw_mask_free_param(&mask_rout_param);
316     }
317     if(grad) {
318         lv_gradient_cleanup(grad);
319     }
320 
321 #endif
322 }
323 
draw_bg_img(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)324 static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
325 {
326     if(dsc->bg_img_src == NULL) return;
327     if(dsc->bg_img_opa <= LV_OPA_MIN) return;
328 
329     lv_area_t clip_area;
330     if(!_lv_area_intersect(&clip_area, coords, draw_ctx->clip_area)) {
331         return;
332     }
333 
334     const lv_area_t * clip_area_ori = draw_ctx->clip_area;
335     draw_ctx->clip_area = &clip_area;
336 
337     lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src);
338     if(src_type == LV_IMG_SRC_SYMBOL) {
339         lv_point_t size;
340         lv_txt_get_size(&size, dsc->bg_img_src, dsc->bg_img_symbol_font, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
341         lv_area_t a;
342         a.x1 = coords->x1 + lv_area_get_width(coords) / 2 - size.x / 2;
343         a.x2 = a.x1 + size.x - 1;
344         a.y1 = coords->y1 + lv_area_get_height(coords) / 2 - size.y / 2;
345         a.y2 = a.y1 + size.y - 1;
346 
347         lv_draw_label_dsc_t label_draw_dsc;
348         lv_draw_label_dsc_init(&label_draw_dsc);
349         label_draw_dsc.font = dsc->bg_img_symbol_font;
350         label_draw_dsc.color = dsc->bg_img_recolor;
351         label_draw_dsc.opa = dsc->bg_img_opa;
352         lv_draw_label(draw_ctx, &label_draw_dsc, &a, dsc->bg_img_src, NULL);
353     }
354     else {
355         lv_img_header_t header;
356         lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header);
357         if(res == LV_RES_OK) {
358             lv_draw_img_dsc_t img_dsc;
359             lv_draw_img_dsc_init(&img_dsc);
360             img_dsc.blend_mode = dsc->blend_mode;
361             img_dsc.recolor = dsc->bg_img_recolor;
362             img_dsc.recolor_opa = dsc->bg_img_recolor_opa;
363             img_dsc.opa = dsc->bg_img_opa;
364 
365             /*Center align*/
366             if(dsc->bg_img_tiled == false) {
367                 lv_area_t area;
368                 area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2;
369                 area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2;
370                 area.x2 = area.x1 + header.w - 1;
371                 area.y2 = area.y1 + header.h - 1;
372 
373                 lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src);
374             }
375             else {
376                 lv_area_t area;
377                 area.y1 = coords->y1;
378                 area.y2 = area.y1 + header.h - 1;
379 
380                 for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) {
381 
382                     area.x1 = coords->x1;
383                     area.x2 = area.x1 + header.w - 1;
384                     for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) {
385                         lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src);
386                     }
387                 }
388             }
389         }
390         else {
391             LV_LOG_WARN("Couldn't read the background image");
392         }
393     }
394 
395     draw_ctx->clip_area = clip_area_ori;
396 }
397 
draw_border(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)398 static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
399 {
400     if(dsc->border_opa <= LV_OPA_MIN) return;
401     if(dsc->border_width == 0) return;
402     if(dsc->border_side == LV_BORDER_SIDE_NONE) return;
403     if(dsc->border_post) return;
404 
405     int32_t coords_w = lv_area_get_width(coords);
406     int32_t coords_h = lv_area_get_height(coords);
407     int32_t rout = dsc->radius;
408     int32_t short_side = LV_MIN(coords_w, coords_h);
409     if(rout > short_side >> 1) rout = short_side >> 1;
410 
411     /*Get the inner area*/
412     lv_area_t area_inner;
413     lv_area_copy(&area_inner, coords);
414     area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout));
415     area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout));
416     area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout));
417     area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout));
418 
419     lv_coord_t rin = rout - dsc->border_width;
420     if(rin < 0) rin = 0;
421 
422     draw_border_generic(draw_ctx, coords, &area_inner, rout, rin, dsc->border_color, dsc->border_opa, dsc->blend_mode);
423 
424 }
425 
426 #if LV_DRAW_COMPLEX
draw_shadow(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)427 static void LV_ATTRIBUTE_FAST_MEM draw_shadow(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc,
428                                               const lv_area_t * coords)
429 {
430     /*Check whether the shadow is visible*/
431     if(dsc->shadow_width == 0) return;
432     if(dsc->shadow_opa <= LV_OPA_MIN) return;
433 
434     if(dsc->shadow_width == 1 && dsc->shadow_spread <= 0 &&
435        dsc->shadow_ofs_x == 0 && dsc->shadow_ofs_y == 0) {
436         return;
437     }
438 
439     /*Calculate the rectangle which is blurred to get the shadow in `shadow_area`*/
440     lv_area_t core_area;
441     core_area.x1 = coords->x1  + dsc->shadow_ofs_x - dsc->shadow_spread;
442     core_area.x2 = coords->x2  + dsc->shadow_ofs_x + dsc->shadow_spread;
443     core_area.y1 = coords->y1  + dsc->shadow_ofs_y - dsc->shadow_spread;
444     core_area.y2 = coords->y2  + dsc->shadow_ofs_y + dsc->shadow_spread;
445 
446     /*Calculate the bounding box of the shadow*/
447     lv_area_t shadow_area;
448     shadow_area.x1 = core_area.x1 - dsc->shadow_width / 2 - 1;
449     shadow_area.x2 = core_area.x2 + dsc->shadow_width / 2 + 1;
450     shadow_area.y1 = core_area.y1 - dsc->shadow_width / 2 - 1;
451     shadow_area.y2 = core_area.y2 + dsc->shadow_width / 2 + 1;
452 
453     lv_opa_t opa = dsc->shadow_opa;
454     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
455 
456     /*Get clipped draw area which is the real draw area.
457      *It is always the same or inside `shadow_area`*/
458     lv_area_t draw_area;
459     if(!_lv_area_intersect(&draw_area, &shadow_area, draw_ctx->clip_area)) return;
460 
461     /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/
462     lv_area_t bg_area;
463     lv_area_copy(&bg_area, coords);
464     lv_area_increase(&bg_area, -1, -1);
465 
466     /*Get the clamped radius*/
467     int32_t r_bg = dsc->radius;
468     lv_coord_t short_side = LV_MIN(lv_area_get_width(&bg_area), lv_area_get_height(&bg_area));
469     if(r_bg > short_side >> 1) r_bg = short_side >> 1;
470 
471     /*Get the clamped radius*/
472     int32_t r_sh = dsc->radius;
473     short_side = LV_MIN(lv_area_get_width(&core_area), lv_area_get_height(&core_area));
474     if(r_sh > short_side >> 1) r_sh = short_side >> 1;
475 
476     /*Get how many pixels are affected by the blur on the corners*/
477     int32_t corner_size = dsc->shadow_width  + r_sh;
478 
479     lv_opa_t * sh_buf;
480 
481 #if LV_SHADOW_CACHE_SIZE
482     if(sh_cache_size == corner_size && sh_cache_r == r_sh) {
483         /*Use the cache if available*/
484         sh_buf = lv_mem_buf_get(corner_size * corner_size);
485         lv_memcpy(sh_buf, sh_cache, corner_size * corner_size);
486     }
487     else {
488         /*A larger buffer is required for calculation*/
489         sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t));
490         shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh);
491 
492         /*Cache the corner if it fits into the cache size*/
493         if((uint32_t)corner_size * corner_size < sizeof(sh_cache)) {
494             lv_memcpy(sh_cache, sh_buf, corner_size * corner_size);
495             sh_cache_size = corner_size;
496             sh_cache_r = r_sh;
497         }
498     }
499 #else
500     sh_buf = lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t));
501     shadow_draw_corner_buf(&core_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh);
502 #endif
503 
504     /*Skip a lot of masking if the background will cover the shadow that would be masked out*/
505     bool mask_any = lv_draw_mask_is_any(&shadow_area);
506     bool simple = true;
507     if(mask_any || dsc->bg_opa < LV_OPA_COVER || dsc->blend_mode != LV_BLEND_MODE_NORMAL) simple = false;
508 
509     /*Create a radius mask to clip remove shadow on the bg area*/
510 
511     lv_draw_mask_radius_param_t mask_rout_param;
512     int16_t mask_rout_id = LV_MASK_ID_INV;
513     if(!simple) {
514         lv_draw_mask_radius_init(&mask_rout_param, &bg_area, r_bg, true);
515         mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
516     }
517     lv_opa_t * mask_buf = lv_mem_buf_get(lv_area_get_width(&shadow_area));
518     lv_area_t blend_area;
519     lv_area_t clip_area_sub;
520     lv_opa_t * sh_buf_tmp;
521     lv_coord_t y;
522     bool simple_sub;
523 
524     lv_draw_sw_blend_dsc_t blend_dsc;
525     lv_memset_00(&blend_dsc, sizeof(blend_dsc));
526     blend_dsc.blend_area = &blend_area;
527     blend_dsc.mask_area = &blend_area;
528     blend_dsc.mask_buf = mask_buf;
529     blend_dsc.color = dsc->shadow_color;
530     blend_dsc.opa = dsc->shadow_opa;
531     blend_dsc.blend_mode = dsc->blend_mode;
532 
533     lv_coord_t w_half = shadow_area.x1 + lv_area_get_width(&shadow_area) / 2;
534     lv_coord_t h_half = shadow_area.y1 + lv_area_get_height(&shadow_area) / 2;
535 
536     /*Draw the corners if they are on the current clip area and not fully covered by the bg*/
537 
538     /*Top right corner*/
539     blend_area.x2 = shadow_area.x2;
540     blend_area.x1 = shadow_area.x2 - corner_size + 1;
541     blend_area.y1 = shadow_area.y1;
542     blend_area.y2 = shadow_area.y1 + corner_size - 1;
543     /*Do not overdraw the other top corners*/
544     blend_area.x1 = LV_MAX(blend_area.x1, w_half);
545     blend_area.y2 = LV_MIN(blend_area.y2, h_half);
546 
547     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
548        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
549         lv_coord_t w = lv_area_get_width(&clip_area_sub);
550         sh_buf_tmp = sh_buf;
551         sh_buf_tmp += (clip_area_sub.y1 - shadow_area.y1) * corner_size;
552         sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1);
553 
554         /*Do not mask if out of the bg*/
555         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
556         else simple_sub = simple;
557         if(w > 0) {
558             blend_dsc.mask_buf = mask_buf;
559             blend_area.x1 = clip_area_sub.x1;
560             blend_area.x2 = clip_area_sub.x2;
561             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
562             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
563                 blend_area.y1 = y;
564                 blend_area.y2 = y;
565 
566                 if(!simple_sub) {
567                     lv_memcpy(mask_buf, sh_buf_tmp, corner_size);
568                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
569                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
570                 }
571                 else {
572                     blend_dsc.mask_buf = sh_buf_tmp;
573                 }
574                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
575                 sh_buf_tmp += corner_size;
576             }
577         }
578     }
579 
580     /*Bottom right corner.
581      *Almost the same as top right just read the lines of `sh_buf` from then end*/
582     blend_area.x2 = shadow_area.x2;
583     blend_area.x1 = shadow_area.x2 - corner_size + 1;
584     blend_area.y1 = shadow_area.y2 - corner_size + 1;
585     blend_area.y2 = shadow_area.y2;
586     /*Do not overdraw the other corners*/
587     blend_area.x1 = LV_MAX(blend_area.x1, w_half);
588     blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1);
589 
590     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
591        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
592         lv_coord_t w = lv_area_get_width(&clip_area_sub);
593         sh_buf_tmp = sh_buf;
594         sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size;
595         sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1);
596         /*Do not mask if out of the bg*/
597         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
598         else simple_sub = simple;
599 
600         if(w > 0) {
601             blend_dsc.mask_buf = mask_buf;
602             blend_area.x1 = clip_area_sub.x1;
603             blend_area.x2 = clip_area_sub.x2;
604             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
605             for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) {
606                 blend_area.y1 = y;
607                 blend_area.y2 = y;
608 
609                 if(!simple_sub) {
610                     lv_memcpy(mask_buf, sh_buf_tmp, corner_size);
611                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
612                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
613                 }
614                 else {
615                     blend_dsc.mask_buf = sh_buf_tmp;
616                 }
617                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
618                 sh_buf_tmp += corner_size;
619             }
620         }
621     }
622 
623     /*Top side*/
624     blend_area.x1 = shadow_area.x1 + corner_size;
625     blend_area.x2 = shadow_area.x2 - corner_size;
626     blend_area.y1 = shadow_area.y1;
627     blend_area.y2 = shadow_area.y1 + corner_size - 1;
628     blend_area.y2 = LV_MIN(blend_area.y2, h_half);
629 
630     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
631        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
632         lv_coord_t w = lv_area_get_width(&clip_area_sub);
633         sh_buf_tmp = sh_buf;
634         sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size;
635 
636         /*Do not mask if out of the bg*/
637         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
638         else simple_sub = simple;
639 
640         if(w > 0) {
641             if(!simple_sub) {
642                 blend_dsc.mask_buf = mask_buf;
643             }
644             else {
645                 blend_dsc.mask_buf = NULL;
646             }
647             blend_area.x1 = clip_area_sub.x1;
648             blend_area.x2 = clip_area_sub.x2;
649 
650             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
651                 blend_area.y1 = y;
652                 blend_area.y2 = y;
653 
654                 if(!simple_sub) {
655                     lv_memset(mask_buf, sh_buf_tmp[0], w);
656                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
657                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
658                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
659                 }
660                 else {
661                     blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8;
662                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
663                 }
664                 sh_buf_tmp += corner_size;
665             }
666         }
667     }
668     blend_dsc.opa = dsc->shadow_opa;    /*Restore*/
669 
670     /*Bottom side*/
671     blend_area.x1 = shadow_area.x1 + corner_size;
672     blend_area.x2 = shadow_area.x2 - corner_size;
673     blend_area.y1 = shadow_area.y2 - corner_size + 1;
674     blend_area.y2 = shadow_area.y2;
675     blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1);
676 
677     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
678        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
679         lv_coord_t w = lv_area_get_width(&clip_area_sub);
680         sh_buf_tmp = sh_buf;
681         sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size;
682         if(w > 0) {
683             /*Do not mask if out of the bg*/
684             if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
685             else simple_sub = simple;
686 
687             if(!simple_sub) {
688                 blend_dsc.mask_buf = mask_buf;
689             }
690             else {
691                 blend_dsc.mask_buf = NULL;
692             }
693             blend_area.x1 = clip_area_sub.x1;
694             blend_area.x2 = clip_area_sub.x2;
695 
696             for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) {
697                 blend_area.y1 = y;
698                 blend_area.y2 = y;
699 
700                 /*Do not mask if out of the bg*/
701                 if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
702                 else simple_sub = simple;
703 
704                 if(!simple_sub) {
705                     lv_memset(mask_buf, sh_buf_tmp[0], w);
706                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
707                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
708                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
709                 }
710                 else {
711                     blend_dsc.opa = opa == LV_OPA_COVER ? sh_buf_tmp[0] : (sh_buf_tmp[0] * dsc->shadow_opa) >> 8;
712                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
713 
714                 }
715                 sh_buf_tmp += corner_size;
716             }
717         }
718     }
719 
720     blend_dsc.opa = dsc->shadow_opa;    /*Restore*/
721 
722     /*Right side*/
723     blend_area.x1 = shadow_area.x2 - corner_size + 1;
724     blend_area.x2 = shadow_area.x2;
725     blend_area.y1 = shadow_area.y1 + corner_size;
726     blend_area.y2 = shadow_area.y2 - corner_size;
727     /*Do not overdraw the other corners*/
728     blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1);
729     blend_area.y2 = LV_MAX(blend_area.y2, h_half);
730     blend_area.x1 = LV_MAX(blend_area.x1, w_half);
731 
732     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
733        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
734         lv_coord_t w = lv_area_get_width(&clip_area_sub);
735         sh_buf_tmp = sh_buf;
736         sh_buf_tmp += (corner_size - 1) * corner_size;
737         sh_buf_tmp += clip_area_sub.x1 - (shadow_area.x2 - corner_size + 1);
738 
739         /*Do not mask if out of the bg*/
740         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
741         else simple_sub = simple;
742         blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf;
743 
744         if(w > 0) {
745             blend_area.x1 = clip_area_sub.x1;
746             blend_area.x2 = clip_area_sub.x2;
747             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
748             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
749                 blend_area.y1 = y;
750                 blend_area.y2 = y;
751 
752                 if(!simple_sub) {
753                     lv_memcpy(mask_buf, sh_buf_tmp, w);
754                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
755                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
756                 }
757                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
758             }
759         }
760     }
761 
762     /*Mirror the shadow corner buffer horizontally*/
763     sh_buf_tmp = sh_buf ;
764     for(y = 0; y < corner_size; y++) {
765         int32_t x;
766         lv_opa_t * start = sh_buf_tmp;
767         lv_opa_t * end = sh_buf_tmp + corner_size - 1;
768         for(x = 0; x < corner_size / 2; x++) {
769             lv_opa_t tmp = *start;
770             *start = *end;
771             *end = tmp;
772 
773             start++;
774             end--;
775         }
776         sh_buf_tmp += corner_size;
777     }
778 
779     /*Left side*/
780     blend_area.x1 = shadow_area.x1;
781     blend_area.x2 = shadow_area.x1 + corner_size - 1;
782     blend_area.y1 = shadow_area.y1 + corner_size;
783     blend_area.y2 = shadow_area.y2 - corner_size;
784     /*Do not overdraw the other corners*/
785     blend_area.y1 = LV_MIN(blend_area.y1, h_half + 1);
786     blend_area.y2 = LV_MAX(blend_area.y2, h_half);
787     blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1);
788 
789     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
790        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
791         lv_coord_t w = lv_area_get_width(&clip_area_sub);
792         sh_buf_tmp = sh_buf;
793         sh_buf_tmp += (corner_size - 1) * corner_size;
794         sh_buf_tmp += clip_area_sub.x1 - blend_area.x1;
795 
796         /*Do not mask if out of the bg*/
797         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
798         else simple_sub = simple;
799         blend_dsc.mask_buf = simple_sub ? sh_buf_tmp : mask_buf;
800         if(w > 0) {
801             blend_area.x1 = clip_area_sub.x1;
802             blend_area.x2 = clip_area_sub.x2;
803             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
804             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
805                 blend_area.y1 = y;
806                 blend_area.y2 = y;
807 
808                 if(!simple_sub) {
809                     lv_memcpy(mask_buf, sh_buf_tmp, w);
810                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
811                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
812                 }
813 
814                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
815             }
816         }
817     }
818 
819     /*Top left corner*/
820     blend_area.x1 = shadow_area.x1;
821     blend_area.x2 = shadow_area.x1 + corner_size - 1;
822     blend_area.y1 = shadow_area.y1;
823     blend_area.y2 = shadow_area.y1 + corner_size - 1;
824     /*Do not overdraw the other corners*/
825     blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1);
826     blend_area.y2 = LV_MIN(blend_area.y2, h_half);
827 
828     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
829        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
830         lv_coord_t w = lv_area_get_width(&clip_area_sub);
831         sh_buf_tmp = sh_buf;
832         sh_buf_tmp += (clip_area_sub.y1 - blend_area.y1) * corner_size;
833         sh_buf_tmp += clip_area_sub.x1 - blend_area.x1;
834 
835         /*Do not mask if out of the bg*/
836         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
837         else simple_sub = simple;
838         blend_dsc.mask_buf = mask_buf;
839 
840         if(w > 0) {
841             blend_area.x1 = clip_area_sub.x1;
842             blend_area.x2 = clip_area_sub.x2;
843             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
844             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
845                 blend_area.y1 = y;
846                 blend_area.y2 = y;
847 
848                 if(!simple_sub) {
849                     lv_memcpy(mask_buf, sh_buf_tmp, corner_size);
850                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
851                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
852                 }
853                 else {
854                     blend_dsc.mask_buf = sh_buf_tmp;
855                 }
856 
857                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
858                 sh_buf_tmp += corner_size;
859             }
860         }
861     }
862 
863     /*Bottom left corner.
864      *Almost the same as bottom right just read the lines of `sh_buf` from then end*/
865     blend_area.x1 = shadow_area.x1 ;
866     blend_area.x2 = shadow_area.x1 + corner_size - 1;
867     blend_area.y1 = shadow_area.y2 - corner_size + 1;
868     blend_area.y2 = shadow_area.y2;
869     /*Do not overdraw the other corners*/
870     blend_area.y1 = LV_MAX(blend_area.y1, h_half + 1);
871     blend_area.x2 = LV_MIN(blend_area.x2, w_half - 1);
872 
873     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
874        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
875         lv_coord_t w = lv_area_get_width(&clip_area_sub);
876         sh_buf_tmp = sh_buf;
877         sh_buf_tmp += (blend_area.y2 - clip_area_sub.y2) * corner_size;
878         sh_buf_tmp += clip_area_sub.x1 - blend_area.x1;
879 
880         /*Do not mask if out of the bg*/
881         if(simple && _lv_area_is_out(&clip_area_sub, &bg_area, r_bg)) simple_sub = true;
882         else simple_sub = simple;
883         blend_dsc.mask_buf = mask_buf;
884         if(w > 0) {
885             blend_area.x1 = clip_area_sub.x1;
886             blend_area.x2 = clip_area_sub.x2;
887             blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;    /*In simple mode it won't be overwritten*/
888             for(y = clip_area_sub.y2; y >= clip_area_sub.y1; y--) {
889                 blend_area.y1 = y;
890                 blend_area.y2 = y;
891 
892                 if(!simple_sub) {
893                     lv_memcpy(mask_buf, sh_buf_tmp, corner_size);
894                     blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
895                     if(blend_dsc.mask_res == LV_DRAW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED;
896                 }
897                 else {
898                     blend_dsc.mask_buf = sh_buf_tmp;
899                 }
900                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
901                 sh_buf_tmp += corner_size;
902             }
903         }
904     }
905 
906     /*Draw the center rectangle.*/
907     blend_area.x1 = shadow_area.x1 + corner_size ;
908     blend_area.x2 = shadow_area.x2 - corner_size;
909     blend_area.y1 = shadow_area.y1 + corner_size;
910     blend_area.y2 = shadow_area.y2 - corner_size;
911     blend_dsc.mask_buf = mask_buf;
912 
913     if(_lv_area_intersect(&clip_area_sub, &blend_area, draw_ctx->clip_area) &&
914        !_lv_area_is_in(&clip_area_sub, &bg_area, r_bg)) {
915         lv_coord_t w = lv_area_get_width(&clip_area_sub);
916         if(w > 0) {
917             blend_area.x1 = clip_area_sub.x1;
918             blend_area.x2 = clip_area_sub.x2;
919             for(y = clip_area_sub.y1; y <= clip_area_sub.y2; y++) {
920                 blend_area.y1 = y;
921                 blend_area.y2 = y;
922 
923                 lv_memset_ff(mask_buf, w);
924                 blend_dsc.mask_res = lv_draw_mask_apply(mask_buf, clip_area_sub.x1, y, w);
925                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
926             }
927         }
928     }
929 
930     if(!simple) {
931         lv_draw_mask_free_param(&mask_rout_param);
932         lv_draw_mask_remove_id(mask_rout_id);
933     }
934     lv_mem_buf_release(sh_buf);
935     lv_mem_buf_release(mask_buf);
936 }
937 
938 /**
939  * Calculate a blurred corner
940  * @param coords Coordinates of the shadow
941  * @param sh_buf a buffer to store the result. Its size should be `(sw + r)^2 * 2`
942  * @param sw shadow width
943  * @param r radius
944  */
shadow_draw_corner_buf(const lv_area_t * coords,uint16_t * sh_buf,lv_coord_t sw,lv_coord_t r)945 static void LV_ATTRIBUTE_FAST_MEM shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf,
946                                                          lv_coord_t sw, lv_coord_t r)
947 {
948     int32_t sw_ori = sw;
949     int32_t size = sw_ori  + r;
950 
951     lv_area_t sh_area;
952     lv_area_copy(&sh_area, coords);
953     sh_area.x2 = sw / 2 + r - 1  - ((sw & 1) ? 0 : 1);
954     sh_area.y1 = sw / 2 + 1;
955 
956     sh_area.x1 = sh_area.x2 - lv_area_get_width(coords);
957     sh_area.y2 = sh_area.y1 + lv_area_get_height(coords);
958 
959     lv_draw_mask_radius_param_t mask_param;
960     lv_draw_mask_radius_init(&mask_param, &sh_area, r, false);
961 
962 #if SHADOW_ENHANCE
963     /*Set half shadow width width because blur will be repeated*/
964     if(sw_ori == 1) sw = 1;
965     else sw = sw_ori >> 1;
966 #endif
967 
968     int32_t y;
969     lv_opa_t * mask_line = lv_mem_buf_get(size);
970     uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf;
971     for(y = 0; y < size; y++) {
972         lv_memset_ff(mask_line, size);
973         lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param);
974         if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
975             lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0]));
976         }
977         else {
978             int32_t i;
979             sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSCALE_SHIFT) / sw;
980             for(i = 1; i < size; i++) {
981                 if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1];
982                 else  sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSCALE_SHIFT) / sw;
983             }
984         }
985 
986         sh_ups_tmp_buf += size;
987     }
988     lv_mem_buf_release(mask_line);
989 
990     lv_draw_mask_free_param(&mask_param);
991 
992     if(sw == 1) {
993         int32_t i;
994         lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
995         for(i = 0; i < size * size; i++) {
996             res_buf[i] = (sh_buf[i] >> SHADOW_UPSCALE_SHIFT);
997         }
998         return;
999     }
1000 
1001     shadow_blur_corner(size, sw, sh_buf);
1002 
1003 #if SHADOW_ENHANCE == 0
1004     /*The result is required in lv_opa_t not uint16_t*/
1005     uint32_t x;
1006     lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
1007     for(x = 0; x < size * size; x++) {
1008         res_buf[x] = sh_buf[x];
1009     }
1010 #else
1011     sw += sw_ori & 1;
1012     if(sw > 1) {
1013         uint32_t i;
1014         uint32_t max_v_div = (LV_OPA_COVER << SHADOW_UPSCALE_SHIFT) / sw;
1015         for(i = 0; i < (uint32_t)size * size; i++) {
1016             if(sh_buf[i] == 0) continue;
1017             else if(sh_buf[i] == LV_OPA_COVER) sh_buf[i] = max_v_div;
1018             else  sh_buf[i] = (sh_buf[i] << SHADOW_UPSCALE_SHIFT) / sw;
1019         }
1020 
1021         shadow_blur_corner(size, sw, sh_buf);
1022     }
1023     int32_t x;
1024     lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
1025     for(x = 0; x < size * size; x++) {
1026         res_buf[x] = sh_buf[x];
1027     }
1028 #endif
1029 
1030 }
1031 
shadow_blur_corner(lv_coord_t size,lv_coord_t sw,uint16_t * sh_ups_buf)1032 static void LV_ATTRIBUTE_FAST_MEM shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf)
1033 {
1034     int32_t s_left = sw >> 1;
1035     int32_t s_right = (sw >> 1);
1036     if((sw & 1) == 0) s_left--;
1037 
1038     /*Horizontal blur*/
1039     uint16_t * sh_ups_blur_buf = lv_mem_buf_get(size * sizeof(uint16_t));
1040 
1041     int32_t x;
1042     int32_t y;
1043 
1044     uint16_t * sh_ups_tmp_buf = sh_ups_buf;
1045 
1046     for(y = 0; y < size; y++) {
1047         int32_t v = sh_ups_tmp_buf[size - 1] * sw;
1048         for(x = size - 1; x >= 0; x--) {
1049             sh_ups_blur_buf[x] = v;
1050 
1051             /*Forget the right pixel*/
1052             uint32_t right_val = 0;
1053             if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right];
1054             v -= right_val;
1055 
1056             /*Add the left pixel*/
1057             uint32_t left_val;
1058             if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0];
1059             else left_val = sh_ups_tmp_buf[x - s_left - 1];
1060             v += left_val;
1061         }
1062         lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t));
1063         sh_ups_tmp_buf += size;
1064     }
1065 
1066     /*Vertical blur*/
1067     uint32_t i;
1068     uint32_t max_v = LV_OPA_COVER << SHADOW_UPSCALE_SHIFT;
1069     uint32_t max_v_div = max_v / sw;
1070     for(i = 0; i < (uint32_t)size * size; i++) {
1071         if(sh_ups_buf[i] == 0) continue;
1072         else if(sh_ups_buf[i] == max_v) sh_ups_buf[i] = max_v_div;
1073         else sh_ups_buf[i] = sh_ups_buf[i] / sw;
1074     }
1075 
1076     for(x = 0; x < size; x++) {
1077         sh_ups_tmp_buf = &sh_ups_buf[x];
1078         int32_t v = sh_ups_tmp_buf[0] * sw;
1079         for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) {
1080             sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSCALE_SHIFT);
1081 
1082             /*Forget the top pixel*/
1083             uint32_t top_val;
1084             if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0];
1085             else top_val = sh_ups_buf[(y - s_right) * size + x];
1086             v -= top_val;
1087 
1088             /*Add the bottom pixel*/
1089             uint32_t bottom_val;
1090             if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x];
1091             else bottom_val = sh_ups_buf[(size - 1) * size + x];
1092             v += bottom_val;
1093         }
1094 
1095         /*Write back the result into `sh_ups_buf`*/
1096         sh_ups_tmp_buf = &sh_ups_buf[x];
1097         for(y = 0; y < size; y++, sh_ups_tmp_buf += size) {
1098             (*sh_ups_tmp_buf) = sh_ups_blur_buf[y];
1099         }
1100     }
1101 
1102     lv_mem_buf_release(sh_ups_blur_buf);
1103 }
1104 #endif
1105 
draw_outline(lv_draw_ctx_t * draw_ctx,const lv_draw_rect_dsc_t * dsc,const lv_area_t * coords)1106 static void draw_outline(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
1107 {
1108     if(dsc->outline_opa <= LV_OPA_MIN) return;
1109     if(dsc->outline_width == 0) return;
1110 
1111     lv_opa_t opa = dsc->outline_opa;
1112 
1113     if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
1114 
1115     /*Get the inner radius*/
1116     lv_area_t area_inner;
1117     lv_area_copy(&area_inner, coords);
1118 
1119     /*Bring the outline closer to make sure there is no color bleeding with pad=0*/
1120     lv_coord_t pad = dsc->outline_pad - 1;
1121     area_inner.x1 -= pad;
1122     area_inner.y1 -= pad;
1123     area_inner.x2 += pad;
1124     area_inner.y2 += pad;
1125 
1126     lv_area_t area_outer;
1127     lv_area_copy(&area_outer, &area_inner);
1128 
1129     area_outer.x1 -= dsc->outline_width;
1130     area_outer.x2 += dsc->outline_width;
1131     area_outer.y1 -= dsc->outline_width;
1132     area_outer.y2 += dsc->outline_width;
1133 
1134     int32_t inner_w = lv_area_get_width(&area_inner);
1135     int32_t inner_h = lv_area_get_height(&area_inner);
1136     int32_t rin = dsc->radius;
1137     int32_t short_side = LV_MIN(inner_w, inner_h);
1138     if(rin > short_side >> 1) rin = short_side >> 1;
1139 
1140     lv_coord_t rout = rin + dsc->outline_width;
1141 
1142     draw_border_generic(draw_ctx, &area_outer, &area_inner, rout, rin, dsc->outline_color, dsc->outline_opa,
1143                         dsc->blend_mode);
1144 }
1145 
draw_border_generic(lv_draw_ctx_t * draw_ctx,const lv_area_t * outer_area,const lv_area_t * inner_area,lv_coord_t rout,lv_coord_t rin,lv_color_t color,lv_opa_t opa,lv_blend_mode_t blend_mode)1146 void draw_border_generic(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
1147                          lv_coord_t rout, lv_coord_t rin, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
1148 {
1149     opa = opa >= LV_OPA_COVER ? LV_OPA_COVER : opa;
1150 
1151     bool mask_any = lv_draw_mask_is_any(outer_area);
1152 
1153 #if LV_DRAW_COMPLEX
1154 
1155     if(!mask_any && rout == 0 && rin == 0) {
1156         draw_border_simple(draw_ctx, outer_area, inner_area, color, opa);
1157         return;
1158     }
1159 
1160     /*Get clipped draw area which is the real draw area.
1161      *It is always the same or inside `coords`*/
1162     lv_area_t draw_area;
1163     if(!_lv_area_intersect(&draw_area, outer_area, draw_ctx->clip_area)) return;
1164     int32_t draw_area_w = lv_area_get_width(&draw_area);
1165 
1166     lv_draw_sw_blend_dsc_t blend_dsc;
1167     lv_memset_00(&blend_dsc, sizeof(blend_dsc));
1168     blend_dsc.mask_buf = lv_mem_buf_get(draw_area_w);;
1169 
1170     /*Create mask for the outer area*/
1171     int16_t mask_rout_id = LV_MASK_ID_INV;
1172     lv_draw_mask_radius_param_t mask_rout_param;
1173     if(rout > 0) {
1174         lv_draw_mask_radius_init(&mask_rout_param, outer_area, rout, false);
1175         mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
1176     }
1177 
1178     /*Create mask for the inner mask*/
1179     lv_draw_mask_radius_param_t mask_rin_param;
1180     lv_draw_mask_radius_init(&mask_rin_param, inner_area, rin, true);
1181     int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL);
1182 
1183     int32_t h;
1184     lv_area_t blend_area;
1185     blend_dsc.blend_area = &blend_area;
1186     blend_dsc.mask_area = &blend_area;
1187     blend_dsc.color = color;
1188     blend_dsc.opa = opa;
1189     blend_dsc.blend_mode = blend_mode;
1190 
1191     /*Calculate the x and y coordinates where the straight parts area*/
1192     lv_area_t core_area;
1193     core_area.x1 = LV_MAX(outer_area->x1 + rout, inner_area->x1);
1194     core_area.x2 = LV_MIN(outer_area->x2 - rout, inner_area->x2);
1195     core_area.y1 = LV_MAX(outer_area->y1 + rout, inner_area->y1);
1196     core_area.y2 = LV_MIN(outer_area->y2 - rout, inner_area->y2);
1197     lv_coord_t core_w = lv_area_get_width(&core_area);
1198 
1199     bool top_side = outer_area->y1 <= inner_area->y1 ? true : false;
1200     bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false;
1201 
1202     /*If there is other masks, need to draw line by line*/
1203     if(mask_any) {
1204         blend_area.x1 = draw_area.x1;
1205         blend_area.x2 = draw_area.x2;
1206         for(h = draw_area.y1; h <= draw_area.y2; h++) {
1207             if(!top_side && h < core_area.y1) continue;
1208             if(!bottom_side && h > core_area.y2) break;
1209 
1210             blend_area.y1 = h;
1211             blend_area.y2 = h;
1212 
1213             lv_memset_ff(blend_dsc.mask_buf, draw_area_w);
1214             blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, draw_area.x1, h, draw_area_w);
1215             lv_draw_sw_blend(draw_ctx, &blend_dsc);
1216         }
1217 
1218         lv_draw_mask_free_param(&mask_rin_param);
1219         lv_draw_mask_remove_id(mask_rin_id);
1220         if(mask_rout_id != LV_MASK_ID_INV) {
1221             lv_draw_mask_free_param(&mask_rout_param);
1222             lv_draw_mask_remove_id(mask_rout_id);
1223         }
1224         lv_mem_buf_release(blend_dsc.mask_buf);
1225         return;
1226     }
1227 
1228     /*No masks*/
1229     bool left_side = outer_area->x1 <= inner_area->x1 ? true : false;
1230     bool right_side = outer_area->x2 >= inner_area->x2 ? true : false;
1231 
1232     bool split_hor = true;
1233     if(left_side && right_side && top_side && bottom_side &&
1234        core_w < SPLIT_LIMIT) {
1235         split_hor = false;
1236     }
1237 
1238     blend_dsc.mask_res = LV_DRAW_MASK_RES_FULL_COVER;
1239     /*Draw the straight lines first if they are long enough*/
1240     if(top_side && split_hor) {
1241         blend_area.x1 = core_area.x1;
1242         blend_area.x2 = core_area.x2;
1243         blend_area.y1 = outer_area->y1;
1244         blend_area.y2 = inner_area->y1 - 1;
1245         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1246     }
1247 
1248     if(bottom_side && split_hor) {
1249         blend_area.x1 = core_area.x1;
1250         blend_area.x2 = core_area.x2;
1251         blend_area.y1 = inner_area->y2 + 1;
1252         blend_area.y2 = outer_area->y2;
1253         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1254     }
1255 
1256     if(left_side) {
1257         blend_area.x1 = outer_area->x1;
1258         blend_area.x2 = inner_area->x1 - 1;
1259         blend_area.y1 = core_area.y1;
1260         blend_area.y2 = core_area.y2;
1261         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1262     }
1263 
1264     if(right_side) {
1265         blend_area.x1 = inner_area->x2 + 1;
1266         blend_area.x2 = outer_area->x2;
1267         blend_area.y1 = core_area.y1;
1268         blend_area.y2 = core_area.y2;
1269         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1270     }
1271 
1272     /*Draw the corners*/
1273     lv_coord_t blend_w;
1274 
1275     /*Left and right corner together if they are close to each other*/
1276     if(!split_hor) {
1277         /*Calculate the top corner and mirror it to the bottom*/
1278         blend_area.x1 = draw_area.x1;
1279         blend_area.x2 = draw_area.x2;
1280         lv_coord_t max_h = LV_MAX(rout, inner_area->y1 - outer_area->y1);
1281         for(h = 0; h < max_h; h++) {
1282             lv_coord_t top_y = outer_area->y1 + h;
1283             lv_coord_t bottom_y = outer_area->y2 - h;
1284             if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue;   /*This line is clipped now*/
1285 
1286             lv_memset_ff(blend_dsc.mask_buf, draw_area_w);
1287             blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, top_y, draw_area_w);
1288 
1289             if(top_y >= draw_area.y1) {
1290                 blend_area.y1 = top_y;
1291                 blend_area.y2 = top_y;
1292                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
1293             }
1294 
1295             if(bottom_y <= draw_area.y2) {
1296                 blend_area.y1 = bottom_y;
1297                 blend_area.y2 = bottom_y;
1298                 lv_draw_sw_blend(draw_ctx, &blend_dsc);
1299             }
1300         }
1301     }
1302     else {
1303         /*Left corners*/
1304         blend_area.x1 = draw_area.x1;
1305         blend_area.x2 = LV_MIN(draw_area.x2, core_area.x1 - 1);
1306         blend_w = lv_area_get_width(&blend_area);
1307         if(blend_w > 0) {
1308             if(left_side || top_side) {
1309                 for(h = draw_area.y1; h < core_area.y1; h++) {
1310                     blend_area.y1 = h;
1311                     blend_area.y2 = h;
1312 
1313                     lv_memset_ff(blend_dsc.mask_buf, blend_w);
1314                     blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w);
1315                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
1316                 }
1317             }
1318 
1319             if(left_side || bottom_side) {
1320                 for(h = core_area.y2 + 1; h <= draw_area.y2; h++) {
1321                     blend_area.y1 = h;
1322                     blend_area.y2 = h;
1323 
1324                     lv_memset_ff(blend_dsc.mask_buf, blend_w);
1325                     blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w);
1326                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
1327                 }
1328             }
1329         }
1330 
1331         /*Right corners*/
1332         blend_area.x1 = LV_MAX(draw_area.x1, core_area.x2 + 1);
1333         blend_area.x2 = draw_area.x2;
1334         blend_w = lv_area_get_width(&blend_area);
1335 
1336         if(blend_w > 0) {
1337             if(right_side || top_side) {
1338                 for(h = draw_area.y1; h < core_area.y1; h++) {
1339                     blend_area.y1 = h;
1340                     blend_area.y2 = h;
1341 
1342                     lv_memset_ff(blend_dsc.mask_buf, blend_w);
1343                     blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w);
1344                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
1345                 }
1346             }
1347 
1348             if(right_side || bottom_side) {
1349                 for(h = core_area.y2 + 1; h <= draw_area.y2; h++) {
1350                     blend_area.y1 = h;
1351                     blend_area.y2 = h;
1352 
1353                     lv_memset_ff(blend_dsc.mask_buf, blend_w);
1354                     blend_dsc.mask_res = lv_draw_mask_apply(blend_dsc.mask_buf, blend_area.x1, h, blend_w);
1355                     lv_draw_sw_blend(draw_ctx, &blend_dsc);
1356                 }
1357             }
1358         }
1359     }
1360 
1361     lv_draw_mask_free_param(&mask_rin_param);
1362     lv_draw_mask_remove_id(mask_rin_id);
1363     lv_draw_mask_free_param(&mask_rout_param);
1364     lv_draw_mask_remove_id(mask_rout_id);
1365     lv_mem_buf_release(blend_dsc.mask_buf);
1366 
1367 #else /*LV_DRAW_COMPLEX*/
1368     LV_UNUSED(blend_mode);
1369     LV_UNUSED(rout);
1370     LV_UNUSED(rin);
1371     if(!mask_any) {
1372         draw_border_simple(draw_ctx, outer_area, inner_area, color, opa);
1373         return;
1374     }
1375 
1376 #endif /*LV_DRAW_COMPLEX*/
1377 }
draw_border_simple(lv_draw_ctx_t * draw_ctx,const lv_area_t * outer_area,const lv_area_t * inner_area,lv_color_t color,lv_opa_t opa)1378 static void draw_border_simple(lv_draw_ctx_t * draw_ctx, const lv_area_t * outer_area, const lv_area_t * inner_area,
1379                                lv_color_t color, lv_opa_t opa)
1380 {
1381     lv_area_t a;
1382     lv_draw_sw_blend_dsc_t blend_dsc;
1383     lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
1384     blend_dsc.blend_area = &a;
1385     blend_dsc.color = color;
1386     blend_dsc.opa = opa;
1387 
1388     bool top_side = outer_area->y1 <= inner_area->y1 ? true : false;
1389     bool bottom_side = outer_area->y2 >= inner_area->y2 ? true : false;
1390     bool left_side = outer_area->x1 <= inner_area->x1 ? true : false;
1391     bool right_side = outer_area->x2 >= inner_area->x2 ? true : false;
1392 
1393     /*Top*/
1394     a.x1 = outer_area->x1;
1395     a.x2 = outer_area->x2;
1396     a.y1 = outer_area->y1;
1397     a.y2 = inner_area->y1 - 1;
1398     if(top_side) {
1399         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1400     }
1401 
1402     /*Bottom*/
1403     a.y1 = inner_area->y2 + 1;
1404     a.y2 = outer_area->y2;
1405     if(bottom_side) {
1406         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1407     }
1408 
1409     /*Left*/
1410     a.x1 = outer_area->x1;
1411     a.x2 = inner_area->x1 - 1;
1412     a.y1 = (top_side) ? inner_area->y1 : outer_area->y1;
1413     a.y2 = (bottom_side) ? inner_area->y2 : outer_area->y2;
1414     if(left_side) {
1415         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1416     }
1417 
1418     /*Right*/
1419     a.x1 = inner_area->x2 + 1;
1420     a.x2 = outer_area->x2;
1421     if(right_side) {
1422         lv_draw_sw_blend(draw_ctx, &blend_dsc);
1423     }
1424 }
1425