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