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