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