1 /**
2 * @file lv_draw_rect.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_draw_rect.h"
10 #include "lv_draw_blend.h"
11 #include "lv_draw_mask.h"
12 #include "../lv_misc/lv_math.h"
13 #include "../lv_core/lv_refr.h"
14 #include "../lv_misc/lv_debug.h"
15
16 /*********************
17 * DEFINES
18 *********************/
19 #define SHADOW_UPSACALE_SHIFT 6
20 #define SHADOW_ENHANCE 1
21 #define SPLIT_LIMIT 50
22
23 /**********************
24 * TYPEDEFS
25 **********************/
26
27 /**********************
28 * STATIC PROTOTYPES
29 **********************/
30 LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip,
31 const lv_draw_rect_dsc_t * dsc);
32 LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip,
33 const lv_draw_rect_dsc_t * dsc);
34
35 #if LV_USE_OUTLINE
36 static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc);
37 #endif
38 #if LV_USE_SHADOW
39 LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip,
40 const lv_draw_rect_dsc_t * dsc);
41 LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t s,
42 lv_coord_t r);
43 LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf);
44 #endif
45
46 #if LV_USE_PATTERN
47 static void draw_pattern(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc);
48 #endif
49
50 #if LV_USE_VALUE_STR
51 static void draw_value_str(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc);
52 #endif
53 static void draw_full_border(const lv_area_t * area_inner, const lv_area_t * area_outer, const lv_area_t * clip,
54 lv_coord_t radius, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode);
55 LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i);
56
57 /**********************
58 * STATIC VARIABLES
59 **********************/
60 #if LV_USE_SHADOW && LV_SHADOW_CACHE_SIZE
61 static uint8_t sh_cache[LV_SHADOW_CACHE_SIZE * LV_SHADOW_CACHE_SIZE];
62 static int32_t sh_cache_size = -1;
63 static int32_t sh_cache_r = -1;
64 #endif
65
66 /**********************
67 * MACROS
68 **********************/
69
70 /**********************
71 * GLOBAL FUNCTIONS
72 **********************/
73
lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)74 LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc)
75 {
76 _lv_memset_00(dsc, sizeof(lv_draw_rect_dsc_t));
77 dsc->bg_color = LV_COLOR_WHITE;
78 dsc->bg_grad_color = LV_COLOR_BLACK;
79 dsc->border_color = LV_COLOR_BLACK;
80 dsc->pattern_recolor = LV_COLOR_BLACK;
81 dsc->value_color = LV_COLOR_BLACK;
82 dsc->shadow_color = LV_COLOR_BLACK;
83 dsc->bg_grad_color_stop = 0xFF;
84 dsc->bg_opa = LV_OPA_COVER;
85 dsc->outline_opa = LV_OPA_COVER;
86 dsc->border_opa = LV_OPA_COVER;
87 dsc->pattern_opa = LV_OPA_COVER;
88 dsc->pattern_font = LV_THEME_DEFAULT_FONT_NORMAL;
89 dsc->value_opa = LV_OPA_COVER;
90 dsc->value_font = LV_THEME_DEFAULT_FONT_NORMAL;
91 dsc->shadow_opa = LV_OPA_COVER;
92 dsc->border_side = LV_BORDER_SIDE_FULL;
93
94 }
95
96 /**
97 * Draw a rectangle
98 * @param coords the coordinates of the rectangle
99 * @param mask the rectangle will be drawn only in this mask
100 * @param dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
101 */
lv_draw_rect(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)102 void lv_draw_rect(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc)
103 {
104 if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
105 #if LV_USE_SHADOW
106 draw_shadow(coords, clip, dsc);
107 #endif
108
109 draw_bg(coords, clip, dsc);
110
111 #if LV_USE_PATTERN
112 draw_pattern(coords, clip, dsc);
113 #endif
114 draw_border(coords, clip, dsc);
115
116 #if LV_USE_VALUE_STR
117 draw_value_str(coords, clip, dsc);
118 #endif
119
120 #if LV_USE_OUTLINE
121 draw_outline(coords, clip, dsc);
122 #endif
123
124 LV_ASSERT_MEM_INTEGRITY();
125 }
126
127 /**
128 * Draw a pixel
129 * @param point the coordinates of the point to draw
130 * @param mask the pixel will be drawn only in this mask
131 * @param style pointer to a style
132 * @param opa_scale scale down the opacity by the factor
133 */
lv_draw_px(const lv_point_t * point,const lv_area_t * clip_area,const lv_style_t * style)134 void lv_draw_px(const lv_point_t * point, const lv_area_t * clip_area, const lv_style_t * style)
135 {
136 LV_UNUSED(point);
137 LV_UNUSED(clip_area);
138 LV_UNUSED(style);
139 // lv_opa_t opa = style->body.opa;
140 // if(opa_scale != LV_OPA_COVER) opa = (opa * opa_scale) >> 8;
141 //
142 // if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
143 //
144 // lv_area_t fill_area;
145 // fill_area.x1 = point->x;
146 // fill_area.y1 = point->y;
147 // fill_area.x2 = point->x;
148 // fill_area.y2 = point->y;
149 //
150 // uint8_t mask_cnt = lv_draw_mask_get_cnt();
151 //
152 // if(mask_cnt == 0) {
153 // lv_blend_fill(clip_area, &fill_area, style->body.main_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, style->body.blend_mode);
154 // } else {
155 // uint8_t mask_buf;
156 // lv_draw_mask_res_t mask_res;
157 // mask_res = lv_draw_mask_apply(&mask_buf, point->x, point->y, 1);
158 // lv_blend_fill(clip_area, &fill_area, style->body.main_color, &mask_buf, mask_res, opa, style->body.blend_mode);
159 // }
160 }
161
162 /**********************
163 * STATIC FUNCTIONS
164 **********************/
165
draw_bg(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)166 LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip,
167 const lv_draw_rect_dsc_t * dsc)
168 {
169 if(dsc->bg_opa <= LV_OPA_MIN) return;
170
171 lv_area_t coords_bg;
172 lv_area_copy(&coords_bg, coords);
173
174 /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/
175 if(dsc->border_width > 1 && dsc->border_opa >= LV_OPA_MAX && dsc->radius != 0) {
176 coords_bg.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0;
177 coords_bg.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0;
178 coords_bg.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0;
179 coords_bg.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0;
180 }
181
182 lv_opa_t opa = dsc->bg_opa;
183
184 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
185
186 lv_disp_t * disp = _lv_refr_get_disp_refreshing();
187 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
188
189 /* Get clipped fill area which is the real draw area.
190 * It is always the same or inside `fill_area` */
191 lv_area_t draw_area;
192 bool is_common;
193 is_common = _lv_area_intersect(&draw_area, &coords_bg, clip);
194 if(is_common == false) return;
195
196 const lv_area_t * disp_area = &vdb->area;
197
198 /* Now `draw_area` has absolute coordinates.
199 * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
200 draw_area.x1 -= disp_area->x1;
201 draw_area.y1 -= disp_area->y1;
202 draw_area.x2 -= disp_area->x1;
203 draw_area.y2 -= disp_area->y1;
204
205 int32_t draw_area_w = lv_area_get_width(&draw_area);
206
207 /*Create a mask if there is a radius*/
208 lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w);
209
210 lv_grad_dir_t grad_dir = dsc->bg_grad_dir;
211 if(dsc->bg_color.full == dsc->bg_grad_color.full) grad_dir = LV_GRAD_DIR_NONE;
212
213 uint16_t other_mask_cnt = lv_draw_mask_get_cnt();
214 bool simple_mode = true;
215 if(other_mask_cnt) simple_mode = false;
216 else if(grad_dir == LV_GRAD_DIR_HOR) simple_mode = false;
217
218 int16_t mask_rout_id = LV_MASK_ID_INV;
219
220 int32_t coords_w = lv_area_get_width(&coords_bg);
221 int32_t coords_h = lv_area_get_height(&coords_bg);
222
223 /*Get the real radius*/
224 int32_t rout = dsc->radius;
225 int32_t short_side = LV_MATH_MIN(coords_w, coords_h);
226 if(rout > short_side >> 1) rout = short_side >> 1;
227
228 /*Most simple case: just a plain rectangle*/
229 if(simple_mode && rout == 0 && (grad_dir == LV_GRAD_DIR_NONE)) {
230 _lv_blend_fill(clip, &coords_bg,
231 dsc->bg_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa,
232 dsc->bg_blend_mode);
233 }
234 /*More complex case: there is a radius, gradient or other mask.*/
235 else {
236 lv_draw_mask_radius_param_t mask_rout_param;
237 if(rout > 0) {
238 lv_draw_mask_radius_init(&mask_rout_param, &coords_bg, rout, false);
239 mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
240 }
241
242 /*Draw the background line by line*/
243 int32_t h;
244 lv_draw_mask_res_t mask_res = LV_DRAW_MASK_RES_FULL_COVER;
245 lv_color_t grad_color = dsc->bg_color;
246
247
248 lv_color_t * grad_map = NULL;
249 /*In case of horizontal gradient pre-compute a line with a gradient*/
250 if(grad_dir == LV_GRAD_DIR_HOR) {
251 grad_map = _lv_mem_buf_get(coords_w * sizeof(lv_color_t));
252
253 int32_t i;
254 for(i = 0; i < coords_w; i++) {
255 grad_map[i] = grad_get(dsc, coords_w, i);
256 }
257 }
258
259 bool split = false;
260 if(lv_area_get_width(&coords_bg) - 2 * rout > SPLIT_LIMIT) split = true;
261
262 lv_opa_t opa2;
263
264 lv_area_t fill_area;
265 fill_area.x1 = coords_bg.x1;
266 fill_area.x2 = coords_bg.x2;
267 fill_area.y1 = disp_area->y1 + draw_area.y1;
268 fill_area.y2 = fill_area.y1;
269 for(h = draw_area.y1; h <= draw_area.y2; h++) {
270 int32_t y = h + vdb->area.y1;
271
272 opa2 = opa;
273
274 /*In not corner areas apply the mask only if required*/
275 if(y > coords_bg.y1 + rout + 1 &&
276 y < coords_bg.y2 - rout - 1) {
277 mask_res = LV_DRAW_MASK_RES_FULL_COVER;
278 if(simple_mode == false) {
279 _lv_memset(mask_buf, opa, draw_area_w);
280 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
281 }
282 }
283 /*In corner areas apply the mask anyway*/
284 else {
285 _lv_memset(mask_buf, opa, draw_area_w);
286 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
287 }
288
289 /*If mask will taken into account its base opacity was already set by memset above*/
290 if(mask_res == LV_DRAW_MASK_RES_CHANGED) {
291 opa2 = LV_OPA_COVER;
292 }
293
294 /*Get the current line color*/
295 if(grad_dir == LV_GRAD_DIR_VER) {
296 grad_color = grad_get(dsc, lv_area_get_height(&coords_bg), y - coords_bg.y1);
297 }
298
299 /* If there is not other mask and drawing the corner area split the drawing to corner and middle areas
300 * because it the middle mask shouldn't be taken into account (therefore its faster)*/
301 if(simple_mode && split &&
302 (y < coords_bg.y1 + rout + 1 ||
303 y > coords_bg.y2 - rout - 1)) {
304
305 /*Left part*/
306 lv_area_t fill_area2;
307 fill_area2.x1 = coords_bg.x1;
308 fill_area2.x2 = coords_bg.x1 + rout - 1;
309 fill_area2.y1 = fill_area.y1;
310 fill_area2.y2 = fill_area.y2;
311
312 _lv_blend_fill(clip, &fill_area2,
313 grad_color, mask_buf, mask_res, opa2, dsc->bg_blend_mode);
314
315 /*Center part*/
316 if(grad_dir == LV_GRAD_DIR_VER) {
317 fill_area2.x1 = coords_bg.x1 + rout;
318 fill_area2.x2 = coords_bg.x2 - rout;
319 _lv_blend_fill(clip, &fill_area2,
320 grad_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->bg_blend_mode);
321 }
322
323 /*Right part*/
324 fill_area2.x1 = coords_bg.x2 - rout + 1;
325 fill_area2.x2 = coords_bg.x2;
326
327 int32_t mask_ofs = (coords_bg.x2 - rout + 1) - (vdb->area.x1 + draw_area.x1);
328 if(mask_ofs < 0) mask_ofs = 0;
329 _lv_blend_fill(clip, &fill_area2,
330 grad_color, mask_buf + mask_ofs, mask_res, opa2, dsc->bg_blend_mode);
331
332
333 }
334 else {
335 if(grad_dir == LV_GRAD_DIR_HOR) {
336 _lv_blend_map(clip, &fill_area, grad_map, mask_buf, mask_res, opa2, dsc->bg_blend_mode);
337 }
338 else if(grad_dir == LV_GRAD_DIR_VER) {
339 _lv_blend_fill(clip, &fill_area,
340 grad_color, mask_buf, mask_res, opa2, dsc->bg_blend_mode);
341 }
342 else if(other_mask_cnt != 0 || !split) {
343 _lv_blend_fill(clip, &fill_area,
344 grad_color, mask_buf, mask_res, opa2, dsc->bg_blend_mode);
345 }
346 }
347 fill_area.y1++;
348 fill_area.y2++;
349 }
350
351 if(grad_dir == LV_GRAD_DIR_NONE && other_mask_cnt == 0 && split) {
352 /*Central part*/
353 fill_area.x1 = coords_bg.x1 + rout;
354 fill_area.x2 = coords_bg.x2 - rout;
355 fill_area.y1 = coords_bg.y1;
356 fill_area.y2 = coords_bg.y1 + rout;
357
358 _lv_blend_fill(clip, &fill_area,
359 dsc->bg_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->bg_blend_mode);
360
361 fill_area.y1 = coords_bg.y2 - rout;
362 if(fill_area.y1 <= fill_area.y2) fill_area.y1 = fill_area.y2 + 1; /*Avoid overdrawing the last line*/
363 fill_area.y2 = coords_bg.y2;
364
365
366 _lv_blend_fill(clip, &fill_area,
367 dsc->bg_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->bg_blend_mode);
368
369 fill_area.x1 = coords_bg.x1;
370 fill_area.x2 = coords_bg.x2;
371 fill_area.y1 = coords_bg.y1 + rout + 1;
372 fill_area.y2 = coords_bg.y2 - rout - 1;
373
374 _lv_blend_fill(clip, &fill_area,
375 dsc->bg_color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, dsc->bg_blend_mode);
376
377 }
378
379 if(grad_map) _lv_mem_buf_release(grad_map);
380 }
381
382 lv_draw_mask_remove_id(mask_rout_id);
383
384 _lv_mem_buf_release(mask_buf);
385
386 }
387
draw_border(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)388 LV_ATTRIBUTE_FAST_MEM static void draw_border(const lv_area_t * coords, const lv_area_t * clip,
389 const lv_draw_rect_dsc_t * dsc)
390 {
391 if(dsc->border_opa <= LV_OPA_MIN) return;
392 if(dsc->border_width == 0) return;
393 if(dsc->border_side == LV_BORDER_SIDE_NONE) return;
394 if(dsc->border_post) return;
395
396 int32_t coords_w = lv_area_get_width(coords);
397 int32_t coords_h = lv_area_get_height(coords);
398
399 /*Get the real radius*/
400 int32_t rout = dsc->radius;
401 int32_t short_side = LV_MATH_MIN(coords_w, coords_h);
402 if(rout > short_side >> 1) rout = short_side >> 1;
403
404 /*Get the inner area*/
405 lv_area_t area_inner;
406 lv_area_copy(&area_inner, coords);
407 area_inner.x1 += ((dsc->border_side & LV_BORDER_SIDE_LEFT) ? dsc->border_width : - (dsc->border_width + rout));
408 area_inner.x2 -= ((dsc->border_side & LV_BORDER_SIDE_RIGHT) ? dsc->border_width : - (dsc->border_width + rout));
409 area_inner.y1 += ((dsc->border_side & LV_BORDER_SIDE_TOP) ? dsc->border_width : - (dsc->border_width + rout));
410 area_inner.y2 -= ((dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? dsc->border_width : - (dsc->border_width + rout));
411
412 if(dsc->border_side == LV_BORDER_SIDE_FULL) {
413 draw_full_border(&area_inner, coords, clip, dsc->radius, dsc->border_color, dsc->border_opa, dsc->border_blend_mode);
414 }
415 else {
416 lv_opa_t opa = dsc->border_opa;
417 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
418
419 lv_disp_t * disp = _lv_refr_get_disp_refreshing();
420 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
421
422 /* Get clipped fill area which is the real draw area.
423 * It is always the same or inside `fill_area` */
424 lv_area_t draw_area;
425 bool is_common;
426 is_common = _lv_area_intersect(&draw_area, coords, clip);
427 if(is_common == false) return;
428
429 const lv_area_t * disp_area = &vdb->area;
430
431 /* Now `draw_area` has absolute coordinates.
432 * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
433 draw_area.x1 -= disp_area->x1;
434 draw_area.y1 -= disp_area->y1;
435 draw_area.x2 -= disp_area->x1;
436 draw_area.y2 -= disp_area->y1;
437
438 int32_t draw_area_w = lv_area_get_width(&draw_area);
439
440 /*Create a mask if there is a radius*/
441 lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w);
442
443 /*Create mask for the outer area*/
444 int16_t mask_rout_id = LV_MASK_ID_INV;
445 lv_draw_mask_radius_param_t mask_rout_param;
446 if(rout > 0) {
447 lv_draw_mask_radius_init(&mask_rout_param, coords, rout, false);
448 mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
449 }
450
451 /*Create mask for the inner mask*/
452 int32_t rin = rout - dsc->border_width;
453 if(rin < 0) rin = 0;
454 lv_draw_mask_radius_param_t mask_rin_param;
455 lv_draw_mask_radius_init(&mask_rin_param, &area_inner, rout - dsc->border_width, true);
456 int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL);
457
458 int32_t corner_size = LV_MATH_MAX(rout, dsc->border_width - 1);
459
460 int32_t h;
461 lv_draw_mask_res_t mask_res;
462 lv_area_t fill_area;
463
464 lv_color_t color = dsc->border_color;
465 lv_blend_mode_t blend_mode = dsc->border_blend_mode;
466
467 fill_area.x1 = coords->x1;
468 fill_area.x2 = coords->x2;
469 fill_area.y1 = disp_area->y1 + draw_area.y1;
470 fill_area.y2 = fill_area.y1;
471
472 uint32_t buf_ofs = 0;
473 if(dsc->border_side == LV_BORDER_SIDE_LEFT) fill_area.x2 = coords->x1 + corner_size;
474 else if(dsc->border_side == LV_BORDER_SIDE_RIGHT) {
475 fill_area.x1 = coords->x2 - corner_size;
476 buf_ofs = fill_area.x1 - coords->x1;
477 }
478
479 volatile bool top_only = false;
480 volatile bool bottom_only = false;
481 if(dsc->border_side == LV_BORDER_SIDE_TOP) top_only = true;
482 if(dsc->border_side == LV_BORDER_SIDE_BOTTOM) bottom_only = true;
483 if(dsc->border_side == (LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_BOTTOM)) {
484 top_only = true;
485 bottom_only = true;
486 }
487
488 volatile bool normal = !top_only && !bottom_only ? true : false;
489
490 for(h = draw_area.y1; h <= draw_area.y2; h++) {
491 if(normal ||
492 (top_only && fill_area.y1 <= coords->y1 + corner_size) ||
493 (bottom_only && fill_area.y1 >= coords->y2 - corner_size)) {
494 _lv_memset_ff(mask_buf, draw_area_w);
495 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
496 _lv_blend_fill(clip, &fill_area, color, mask_buf + buf_ofs, mask_res, opa, blend_mode);
497 }
498 fill_area.y1++;
499 fill_area.y2++;
500
501 }
502 lv_draw_mask_remove_id(mask_rin_id);
503 lv_draw_mask_remove_id(mask_rout_id);
504 _lv_mem_buf_release(mask_buf);
505 }
506 }
507
grad_get(const lv_draw_rect_dsc_t * dsc,lv_coord_t s,lv_coord_t i)508 LV_ATTRIBUTE_FAST_MEM static inline lv_color_t grad_get(const lv_draw_rect_dsc_t * dsc, lv_coord_t s, lv_coord_t i)
509 {
510 int32_t min = (dsc->bg_main_color_stop * s) >> 8;
511 if(i <= min) return dsc->bg_color;
512
513 int32_t max = (dsc->bg_grad_color_stop * s) >> 8;
514 if(i >= max) return dsc->bg_grad_color;
515
516 int32_t d = dsc->bg_grad_color_stop - dsc->bg_main_color_stop;
517 d = (s * d) >> 8;
518 i -= min;
519 lv_opa_t mix = (i * 255) / d;
520 return lv_color_mix(dsc->bg_grad_color, dsc->bg_color, mix);
521 }
522
523 #if LV_USE_SHADOW
draw_shadow(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)524 LV_ATTRIBUTE_FAST_MEM static void draw_shadow(const lv_area_t * coords, const lv_area_t * clip,
525 const lv_draw_rect_dsc_t * dsc)
526 {
527 /*Check whether the shadow is visible*/
528 if(dsc->shadow_width == 0) return;
529 if(dsc->shadow_opa <= LV_OPA_MIN) return;
530
531 if(dsc->shadow_width == 1 && dsc->shadow_ofs_x == 0 &&
532 dsc->shadow_ofs_y == 0 && dsc->shadow_spread <= 0) {
533 return;
534 }
535
536 int32_t sw = dsc->shadow_width;
537
538 lv_area_t sh_rect_area;
539 sh_rect_area.x1 = coords->x1 + dsc->shadow_ofs_x - dsc->shadow_spread;
540 sh_rect_area.x2 = coords->x2 + dsc->shadow_ofs_x + dsc->shadow_spread;
541 sh_rect_area.y1 = coords->y1 + dsc->shadow_ofs_y - dsc->shadow_spread;
542 sh_rect_area.y2 = coords->y2 + dsc->shadow_ofs_y + dsc->shadow_spread;
543
544 lv_area_t sh_area;
545 sh_area.x1 = sh_rect_area.x1 - sw / 2 - 1;
546 sh_area.x2 = sh_rect_area.x2 + sw / 2 + 1;
547 sh_area.y1 = sh_rect_area.y1 - sw / 2 - 1;
548 sh_area.y2 = sh_rect_area.y2 + sw / 2 + 1;
549
550 lv_opa_t opa = dsc->shadow_opa;
551
552 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
553
554 lv_disp_t * disp = _lv_refr_get_disp_refreshing();
555 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
556
557 /* Get clipped fill area which is the real draw area.
558 * It is always the same or inside `fill_area` */
559 lv_area_t draw_area;
560 bool is_common;
561 is_common = _lv_area_intersect(&draw_area, &sh_area, clip);
562 if(is_common == false) return;
563
564 const lv_area_t * disp_area = &vdb->area;
565
566 /* Now `draw_area` has absolute coordinates.
567 * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
568 draw_area.x1 -= disp_area->x1;
569 draw_area.y1 -= disp_area->y1;
570 draw_area.x2 -= disp_area->x1;
571 draw_area.y2 -= disp_area->y1;
572
573 /*Consider 1 px smaller bg to be sure the edge will be covered by the shadow*/
574 lv_area_t bg_coords;
575 lv_area_copy(&bg_coords, coords);
576 bg_coords.x1 += 1;
577 bg_coords.y1 += 1;
578 bg_coords.x2 -= 1;
579 bg_coords.y2 -= 1;
580
581 /*Get the real radius*/
582 int32_t r_bg = dsc->radius;
583 int32_t short_side = LV_MATH_MIN(lv_area_get_width(&bg_coords), lv_area_get_height(&bg_coords));
584 if(r_bg > short_side >> 1) r_bg = short_side >> 1;
585
586 int32_t r_sh = dsc->radius;
587 short_side = LV_MATH_MIN(lv_area_get_width(&sh_rect_area), lv_area_get_height(&sh_rect_area));
588 if(r_sh > short_side >> 1) r_sh = short_side >> 1;
589
590
591 int32_t corner_size = sw + r_sh;
592
593 lv_opa_t * sh_buf;
594
595 #if LV_SHADOW_CACHE_SIZE
596 if(sh_cache_size == corner_size && sh_cache_r == r_sh) {
597 /*Use the cache if available*/
598 sh_buf = _lv_mem_buf_get(corner_size * corner_size);
599 _lv_memcpy(sh_buf, sh_cache, corner_size * corner_size);
600 }
601 else {
602 /*A larger buffer is required for calculation */
603 sh_buf = _lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t));
604 shadow_draw_corner_buf(&sh_rect_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh);
605
606 /*Cache the corner if it fits into the cache size*/
607 if(corner_size * corner_size < sizeof(sh_cache)) {
608 _lv_memcpy(sh_cache, sh_buf, corner_size * corner_size);
609 sh_cache_size = corner_size;
610 sh_cache_r = r_sh;
611 }
612 }
613 #else
614 sh_buf = _lv_mem_buf_get(corner_size * corner_size * sizeof(uint16_t));
615 shadow_draw_corner_buf(&sh_rect_area, (uint16_t *)sh_buf, dsc->shadow_width, r_sh);
616 #endif
617
618 lv_coord_t h_half = sh_area.y1 + lv_area_get_height(&sh_area) / 2;
619 lv_coord_t w_half = sh_area.x1 + lv_area_get_width(&sh_area) / 2;
620
621 bool simple_mode = true;
622 if(lv_draw_mask_get_cnt() > 0) simple_mode = false;
623 else if(dsc->shadow_ofs_x != 0 || dsc->shadow_ofs_y != 0) simple_mode = false;
624 else if(dsc->shadow_spread != 0) simple_mode = false;
625
626 /*Create a mask*/
627 lv_draw_mask_res_t mask_res;
628 lv_opa_t * mask_buf = _lv_mem_buf_get(lv_area_get_width(&sh_area));
629
630 lv_draw_mask_radius_param_t mask_rout_param;
631 lv_draw_mask_radius_init(&mask_rout_param, &bg_coords, r_bg, true);
632
633 int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
634
635 lv_area_t a;
636
637 /*Draw the top right corner*/
638 int32_t y;
639 lv_opa_t * sh_buf_tmp;
640 a.x2 = sh_area.x2;
641 a.x1 = a.x2 - corner_size + 1;
642 a.y1 = sh_area.y1;
643 a.y2 = a.y1 + corner_size - 1;
644
645 lv_area_t ca;
646 bool has_com = _lv_area_intersect(&ca, &a, clip);
647 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
648 /*Avoid overlap in the middle with large radius*/
649 if(ca.y2 > h_half) ca.y2 = h_half;
650 if(ca.x1 <= w_half) ca.x1 = w_half + 1;
651
652 lv_coord_t h = lv_area_get_height(&ca);
653 lv_coord_t w = lv_area_get_width(&ca);
654 if(w > 0) {
655 sh_buf_tmp = sh_buf + (ca.x1 - a.x1);
656 sh_buf_tmp += corner_size * (ca.y1 - a.y1);
657
658 lv_area_t fa;
659 lv_area_copy(&fa, &ca);
660 fa.y2 = fa.y1;
661
662 for(y = 0; y < h; y++) {
663 _lv_memcpy(mask_buf, sh_buf_tmp, w);
664 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
665 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
666
667 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
668 mask_res, opa, dsc->shadow_blend_mode);
669 fa.y1++;
670 fa.y2++;
671 sh_buf_tmp += corner_size;
672 }
673 }
674 }
675
676 /*Draw the bottom right corner*/
677 a.x2 = sh_area.x2;
678 a.x1 = a.x2 - corner_size + 1;
679 a.y1 = sh_area.y2 - corner_size + 1;
680 a.y2 = sh_area.y2;
681
682 has_com = _lv_area_intersect(&ca, &a, clip);
683 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
684 /*Avoid overlap in the middle with large radius*/
685 if(ca.y1 <= h_half) ca.y1 = h_half + 1;
686 if(ca.x1 <= w_half) ca.x1 = w_half + 1;
687
688 lv_coord_t h = lv_area_get_height(&ca);
689 lv_coord_t w = lv_area_get_width(&ca);
690
691 if(w > 0) {
692 sh_buf_tmp = sh_buf + (ca.x1 - a.x1);
693 sh_buf_tmp += corner_size * (a.y2 - ca.y2);
694
695 lv_area_t fa;
696 lv_area_copy(&fa, &ca);
697 fa.y1 = fa.y2; /*Fill from bottom to top*/
698
699 for(y = 0; y < h; y++) {
700 _lv_memcpy(mask_buf, sh_buf_tmp, w);
701 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
702 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
703
704 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
705 mask_res, opa, dsc->shadow_blend_mode);
706 fa.y1--;
707 fa.y2--;
708 sh_buf_tmp += corner_size;
709 }
710 }
711 }
712
713 /*Fill the right side*/
714 a.x2 = sh_area.x2;
715 a.x1 = a.x2 - corner_size + 1;
716 a.y1 = sh_area.y1 + corner_size;
717 a.y2 = sh_area.y2 - corner_size;
718
719 has_com = _lv_area_intersect(&ca, &a, clip);
720 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
721 if(simple_mode) ca.x1 = LV_MATH_MAX(ca.x1, coords->x2);
722 /*Draw horizontal lines*/
723 lv_coord_t w = lv_area_get_width(&ca);
724 if(w > 0) {
725 lv_coord_t h = lv_area_get_height(&ca);
726
727 /*The last line of the shadow is repeated on the side*/
728 sh_buf_tmp = sh_buf + corner_size * (corner_size - 1);
729 sh_buf_tmp += ca.x1 - a.x1;
730
731 lv_area_t fa;
732 lv_area_copy(&fa, &ca);
733 fa.y2 = fa.y1;
734 mask_res = LV_DRAW_MASK_RES_FULL_COVER;
735 for(y = 0; y < h; y++) {
736 _lv_memcpy(mask_buf, sh_buf_tmp, w);
737
738 if(simple_mode) {
739 mask_res = LV_DRAW_MASK_RES_CHANGED;
740 }
741 else {
742 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
743 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
744 }
745
746 _lv_blend_fill(clip, &fa,
747 dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, dsc->shadow_blend_mode);
748 fa.y1++;
749 fa.y2++;
750 }
751 }
752 }
753
754 /*Invert the shadow corner buffer and draw the corners on the left*/
755 sh_buf_tmp = sh_buf ;
756 for(y = 0; y < corner_size; y++) {
757 int32_t x;
758 for(x = 0; x < corner_size / 2; x++) {
759 lv_opa_t tmp = sh_buf_tmp[x];
760 sh_buf_tmp[x] = sh_buf_tmp[corner_size - x - 1];
761 sh_buf_tmp[corner_size - x - 1] = tmp;
762 }
763 sh_buf_tmp += corner_size;
764 }
765
766 /*Draw the top left corner*/
767 a.x1 = sh_area.x1;
768 a.x2 = a.x1 + corner_size - 1;
769 a.y1 = sh_area.y1;
770 a.y2 = a.y1 + corner_size - 1;
771
772 has_com = _lv_area_intersect(&ca, &a, clip);
773 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
774 /*Avoid overlap in the middle with large radius*/
775 if(ca.y2 > h_half) ca.y2 = h_half;
776 if(ca.x2 > w_half) ca.x2 = w_half;
777
778 lv_coord_t h = lv_area_get_height(&ca);
779 lv_coord_t w = lv_area_get_width(&ca);
780 if(w > 0) {
781 sh_buf_tmp = sh_buf + (ca.x1 - a.x1);
782 sh_buf_tmp += corner_size * (ca.y1 - a.y1);
783
784 lv_area_t fa;
785 lv_area_copy(&fa, &ca);
786 fa.y2 = fa.y1;
787
788 for(y = 0; y < h; y++) {
789 _lv_memcpy(mask_buf, sh_buf_tmp, w);
790 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
791 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
792
793 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
794 mask_res, opa, dsc->shadow_blend_mode);
795 fa.y1++;
796 fa.y2++;
797 sh_buf_tmp += corner_size;
798 }
799 }
800 }
801
802 /*Draw the bottom left corner*/
803 a.x1 = sh_area.x1;
804 a.x2 = a.x1 + corner_size - 1;
805 a.y1 = sh_area.y2 - corner_size + 1;
806 a.y2 = sh_area.y2;
807
808 has_com = _lv_area_intersect(&ca, &a, clip);
809 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
810 /*Avoid overlap in the middle with large radius*/
811 if(ca.y1 <= h_half) ca.y1 = h_half + 1;
812 if(ca.x2 > w_half) ca.x2 = w_half;
813 lv_coord_t h = lv_area_get_height(&ca);
814 lv_coord_t w = lv_area_get_width(&ca);
815
816 if(w > 0) {
817 sh_buf_tmp = sh_buf + (ca.x1 - a.x1);
818 sh_buf_tmp += corner_size * (a.y2 - ca.y2);
819
820 lv_area_t fa;
821 lv_area_copy(&fa, &ca);
822 fa.y1 = fa.y2; /*Fill from bottom to top*/
823
824 for(y = 0; y < h; y++) {
825 _lv_memcpy(mask_buf, sh_buf_tmp, w);
826 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
827 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
828
829 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
830 mask_res, opa, dsc->shadow_blend_mode);
831 fa.y1--;
832 fa.y2--;
833 sh_buf_tmp += corner_size;
834 }
835 }
836 }
837
838 /*Fill the left side*/
839 a.x1 = sh_area.x1;
840 a.x2 = a.x1 + corner_size - 1;
841 a.y1 = sh_area.y1 + corner_size;
842 a.y2 = sh_area.y2 - corner_size;
843
844 has_com = _lv_area_intersect(&ca, &a, clip);
845 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
846 if(simple_mode) ca.x2 = LV_MATH_MIN(coords->x1, ca.x2);
847 /*Draw vertical lines*/
848 lv_coord_t w = lv_area_get_width(&ca);
849 if(w > 0) {
850 lv_coord_t h = lv_area_get_height(&ca);
851 /*The last line of the shadow is repeated on the side*/
852 sh_buf_tmp = sh_buf + corner_size * (corner_size - 1);
853 sh_buf_tmp += ca.x1 - a.x1;
854
855 lv_area_t fa;
856 lv_area_copy(&fa, &ca);
857 fa.y2 = fa.y1;
858 for(y = 0; y < h; y++) {
859 _lv_memcpy(mask_buf, sh_buf_tmp, w);
860 if(simple_mode) {
861 mask_res = LV_DRAW_MASK_RES_CHANGED;
862 }
863 else {
864 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
865 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
866 }
867
868 _lv_blend_fill(clip, &fa,
869 dsc->shadow_color, mask_buf, mask_res, dsc->shadow_opa, dsc->shadow_blend_mode);
870 fa.y1++;
871 fa.y2++;
872 }
873 }
874 }
875
876 /*Fill the top side*/
877 a.x1 = sh_area.x1 + corner_size;
878 a.x2 = sh_area.x2 - corner_size;
879 a.y1 = sh_area.y1;
880 a.y2 = sh_area.y1 + corner_size - 1;
881
882 has_com = _lv_area_intersect(&ca, &a, clip);
883 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
884 if(simple_mode) ca.y2 = LV_MATH_MIN(ca.y2, coords->y1);
885 /*Draw horizontal lines*/
886 lv_coord_t w = lv_area_get_width(&ca);
887 lv_coord_t h = lv_area_get_height(&ca);
888 sh_buf_tmp = sh_buf + corner_size - 1;
889 sh_buf_tmp += corner_size * (ca.y1 - a.y1);
890
891 lv_area_t fa;
892 lv_area_copy(&fa, &ca);
893 fa.y2 = fa.y1;
894 mask_res = LV_DRAW_MASK_RES_FULL_COVER;
895 for(y = 0; y < h; y++) {
896 lv_opa_t opa_tmp = sh_buf_tmp[0];
897 if(opa_tmp != LV_OPA_COVER || opa != LV_OPA_COVER) opa_tmp = (opa * opa_tmp) >> 8;
898
899 _lv_memset(mask_buf, opa_tmp, w);
900
901 if(simple_mode) {
902 mask_res = LV_DRAW_MASK_RES_CHANGED;
903 }
904 else {
905 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
906 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
907 }
908
909 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
910 mask_res, LV_OPA_COVER, dsc->shadow_blend_mode);
911 fa.y1++;
912 fa.y2++;
913 sh_buf_tmp += corner_size;
914 }
915 }
916
917
918 /*Fill the bottom side*/
919 a.x1 = sh_area.x1 + corner_size;
920 a.x2 = sh_area.x2 - corner_size;
921 a.y1 = sh_area.y2 - corner_size + 1;
922 a.y2 = sh_area.y2;
923
924 has_com = _lv_area_intersect(&ca, &a, clip);
925 if(has_com && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
926 if(simple_mode) ca.y1 = LV_MATH_MAX(ca.y1, coords->y2);
927 /*Draw horizontal lines*/
928 lv_coord_t w = lv_area_get_width(&ca);
929 lv_coord_t h = lv_area_get_height(&ca);
930 sh_buf_tmp = sh_buf + corner_size - 1;
931 sh_buf_tmp += corner_size * (a.y2 - ca.y2);
932
933 lv_area_t fa;
934 lv_area_copy(&fa, &ca);
935 fa.y1 = fa.y2;
936 for(y = 0; y < h; y++) {
937 lv_opa_t opa_tmp = sh_buf_tmp[0];
938 if(opa_tmp != LV_OPA_COVER || opa != LV_OPA_COVER) opa_tmp = (opa * opa_tmp) >> 8;
939
940 _lv_memset(mask_buf, opa_tmp, w);
941 if(simple_mode) {
942 mask_res = LV_DRAW_MASK_RES_CHANGED;
943 }
944 else {
945 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
946 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
947 }
948
949 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
950 mask_res, LV_OPA_COVER, dsc->shadow_blend_mode);
951 fa.y1--;
952 fa.y2--;
953 sh_buf_tmp += corner_size;
954 }
955 }
956
957 /*Draw the middle area*/
958 a.x1 = sh_area.x1 + corner_size;
959 a.x2 = sh_area.x2 - corner_size;
960 a.y1 = sh_area.y1 + corner_size;
961 a.y2 = sh_area.y2 - corner_size;
962
963 has_com = _lv_area_intersect(&ca, &a, clip);
964 if(has_com && simple_mode == false && _lv_area_is_in(&a, &bg_coords, r_bg) == false) {
965 /*Draw horizontal lines*/
966 lv_coord_t w = lv_area_get_width(&ca);
967 lv_coord_t h = lv_area_get_height(&ca);
968
969 lv_area_t fa;
970 lv_area_copy(&fa, &ca);
971 fa.y2 = fa.y1;
972 for(y = 0; y < h; y++) {
973 _lv_memset(mask_buf, dsc->shadow_opa, w);
974 mask_res = lv_draw_mask_apply(mask_buf, fa.x1, fa.y1, w);
975 if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask_res = LV_DRAW_MASK_RES_CHANGED;
976
977 _lv_blend_fill(clip, &fa, dsc->shadow_color, mask_buf,
978 mask_res, LV_OPA_COVER, dsc->shadow_blend_mode);
979 fa.y1++;
980 fa.y2++;
981 }
982 }
983
984 lv_draw_mask_remove_id(mask_rout_id);
985 _lv_mem_buf_release(mask_buf);
986 _lv_mem_buf_release(sh_buf);
987 }
988
989 /**
990 * Calculate a blurred corner
991 * @param coords Coordinates of the shadow
992 * @param sh_buf a buffer to store the result. It's size should be `(sw + r)^2 * 2`
993 * @param sw shadow width
994 * @param r radius
995 */
shadow_draw_corner_buf(const lv_area_t * coords,uint16_t * sh_buf,lv_coord_t sw,lv_coord_t r)996 LV_ATTRIBUTE_FAST_MEM static void shadow_draw_corner_buf(const lv_area_t * coords, uint16_t * sh_buf, lv_coord_t sw,
997 lv_coord_t r)
998 {
999 int32_t sw_ori = sw;
1000 int32_t size = sw_ori + r;
1001
1002 lv_area_t sh_area;
1003 lv_area_copy(&sh_area, coords);
1004 sh_area.x2 = sw / 2 + r - 1 - ((sw & 1) ? 0 : 1);
1005 sh_area.y1 = sw / 2 + 1;
1006
1007 sh_area.x1 = sh_area.x2 - lv_area_get_width(coords);
1008 sh_area.y2 = sh_area.y1 + lv_area_get_height(coords);
1009
1010 lv_draw_mask_radius_param_t mask_param;
1011 lv_draw_mask_radius_init(&mask_param, &sh_area, r, false);
1012
1013 #if SHADOW_ENHANCE
1014 /*Set half shadow width width because blur will be repeated*/
1015 if(sw_ori == 1) sw = 1;
1016 else sw = sw_ori >> 1;
1017 #endif
1018
1019 int32_t y;
1020 lv_opa_t * mask_line = _lv_mem_buf_get(size);
1021 uint16_t * sh_ups_tmp_buf = (uint16_t *)sh_buf;
1022 for(y = 0; y < size; y++) {
1023 _lv_memset_ff(mask_line, size);
1024 lv_draw_mask_res_t mask_res = mask_param.dsc.cb(mask_line, 0, y, size, &mask_param);
1025 if(mask_res == LV_DRAW_MASK_RES_TRANSP) {
1026 _lv_memset_00(sh_ups_tmp_buf, size * sizeof(sh_ups_tmp_buf[0]));
1027 }
1028 else {
1029 int32_t i;
1030 sh_ups_tmp_buf[0] = (mask_line[0] << SHADOW_UPSACALE_SHIFT) / sw;
1031 for(i = 1; i < size; i++) {
1032 if(mask_line[i] == mask_line[i - 1]) sh_ups_tmp_buf[i] = sh_ups_tmp_buf[i - 1];
1033 else sh_ups_tmp_buf[i] = (mask_line[i] << SHADOW_UPSACALE_SHIFT) / sw;
1034 }
1035 }
1036
1037 sh_ups_tmp_buf += size;
1038 }
1039 _lv_mem_buf_release(mask_line);
1040
1041 if(sw == 1) {
1042 int32_t i;
1043 lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
1044 for(i = 0; i < size * size; i++) {
1045 res_buf[i] = (sh_buf[i] >> SHADOW_UPSACALE_SHIFT);
1046 }
1047 return;
1048 }
1049
1050 shadow_blur_corner(size, sw, sh_buf);
1051
1052 #if SHADOW_ENHANCE == 0
1053 /*The result is required in lv_opa_t not uint16_t*/
1054 uint32_t x;
1055 lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
1056 for(x = 0; x < size * size; x++) {
1057 res_buf[x] = sh_buf[x];
1058 }
1059 #else
1060 sw += sw_ori & 1;
1061 if(sw > 1) {
1062 uint32_t i;
1063 sh_buf[0] = (sh_buf[0] << SHADOW_UPSACALE_SHIFT) / sw;
1064 for(i = 1; i < (uint32_t) size * size; i++) {
1065 if(sh_buf[i] == sh_buf[i - 1]) sh_buf[i] = sh_buf[i - 1];
1066 else sh_buf[i] = (sh_buf[i] << SHADOW_UPSACALE_SHIFT) / sw;
1067 }
1068
1069 shadow_blur_corner(size, sw, sh_buf);
1070 }
1071 int32_t x;
1072 lv_opa_t * res_buf = (lv_opa_t *)sh_buf;
1073 for(x = 0; x < size * size; x++) {
1074 res_buf[x] = sh_buf[x];
1075 }
1076 #endif
1077
1078 }
1079
shadow_blur_corner(lv_coord_t size,lv_coord_t sw,uint16_t * sh_ups_buf)1080 LV_ATTRIBUTE_FAST_MEM static void shadow_blur_corner(lv_coord_t size, lv_coord_t sw, uint16_t * sh_ups_buf)
1081 {
1082 int32_t s_left = sw >> 1;
1083 int32_t s_right = (sw >> 1);
1084 if((sw & 1) == 0) s_left--;
1085
1086 /*Horizontal blur*/
1087 uint16_t * sh_ups_blur_buf = _lv_mem_buf_get(size * sizeof(uint16_t));
1088
1089 int32_t x;
1090 int32_t y;
1091
1092 uint16_t * sh_ups_tmp_buf = sh_ups_buf;
1093
1094 for(y = 0; y < size; y++) {
1095 int32_t v = sh_ups_tmp_buf[size - 1] * sw;
1096 for(x = size - 1; x >= 0; x--) {
1097 sh_ups_blur_buf[x] = v;
1098
1099 /*Forget the right pixel*/
1100 uint32_t right_val = 0;
1101 if(x + s_right < size) right_val = sh_ups_tmp_buf[x + s_right];
1102 v -= right_val;
1103
1104 /*Add the left pixel*/
1105 uint32_t left_val;
1106 if(x - s_left - 1 < 0) left_val = sh_ups_tmp_buf[0];
1107 else left_val = sh_ups_tmp_buf[x - s_left - 1];
1108 v += left_val;
1109 }
1110 _lv_memcpy(sh_ups_tmp_buf, sh_ups_blur_buf, size * sizeof(uint16_t));
1111 sh_ups_tmp_buf += size;
1112 }
1113
1114 /*Vertical blur*/
1115 uint32_t i;
1116 sh_ups_buf[0] = sh_ups_buf[0] / sw;
1117 for(i = 1; i < (uint32_t)size * size; i++) {
1118 if(sh_ups_buf[i] == sh_ups_buf[i - 1]) sh_ups_buf[i] = sh_ups_buf[i - 1];
1119 else sh_ups_buf[i] = sh_ups_buf[i] / sw;
1120 }
1121
1122 for(x = 0; x < size; x++) {
1123 sh_ups_tmp_buf = &sh_ups_buf[x];
1124 int32_t v = sh_ups_tmp_buf[0] * sw;
1125 for(y = 0; y < size ; y++, sh_ups_tmp_buf += size) {
1126 sh_ups_blur_buf[y] = v < 0 ? 0 : (v >> SHADOW_UPSACALE_SHIFT);
1127
1128 /*Forget the top pixel*/
1129 uint32_t top_val;
1130 if(y - s_right <= 0) top_val = sh_ups_tmp_buf[0];
1131 else top_val = sh_ups_buf[(y - s_right) * size + x];
1132 v -= top_val;
1133
1134 /*Add the bottom pixel*/
1135 uint32_t bottom_val;
1136 if(y + s_left + 1 < size) bottom_val = sh_ups_buf[(y + s_left + 1) * size + x];
1137 else bottom_val = sh_ups_buf[(size - 1) * size + x];
1138 v += bottom_val;
1139 }
1140
1141 /*Write back the result into `sh_ups_buf`*/
1142 sh_ups_tmp_buf = &sh_ups_buf[x];
1143 for(y = 0; y < size; y++, sh_ups_tmp_buf += size) {
1144 (*sh_ups_tmp_buf) = sh_ups_blur_buf[y];
1145 }
1146 }
1147
1148 _lv_mem_buf_release(sh_ups_blur_buf);
1149 }
1150
1151 #endif
1152
1153 #if LV_USE_OUTLINE
draw_outline(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)1154 static void draw_outline(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc)
1155 {
1156 if(dsc->outline_opa <= LV_OPA_MIN) return;
1157 if(dsc->outline_width == 0) return;
1158
1159 lv_opa_t opa = dsc->outline_opa;
1160
1161 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
1162
1163 /*Get the inner radius*/
1164 lv_area_t area_inner;
1165 lv_area_copy(&area_inner, coords);
1166 area_inner.x1 -= dsc->outline_pad;
1167 area_inner.y1 -= dsc->outline_pad;
1168 area_inner.x2 += dsc->outline_pad;
1169 area_inner.y2 += dsc->outline_pad;
1170
1171 lv_area_t area_outer;
1172 lv_area_copy(&area_outer, &area_inner);
1173
1174 area_outer.x1 -= dsc->outline_width;
1175 area_outer.x2 += dsc->outline_width;
1176 area_outer.y1 -= dsc->outline_width;
1177 area_outer.y2 += dsc->outline_width;
1178
1179 draw_full_border(&area_inner, &area_outer, clip, dsc->radius, dsc->outline_color, dsc->outline_opa,
1180 dsc->outline_blend_mode);
1181 }
1182 #endif
1183
1184 #if LV_USE_PATTERN
draw_pattern(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)1185 static void draw_pattern(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc)
1186 {
1187 if(dsc->pattern_image == NULL) return;
1188 if(dsc->pattern_opa <= LV_OPA_MIN) return;
1189
1190 lv_img_src_t src_type = lv_img_src_get_type(dsc->pattern_image);
1191
1192 lv_draw_img_dsc_t img_dsc;
1193 lv_draw_label_dsc_t label_dsc;
1194 int32_t img_w;
1195 int32_t img_h;
1196
1197 if(src_type == LV_IMG_SRC_FILE || src_type == LV_IMG_SRC_VARIABLE) {
1198 lv_img_header_t header;
1199 lv_res_t res = lv_img_decoder_get_info(dsc->pattern_image, &header);
1200 if(res != LV_RES_OK) {
1201 LV_LOG_WARN("draw_img: can't get image info");
1202 return;
1203 }
1204
1205 img_w = header.w;
1206 img_h = header.h;
1207
1208 lv_draw_img_dsc_init(&img_dsc);
1209 img_dsc.opa = dsc->pattern_opa;
1210 img_dsc.recolor_opa = dsc->pattern_recolor_opa;
1211 img_dsc.recolor = dsc->pattern_recolor;
1212 }
1213 else if(src_type == LV_IMG_SRC_SYMBOL) {
1214 lv_draw_label_dsc_init(&label_dsc);
1215 label_dsc.color = dsc->pattern_recolor;
1216 label_dsc.font = dsc->pattern_font;
1217 label_dsc.opa = dsc->pattern_opa;
1218 lv_point_t s;
1219 _lv_txt_get_size(&s, dsc->pattern_image, label_dsc.font, label_dsc.letter_space, label_dsc.line_space, LV_COORD_MAX,
1220 LV_TXT_FLAG_NONE);
1221 img_w = s.x;
1222 img_h = s.y;
1223
1224 }
1225 else {
1226 /*Trigger the error handler of image drawer*/
1227 LV_LOG_WARN("lv_img_design: image source type is unknown");
1228 lv_draw_img(coords, clip, NULL, NULL);
1229 return;
1230 }
1231
1232 /*Can't draw zero sized images*/
1233 if(img_w == 0 || img_h == 0) return;
1234
1235 lv_area_t coords_tmp;
1236
1237 if(dsc->pattern_repeat) {
1238 lv_draw_mask_radius_param_t radius_mask_param;
1239 lv_draw_mask_radius_init(&radius_mask_param, coords, dsc->radius, false);
1240 int16_t radius_mask_id = lv_draw_mask_add(&radius_mask_param, NULL);
1241
1242 /*Align the pattern to the middle*/
1243 int32_t ofs_x = (lv_area_get_width(coords) - (lv_area_get_width(coords) / img_w) * img_w) / 2;
1244 int32_t ofs_y = (lv_area_get_height(coords) - (lv_area_get_height(coords) / img_h) * img_h) / 2;
1245
1246 coords_tmp.y1 = coords->y1 - ofs_y;
1247 coords_tmp.y2 = coords_tmp.y1 + img_h - 1;
1248 for(; coords_tmp.y1 <= coords->y2; coords_tmp.y1 += img_h, coords_tmp.y2 += img_h) {
1249 coords_tmp.x1 = coords->x1 - ofs_x;
1250 coords_tmp.x2 = coords_tmp.x1 + img_w - 1;
1251 for(; coords_tmp.x1 <= coords->x2; coords_tmp.x1 += img_w, coords_tmp.x2 += img_w) {
1252 if(src_type == LV_IMG_SRC_SYMBOL) lv_draw_label(&coords_tmp, clip, &label_dsc, dsc->pattern_image, NULL);
1253 else lv_draw_img(&coords_tmp, clip, dsc->pattern_image, &img_dsc);
1254 }
1255 }
1256 lv_draw_mask_remove_id(radius_mask_id);
1257 }
1258 else {
1259 int32_t obj_w = lv_area_get_width(coords);
1260 int32_t obj_h = lv_area_get_height(coords);
1261 coords_tmp.x1 = coords->x1 + (obj_w - img_w) / 2;
1262 coords_tmp.y1 = coords->y1 + (obj_h - img_h) / 2;
1263 coords_tmp.x2 = coords_tmp.x1 + img_w - 1;
1264 coords_tmp.y2 = coords_tmp.y1 + img_h - 1;
1265
1266 /* If the (obj_h - img_h) is odd there is a rounding error when divided by 2.
1267 * It's better round up in case of symbols because probably there is some extra space in the bottom
1268 * due to the base line of font*/
1269 if(src_type == LV_IMG_SRC_SYMBOL) {
1270 int32_t y_corr = (obj_h - img_h) & 0x1;
1271 coords_tmp.y1 += y_corr;
1272 coords_tmp.y2 += y_corr;
1273 }
1274
1275 int16_t radius_mask_id = LV_MASK_ID_INV;
1276 if(_lv_area_is_in(&coords_tmp, coords, dsc->radius) == false) {
1277 lv_draw_mask_radius_param_t radius_mask_param;
1278 lv_draw_mask_radius_init(&radius_mask_param, coords, dsc->radius, false);
1279 radius_mask_id = lv_draw_mask_add(&radius_mask_param, NULL);
1280 }
1281
1282 if(src_type == LV_IMG_SRC_SYMBOL) lv_draw_label(&coords_tmp, clip, &label_dsc, dsc->pattern_image, NULL);
1283 else lv_draw_img(&coords_tmp, clip, dsc->pattern_image, &img_dsc);
1284
1285 lv_draw_mask_remove_id(radius_mask_id);
1286 }
1287 }
1288 #endif
1289
1290
1291 #if LV_USE_VALUE_STR
draw_value_str(const lv_area_t * coords,const lv_area_t * clip,const lv_draw_rect_dsc_t * dsc)1292 static void draw_value_str(const lv_area_t * coords, const lv_area_t * clip, const lv_draw_rect_dsc_t * dsc)
1293 {
1294 if(dsc->value_str == NULL) return;
1295 if(dsc->value_opa <= LV_OPA_MIN) return;
1296
1297 lv_point_t s;
1298 _lv_txt_get_size(&s, dsc->value_str, dsc->value_font, dsc->value_letter_space, dsc->value_line_space, LV_COORD_MAX,
1299 LV_TXT_FLAG_NONE);
1300
1301 lv_area_t value_area;
1302 value_area.x1 = 0;
1303 value_area.y1 = 0;
1304 value_area.x2 = s.x - 1;
1305 value_area.y2 = s.y - 1;
1306
1307 lv_point_t p_align;
1308 _lv_area_align(coords, &value_area, dsc->value_align, &p_align);
1309
1310 value_area.x1 += p_align.x + dsc->value_ofs_x;
1311 value_area.y1 += p_align.y + dsc->value_ofs_y;
1312 value_area.x2 += p_align.x + dsc->value_ofs_x;
1313 value_area.y2 += p_align.y + dsc->value_ofs_y;
1314
1315 lv_draw_label_dsc_t label_dsc;
1316 lv_draw_label_dsc_init(&label_dsc);
1317 label_dsc.font = dsc->value_font;
1318 label_dsc.letter_space = dsc->value_letter_space;
1319 label_dsc.line_space = dsc->value_line_space;
1320 label_dsc.color = dsc->value_color;
1321 label_dsc.opa = dsc->value_opa;
1322
1323 lv_draw_label(&value_area, clip, &label_dsc, dsc->value_str, NULL);
1324 }
1325 #endif
1326
draw_full_border(const lv_area_t * area_inner,const lv_area_t * area_outer,const lv_area_t * clip,lv_coord_t radius,lv_color_t color,lv_opa_t opa,lv_blend_mode_t blend_mode)1327 static void draw_full_border(const lv_area_t * area_inner, const lv_area_t * area_outer, const lv_area_t * clip,
1328 lv_coord_t radius, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode)
1329 {
1330 uint8_t other_mask_cnt = lv_draw_mask_get_cnt();
1331 bool simple_mode = true;
1332 if(other_mask_cnt) simple_mode = false;
1333
1334 int32_t inner_w = lv_area_get_width(area_inner);
1335 int32_t inner_h = lv_area_get_height(area_inner);
1336 lv_coord_t border_width = area_outer->x2 - area_inner->x2;
1337 int32_t rin = radius;
1338
1339 int32_t short_side = LV_MATH_MIN(inner_w, inner_h);
1340 if(rin > short_side >> 1) rin = short_side >> 1;
1341
1342 /*Get the outer area*/
1343 int32_t rout = rin + border_width;
1344
1345 int32_t coords_out_w = lv_area_get_width(area_outer);
1346 int32_t coords_out_h = lv_area_get_height(area_outer);
1347 short_side = LV_MATH_MIN(coords_out_w, coords_out_h);
1348 if(rout > short_side >> 1) rout = short_side >> 1;
1349
1350 lv_disp_t * disp = _lv_refr_get_disp_refreshing();
1351 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
1352
1353 /* Get clipped fill area which is the real draw area.
1354 * It is always the same or inside `fill_area` */
1355 lv_area_t draw_area;
1356 bool is_common;
1357 is_common = _lv_area_intersect(&draw_area, area_outer, clip);
1358 if(is_common == false) return;
1359
1360 const lv_area_t * disp_area = &vdb->area;
1361
1362 /* Now `draw_area` has absolute coordinates.
1363 * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
1364 draw_area.x1 -= disp_area->x1;
1365 draw_area.y1 -= disp_area->y1;
1366 draw_area.x2 -= disp_area->x1;
1367 draw_area.y2 -= disp_area->y1;
1368
1369 int32_t draw_area_w = lv_area_get_width(&draw_area);
1370
1371 /*Create inner the mask*/
1372 lv_draw_mask_radius_param_t mask_rin_param;
1373 lv_draw_mask_radius_init(&mask_rin_param, area_inner, rin, true);
1374 int16_t mask_rin_id = lv_draw_mask_add(&mask_rin_param, NULL);
1375
1376 lv_draw_mask_radius_param_t mask_rout_param;
1377 lv_draw_mask_radius_init(&mask_rout_param, area_outer, rout, false);
1378 int16_t mask_rout_id = lv_draw_mask_add(&mask_rout_param, NULL);
1379
1380 lv_opa_t * mask_buf = _lv_mem_buf_get(draw_area_w);
1381
1382 int32_t corner_size = LV_MATH_MAX(rout, border_width - 1);
1383
1384 int32_t h;
1385 lv_draw_mask_res_t mask_res;
1386 lv_area_t fill_area;
1387
1388 /*Apply some optimization if there is no other mask*/
1389 if(simple_mode) {
1390 /*Draw the upper corner area*/
1391 int32_t upper_corner_end = area_outer->y1 - disp_area->y1 + corner_size;
1392
1393 fill_area.x1 = area_outer->x1;
1394 fill_area.x2 = area_outer->x2;
1395 fill_area.y1 = disp_area->y1 + draw_area.y1;
1396 fill_area.y2 = fill_area.y1;
1397 for(h = draw_area.y1; h <= upper_corner_end; h++) {
1398 _lv_memset_ff(mask_buf, draw_area_w);
1399 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
1400
1401 lv_area_t fill_area2;
1402 fill_area2.y1 = fill_area.y1;
1403 fill_area2.y2 = fill_area.y2;
1404
1405 fill_area2.x1 = area_outer->x1;
1406 fill_area2.x2 = area_outer->x1 + rout - 1;
1407
1408 _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode);
1409
1410 /*Draw the top horizontal line*/
1411 if(fill_area2.y2 < area_outer->y1 + border_width) {
1412 fill_area2.x1 = area_outer->x1 + rout;
1413 fill_area2.x2 = area_outer->x2 - rout;
1414
1415 _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode);
1416 }
1417
1418 fill_area2.x1 = area_outer->x2 - rout + 1;
1419 fill_area2.x2 = area_outer->x2;
1420
1421 int32_t mask_ofs = (area_outer->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1);
1422 if(mask_ofs < 0) mask_ofs = 0;
1423 _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode);
1424
1425 fill_area.y1++;
1426 fill_area.y2++;
1427 }
1428
1429 /*Draw the lower corner area */
1430 int32_t lower_corner_end = area_outer->y2 - disp_area->y1 - corner_size;
1431 if(lower_corner_end <= upper_corner_end) lower_corner_end = upper_corner_end + 1;
1432 fill_area.y1 = disp_area->y1 + lower_corner_end;
1433 fill_area.y2 = fill_area.y1;
1434 for(h = lower_corner_end; h <= draw_area.y2; h++) {
1435 _lv_memset_ff(mask_buf, draw_area_w);
1436 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
1437
1438 lv_area_t fill_area2;
1439 fill_area2.x1 = area_outer->x1;
1440 fill_area2.x2 = area_outer->x1 + rout - 1;
1441 fill_area2.y1 = fill_area.y1;
1442 fill_area2.y2 = fill_area.y2;
1443
1444 _lv_blend_fill(clip, &fill_area2, color, mask_buf, mask_res, opa, blend_mode);
1445
1446 /*Draw the bottom horizontal line*/
1447 if(fill_area2.y2 > area_outer->y2 - border_width) {
1448 fill_area2.x1 = area_outer->x1 + rout;
1449 fill_area2.x2 = area_outer->x2 - rout;
1450
1451 _lv_blend_fill(clip, &fill_area2, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode);
1452 }
1453 fill_area2.x1 = area_outer->x2 - rout + 1;
1454 fill_area2.x2 = area_outer->x2;
1455
1456 int32_t mask_ofs = (area_outer->x2 - rout + 1) - (vdb->area.x1 + draw_area.x1);
1457 if(mask_ofs < 0) mask_ofs = 0;
1458 _lv_blend_fill(clip, &fill_area2, color, mask_buf + mask_ofs, mask_res, opa, blend_mode);
1459
1460
1461 fill_area.y1++;
1462 fill_area.y2++;
1463 }
1464
1465 /*Draw the left vertical part*/
1466 fill_area.y1 = area_outer->y1 + corner_size + 1;
1467 fill_area.y2 = area_outer->y2 - corner_size - 1;
1468
1469 fill_area.x1 = area_outer->x1;
1470 fill_area.x2 = area_outer->x1 + border_width - 1;
1471 _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode);
1472
1473 /*Draw the right vertical border*/
1474 fill_area.x1 = area_outer->x2 - border_width + 1;
1475 fill_area.x2 = area_outer->x2;
1476
1477 _lv_blend_fill(clip, &fill_area, color, NULL, LV_DRAW_MASK_RES_FULL_COVER, opa, blend_mode);
1478 }
1479 /*Process line by line if there is other mask too*/
1480 else {
1481 fill_area.x1 = area_outer->x1;
1482 fill_area.x2 = area_outer->x2;
1483 fill_area.y1 = disp_area->y1 + draw_area.y1;
1484 fill_area.y2 = fill_area.y1;
1485
1486 for(h = draw_area.y1; h <= draw_area.y2; h++) {
1487 _lv_memset_ff(mask_buf, draw_area_w);
1488 mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
1489
1490 _lv_blend_fill(clip, &fill_area, color, mask_buf, mask_res, opa, blend_mode);
1491 fill_area.y1++;
1492 fill_area.y2++;
1493
1494 }
1495 }
1496 lv_draw_mask_remove_id(mask_rin_id);
1497 lv_draw_mask_remove_id(mask_rout_id);
1498 _lv_mem_buf_release(mask_buf);
1499 }
1500
1501