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