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(¢er_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