1 /**
2 * @file lv_draw_vg_lite_border.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "../../misc/lv_area_private.h"
11 #include "../lv_draw_private.h"
12 #include "lv_draw_vg_lite.h"
13
14 #if LV_USE_DRAW_VG_LITE
15
16 #include "lv_draw_vg_lite_type.h"
17 #include "lv_vg_lite_utils.h"
18 #include "lv_vg_lite_path.h"
19 #include "lv_vg_lite_math.h"
20
21 /*********************
22 * DEFINES
23 *********************/
24
25 #define HAS_BORDER_SIDE(dsc_side, side) (((dsc_side) & (side)) == (side))
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30
31 /**********************
32 * STATIC PROTOTYPES
33 **********************/
34
35 static vg_lite_fill_t path_append_inner_rect(lv_vg_lite_path_t * path,
36 const lv_draw_border_dsc_t * dsc,
37 int32_t x, int32_t y, int32_t w, int32_t h,
38 float r);
39
40 /**********************
41 * STATIC VARIABLES
42 **********************/
43
44 /**********************
45 * MACROS
46 **********************/
47
48 /**********************
49 * GLOBAL FUNCTIONS
50 **********************/
51
lv_draw_vg_lite_border(lv_draw_unit_t * draw_unit,const lv_draw_border_dsc_t * dsc,const lv_area_t * coords)52 void lv_draw_vg_lite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc,
53 const lv_area_t * coords)
54 {
55 lv_draw_vg_lite_unit_t * u = (lv_draw_vg_lite_unit_t *)draw_unit;
56
57 lv_area_t clip_area;
58 if(!lv_area_intersect(&clip_area, coords, draw_unit->clip_area)) {
59 /*Fully clipped, nothing to do*/
60 return;
61 }
62
63 LV_PROFILER_DRAW_BEGIN;
64
65 int32_t w = lv_area_get_width(coords);
66 int32_t h = lv_area_get_height(coords);
67 float r_out = dsc->radius;
68 if(dsc->radius) {
69 float r_short = LV_MIN(w, h) / 2.0f;
70 r_out = LV_MIN(r_out, r_short);
71 }
72
73 lv_vg_lite_path_t * path = lv_vg_lite_path_get(u, VG_LITE_FP32);
74 lv_vg_lite_path_set_quality(path, dsc->radius == 0 ? VG_LITE_LOW : VG_LITE_HIGH);
75 lv_vg_lite_path_set_bounding_box_area(path, &clip_area);
76
77 /* outer rect */
78 lv_vg_lite_path_append_rect(path,
79 coords->x1, coords->y1,
80 w, h,
81 r_out);
82
83 /* inner rect */
84 vg_lite_fill_t fill_rule = path_append_inner_rect(path, dsc, coords->x1, coords->y1, w, h, r_out);
85
86 lv_vg_lite_path_end(path);
87
88 vg_lite_matrix_t matrix = u->global_matrix;
89
90 vg_lite_color_t color = lv_vg_lite_color(dsc->color, dsc->opa, true);
91
92 vg_lite_path_t * vg_lite_path = lv_vg_lite_path_get_path(path);
93
94 LV_VG_LITE_ASSERT_DEST_BUFFER(&u->target_buffer);
95 LV_VG_LITE_ASSERT_PATH(vg_lite_path);
96 LV_VG_LITE_ASSERT_MATRIX(&matrix);
97
98 LV_PROFILER_DRAW_BEGIN_TAG("vg_lite_draw");
99 LV_VG_LITE_CHECK_ERROR(vg_lite_draw(
100 &u->target_buffer,
101 vg_lite_path,
102 fill_rule,
103 &matrix,
104 VG_LITE_BLEND_SRC_OVER,
105 color));
106 LV_PROFILER_DRAW_END_TAG("vg_lite_draw");
107
108 lv_vg_lite_path_drop(u, path);
109 LV_PROFILER_DRAW_END;
110 }
111
112 /**********************
113 * STATIC FUNCTIONS
114 **********************/
115
path_append_inner_rect(lv_vg_lite_path_t * path,const lv_draw_border_dsc_t * dsc,int32_t x,int32_t y,int32_t w,int32_t h,float r)116 static vg_lite_fill_t path_append_inner_rect(lv_vg_lite_path_t * path,
117 const lv_draw_border_dsc_t * dsc,
118 int32_t x, int32_t y, int32_t w, int32_t h,
119 float r)
120 {
121 LV_PROFILER_DRAW_BEGIN;
122
123 const float half_w = w / 2.0f;
124 const float half_h = h / 2.0f;
125 const int32_t border_w = dsc->width;
126 const float border_w_max = LV_MIN(half_w, half_h);
127
128 /* normal fill, no inner rect */
129 if(border_w >= border_w_max) {
130 LV_PROFILER_DRAW_END;
131 return VG_LITE_FILL_EVEN_ODD;
132 }
133
134 const float r_in = r - border_w;
135
136 /* full border, simple rect */
137 if(dsc->side == LV_BORDER_SIDE_FULL) {
138 lv_vg_lite_path_append_rect(path,
139 x + border_w, y + border_w,
140 w - border_w * 2, h - border_w * 2,
141 r_in < 0 ? 0 : r_in);
142 LV_PROFILER_DRAW_END;
143 return VG_LITE_FILL_EVEN_ODD;
144 }
145
146 /* no-radius case, simple inner rect */
147 if(dsc->radius <= 0) {
148 int32_t x_offset = 0;
149 int32_t y_offset = 0;
150 int32_t w_inner = w;
151 int32_t h_inner = h;
152
153 if(dsc->side & LV_BORDER_SIDE_TOP) {
154 y_offset += border_w;
155 h_inner -= border_w;
156 }
157 if(dsc->side & LV_BORDER_SIDE_LEFT) {
158 x_offset += border_w;
159 w_inner -= border_w;
160 }
161 if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
162 h_inner -= border_w;
163 }
164 if(dsc->side & LV_BORDER_SIDE_RIGHT) {
165 w_inner -= border_w;
166 }
167
168 lv_vg_lite_path_append_rect(path,
169 x + x_offset,
170 y + y_offset,
171 w_inner,
172 h_inner,
173 0);
174 LV_PROFILER_DRAW_END;
175 return VG_LITE_FILL_EVEN_ODD;
176 }
177
178 /* reset outter rect path */
179 lv_vg_lite_path_reset(path, VG_LITE_FP32);
180
181 /* coordinate reference map: https://github.com/lvgl/lvgl/pull/6796 */
182 const float c1_x = x + r;
183 const float c1_y = y + r;
184 const float c2_x = x + w - r;
185 const float c2_y = c1_y;
186 const float c3_x = c2_x;
187 const float c3_y = y + h - r;
188 const float c4_x = c1_x;
189 const float c4_y = c3_y;
190
191 /* When border_w > r, No need to calculate the intersection of the arc and the line */
192 if(r_in <= 0) {
193 const float p1_x = x;
194 const float p1_y = y + border_w;
195 const float p2_x = x;
196 const float p2_y = y + r;
197 const float p3_x = x + r;
198 const float p3_y = y;
199 const float p4_x = x + border_w;
200 const float p4_y = y;
201
202 const float p5_x = x + w - border_w;
203 const float p5_y = y;
204 const float p6_x = x + w - r;
205 const float p6_y = y;
206 const float p7_x = x + w;
207 const float p7_y = y + r;
208 const float p8_x = x + w;
209 const float p8_y = y + border_w;
210
211 const float p9_x = x + w;
212 const float p9_y = y + h - border_w;
213 const float p10_x = x + w;
214 const float p10_y = y + h - r;
215 const float p11_x = x + w - r;
216 const float p11_y = y + h;
217 const float p12_x = x + w - border_w;
218 const float p12_y = y + h;
219
220 const float p13_x = x + border_w;
221 const float p13_y = y + h;
222 const float p14_x = x + r;
223 const float p14_y = y + h;
224 const float p15_x = x;
225 const float p15_y = y + h - r;
226 const float p16_x = x;
227 const float p16_y = y + h - border_w;
228
229 if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
230 lv_vg_lite_path_move_to(path, p16_x, p16_y);
231 lv_vg_lite_path_line_to(path, p9_x, p9_y);
232 lv_vg_lite_path_line_to(path, p10_x, p10_y);
233 lv_vg_lite_path_append_arc_right_angle(path, p10_x, p10_y, c3_x, c3_y, p11_x, p11_y);
234 lv_vg_lite_path_line_to(path, p14_x, p14_y);
235 lv_vg_lite_path_append_arc_right_angle(path, p14_x, p14_y, c4_x, c4_y, p15_x, p15_y);
236 lv_vg_lite_path_close(path);
237 }
238
239 if(dsc->side & LV_BORDER_SIDE_TOP) {
240 lv_vg_lite_path_move_to(path, p1_x, p1_y);
241 lv_vg_lite_path_line_to(path, p2_x, p2_y);
242 lv_vg_lite_path_append_arc_right_angle(path, p2_x, p2_y, c1_x, c1_y, p3_x, p3_y);
243 lv_vg_lite_path_line_to(path, p6_x, p6_y);
244 lv_vg_lite_path_append_arc_right_angle(path, p6_x, p6_y, c2_x, c2_y, p7_x, p7_y);
245 lv_vg_lite_path_line_to(path, p8_x, p8_y);
246 lv_vg_lite_path_close(path);
247 }
248
249 if(dsc->side & LV_BORDER_SIDE_LEFT) {
250 lv_vg_lite_path_move_to(path, p4_x, p4_y);
251 lv_vg_lite_path_line_to(path, p13_x, p13_y);
252 lv_vg_lite_path_line_to(path, p14_x, p14_y);
253 lv_vg_lite_path_append_arc_right_angle(path, p14_x, p14_y, c4_x, c4_y, p15_x, p15_y);
254 lv_vg_lite_path_line_to(path, p2_x, p2_y);
255 lv_vg_lite_path_append_arc_right_angle(path, p2_x, p2_y, c1_x, c1_y, p3_x, p3_y);
256 lv_vg_lite_path_close(path);
257 }
258
259 if(dsc->side & LV_BORDER_SIDE_RIGHT) {
260 lv_vg_lite_path_move_to(path, p5_x, p5_y);
261 lv_vg_lite_path_line_to(path, p6_x, p6_y);
262 lv_vg_lite_path_append_arc_right_angle(path, p6_x, p6_y, c2_x, c2_y, p7_x, p7_y);
263 lv_vg_lite_path_line_to(path, p10_x, p10_y);
264 lv_vg_lite_path_append_arc_right_angle(path, p10_x, p10_y, c3_x, c3_y, p11_x, p11_y);
265 lv_vg_lite_path_line_to(path, p12_x, p12_y);
266 lv_vg_lite_path_close(path);
267 }
268
269 LV_PROFILER_DRAW_END;
270 return VG_LITE_FILL_NON_ZERO;
271 }
272
273 /* When border_w < r, Calculate the intersection of an arc and a line */
274
275 /* r^2 - r_in^2 = offset^2 */
276 const float offset = MATH_SQRTF((2 * r - border_w) * border_w);
277 const float sweep_alpha = MATH_DEGREES(MATH_ACOSF(r_in / r));
278 const float sweep_beta = 90 - sweep_alpha;
279
280 const float p1_x = x + border_w;
281 const float p1_y = y + r;
282 const float p2_x = x;
283 const float p2_y = y + r;
284 const float p3_x = x + border_w;
285 const float p3_y = y + r - offset;
286 const float p4_x = x + r - offset;
287 const float p4_y = y + border_w;
288 const float p5_x = x + r;
289 const float p5_y = y;
290 const float p6_x = x + r;
291 const float p6_y = y + border_w;
292
293 const float p7_x = x + w - r;
294 const float p7_y = y + border_w;
295 const float p8_x = x + w - r;
296 const float p8_y = y;
297 const float p10_x = x + w - border_w;
298 const float p10_y = y + r - offset;
299 const float p11_x = x + w;
300 const float p11_y = y + r;
301 const float p12_x = x + w - border_w;
302 const float p12_y = y + r;
303
304 const float p13_x = x + w - border_w;
305 const float p13_y = y + h - r;
306 const float p14_x = x + w;
307 const float p14_y = y + h - r;
308 const float p16_x = x + w - r + offset;
309 const float p16_y = y + h - border_w;
310 const float p17_x = x + w - r;
311 const float p17_y = y + h;
312 const float p18_x = x + w - r;
313 const float p18_y = y + h - border_w;
314
315 const float p19_x = x + r;
316 const float p19_y = y + h - border_w;
317 const float p20_x = x + r;
318 const float p20_y = y + h;
319 const float p21_x = x + r - offset;
320 const float p21_y = y + h - border_w;
321 const float p22_x = x + border_w;
322 const float p22_y = y + h - r + offset;
323 const float p23_x = x;
324 const float p23_y = y + h - r;
325 const float p24_x = x + border_w;
326 const float p24_y = y + h - r;
327
328 if(dsc->side & LV_BORDER_SIDE_BOTTOM) {
329 lv_vg_lite_path_move_to(path, p21_x, p21_y);
330 lv_vg_lite_path_line_to(path, p16_x, p16_y);
331 lv_vg_lite_path_append_arc(path, c3_x, c3_y, r, sweep_beta, sweep_alpha, false);
332 lv_vg_lite_path_line_to(path, p20_x, p20_y);
333 lv_vg_lite_path_append_arc(path, c4_x, c4_y, r, 90, sweep_alpha, false);
334 lv_vg_lite_path_close(path);
335 }
336
337 if(dsc->side & LV_BORDER_SIDE_TOP) {
338 lv_vg_lite_path_move_to(path, p4_x, p4_y);
339 lv_vg_lite_path_append_arc(path, c1_x, c1_y, r, 270 - sweep_alpha, sweep_alpha, false);
340 lv_vg_lite_path_line_to(path, p8_x, p8_y);
341 lv_vg_lite_path_append_arc(path, c2_x, c2_y, r, 270, sweep_alpha, false);
342 lv_vg_lite_path_close(path);
343 }
344
345 if(dsc->side & LV_BORDER_SIDE_LEFT) {
346 lv_vg_lite_path_move_to(path, p3_x, p3_y);
347 lv_vg_lite_path_line_to(path, p22_x, p22_y);
348 lv_vg_lite_path_append_arc(path, c4_x, c4_y, r, 90 + sweep_beta, sweep_alpha, false);
349 lv_vg_lite_path_line_to(path, p2_x, p2_y);
350 lv_vg_lite_path_append_arc(path, c1_x, c1_y, r, 180, sweep_alpha, false);
351 lv_vg_lite_path_close(path);
352 }
353
354 if(dsc->side & LV_BORDER_SIDE_RIGHT) {
355 lv_vg_lite_path_move_to(path, p10_x, p10_y);
356 lv_vg_lite_path_append_arc(path, c2_x, c2_y, r, 270 + sweep_beta, sweep_alpha, false);
357 lv_vg_lite_path_line_to(path, p14_x, p14_y);
358 lv_vg_lite_path_append_arc(path, c3_x, c3_y, r, 0, sweep_alpha, false);
359 lv_vg_lite_path_close(path);
360 }
361
362 /* Draw the rounded corners adjacent to the border */
363
364 if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_LEFT)) {
365 lv_vg_lite_path_move_to(path, p2_x, p2_y);
366 lv_vg_lite_path_append_arc_right_angle(path, p2_x, p2_y, c1_x, c1_y, p5_x, p5_y);
367 lv_vg_lite_path_line_to(path, p6_x, p6_y);
368 lv_vg_lite_path_append_arc_right_angle(path, p6_x, p6_y, c1_x, c1_y, p1_x, p1_y);
369 lv_vg_lite_path_close(path);
370 }
371
372 if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_RIGHT)) {
373 lv_vg_lite_path_move_to(path, p8_x, p8_y);
374 lv_vg_lite_path_append_arc_right_angle(path, p8_x, p8_y, c2_x, c2_y, p11_x, p11_y);
375 lv_vg_lite_path_line_to(path, p12_x, p12_y);
376 lv_vg_lite_path_append_arc_right_angle(path, p12_x, p12_y, c2_x, c2_y, p7_x, p7_y);
377 lv_vg_lite_path_close(path);
378 }
379
380 if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT)) {
381 lv_vg_lite_path_move_to(path, p20_x, p20_y);
382 lv_vg_lite_path_append_arc_right_angle(path, p20_x, p20_y, c4_x, c4_y, p23_x, p23_y);
383 lv_vg_lite_path_line_to(path, p24_x, p24_y);
384 lv_vg_lite_path_append_arc_right_angle(path, p24_x, p24_y, c4_x, c4_y, p19_x, p19_y);
385 lv_vg_lite_path_close(path);
386 }
387
388 if(HAS_BORDER_SIDE(dsc->side, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT)) {
389 lv_vg_lite_path_move_to(path, p14_x, p14_y);
390 lv_vg_lite_path_append_arc_right_angle(path, p14_x, p14_y, c3_x, c3_y, p17_x, p17_y);
391 lv_vg_lite_path_line_to(path, p18_x, p18_y);
392 lv_vg_lite_path_append_arc_right_angle(path, p18_x, p18_y, c3_x, c3_y, p13_x, p13_y);
393 lv_vg_lite_path_close(path);
394 }
395
396 LV_PROFILER_DRAW_END;
397 return VG_LITE_FILL_NON_ZERO;
398 }
399
400 #endif /*LV_USE_DRAW_VG_LITE*/
401