1 /**
2  * @file lv_draw_vglite_arc.c
3  *
4  */
5 
6 /**
7  * Copyright 2021-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 "../../../stdlib/lv_string.h"
24 #include <math.h>
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 
30 #define T_FRACTION 16384.0f
31 
32 #define DICHOTO_ITER 5
33 
34 static const uint16_t TperDegree[90] = {
35     0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581,
36     1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384,
37     3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222,
38     5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077,
39     7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936,
40     9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794,
41     10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637,
42     12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448,
43     14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212
44 };
45 
46 /**********************
47  *      TYPEDEFS
48  **********************/
49 
50 /* intermediate arc params */
51 typedef struct _vg_arc {
52     int32_t angle;   /* angle <90deg */
53     int32_t quarter; /* 0-3 counter-clockwise */
54     int32_t rad;     /* radius */
55     int32_t p0x;       /* point P0 */
56     int32_t p0y;
57     int32_t p1x;      /* point P1 */
58     int32_t p1y;
59     int32_t p2x;      /* point P2 */
60     int32_t p2y;
61     int32_t p3x;      /* point P3 */
62     int32_t p3y;
63 } vg_arc;
64 
65 typedef struct _cubic_cont_pt {
66     float p0;
67     float p1;
68     float p2;
69     float p3;
70 } cubic_cont_pt;
71 
72 /**********************
73  *  STATIC PROTOTYPES
74  **********************/
75 
76 /**
77  * Draw arc shape with effects
78  *
79  * @param[in] center Arc center with relative coordinates
80  * @param[in] clip_area Clip area with relative coordinates to dest buff
81  * @param[in] dsc Arc description structure (width, rounded ending, opacity)
82  *
83  */
84 static void _vglite_draw_arc(const lv_point_t * center, const lv_area_t * clip_area,
85                              const lv_draw_arc_dsc_t * dsc);
86 
87 /**********************
88  *  STATIC VARIABLES
89  **********************/
90 
91 /**********************
92  *      MACROS
93  **********************/
94 
95 /**********************
96  *   GLOBAL FUNCTIONS
97  **********************/
98 
lv_draw_vglite_arc(lv_draw_unit_t * draw_unit,const lv_draw_arc_dsc_t * dsc,const lv_area_t * coords)99 void lv_draw_vglite_arc(lv_draw_unit_t * draw_unit, const lv_draw_arc_dsc_t * dsc,
100                         const lv_area_t * coords)
101 {
102     LV_UNUSED(coords);
103 
104     if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
105         return;
106     if(dsc->width == 0)
107         return;
108     if(dsc->start_angle == dsc->end_angle)
109         return;
110 
111     lv_layer_t * layer = draw_unit->target_layer;
112     lv_point_t center = {dsc->center.x - layer->buf_area.x1, dsc->center.y - layer->buf_area.y1};
113 
114     lv_area_t clip_area;
115     lv_area_copy(&clip_area, draw_unit->clip_area);
116     lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
117 
118     _vglite_draw_arc(&center, &clip_area, dsc);
119 }
120 
121 /**********************
122  *   STATIC FUNCTIONS
123  **********************/
124 
_copy_arc(vg_arc * dst,vg_arc * src)125 static void _copy_arc(vg_arc * dst, vg_arc * src)
126 {
127     dst->quarter = src->quarter;
128     dst->rad = src->rad;
129     dst->angle = src->angle;
130     dst->p0x = src->p0x;
131     dst->p1x = src->p1x;
132     dst->p2x = src->p2x;
133     dst->p3x = src->p3x;
134     dst->p0y = src->p0y;
135     dst->p1y = src->p1y;
136     dst->p2y = src->p2y;
137     dst->p3y = src->p3y;
138 }
139 
140 /**
141  * Rotate the point according given rotation angle rotation center is 0,0
142  */
_rotate_point(int32_t angle,int32_t * x,int32_t * y)143 static void _rotate_point(int32_t angle, int32_t * x, int32_t * y)
144 {
145     int32_t ori_x = *x;
146     int32_t ori_y = *y;
147     int16_t alpha = (int16_t)angle;
148     *x = ((lv_trigo_cos(alpha) * ori_x) / LV_TRIGO_SIN_MAX) - ((lv_trigo_sin(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
149     *y = ((lv_trigo_sin(alpha) * ori_x) / LV_TRIGO_SIN_MAX) + ((lv_trigo_cos(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
150 }
151 
152 /**
153  * Set full arc control points depending on quarter.
154  * Control points match the best approximation of a circle.
155  * Arc Quarter position is:
156  * Q2 | Q3
157  * ---+---
158  * Q1 | Q0
159  */
_set_full_arc(vg_arc * fullarc)160 static void _set_full_arc(vg_arc * fullarc)
161 {
162     /* the tangent length for the bezier circle approx */
163     float tang = ((float)fullarc->rad) * BEZIER_OPTIM_CIRCLE;
164     switch(fullarc->quarter) {
165         case 0:
166             /* first quarter */
167             fullarc->p0x = fullarc->rad;
168             fullarc->p0y = 0;
169             fullarc->p1x = fullarc->rad;
170             fullarc->p1y = (int32_t)tang;
171             fullarc->p2x = (int32_t)tang;
172             fullarc->p2y = fullarc->rad;
173             fullarc->p3x = 0;
174             fullarc->p3y = fullarc->rad;
175             break;
176         case 1:
177             /* second quarter */
178             fullarc->p0x = 0;
179             fullarc->p0y = fullarc->rad;
180             fullarc->p1x = 0 - (int32_t)tang;
181             fullarc->p1y = fullarc->rad;
182             fullarc->p2x = 0 - fullarc->rad;
183             fullarc->p2y = (int32_t)tang;
184             fullarc->p3x = 0 - fullarc->rad;
185             fullarc->p3y = 0;
186             break;
187         case 2:
188             /* third quarter */
189             fullarc->p0x = 0 - fullarc->rad;
190             fullarc->p0y = 0;
191             fullarc->p1x = 0 - fullarc->rad;
192             fullarc->p1y = 0 - (int32_t)tang;
193             fullarc->p2x = 0 - (int32_t)tang;
194             fullarc->p2y = 0 - fullarc->rad;
195             fullarc->p3x = 0;
196             fullarc->p3y = 0 - fullarc->rad;
197             break;
198         case 3:
199             /* fourth quarter */
200             fullarc->p0x = 0;
201             fullarc->p0y = 0 - fullarc->rad;
202             fullarc->p1x = (int32_t)tang;
203             fullarc->p1y = 0 - fullarc->rad;
204             fullarc->p2x = fullarc->rad;
205             fullarc->p2y = 0 - (int32_t)tang;
206             fullarc->p3x = fullarc->rad;
207             fullarc->p3y = 0;
208             break;
209         default:
210             VGLITE_ASSERT_MSG(false, "Invalid arc quarter.");
211             break;
212     }
213 }
214 
215 /**
216  * Linear interpolation between two points 'a' and 'b'
217  * 't' parameter is the proportion ratio expressed in range [0 ; T_FRACTION ]
218  */
_lerp(float coord_a,float coord_b,uint16_t t)219 static inline float _lerp(float coord_a, float coord_b, uint16_t t)
220 {
221     float tf = (float)t;
222     return ((T_FRACTION - tf) * coord_a + tf * coord_b) / T_FRACTION;
223 }
224 
225 /**
226  * Computes a point of bezier curve given 't' param
227  */
_comp_bezier_point(float t,cubic_cont_pt cp)228 static inline float _comp_bezier_point(float t, cubic_cont_pt cp)
229 {
230     float t_sq = t * t;
231     float inv_t_sq = (1.0f - t) * (1.0f - t);
232     float apt = (1.0f - t) * inv_t_sq * cp.p0 + 3.0f * inv_t_sq * t * cp.p1 + 3.0f * (1.0f - t) * t_sq * cp.p2 + t * t_sq *
233                 cp.p3;
234     return apt;
235 }
236 
237 /**
238  * Find parameter 't' in curve at point 'pt'
239  * proceed by dichotomy on only 1 dimension,
240  * works only if the curve is monotonic
241  * bezier curve is defined by control points [p0 p1 p2 p3]
242  * 'dec' tells if curve is decreasing (true) or increasing (false)
243  */
_get_bez_t_from_pos(float pt,cubic_cont_pt cp,bool dec)244 static uint16_t _get_bez_t_from_pos(float pt, cubic_cont_pt cp, bool dec)
245 {
246     /* initialize dichotomy with boundary 't' values */
247     float t_low = 0.0f;
248     float t_mid = 0.5f;
249     float t_hig = 1.0f;
250     float a_pt;
251     /* dichotomy loop */
252     for(int i = 0; i < DICHOTO_ITER; i++) {
253         a_pt = _comp_bezier_point(t_mid, cp);
254         /* check mid-point position on bezier curve versus targeted point */
255         if((a_pt > pt) != dec) {
256             t_hig = t_mid;
257         }
258         else {
259             t_low = t_mid;
260         }
261         /* define new 't' param for mid-point */
262         t_mid = (t_low + t_hig) / 2.0f;
263     }
264     /* return parameter 't' in integer range [0 ; T_FRACTION] */
265     return (uint16_t)floorf(t_mid * T_FRACTION + 0.5f);
266 }
267 
268 /**
269  * Gives relative coords of the control points
270  * for the sub-arc starting at angle with given angle span
271  */
_get_subarc_control_points(vg_arc * arc,int32_t span)272 static void _get_subarc_control_points(vg_arc * arc, int32_t span)
273 {
274     vg_arc fullarc = {0};
275     fullarc.angle = arc->angle;
276     fullarc.quarter = arc->quarter;
277     fullarc.rad = arc->rad;
278     _set_full_arc(&fullarc);
279 
280     /* special case of full arc */
281     if(arc->angle == 90) {
282         _copy_arc(arc, &fullarc);
283         return;
284     }
285 
286     /* compute 1st arc using the geometric construction of curve */
287     uint16_t t2 = TperDegree[arc->angle + span];
288 
289     /* lerp for A */
290     float a2x = _lerp((float)fullarc.p0x, (float)fullarc.p1x, t2);
291     float a2y = _lerp((float)fullarc.p0y, (float)fullarc.p1y, t2);
292     /* lerp for B */
293     float b2x = _lerp((float)fullarc.p1x, (float)fullarc.p2x, t2);
294     float b2y = _lerp((float)fullarc.p1y, (float)fullarc.p2y, t2);
295     /* lerp for C */
296     float c2x = _lerp((float)fullarc.p2x, (float)fullarc.p3x, t2);
297     float c2y = _lerp((float)fullarc.p2y, (float)fullarc.p3y, t2);
298 
299     /* lerp for D */
300     float d2x = _lerp(a2x, b2x, t2);
301     float d2y = _lerp(a2y, b2y, t2);
302     /* lerp for E */
303     float e2x = _lerp(b2x, c2x, t2);
304     float e2y = _lerp(b2y, c2y, t2);
305 
306     float pt2x = _lerp(d2x, e2x, t2);
307     float pt2y = _lerp(d2y, e2y, t2);
308 
309     /* compute sub-arc using the geometric construction of curve */
310     uint16_t t1 = TperDegree[arc->angle];
311 
312     /* lerp for A */
313     float a1x = _lerp((float)fullarc.p0x, (float)fullarc.p1x, t1);
314     float a1y = _lerp((float)fullarc.p0y, (float)fullarc.p1y, t1);
315     /* lerp for B */
316     float b1x = _lerp((float)fullarc.p1x, (float)fullarc.p2x, t1);
317     float b1y = _lerp((float)fullarc.p1y, (float)fullarc.p2y, t1);
318     /* lerp for C */
319     float c1x = _lerp((float)fullarc.p2x, (float)fullarc.p3x, t1);
320     float c1y = _lerp((float)fullarc.p2y, (float)fullarc.p3y, t1);
321 
322     /* lerp for D */
323     float d1x = _lerp(a1x, b1x, t1);
324     float d1y = _lerp(a1y, b1y, t1);
325     /* lerp for E */
326     float e1x = _lerp(b1x, c1x, t1);
327     float e1y = _lerp(b1y, c1y, t1);
328 
329     float pt1x = _lerp(d1x, e1x, t1);
330     float pt1y = _lerp(d1y, e1y, t1);
331 
332     /* find the 't3' parameter for point P(t1) on the sub-arc [P0 A2 D2 P(t2)] using dichotomy
333      * use position of x axis only */
334     uint16_t t3;
335     t3 = _get_bez_t_from_pos(pt1x,
336     (cubic_cont_pt) {
337         .p0 = ((float)fullarc.p0x), .p1 = a2x, .p2 = d2x, .p3 = pt2x
338     },
339     (bool)(pt2x < (float)fullarc.p0x));
340 
341     /* lerp for B */
342     float b3x = _lerp(a2x, d2x, t3);
343     float b3y = _lerp(a2y, d2y, t3);
344     /* lerp for C */
345     float c3x = _lerp(d2x, pt2x, t3);
346     float c3y = _lerp(d2y, pt2y, t3);
347 
348     /* lerp for E */
349     float e3x = _lerp(b3x, c3x, t3);
350     float e3y = _lerp(b3y, c3y, t3);
351 
352     arc->p0x = (int32_t)floorf(0.5f + pt1x);
353     arc->p0y = (int32_t)floorf(0.5f + pt1y);
354     arc->p1x = (int32_t)floorf(0.5f + e3x);
355     arc->p1y = (int32_t)floorf(0.5f + e3y);
356     arc->p2x = (int32_t)floorf(0.5f + c3x);
357     arc->p2y = (int32_t)floorf(0.5f + c3y);
358     arc->p3x = (int32_t)floorf(0.5f + pt2x);
359     arc->p3y = (int32_t)floorf(0.5f + pt2y);
360 }
361 
362 /**
363  * Gives relative coords of the control points
364  */
_get_arc_control_points(vg_arc * arc,bool start)365 static void _get_arc_control_points(vg_arc * arc, bool start)
366 {
367     vg_arc fullarc = {0};
368     fullarc.angle = arc->angle;
369     fullarc.quarter = arc->quarter;
370     fullarc.rad = arc->rad;
371     _set_full_arc(&fullarc);
372 
373     /* special case of full arc */
374     if(arc->angle == 90) {
375         _copy_arc(arc, &fullarc);
376         return;
377     }
378 
379     /* compute sub-arc using the geometric construction of curve */
380     uint16_t t = TperDegree[arc->angle];
381     /* lerp for A */
382     float ax = _lerp((float)fullarc.p0x, (float)fullarc.p1x, t);
383     float ay = _lerp((float)fullarc.p0y, (float)fullarc.p1y, t);
384     /* lerp for B */
385     float bx = _lerp((float)fullarc.p1x, (float)fullarc.p2x, t);
386     float by = _lerp((float)fullarc.p1y, (float)fullarc.p2y, t);
387     /* lerp for C */
388     float cx = _lerp((float)fullarc.p2x, (float)fullarc.p3x, t);
389     float cy = _lerp((float)fullarc.p2y, (float)fullarc.p3y, t);
390 
391     /* lerp for D */
392     float dx = _lerp(ax, bx, t);
393     float dy = _lerp(ay, by, t);
394     /* lerp for E */
395     float ex = _lerp(bx, cx, t);
396     float ey = _lerp(by, cy, t);
397 
398     /* sub-arc's control points are tangents of DeCasteljau's algorithm */
399     if(start) {
400         arc->p0x = (int32_t)floorf(0.5f + _lerp(dx, ex, t));
401         arc->p0y = (int32_t)floorf(0.5f + _lerp(dy, ey, t));
402         arc->p1x = (int32_t)floorf(0.5f + ex);
403         arc->p1y = (int32_t)floorf(0.5f + ey);
404         arc->p2x = (int32_t)floorf(0.5f + cx);
405         arc->p2y = (int32_t)floorf(0.5f + cy);
406         arc->p3x = fullarc.p3x;
407         arc->p3y = fullarc.p3y;
408     }
409     else {
410         arc->p0x = fullarc.p0x;
411         arc->p0y = fullarc.p0y;
412         arc->p1x = (int32_t)floorf(0.5f + ax);
413         arc->p1y = (int32_t)floorf(0.5f + ay);
414         arc->p2x = (int32_t)floorf(0.5f + dx);
415         arc->p2y = (int32_t)floorf(0.5f + dy);
416         arc->p3x = (int32_t)floorf(0.5f + _lerp(dx, ex, t));
417         arc->p3y = (int32_t)floorf(0.5f + _lerp(dy, ey, t));
418     }
419 }
420 
421 /**
422  * Add the arc control points into the path data for vglite,
423  * taking into account the real center of the arc (translation).
424  * arc_path: (in/out) the path data array for vglite
425  * pidx: (in/out) index of last element added in arc_path
426  * q_arc: (in) the arc data containing control points
427  * center: (in) the center of the circle in draw coordinates
428  * cw: (in) true if arc is clockwise
429  */
_add_split_arc_path(int32_t * arc_path,int * pidx,vg_arc * q_arc,const lv_point_t * center,bool cw)430 static void _add_split_arc_path(int32_t * arc_path, int * pidx, vg_arc * q_arc, const lv_point_t * center, bool cw)
431 {
432     /* assumes first control point already in array arc_path[] */
433     int idx = *pidx;
434     if(cw) {
435 #if BEZIER_DBG_CONTROL_POINTS
436         arc_path[idx++] = VLC_OP_LINE;
437         arc_path[idx++] = q_arc->p1x + center->x;
438         arc_path[idx++] = q_arc->p1y + center->y;
439         arc_path[idx++] = VLC_OP_LINE;
440         arc_path[idx++] = q_arc->p2x + center->x;
441         arc_path[idx++] = q_arc->p2y + center->y;
442         arc_path[idx++] = VLC_OP_LINE;
443         arc_path[idx++] = q_arc->p3x + center->x;
444         arc_path[idx++] = q_arc->p3y + center->y;
445 #else
446         arc_path[idx++] = VLC_OP_CUBIC;
447         arc_path[idx++] = q_arc->p1x + center->x;
448         arc_path[idx++] = q_arc->p1y + center->y;
449         arc_path[idx++] = q_arc->p2x + center->x;
450         arc_path[idx++] = q_arc->p2y + center->y;
451         arc_path[idx++] = q_arc->p3x + center->x;
452         arc_path[idx++] = q_arc->p3y + center->y;
453 #endif
454     }
455     else {      /* reverse points order when counter-clockwise */
456 #if BEZIER_DBG_CONTROL_POINTS
457         arc_path[idx++] = VLC_OP_LINE;
458         arc_path[idx++] = q_arc->p2x + center->x;
459         arc_path[idx++] = q_arc->p2y + center->y;
460         arc_path[idx++] = VLC_OP_LINE;
461         arc_path[idx++] = q_arc->p1x + center->x;
462         arc_path[idx++] = q_arc->p1y + center->y;
463         arc_path[idx++] = VLC_OP_LINE;
464         arc_path[idx++] = q_arc->p0x + center->x;
465         arc_path[idx++] = q_arc->p0y + center->y;
466 #else
467         arc_path[idx++] = VLC_OP_CUBIC;
468         arc_path[idx++] = q_arc->p2x + center->x;
469         arc_path[idx++] = q_arc->p2y + center->y;
470         arc_path[idx++] = q_arc->p1x + center->x;
471         arc_path[idx++] = q_arc->p1y + center->y;
472         arc_path[idx++] = q_arc->p0x + center->x;
473         arc_path[idx++] = q_arc->p0y + center->y;
474 #endif
475     }
476     /* update index i n path array*/
477     *pidx = idx;
478 }
479 
_add_arc_path(int32_t * arc_path,int * pidx,int32_t radius,int32_t start_angle,int32_t end_angle,const lv_point_t * center,bool cw)480 static void _add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
481                           int32_t start_angle, int32_t end_angle, const lv_point_t * center, bool cw)
482 {
483     /* set number of arcs to draw */
484     vg_arc q_arc;
485     int32_t start_arc_angle = start_angle % 90;
486     int32_t end_arc_angle = end_angle % 90;
487     int32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0;
488     int32_t nbarc = (end_angle - start_angle - inv_start_arc_angle - end_arc_angle) / 90;
489     q_arc.rad = radius;
490 
491     /* handle special case of start & end point in the same quarter */
492     if(((start_angle / 90) == (end_angle / 90)) && (nbarc <= 0)) {
493         q_arc.quarter = (start_angle / 90) % 4;
494         q_arc.angle = start_arc_angle;
495         _get_subarc_control_points(&q_arc, end_arc_angle - start_arc_angle);
496         _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
497         return;
498     }
499 
500     if(cw) {
501         /* partial starting arc */
502         if(start_arc_angle > 0) {
503             q_arc.quarter = (start_angle / 90) % 4;
504             q_arc.angle = start_arc_angle;
505             /* get cubic points relative to center */
506             _get_arc_control_points(&q_arc, true);
507             /* put cubic points in arc_path */
508             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
509         }
510         /* full arcs */
511         for(int32_t q = 0; q < nbarc ; q++) {
512             q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
513             q_arc.angle = 90;
514             /* get cubic points relative to center */
515             _get_arc_control_points(&q_arc, true);   /* 2nd parameter 'start' ignored */
516             /* put cubic points in arc_path */
517             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
518         }
519         /* partial ending arc */
520         if(end_arc_angle > 0) {
521             q_arc.quarter = (end_angle / 90) % 4;
522             q_arc.angle = end_arc_angle;
523             /* get cubic points relative to center */
524             _get_arc_control_points(&q_arc, false);
525             /* put cubic points in arc_path */
526             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
527         }
528 
529     }
530     else {   /* counter clockwise */
531 
532         /* partial ending arc */
533         if(end_arc_angle > 0) {
534             q_arc.quarter = (end_angle / 90) % 4;
535             q_arc.angle = end_arc_angle;
536             /* get cubic points relative to center */
537             _get_arc_control_points(&q_arc, false);
538             /* put cubic points in arc_path */
539             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
540         }
541         /* full arcs */
542         for(int32_t q = nbarc - 1; q >= 0; q--) {
543             q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
544             q_arc.angle = 90;
545             /* get cubic points relative to center */
546             _get_arc_control_points(&q_arc, true);   /* 2nd parameter 'start' ignored */
547             /* put cubic points in arc_path */
548             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
549         }
550         /* partial starting arc */
551         if(start_arc_angle > 0) {
552             q_arc.quarter = (start_angle / 90) % 4;
553             q_arc.angle = start_arc_angle;
554             /* get cubic points relative to center */
555             _get_arc_control_points(&q_arc, true);
556             /* put cubic points in arc_path */
557             _add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
558         }
559     }
560 }
561 
_vglite_draw_arc(const lv_point_t * center,const lv_area_t * clip_area,const lv_draw_arc_dsc_t * dsc)562 static void _vglite_draw_arc(const lv_point_t * center, const lv_area_t * clip_area,
563                              const lv_draw_arc_dsc_t * dsc)
564 {
565     vg_lite_path_t path;
566     uint16_t start_angle = dsc->start_angle;
567     uint16_t end_angle = dsc->end_angle;
568 
569     /* be sure end_angle > start_angle */
570     if(end_angle < start_angle)
571         end_angle += 360;
572 
573     bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false;
574     vg_lite_buffer_t * vgbuf = vglite_get_dest_buf();
575 
576     int32_t arc_path[ARC_PATH_DATA_MAX_SIZE];
577     lv_memzero(arc_path, sizeof(arc_path));
578 
579     /*** Init path ***/
580     int32_t width = dsc->width;  /* inner arc radius = outer arc radius - width */
581     uint16_t radius = dsc->radius;
582 
583     if(width > radius)
584         width = radius;
585 
586     int pidx = 0;
587     int32_t cp_x, cp_y; /* control point coords */
588 
589     /* first control point of curve */
590     cp_x = radius;
591     cp_y = 0;
592     _rotate_point(start_angle, &cp_x, &cp_y);
593     arc_path[pidx++] = VLC_OP_MOVE;
594     arc_path[pidx++] = center->x + cp_x;
595     arc_path[pidx++] = center->y + cp_y;
596 
597     /* draw 1-5 outer quarters */
598     _add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, center, true);
599 
600     if(donut) {
601         /* close outer circle */
602         cp_x = radius;
603         cp_y = 0;
604         _rotate_point(start_angle, &cp_x, &cp_y);
605         arc_path[pidx++] = VLC_OP_LINE;
606         arc_path[pidx++] = center->x + cp_x;
607         arc_path[pidx++] = center->y + cp_y;
608         /* start inner circle */
609         cp_x = radius - width;
610         cp_y = 0;
611         _rotate_point(start_angle, &cp_x, &cp_y);
612         arc_path[pidx++] = VLC_OP_MOVE;
613         arc_path[pidx++] = center->x + cp_x;
614         arc_path[pidx++] = center->y + cp_y;
615 
616     }
617     else if(dsc->rounded != 0U) {    /* 1st rounded arc ending */
618         cp_x = radius - width / 2;
619         cp_y = 0;
620         _rotate_point(end_angle, &cp_x, &cp_y);
621         lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
622         _add_arc_path(arc_path, &pidx, width / 2, end_angle, (end_angle + 180),
623                       &round_center, true);
624 
625     }
626     else {   /* 1st flat ending */
627         cp_x = radius - width;
628         cp_y = 0;
629         _rotate_point(end_angle, &cp_x, &cp_y);
630         arc_path[pidx++] = VLC_OP_LINE;
631         arc_path[pidx++] = center->x + cp_x;
632         arc_path[pidx++] = center->y + cp_y;
633     }
634 
635     /* draw 1-5 inner quarters */
636     _add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, center, false);
637 
638     /* last control point of curve */
639     if(donut) {     /* close the loop */
640         cp_x = radius - width;
641         cp_y = 0;
642         _rotate_point(start_angle, &cp_x, &cp_y);
643         arc_path[pidx++] = VLC_OP_LINE;
644         arc_path[pidx++] = center->x + cp_x;
645         arc_path[pidx++] = center->y + cp_y;
646 
647     }
648     else if(dsc->rounded != 0U) {    /* 2nd rounded arc ending */
649         cp_x = radius - width / 2;
650         cp_y = 0;
651         _rotate_point(start_angle, &cp_x, &cp_y);
652         lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
653         _add_arc_path(arc_path, &pidx, width / 2, (start_angle + 180), (start_angle + 360),
654                       &round_center, true);
655 
656     }
657     else {   /* 2nd flat ending */
658         cp_x = radius;
659         cp_y = 0;
660         _rotate_point(start_angle, &cp_x, &cp_y);
661         arc_path[pidx++] = VLC_OP_LINE;
662         arc_path[pidx++] = center->x + cp_x;
663         arc_path[pidx++] = center->y + cp_y;
664     }
665 
666     arc_path[pidx++] = VLC_OP_END;
667 
668     VGLITE_CHECK_ERROR(vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, (uint32_t)pidx * sizeof(int32_t), arc_path,
669                                          (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
670                                          ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f));
671 
672     lv_color32_t col32 = lv_color_to_32(dsc->color, dsc->opa);
673     vg_lite_color_t vgcol = vglite_get_color(col32, false);
674 
675     /*** Draw arc ***/
676     VGLITE_CHECK_ERROR(vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, NULL, VG_LITE_BLEND_SRC_OVER, vgcol));
677 
678     vglite_run();
679 
680     VGLITE_CHECK_ERROR(vg_lite_clear_path(&path));
681 }
682 
683 #endif /*LV_USE_DRAW_VGLITE*/
684