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