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