1 /**
2  * @file lv_draw_vglite_border.c
3  *
4  */
5 
6 /**
7  * Copyright 2022-2024 NXP
8  *
9  * SPDX-License-Identifier: MIT
10  */
11 
12 /*********************
13  *      INCLUDES
14  *********************/
15 
16 #include "lv_draw_vglite.h"
17 
18 #if LV_USE_DRAW_VGLITE
19 #include "lv_vglite_buf.h"
20 #include "lv_vglite_path.h"
21 #include "lv_vglite_utils.h"
22 
23 #include <math.h>
24 
25 /*********************
26  *      DEFINES
27  *********************/
28 
29 /*********************
30  *      DEFINES
31  *********************/
32 
33 /*** Define maximum numbers of rectangles needed to clip partial borders ***/
34 #define MAX_NUM_RECTANGLES 4
35 
36 /**********************
37  *      TYPEDEFS
38  **********************/
39 
40 /**********************
41  *  STATIC PROTOTYPES
42  **********************/
43 
44 /**
45  * Draw rectangle border/outline shape with effects (rounded corners, opacity)
46  *
47  * @param[in] coords Coordinates of the rectangle border/outline (relative to dest buff)
48  * @param[in] clip_area Clip area with relative coordinates to dest buff
49  * @param[in] dsc Description of the rectangle border/outline
50  *
51  */
52 static void _vglite_draw_border(const lv_area_t * coords, const lv_area_t * clip_area,
53                                 const lv_draw_border_dsc_t * dsc);
54 
55 /**********************
56  *  STATIC VARIABLES
57  **********************/
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
lv_draw_vglite_border(lv_draw_unit_t * draw_unit,const lv_draw_border_dsc_t * dsc,const lv_area_t * coords)67 void lv_draw_vglite_border(lv_draw_unit_t * draw_unit, const lv_draw_border_dsc_t * dsc,
68                            const lv_area_t * coords)
69 {
70     if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
71         return;
72     if(dsc->width == 0)
73         return;
74     if(dsc->side == (lv_border_side_t)LV_BORDER_SIDE_NONE)
75         return;
76 
77     lv_layer_t * layer = draw_unit->target_layer;
78     lv_area_t inward_coords;
79     int32_t width = dsc->width;
80 
81     /* Move border inwards to align with software rendered border */
82     inward_coords.x1 = coords->x1 + ceil(width / 2.0f);
83     inward_coords.x2 = coords->x2 - floor(width / 2.0f);
84     inward_coords.y1 = coords->y1 + ceil(width / 2.0f);
85     inward_coords.y2 = coords->y2 - floor(width / 2.0f);
86 
87     lv_area_move(&inward_coords, -layer->buf_area.x1, -layer->buf_area.y1);
88 
89     lv_area_t clip_area;
90     lv_area_copy(&clip_area, draw_unit->clip_area);
91     lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
92 
93     lv_area_t clipped_coords;
94     if(!lv_area_intersect(&clipped_coords, &inward_coords, &clip_area))
95         return; /*Fully clipped, nothing to do*/
96 
97     _vglite_draw_border(&inward_coords, &clip_area, dsc);
98 }
99 
100 /**********************
101  *   STATIC FUNCTIONS
102  **********************/
103 
_vglite_draw_border(const lv_area_t * coords,const lv_area_t * clip_area,const lv_draw_border_dsc_t * dsc)104 static void _vglite_draw_border(const lv_area_t * coords, const lv_area_t * clip_area,
105                                 const lv_draw_border_dsc_t * dsc)
106 {
107     int32_t radius = dsc->radius;
108     vg_lite_buffer_t * vgbuf = vglite_get_dest_buf();
109 
110     if(radius < 0)
111         return;
112 
113     int32_t border_half = (int32_t)floor(dsc->width / 2.0f);
114     if(radius > border_half)
115         radius = radius - border_half;
116 
117     vg_lite_cap_style_t cap_style = (radius) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT;
118     vg_lite_join_style_t join_style = (radius) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER;
119 
120     /*** Init path ***/
121     int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
122     uint32_t path_data_size;
123     vglite_create_rect_path_data(path_data, &path_data_size, radius, coords);
124     vg_lite_quality_t path_quality = radius > 0 ? VG_LITE_HIGH : VG_LITE_MEDIUM;
125 
126     vg_lite_path_t path;
127     VGLITE_CHECK_ERROR(vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
128                                          (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
129                                          ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f));
130 
131     lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
132     vg_lite_color_t vgcol = vglite_get_color(col32, false);
133 
134     int32_t line_width = dsc->width;
135     lv_border_side_t border_side = dsc->side;
136 
137     if(border_side == LV_BORDER_SIDE_FULL)
138         border_side = LV_BORDER_SIDE_TOP | LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_RIGHT;
139 
140     uint32_t num_rect = 0;
141     vg_lite_rectangle_t rect[MAX_NUM_RECTANGLES];
142     int32_t rect_width = coords->x2 - coords->x1;
143     int32_t rect_height = coords->y2 - coords->y1;
144     int32_t shortest_side = LV_MIN(rect_width, rect_height);
145     int32_t final_radius = LV_MIN(radius, shortest_side / 2);
146 
147     if(border_side & LV_BORDER_SIDE_TOP) {
148         rect[num_rect].x = coords->x1 - ceil(line_width / 2.0f);
149         rect[num_rect].y = coords->y1 - ceil(line_width / 2.0f);
150         rect[num_rect].width = coords->x2 - coords->x1 + line_width;
151         rect[num_rect].height = final_radius + ceil(line_width / 2.0f);
152         num_rect++;
153     }
154 
155     if(border_side & LV_BORDER_SIDE_LEFT) {
156         rect[num_rect].x = coords->x1 - ceil(line_width / 2.0f);
157         rect[num_rect].y = coords->y1 - ceil(line_width / 2.0f);
158         rect[num_rect].width = final_radius + ceil(line_width / 2.0f);
159         rect[num_rect].height = coords->y2 - coords->y1 + line_width + 1;
160         num_rect++;
161     }
162 
163     if(border_side & LV_BORDER_SIDE_RIGHT) {
164         rect[num_rect].x = coords->x2 - final_radius + 1;
165         rect[num_rect].y = coords->y1 - ceil(line_width / 2.0f);
166         rect[num_rect].width = final_radius + ceil(line_width / 2.0f);
167         rect[num_rect].height = coords->y2 - coords->y1 + line_width + 1;
168         num_rect++;
169     }
170 
171     if(border_side & LV_BORDER_SIDE_BOTTOM) {
172         rect[num_rect].x = coords->x1 - ceil(line_width / 2.0f);
173         rect[num_rect].y = coords->y2 - final_radius + 1;
174         rect[num_rect].width = coords->x2 - coords->x1 + line_width;
175         rect[num_rect].height = final_radius + ceil(line_width / 2.0f);
176         num_rect++;
177     }
178 
179     /*** Enable scissor and apply scissor rects ***/
180     VGLITE_CHECK_ERROR(vg_lite_enable_scissor());
181     VGLITE_CHECK_ERROR(vg_lite_scissor_rects(vgbuf, num_rect, rect));
182 
183     /*** Draw border ***/
184     VGLITE_CHECK_ERROR(vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH));
185 
186     VGLITE_CHECK_ERROR(vg_lite_set_stroke(&path, cap_style, join_style, line_width, 8, NULL, 0, 0, vgcol));
187 
188     VGLITE_CHECK_ERROR(vg_lite_update_stroke(&path));
189 
190     VGLITE_CHECK_ERROR(vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
191 
192     vglite_run();
193 
194     VGLITE_CHECK_ERROR(vg_lite_clear_path(&path));
195 
196     /*** Disable scissor ***/
197     VGLITE_CHECK_ERROR(vg_lite_disable_scissor());
198 }
199 
200 #endif /*LV_USE_DRAW_VGLITE*/
201