1 /**
2  * @file lv_draw_vglite_rect.c
3  *
4  */
5 
6 /**
7  * MIT License
8  *
9  * Copyright 2021-2023 NXP
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights to
14  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15  * the Software, and to permit persons to whom the Software is furnished to do so,
16  * subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice (including the next paragraph)
19  * shall be included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
26  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  *
28  */
29 
30 /*********************
31  *      INCLUDES
32  *********************/
33 
34 #include "lv_draw_vglite_rect.h"
35 
36 #if LV_USE_GPU_NXP_VG_LITE
37 #include "lv_vglite_buf.h"
38 #include <math.h>
39 
40 /*********************
41  *      DEFINES
42  *********************/
43 /*********************
44  *      DEFINES
45  *********************/
46 
47 /* Path data sizes for different elements */
48 #define CUBIC_PATH_DATA_SIZE 7 /* 1 opcode, 6 arguments */
49 #define LINE_PATH_DATA_SIZE 3  /* 1 opcode, 2 arguments */
50 #define MOVE_PATH_DATA_SIZE 3  /* 1 opcode, 2 arguments */
51 #define END_PATH_DATA_SIZE 1   /* 1 opcode, 0 arguments */
52 /* Maximum possible rectangle path size
53  * is in the rounded rectangle case:
54  * - 1 move for the path start
55  * - 4 cubics for the corners
56  * - 4 lines for the sides
57  * - 1 end for the path end */
58 #define RECT_PATH_DATA_MAX_SIZE 1 * MOVE_PATH_DATA_SIZE + 4 * CUBIC_PATH_DATA_SIZE + 4 * LINE_PATH_DATA_SIZE + 1 * END_PATH_DATA_SIZE
59 
60 /**********************
61  *      TYPEDEFS
62  **********************/
63 
64 /**********************
65  *  STATIC PROTOTYPES
66  **********************/
67 
68 /**
69  * Generates path data for rectangle drawing.
70  *
71  * @param[in/out] path The path data to initialize
72  * @param[in/out] path_size The resulting size of the created path data
73  * @param[in] dsc The style descriptor for the rectangle to be drawn
74  * @param[in] coords The coordinates of the rectangle to be drawn
75  */
76 static void lv_vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size,
77                                             lv_coord_t radius,
78                                             const lv_area_t * coords);
79 
80 /**********************
81  *  STATIC VARIABLES
82  **********************/
83 
84 /**********************
85  *      MACROS
86  **********************/
87 
88 /**********************
89  *   GLOBAL FUNCTIONS
90  **********************/
91 
lv_gpu_nxp_vglite_draw_bg(const lv_area_t * coords,const lv_area_t * clip_area,const lv_draw_rect_dsc_t * dsc)92 lv_res_t lv_gpu_nxp_vglite_draw_bg(const lv_area_t * coords, const lv_area_t * clip_area,
93                                    const lv_draw_rect_dsc_t * dsc)
94 {
95     vg_lite_error_t err = VG_LITE_SUCCESS;
96     lv_coord_t width = lv_area_get_width(coords);
97     lv_coord_t height = lv_area_get_height(coords);
98     vg_lite_color_t vgcol;
99     lv_coord_t radius = dsc->radius;
100     vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
101 
102     if(dsc->radius < 0)
103         return LV_RES_INV;
104 
105     /*** Init path ***/
106     int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
107     uint32_t path_data_size;
108     lv_vglite_create_rect_path_data(path_data, &path_data_size, radius, coords);
109     vg_lite_quality_t path_quality = dsc->radius > 0 ? VG_LITE_HIGH : VG_LITE_LOW;
110 
111     vg_lite_path_t path;
112     err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
113                             (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
114                             ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
115     VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
116 
117     vg_lite_matrix_t matrix;
118     vg_lite_identity(&matrix);
119 
120     vg_lite_matrix_t * grad_matrix;
121     vg_lite_linear_gradient_t gradient;
122 
123     /*** Init Color/Gradient ***/
124     if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
125         uint32_t colors[2];
126         uint32_t stops[2];
127         lv_color32_t col32[2];
128 
129         /* Gradient setup */
130         uint8_t cnt = LV_MAX(dsc->bg_grad.stops_count, 2);
131         for(uint8_t i = 0; i < cnt; i++) {
132             col32[i].full = lv_color_to32(dsc->bg_grad.stops[i].color); /*Convert color to RGBA8888*/
133             stops[i] = dsc->bg_grad.stops[i].frac;
134 
135             vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_ABGR8888 : VG_LITE_ARGB8888;
136             if(lv_vglite_premult_and_swizzle(&colors[i], col32[i], dsc->bg_opa, color_format) != LV_RES_OK)
137                 VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
138         }
139 
140         lv_memset_00(&gradient, sizeof(vg_lite_linear_gradient_t));
141 
142         err = vg_lite_init_grad(&gradient);
143         VG_LITE_ERR_RETURN_INV(err, "Init gradient failed");
144 
145         err = vg_lite_set_grad(&gradient, cnt, colors, stops);
146         VG_LITE_ERR_RETURN_INV(err, "Set gradient failed.");
147 
148         err = vg_lite_update_grad(&gradient);
149         VG_LITE_ERR_RETURN_INV(err, "Update gradient failed.");
150 
151         grad_matrix = vg_lite_get_grad_matrix(&gradient);
152         vg_lite_identity(grad_matrix);
153         vg_lite_translate((float)coords->x1, (float)coords->y1, grad_matrix);
154 
155         if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_VER) {
156             vg_lite_scale(1.0f, (float)height / 256.0f, grad_matrix);
157             vg_lite_rotate(90.0f, grad_matrix);
158         }
159         else {   /*LV_GRAD_DIR_HOR*/
160             vg_lite_scale((float)width / 256.0f, 1.0f, grad_matrix);
161         }
162     }
163 
164     lv_color32_t bg_col32 = {.full = lv_color_to32(dsc->bg_color)}; /*Convert color to RGBA8888*/
165     vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
166     if(lv_vglite_premult_and_swizzle(&vgcol, bg_col32, dsc->bg_opa, color_format) != LV_RES_OK)
167         VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
168 
169     lv_vglite_set_scissor(clip_area);
170 
171     /*** Draw rectangle ***/
172     if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
173         err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
174     }
175     else {
176         err = vg_lite_draw_gradient(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, &gradient, VG_LITE_BLEND_SRC_OVER);
177     }
178     VG_LITE_ERR_RETURN_INV(err, "Draw gradient failed.");
179 
180     if(lv_vglite_run() != LV_RES_OK)
181         VG_LITE_RETURN_INV("Run failed.");
182 
183     lv_vglite_disable_scissor();
184 
185     err = vg_lite_clear_path(&path);
186     VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
187 
188     if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) {
189         err = vg_lite_clear_grad(&gradient);
190         VG_LITE_ERR_RETURN_INV(err, "Clear gradient failed.");
191     }
192 
193     return LV_RES_OK;
194 }
195 
lv_gpu_nxp_vglite_draw_border_generic(const lv_area_t * coords,const lv_area_t * clip_area,const lv_draw_rect_dsc_t * dsc,bool border)196 lv_res_t lv_gpu_nxp_vglite_draw_border_generic(const lv_area_t * coords, const lv_area_t * clip_area,
197                                                const lv_draw_rect_dsc_t * dsc, bool border)
198 {
199     vg_lite_error_t err = VG_LITE_SUCCESS;
200     vg_lite_color_t vgcol; /* vglite takes ABGR */
201     lv_coord_t radius = dsc->radius;
202     vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
203 
204     if(radius < 0)
205         return LV_RES_INV;
206 
207     if(border) {
208         /* Draw border - only has radius if object has radius*/
209         lv_coord_t border_half = (lv_coord_t)floor(dsc->border_width / 2.0f);
210         if(radius > border_half)
211             radius = radius - border_half;
212     }
213     else {
214         /* Draw outline - always has radius, leave the same radius in the circle case */
215         lv_coord_t outline_half = (lv_coord_t)ceil(dsc->outline_width / 2.0f);
216         if(radius < (lv_coord_t)LV_RADIUS_CIRCLE - outline_half)
217             radius = radius + outline_half;
218     }
219 
220     vg_lite_cap_style_t cap_style = (radius) ? VG_LITE_CAP_ROUND : VG_LITE_CAP_BUTT;
221     vg_lite_join_style_t join_style = (radius) ? VG_LITE_JOIN_ROUND : VG_LITE_JOIN_MITER;
222 
223     /* Choose vglite blend mode based on given lvgl blend mode */
224     vg_lite_blend_t vglite_blend_mode = lv_vglite_get_blend_mode(dsc->blend_mode);
225 
226     /*** Init path ***/
227     int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
228     uint32_t path_data_size;
229     lv_vglite_create_rect_path_data(path_data, &path_data_size, radius, coords);
230     vg_lite_quality_t path_quality = dsc->radius > 0 ? VG_LITE_HIGH : VG_LITE_LOW;
231 
232     vg_lite_path_t path;
233     err = vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
234                             (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
235                             ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
236     VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
237 
238     vg_lite_matrix_t matrix;
239     vg_lite_identity(&matrix);
240 
241     lv_opa_t opa;
242     lv_color32_t col32;
243     lv_coord_t line_width;
244 
245     if(border) {
246         opa = dsc->border_opa;
247         col32.full = lv_color_to32(dsc->border_color); /*Convert color to RGBA8888*/
248         line_width = dsc->border_width;
249     }
250     else {
251         opa = dsc->outline_opa;
252         col32.full = lv_color_to32(dsc->outline_color); /*Convert color to RGBA8888*/
253         line_width = dsc->outline_width;
254     }
255 
256     vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
257     if(lv_vglite_premult_and_swizzle(&vgcol, col32, opa, color_format) != LV_RES_OK)
258         VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
259 
260     /*** Draw border ***/
261     err = vg_lite_set_draw_path_type(&path, VG_LITE_DRAW_STROKE_PATH);
262     VG_LITE_ERR_RETURN_INV(err, "Set draw path type failed.");
263 
264     err = vg_lite_set_stroke(&path, cap_style, join_style, line_width, 8, NULL, 0, 0, vgcol);
265     VG_LITE_ERR_RETURN_INV(err, "Set stroke failed.");
266 
267     err = vg_lite_update_stroke(&path);
268     VG_LITE_ERR_RETURN_INV(err, "Update stroke failed.");
269 
270     lv_vglite_set_scissor(clip_area);
271 
272     err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, vglite_blend_mode, vgcol);
273     VG_LITE_ERR_RETURN_INV(err, "Draw border failed.");
274 
275     if(lv_vglite_run() != LV_RES_OK)
276         VG_LITE_RETURN_INV("Run failed.");
277 
278     lv_vglite_disable_scissor();
279 
280     err = vg_lite_clear_path(&path);
281     VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
282 
283     return LV_RES_OK;
284 
285 }
286 
287 /**********************
288  *   STATIC FUNCTIONS
289  **********************/
290 
lv_vglite_create_rect_path_data(int32_t * path_data,uint32_t * path_data_size,lv_coord_t radius,const lv_area_t * coords)291 static void lv_vglite_create_rect_path_data(int32_t * path_data, uint32_t * path_data_size,
292                                             lv_coord_t radius,
293                                             const lv_area_t * coords)
294 {
295     lv_coord_t rect_width = lv_area_get_width(coords);
296     lv_coord_t rect_height = lv_area_get_height(coords);
297 
298     /* Get the final radius. Can't be larger than the half of the shortest side */
299     int32_t shortest_side = LV_MIN(rect_width, rect_height);
300     int32_t final_radius = LV_MIN(radius, shortest_side / 2);
301 
302     /* Path data element index */
303     uint8_t pidx = 0;
304 
305     if((radius == (lv_coord_t)LV_RADIUS_CIRCLE) && (rect_width == rect_height)) {
306 
307         /* Get the control point offset for rounded cases */
308         int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE);
309 
310         /* Circle case */
311         /* Starting point */
312         path_data[pidx++] = VLC_OP_MOVE;
313         path_data[pidx++] = coords->x1 + final_radius;
314         path_data[pidx++] = coords->y1;
315 
316         /* Top-right arc */
317         path_data[pidx++] = VLC_OP_CUBIC_REL;
318         path_data[pidx++] = cpoff;
319         path_data[pidx++] = 0;
320         path_data[pidx++] = final_radius;
321         path_data[pidx++] = final_radius - cpoff;
322         path_data[pidx++] = final_radius;
323         path_data[pidx++] = final_radius;
324 
325         /* Bottom-right arc*/
326         path_data[pidx++] = VLC_OP_CUBIC_REL;
327         path_data[pidx++] = 0;
328         path_data[pidx++] = cpoff;
329         path_data[pidx++] = cpoff - final_radius;
330         path_data[pidx++] = final_radius;
331         path_data[pidx++] = 0 - final_radius;
332         path_data[pidx++] = final_radius;
333 
334         /* Bottom-left arc */
335         path_data[pidx++] = VLC_OP_CUBIC_REL;
336         path_data[pidx++] = 0 - cpoff;
337         path_data[pidx++] = 0;
338         path_data[pidx++] = 0 - final_radius;
339         path_data[pidx++] = cpoff - final_radius;
340         path_data[pidx++] = 0 - final_radius;
341         path_data[pidx++] = 0 - final_radius;
342 
343         /* Top-left arc*/
344         path_data[pidx++] = VLC_OP_CUBIC_REL;
345         path_data[pidx++] = 0;
346         path_data[pidx++] = 0 - cpoff;
347         path_data[pidx++] = final_radius - cpoff;
348         path_data[pidx++] = 0 - final_radius;
349         path_data[pidx++] = final_radius;
350         path_data[pidx++] = 0 - final_radius;
351 
352         /* Ending point */
353         path_data[pidx++] = VLC_OP_END;
354     }
355     else if(radius > 0) {
356         /* Get the control point offset for rounded cases */
357         int32_t cpoff = (int32_t)((float)final_radius * BEZIER_OPTIM_CIRCLE);
358 
359         /* Rounded rectangle case */
360         /* Starting point */
361         path_data[pidx++] = VLC_OP_MOVE;
362         path_data[pidx++] = coords->x1 + final_radius;
363         path_data[pidx++] = coords->y1;
364 
365         /* Top side */
366         path_data[pidx++] = VLC_OP_LINE;
367         path_data[pidx++] = coords->x2 - final_radius + 1;  // Extended for VGLite
368         path_data[pidx++] = coords->y1;
369 
370         /* Top-right corner */
371         path_data[pidx++] = VLC_OP_CUBIC_REL;
372         path_data[pidx++] = cpoff;
373         path_data[pidx++] = 0;
374         path_data[pidx++] = final_radius;
375         path_data[pidx++] = final_radius - cpoff;
376         path_data[pidx++] = final_radius;
377         path_data[pidx++] = final_radius;
378 
379         /* Right side */
380         path_data[pidx++] = VLC_OP_LINE;
381         path_data[pidx++] = coords->x2 + 1;                 // Extended for VGLite
382         path_data[pidx++] = coords->y2 - final_radius + 1;  // Extended for VGLite
383 
384         /* Bottom-right corner*/
385         path_data[pidx++] = VLC_OP_CUBIC_REL;
386         path_data[pidx++] = 0;
387         path_data[pidx++] = cpoff;
388         path_data[pidx++] = cpoff - final_radius;
389         path_data[pidx++] = final_radius;
390         path_data[pidx++] = 0 - final_radius;
391         path_data[pidx++] = final_radius;
392 
393         /* Bottom side */
394         path_data[pidx++] = VLC_OP_LINE;
395         path_data[pidx++] = coords->x1 + final_radius;
396         path_data[pidx++] = coords->y2 + 1;                 // Extended for VGLite
397 
398         /* Bottom-left corner */
399         path_data[pidx++] = VLC_OP_CUBIC_REL;
400         path_data[pidx++] = 0 - cpoff;
401         path_data[pidx++] = 0;
402         path_data[pidx++] = 0 - final_radius;
403         path_data[pidx++] = cpoff - final_radius;
404         path_data[pidx++] = 0 - final_radius;
405         path_data[pidx++] = 0 - final_radius;
406 
407         /* Left side*/
408         path_data[pidx++] = VLC_OP_LINE;
409         path_data[pidx++] = coords->x1;
410         path_data[pidx++] = coords->y1 + final_radius;
411 
412         /* Top-left corner */
413         path_data[pidx++] = VLC_OP_CUBIC_REL;
414         path_data[pidx++] = 0;
415         path_data[pidx++] = 0 - cpoff;
416         path_data[pidx++] = final_radius - cpoff;
417         path_data[pidx++] = 0 - final_radius;
418         path_data[pidx++] = final_radius;
419         path_data[pidx++] = 0 - final_radius;
420 
421         /* Ending point */
422         path_data[pidx++] = VLC_OP_END;
423     }
424     else {
425         /* Non-rounded rectangle case */
426         /* Starting point */
427         path_data[pidx++] = VLC_OP_MOVE;
428         path_data[pidx++] = coords->x1;
429         path_data[pidx++] = coords->y1;
430 
431         /* Top side */
432         path_data[pidx++] = VLC_OP_LINE;
433         path_data[pidx++] = coords->x2 + 1; // Extended for VGLite
434         path_data[pidx++] = coords->y1;
435 
436         /* Right side */
437         path_data[pidx++] = VLC_OP_LINE;
438         path_data[pidx++] = coords->x2 + 1; // Extended for VGLite
439         path_data[pidx++] = coords->y2 + 1; // Extended for VGLite
440 
441         /* Bottom side */
442         path_data[pidx++] = VLC_OP_LINE;
443         path_data[pidx++] = coords->x1;
444         path_data[pidx++] = coords->y2 + 1; // Extended for VGLite
445 
446         /* Left side*/
447         path_data[pidx++] = VLC_OP_LINE;
448         path_data[pidx++] = coords->x1;
449         path_data[pidx++] = coords->y1;
450 
451         /* Ending point */
452         path_data[pidx++] = VLC_OP_END;
453     }
454 
455     /* Resulting path size */
456     *path_data_size = pidx * sizeof(int32_t);
457 }
458 
459 #endif /*LV_USE_GPU_NXP_VG_LITE*/
460