acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_draw_sw_rect.c (53171B)

      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 
     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 
     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 
    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 
    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 
    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
    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  */
    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 
   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 
   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 
   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 }
   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