1 /**
2  * @file lv_draw_sw_border.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../misc/lv_area_private.h"
10 #include "lv_draw_sw_mask_private.h"
11 #include "../lv_draw_private.h"
12 #include "../lv_draw_private.h"
13 #include "lv_draw_sw.h"
14 #if LV_USE_DRAW_SW
15 
16 #include "blend/lv_draw_sw_blend_private.h"
17 #include "../../misc/lv_math.h"
18 #include "../../misc/lv_text_ap.h"
19 #include "../../core/lv_refr.h"
20 #include "../../misc/lv_assert.h"
21 #include "../../stdlib/lv_string.h"
22 #include "../lv_draw_mask.h"
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 #define SPLIT_LIMIT             50
28 
29 /**********************
30  *      TYPEDEFS
31  **********************/
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 static void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area,
37                                 int32_t rout, int32_t rin, lv_color_t color, lv_opa_t opa);
38 
39 static void draw_border_simple(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area,
40                                lv_color_t color, lv_opa_t opa);
41 
42 /**********************
43  *  STATIC VARIABLES
44  **********************/
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
53 
lv_draw_sw_border(lv_draw_unit_t * draw_unit,const lv_draw_border_dsc_t * dsc,const lv_area_t * coords)54 void lv_draw_sw_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc, const lv_area_t * coords)
55 {
56     if(dsc->opa <= LV_OPA_MIN) return;
57     if(dsc->width == 0) return;
58     if(dsc->side == LV_BORDER_SIDE_NONE) return;
59 
60     int32_t coords_w = lv_area_get_width(coords);
61     int32_t coords_h = lv_area_get_height(coords);
62     int32_t rout = dsc->radius;
63     int32_t short_side = LV_MIN(coords_w, coords_h);
64     if(rout > short_side >> 1) rout = short_side >> 1;
65 
66     /*Get the inner area*/
67     lv_area_t area_inner;
68     lv_area_copy(&area_inner, coords);
69     area_inner.x1 += ((dsc->side & LV_BORDER_SIDE_LEFT) ? dsc->width : - (dsc->width + rout));
70     area_inner.x2 -= ((dsc->side & LV_BORDER_SIDE_RIGHT) ? dsc->width : - (dsc->width + rout));
71     area_inner.y1 += ((dsc->side & LV_BORDER_SIDE_TOP) ? dsc->width : - (dsc->width + rout));
72     area_inner.y2 -= ((dsc->side & LV_BORDER_SIDE_BOTTOM) ? dsc->width : - (dsc->width + rout));
73 
74     int32_t rin = rout - dsc->width;
75     if(rin < 0) rin = 0;
76 
77     if(rout == 0 && rin == 0) {
78         draw_border_simple(draw_unit, coords, &area_inner, dsc->color, dsc->opa);
79     }
80     else {
81         draw_border_complex(draw_unit, coords, &area_inner, rout, rin, dsc->color, dsc->opa);
82     }
83 
84 }
85 
86 /**********************
87  *   STATIC FUNCTIONS
88  **********************/
89 
draw_border_complex(lv_draw_unit_t * draw_unit,const lv_area_t * outer_area,const lv_area_t * inner_area,int32_t rout,int32_t rin,lv_color_t color,lv_opa_t opa)90 void draw_border_complex(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area,
91                          int32_t rout, int32_t rin, lv_color_t color, lv_opa_t opa)
92 {
93 #if LV_DRAW_SW_COMPLEX
94     /*Get clipped draw area which is the real draw area.
95      *It is always the same or inside `coords`*/
96     lv_area_t draw_area;
97     if(!lv_area_intersect(&draw_area, outer_area, draw_unit->clip_area)) return;
98     int32_t draw_area_w = lv_area_get_width(&draw_area);
99 
100     lv_draw_sw_blend_dsc_t blend_dsc;
101     lv_memzero(&blend_dsc, sizeof(blend_dsc));
102     lv_opa_t * mask_buf = lv_malloc(draw_area_w);
103     blend_dsc.mask_buf = mask_buf;
104 
105     void * mask_list[3] = {0};
106 
107     /*Create mask for the inner mask*/
108     lv_draw_sw_mask_radius_param_t mask_rin_param;
109     lv_draw_sw_mask_radius_init(&mask_rin_param, inner_area, rin, true);
110     mask_list[0] = &mask_rin_param;
111 
112     /*Create mask for the outer area*/
113     lv_draw_sw_mask_radius_param_t mask_rout_param;
114     if(rout > 0) {
115         lv_draw_sw_mask_radius_init(&mask_rout_param, outer_area, rout, false);
116         mask_list[1] = &mask_rout_param;
117     }
118 
119     int32_t h;
120     lv_area_t blend_area;
121     blend_dsc.blend_area = &blend_area;
122     blend_dsc.mask_area = &blend_area;
123     blend_dsc.color = color;
124     blend_dsc.opa = opa;
125 
126     /*Calculate the x and y coordinates where the straight parts area is*/
127     lv_area_t core_area;
128     core_area.x1 = LV_MAX(outer_area->x1 + rout, inner_area->x1);
129     core_area.x2 = LV_MIN(outer_area->x2 - rout, inner_area->x2);
130     core_area.y1 = LV_MAX(outer_area->y1 + rout, inner_area->y1);
131     core_area.y2 = LV_MIN(outer_area->y2 - rout, inner_area->y2);
132     int32_t core_w = lv_area_get_width(&core_area);
133 
134     bool top_side = outer_area->y1 <= inner_area->y1;
135     bool bottom_side = outer_area->y2 >= inner_area->y2;
136 
137     /*No masks*/
138     bool left_side = outer_area->x1 <= inner_area->x1;
139     bool right_side = outer_area->x2 >= inner_area->x2;
140 
141     bool split_hor = true;
142     if(left_side && right_side && top_side && bottom_side &&
143        core_w < SPLIT_LIMIT) {
144         split_hor = false;
145     }
146 
147     blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER;
148     /*Draw the straight lines first if they are long enough*/
149     if(top_side && split_hor) {
150         blend_area.x1 = core_area.x1;
151         blend_area.x2 = core_area.x2;
152         blend_area.y1 = outer_area->y1;
153         blend_area.y2 = inner_area->y1 - 1;
154         lv_draw_sw_blend(draw_unit, &blend_dsc);
155     }
156 
157     if(bottom_side && split_hor) {
158         blend_area.x1 = core_area.x1;
159         blend_area.x2 = core_area.x2;
160         blend_area.y1 = inner_area->y2 + 1;
161         blend_area.y2 = outer_area->y2;
162         lv_draw_sw_blend(draw_unit, &blend_dsc);
163     }
164 
165     /*If the border is very thick and the vertical sides overlap horizontally draw a single rectangle*/
166     if(inner_area->x1 >= inner_area->x2 && left_side && right_side) {
167         blend_area.x1 = outer_area->x1;
168         blend_area.x2 = outer_area->x2;
169         blend_area.y1 = core_area.y1;
170         blend_area.y2 = core_area.y2;
171         lv_draw_sw_blend(draw_unit, &blend_dsc);
172     }
173     else {
174         if(left_side) {
175             blend_area.x1 = outer_area->x1;
176             blend_area.x2 = inner_area->x1 - 1;
177             blend_area.y1 = core_area.y1;
178             blend_area.y2 = core_area.y2;
179             lv_draw_sw_blend(draw_unit, &blend_dsc);
180         }
181 
182         if(right_side) {
183             blend_area.x1 = inner_area->x2 + 1;
184             blend_area.x2 = outer_area->x2;
185             blend_area.y1 = core_area.y1;
186             blend_area.y2 = core_area.y2;
187             lv_draw_sw_blend(draw_unit, &blend_dsc);
188         }
189     }
190 
191     /*Draw the corners*/
192     int32_t blend_w;
193 
194     /*Left and right corner together if they are close to each other*/
195     if(!split_hor) {
196         /*Calculate the top corner and mirror it to the bottom*/
197         blend_area.x1 = draw_area.x1;
198         blend_area.x2 = draw_area.x2;
199         int32_t max_h = LV_MAX(rout, inner_area->y1 - outer_area->y1);
200         for(h = 0; h < max_h; h++) {
201             int32_t top_y = outer_area->y1 + h;
202             int32_t bottom_y = outer_area->y2 - h;
203             if(top_y < draw_area.y1 && bottom_y > draw_area.y2) continue;   /*This line is clipped now*/
204 
205             lv_memset(mask_buf, 0xff, draw_area_w);
206             blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, draw_area_w);
207 
208             if(top_y >= draw_area.y1) {
209                 blend_area.y1 = top_y;
210                 blend_area.y2 = top_y;
211                 lv_draw_sw_blend(draw_unit, &blend_dsc);
212             }
213 
214             if(bottom_y <= draw_area.y2) {
215                 blend_area.y1 = bottom_y;
216                 blend_area.y2 = bottom_y;
217                 lv_draw_sw_blend(draw_unit, &blend_dsc);
218             }
219         }
220     }
221     else {
222         /*Left corners*/
223         blend_area.x1 = draw_area.x1;
224         blend_area.x2 = LV_MIN(draw_area.x2, core_area.x1 - 1);
225         blend_w = lv_area_get_width(&blend_area);
226         if(blend_w > 0) {
227             if(left_side || top_side) {
228                 for(h = draw_area.y1; h < core_area.y1; h++) {
229                     blend_area.y1 = h;
230                     blend_area.y2 = h;
231 
232                     lv_memset(mask_buf, 0xff, blend_w);
233                     blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w);
234                     lv_draw_sw_blend(draw_unit, &blend_dsc);
235                 }
236             }
237 
238             if(left_side || bottom_side) {
239                 for(h = core_area.y2 + 1; h <= draw_area.y2; h++) {
240                     blend_area.y1 = h;
241                     blend_area.y2 = h;
242 
243                     lv_memset(mask_buf, 0xff, blend_w);
244                     blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w);
245                     lv_draw_sw_blend(draw_unit, &blend_dsc);
246                 }
247             }
248         }
249 
250         /*Right corners*/
251         blend_area.x1 = LV_MAX(draw_area.x1, blend_area.x2 + 1);    /*To not overlap with the left side*/
252         blend_area.x1 = LV_MAX(draw_area.x1, core_area.x2 + 1);
253 
254         blend_area.x2 = draw_area.x2;
255         blend_w = lv_area_get_width(&blend_area);
256 
257         if(blend_w > 0) {
258             if(right_side || top_side) {
259                 for(h = draw_area.y1; h < core_area.y1; h++) {
260                     blend_area.y1 = h;
261                     blend_area.y2 = h;
262 
263                     lv_memset(mask_buf, 0xff, blend_w);
264                     blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w);
265                     lv_draw_sw_blend(draw_unit, &blend_dsc);
266                 }
267             }
268 
269             if(right_side || bottom_side) {
270                 for(h = core_area.y2 + 1; h <= draw_area.y2; h++) {
271                     blend_area.y1 = h;
272                     blend_area.y2 = h;
273 
274                     lv_memset(mask_buf, 0xff, blend_w);
275                     blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, h, blend_w);
276                     lv_draw_sw_blend(draw_unit, &blend_dsc);
277                 }
278             }
279         }
280     }
281 
282     lv_draw_sw_mask_free_param(&mask_rin_param);
283     if(rout > 0) lv_draw_sw_mask_free_param(&mask_rout_param);
284     lv_free(mask_buf);
285 
286 #endif /*LV_DRAW_SW_COMPLEX*/
287 }
draw_border_simple(lv_draw_unit_t * draw_unit,const lv_area_t * outer_area,const lv_area_t * inner_area,lv_color_t color,lv_opa_t opa)288 static void draw_border_simple(lv_draw_unit_t * draw_unit, const lv_area_t * outer_area, const lv_area_t * inner_area,
289                                lv_color_t color, lv_opa_t opa)
290 {
291     lv_area_t a;
292     lv_draw_sw_blend_dsc_t blend_dsc;
293     lv_memzero(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t));
294     blend_dsc.blend_area = &a;
295     blend_dsc.color = color;
296     blend_dsc.opa = opa;
297 
298     bool top_side = outer_area->y1 <= inner_area->y1;
299     bool bottom_side = outer_area->y2 >= inner_area->y2;
300     bool left_side = outer_area->x1 <= inner_area->x1;
301     bool right_side = outer_area->x2 >= inner_area->x2;
302 
303     /*Top*/
304     a.x1 = outer_area->x1;
305     a.x2 = outer_area->x2;
306     a.y1 = outer_area->y1;
307     a.y2 = inner_area->y1 - 1;
308     if(top_side) {
309         lv_draw_sw_blend(draw_unit, &blend_dsc);
310     }
311 
312     /*Bottom*/
313     a.y1 = inner_area->y2 + 1;
314     a.y2 = outer_area->y2;
315     if(bottom_side) {
316         lv_draw_sw_blend(draw_unit, &blend_dsc);
317     }
318 
319     /*Left*/
320     a.x1 = outer_area->x1;
321     a.x2 = inner_area->x1 - 1;
322     a.y1 = (top_side) ? inner_area->y1 : outer_area->y1;
323     a.y2 = (bottom_side) ? inner_area->y2 : outer_area->y2;
324     if(left_side) {
325         lv_draw_sw_blend(draw_unit, &blend_dsc);
326     }
327 
328     /*Right*/
329     a.x1 = inner_area->x2 + 1;
330     a.x2 = outer_area->x2;
331     if(right_side) {
332         lv_draw_sw_blend(draw_unit, &blend_dsc);
333     }
334 }
335 
336 #endif /*LV_USE_DRAW_SW*/
337