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