1 /**
2  * @file lv_vg_lite_path.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_vg_lite_path.h"
11 
12 #if LV_USE_DRAW_VG_LITE
13 
14 #include "lv_draw_vg_lite_type.h"
15 #include "lv_vg_lite_math.h"
16 #include <float.h>
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 
22 #define PATH_KAPPA 0.552284f
23 
24 /* Magic number from https://spencermortensen.com/articles/bezier-circle/ */
25 #define PATH_ARC_MAGIC 0.55191502449351f
26 
27 #define PATH_MEM_SIZE_MIN 128
28 
29 #define SIGN(x) (math_zero(x) ? 0 : ((x) > 0 ? 1 : -1))
30 
31 #define VLC_OP_ARG_LEN(OP, LEN) \
32     case VLC_OP_##OP:           \
33     return (LEN)
34 
35 #define PATH_CURRENT_PTR(PATH) ((uint8_t*)(PATH)->base.path + (PATH)->base.path_length)
36 #define PATH_LENGTH_INC(PATH, LENGTH) ((PATH)->base.path_length += (LENGTH))
37 
38 /**********************
39  *      TYPEDEFS
40  **********************/
41 
42 struct _lv_vg_lite_path_t {
43     vg_lite_path_t base;
44     vg_lite_matrix_t matrix;
45     size_t mem_size;
46     uint8_t format_len;
47     bool has_transform;
48 };
49 
50 typedef struct {
51     float min_x;
52     float min_y;
53     float max_x;
54     float max_y;
55 } lv_vg_lite_path_bounds_t;
56 
57 /**********************
58  *  STATIC PROTOTYPES
59  **********************/
60 
61 /**********************
62  *  STATIC VARIABLES
63  **********************/
64 
65 /**********************
66  *      MACROS
67  **********************/
68 
69 /**********************
70  *   GLOBAL FUNCTIONS
71  **********************/
72 
lv_vg_lite_path_init(struct _lv_draw_vg_lite_unit_t * unit)73 void lv_vg_lite_path_init(struct _lv_draw_vg_lite_unit_t * unit)
74 {
75     LV_ASSERT_NULL(unit);
76     unit->global_path = lv_vg_lite_path_create(VG_LITE_FP32);
77     unit->path_in_use = false;
78 }
79 
lv_vg_lite_path_deinit(struct _lv_draw_vg_lite_unit_t * unit)80 void lv_vg_lite_path_deinit(struct _lv_draw_vg_lite_unit_t * unit)
81 {
82     LV_ASSERT_NULL(unit);
83     LV_ASSERT(!unit->path_in_use);
84     lv_vg_lite_path_destroy(unit->global_path);
85     unit->global_path = NULL;
86 }
87 
lv_vg_lite_path_create(vg_lite_format_t data_format)88 lv_vg_lite_path_t * lv_vg_lite_path_create(vg_lite_format_t data_format)
89 {
90     LV_PROFILER_DRAW_BEGIN;
91     lv_vg_lite_path_t * path = lv_malloc_zeroed(sizeof(lv_vg_lite_path_t));
92     LV_ASSERT_MALLOC(path);
93     path->format_len = lv_vg_lite_path_format_len(data_format);
94     LV_ASSERT(vg_lite_init_path(
95                   &path->base,
96                   data_format,
97                   VG_LITE_HIGH,
98                   0,
99                   NULL,
100                   0, 0, 0, 0)
101               == VG_LITE_SUCCESS);
102     LV_PROFILER_DRAW_END;
103     return path;
104 }
105 
lv_vg_lite_path_destroy(lv_vg_lite_path_t * path)106 void lv_vg_lite_path_destroy(lv_vg_lite_path_t * path)
107 {
108     LV_PROFILER_DRAW_BEGIN;
109     LV_ASSERT_NULL(path);
110     if(path->base.path != NULL) {
111         lv_free(path->base.path);
112         path->base.path = NULL;
113 
114         /* clear remaining path data */
115         LV_VG_LITE_CHECK_ERROR(vg_lite_clear_path(&path->base));
116     }
117     lv_free(path);
118     LV_PROFILER_DRAW_END;
119 }
120 
lv_vg_lite_path_get(struct _lv_draw_vg_lite_unit_t * unit,vg_lite_format_t data_format)121 lv_vg_lite_path_t * lv_vg_lite_path_get(struct _lv_draw_vg_lite_unit_t * unit, vg_lite_format_t data_format)
122 {
123     LV_ASSERT_NULL(unit);
124     LV_ASSERT_NULL(unit->global_path);
125     LV_ASSERT(!unit->path_in_use);
126     lv_vg_lite_path_reset(unit->global_path, data_format);
127     unit->path_in_use = true;
128     return unit->global_path;
129 }
130 
lv_vg_lite_path_drop(struct _lv_draw_vg_lite_unit_t * unit,lv_vg_lite_path_t * path)131 void lv_vg_lite_path_drop(struct _lv_draw_vg_lite_unit_t * unit, lv_vg_lite_path_t * path)
132 {
133     LV_ASSERT_NULL(unit);
134     LV_ASSERT_NULL(path);
135     LV_ASSERT(unit->global_path == path);
136     LV_ASSERT(unit->path_in_use);
137     unit->path_in_use = false;
138 }
139 
lv_vg_lite_path_reset(lv_vg_lite_path_t * path,vg_lite_format_t data_format)140 void lv_vg_lite_path_reset(lv_vg_lite_path_t * path, vg_lite_format_t data_format)
141 {
142     LV_ASSERT_NULL(path);
143     path->base.path_length = 0;
144     path->base.format = data_format;
145     path->base.quality = VG_LITE_HIGH;
146     path->base.path_type = VG_LITE_DRAW_ZERO;
147     path->format_len = lv_vg_lite_path_format_len(data_format);
148     path->has_transform = false;
149 }
150 
lv_vg_lite_path_get_path(lv_vg_lite_path_t * path)151 vg_lite_path_t * lv_vg_lite_path_get_path(lv_vg_lite_path_t * path)
152 {
153     LV_ASSERT_NULL(path);
154     return &path->base;
155 }
156 
lv_vg_lite_path_set_bounding_box(lv_vg_lite_path_t * path,float min_x,float min_y,float max_x,float max_y)157 void lv_vg_lite_path_set_bounding_box(lv_vg_lite_path_t * path,
158                                       float min_x, float min_y,
159                                       float max_x, float max_y)
160 {
161     LV_ASSERT_NULL(path);
162     path->base.bounding_box[0] = min_x;
163     path->base.bounding_box[1] = min_y;
164     path->base.bounding_box[2] = max_x;
165     path->base.bounding_box[3] = max_y;
166 }
167 
lv_vg_lite_path_set_bounding_box_area(lv_vg_lite_path_t * path,const lv_area_t * area)168 void lv_vg_lite_path_set_bounding_box_area(lv_vg_lite_path_t * path, const lv_area_t * area)
169 {
170     LV_ASSERT_NULL(path);
171     LV_ASSERT_NULL(area);
172     lv_vg_lite_path_set_bounding_box(path, area->x1, area->y1, area->x2 + 1, area->y2 + 1);
173 }
174 
lv_vg_lite_path_get_bounding_box(lv_vg_lite_path_t * path,float * min_x,float * min_y,float * max_x,float * max_y)175 void lv_vg_lite_path_get_bounding_box(lv_vg_lite_path_t * path,
176                                       float * min_x, float * min_y,
177                                       float * max_x, float * max_y)
178 {
179     LV_ASSERT_NULL(path);
180     if(min_x) *min_x = path->base.bounding_box[0];
181     if(min_y) *min_y = path->base.bounding_box[1];
182     if(max_x) *max_x = path->base.bounding_box[2];
183     if(max_y) *max_y = path->base.bounding_box[3];
184 }
185 
path_bounds_iter_cb(void * user_data,uint8_t op_code,const float * data,uint32_t len)186 static void path_bounds_iter_cb(void * user_data, uint8_t op_code, const float * data, uint32_t len)
187 {
188     LV_UNUSED(op_code);
189 
190     if(len == 0) {
191         return;
192     }
193 
194     typedef struct {
195         float x;
196         float y;
197     } point_t;
198 
199     const int pt_len = sizeof(point_t) / sizeof(float);
200 
201     LV_ASSERT(len % pt_len == 0);
202 
203     const point_t * pt = (point_t *)data;
204     len /= pt_len;
205 
206     lv_vg_lite_path_bounds_t * bounds = user_data;
207 
208     for(uint32_t i = 0; i < len; i++) {
209         if(pt[i].x < bounds->min_x) bounds->min_x = pt[i].x;
210         if(pt[i].y < bounds->min_y) bounds->min_y = pt[i].y;
211         if(pt[i].x > bounds->max_x) bounds->max_x = pt[i].x;
212         if(pt[i].y > bounds->max_y) bounds->max_y = pt[i].y;
213     }
214 }
215 
lv_vg_lite_path_update_bounding_box(lv_vg_lite_path_t * path)216 bool lv_vg_lite_path_update_bounding_box(lv_vg_lite_path_t * path)
217 {
218     LV_ASSERT_NULL(path);
219 
220     if(!path->format_len) {
221         return false;
222     }
223 
224     LV_PROFILER_DRAW_BEGIN;
225 
226     lv_vg_lite_path_bounds_t bounds;
227 
228     /* init bounds */
229     bounds.min_x = FLT_MAX;
230     bounds.min_y = FLT_MAX;
231     bounds.max_x = FLT_MIN;
232     bounds.max_y = FLT_MIN;
233 
234     /* calc bounds */
235     lv_vg_lite_path_for_each_data(lv_vg_lite_path_get_path(path), path_bounds_iter_cb, &bounds);
236 
237     /* set bounds */
238     lv_vg_lite_path_set_bounding_box(path, bounds.min_x, bounds.min_y, bounds.max_x, bounds.max_y);
239 
240     LV_PROFILER_DRAW_END;
241 
242     return true;
243 }
244 
lv_vg_lite_path_set_transform(lv_vg_lite_path_t * path,const vg_lite_matrix_t * matrix)245 void lv_vg_lite_path_set_transform(lv_vg_lite_path_t * path, const vg_lite_matrix_t * matrix)
246 {
247     LV_ASSERT_NULL(path);
248     if(matrix) {
249         path->matrix = *matrix;
250     }
251 
252     path->has_transform = matrix ? true : false;
253 }
254 
lv_vg_lite_path_set_quality(lv_vg_lite_path_t * path,vg_lite_quality_t quality)255 void lv_vg_lite_path_set_quality(lv_vg_lite_path_t * path, vg_lite_quality_t quality)
256 {
257     LV_ASSERT_NULL(path);
258     path->base.quality = quality;
259 }
260 
lv_vg_lite_path_reserve_space(lv_vg_lite_path_t * path,size_t len)261 void lv_vg_lite_path_reserve_space(lv_vg_lite_path_t * path, size_t len)
262 {
263     bool need_reallocated = false;
264 
265     /*Calculate new mem size until match the contidion*/
266     while(path->base.path_length + len > path->mem_size) {
267         if(path->mem_size == 0) {
268             path->mem_size = LV_MAX(len, PATH_MEM_SIZE_MIN);
269         }
270         else {
271             /* Increase memory size by 1.5 times */
272             path->mem_size = path->mem_size * 3 / 2;
273         }
274         need_reallocated = true;
275     }
276 
277     if(!need_reallocated) {
278         return;
279     }
280 
281     path->base.path = lv_realloc(path->base.path, path->mem_size);
282     LV_ASSERT_MALLOC(path->base.path);
283 }
284 
lv_vg_lite_path_append_data(lv_vg_lite_path_t * path,const void * data,size_t len)285 static inline void lv_vg_lite_path_append_data(lv_vg_lite_path_t * path, const void * data, size_t len)
286 {
287     LV_ASSERT_NULL(path);
288     LV_ASSERT_NULL(data);
289     lv_vg_lite_path_reserve_space(path, len);
290     lv_memcpy(PATH_CURRENT_PTR(path), data, len);
291     PATH_LENGTH_INC(path, len);
292 }
293 
lv_vg_lite_path_append_op(lv_vg_lite_path_t * path,uint32_t op)294 static inline void lv_vg_lite_path_append_op(lv_vg_lite_path_t * path, uint32_t op)
295 {
296     void * ptr = PATH_CURRENT_PTR(path);
297     switch(path->base.format) {
298         case VG_LITE_FP32:
299         case VG_LITE_S32:
300             LV_VG_LITE_PATH_SET_OP_CODE(ptr, uint32_t, op);
301             PATH_LENGTH_INC(path, sizeof(uint32_t));
302             break;
303         case VG_LITE_S16:
304             LV_VG_LITE_PATH_SET_OP_CODE(ptr, uint16_t, op);
305             PATH_LENGTH_INC(path, sizeof(uint16_t));
306             break;
307         case VG_LITE_S8:
308             LV_VG_LITE_PATH_SET_OP_CODE(ptr, uint8_t, op);
309             PATH_LENGTH_INC(path, sizeof(uint8_t));
310             break;
311         default:
312             LV_ASSERT_FORMAT_MSG(false, "Invalid format: %d", path->base.format);
313             break;
314     }
315 }
316 
lv_vg_lite_path_append_point(lv_vg_lite_path_t * path,float x,float y)317 static inline void lv_vg_lite_path_append_point(lv_vg_lite_path_t * path, float x, float y)
318 {
319     if(path->has_transform) {
320         LV_VG_LITE_ASSERT_MATRIX(&path->matrix);
321         /* transform point */
322         float ori_x = x;
323         float ori_y = y;
324         x = ori_x * path->matrix.m[0][0] + ori_y * path->matrix.m[0][1] + path->matrix.m[0][2];
325         y = ori_x * path->matrix.m[1][0] + ori_y * path->matrix.m[1][1] + path->matrix.m[1][2];
326     }
327 
328 #define PATH_APPEND_POINT_DATA(X, Y, TYPE)       \
329     do {                                         \
330         TYPE * data = ptr;                       \
331         *data++ = (TYPE)(X);                     \
332         *data++ = (TYPE)(Y);                     \
333         PATH_LENGTH_INC(path, sizeof(TYPE) * 2); \
334     } while(0)
335 
336     void * ptr = PATH_CURRENT_PTR(path);
337     switch(path->base.format) {
338         case VG_LITE_FP32:
339             PATH_APPEND_POINT_DATA(x, y, float);
340             break;
341         case VG_LITE_S32:
342             PATH_APPEND_POINT_DATA(x, y, int32_t);
343             break;
344         case VG_LITE_S16:
345             PATH_APPEND_POINT_DATA(x, y, int16_t);
346             break;
347         case VG_LITE_S8:
348             PATH_APPEND_POINT_DATA(x, y, int8_t);
349             break;
350         default:
351             LV_ASSERT_FORMAT_MSG(false, "Invalid format: %d", path->base.format);
352             break;
353     }
354 }
355 
lv_vg_lite_path_move_to(lv_vg_lite_path_t * path,float x,float y)356 void lv_vg_lite_path_move_to(lv_vg_lite_path_t * path,
357                              float x, float y)
358 {
359     LV_ASSERT_NULL(path);
360     lv_vg_lite_path_reserve_space(path, (1 + 2) * path->format_len);
361     lv_vg_lite_path_append_op(path, VLC_OP_MOVE);
362     lv_vg_lite_path_append_point(path, x, y);
363 }
364 
lv_vg_lite_path_line_to(lv_vg_lite_path_t * path,float x,float y)365 void lv_vg_lite_path_line_to(lv_vg_lite_path_t * path,
366                              float x, float y)
367 {
368     LV_ASSERT_NULL(path);
369     lv_vg_lite_path_reserve_space(path, (1 + 2) * path->format_len);
370     lv_vg_lite_path_append_op(path, VLC_OP_LINE);
371     lv_vg_lite_path_append_point(path, x, y);
372 }
373 
lv_vg_lite_path_quad_to(lv_vg_lite_path_t * path,float cx,float cy,float x,float y)374 void lv_vg_lite_path_quad_to(lv_vg_lite_path_t * path,
375                              float cx, float cy,
376                              float x, float y)
377 {
378     LV_ASSERT_NULL(path);
379     lv_vg_lite_path_reserve_space(path, (1 + 4) * path->format_len);
380     lv_vg_lite_path_append_op(path, VLC_OP_QUAD);
381     lv_vg_lite_path_append_point(path, cx, cy);
382     lv_vg_lite_path_append_point(path, x, y);
383 }
384 
lv_vg_lite_path_cubic_to(lv_vg_lite_path_t * path,float cx1,float cy1,float cx2,float cy2,float x,float y)385 void lv_vg_lite_path_cubic_to(lv_vg_lite_path_t * path,
386                               float cx1, float cy1,
387                               float cx2, float cy2,
388                               float x, float y)
389 {
390     LV_ASSERT_NULL(path);
391     lv_vg_lite_path_reserve_space(path, (1 + 6) * path->format_len);
392     lv_vg_lite_path_append_op(path, VLC_OP_CUBIC);
393     lv_vg_lite_path_append_point(path, cx1, cy1);
394     lv_vg_lite_path_append_point(path, cx2, cy2);
395     lv_vg_lite_path_append_point(path, x, y);
396 }
397 
lv_vg_lite_path_close(lv_vg_lite_path_t * path)398 void lv_vg_lite_path_close(lv_vg_lite_path_t * path)
399 {
400     LV_ASSERT_NULL(path);
401     lv_vg_lite_path_reserve_space(path, 1 * path->format_len);
402     lv_vg_lite_path_append_op(path, VLC_OP_CLOSE);
403 }
404 
lv_vg_lite_path_end(lv_vg_lite_path_t * path)405 void lv_vg_lite_path_end(lv_vg_lite_path_t * path)
406 {
407     LV_ASSERT_NULL(path);
408     lv_vg_lite_path_reserve_space(path, 1 * path->format_len);
409     lv_vg_lite_path_append_op(path, VLC_OP_END);
410     path->base.add_end = 1;
411 }
412 
lv_vg_lite_path_append_rect(lv_vg_lite_path_t * path,float x,float y,float w,float h,float r)413 void lv_vg_lite_path_append_rect(
414     lv_vg_lite_path_t * path,
415     float x, float y,
416     float w, float h,
417     float r)
418 {
419     LV_PROFILER_DRAW_BEGIN;
420     const float half_w = w / 2.0f;
421     const float half_h = h / 2.0f;
422 
423     /*clamping cornerRadius by minimum size*/
424     const float r_max = LV_MIN(half_w, half_h);
425     if(r > r_max)
426         r = r_max;
427 
428     /*rectangle*/
429     if(r <= 0) {
430         lv_vg_lite_path_move_to(path, x, y);
431         lv_vg_lite_path_line_to(path, x + w, y);
432         lv_vg_lite_path_line_to(path, x + w, y + h);
433         lv_vg_lite_path_line_to(path, x, y + h);
434         lv_vg_lite_path_close(path);
435         LV_PROFILER_DRAW_END;
436         return;
437     }
438 
439     /*circle*/
440     if(math_equal(r, half_w) && math_equal(r, half_h)) {
441         lv_vg_lite_path_append_circle(path, x + half_w, y + half_h, r, r);
442         LV_PROFILER_DRAW_END;
443         return;
444     }
445 
446     /* Get the control point offset for rounded cases */
447     const float offset = r * PATH_ARC_MAGIC;
448 
449     /* Rounded rectangle case */
450     /* Starting point */
451     lv_vg_lite_path_move_to(path, x + r, y);
452 
453     /* Top side */
454     lv_vg_lite_path_line_to(path, x + w - r, y);
455 
456     /* Top-right corner */
457     lv_vg_lite_path_cubic_to(path, x + w - r + offset, y, x + w, y + r - offset, x + w, y + r);
458 
459     /* Right side */
460     lv_vg_lite_path_line_to(path, x + w, y + h - r);
461 
462     /* Bottom-right corner*/
463     lv_vg_lite_path_cubic_to(path, x + w, y + h - r + offset, x + w - r + offset, y + h, x + w - r, y + h);
464 
465     /* Bottom side */
466     lv_vg_lite_path_line_to(path, x + r, y + h);
467 
468     /* Bottom-left corner */
469     lv_vg_lite_path_cubic_to(path, x + r - offset, y + h, x, y + h - r + offset, x, y + h - r);
470 
471     /* Left side*/
472     lv_vg_lite_path_line_to(path, x, y + r);
473 
474     /* Top-left corner */
475     lv_vg_lite_path_cubic_to(path, x, y + r - offset, x + r - offset, y, x + r, y);
476 
477     /* Ending point */
478     lv_vg_lite_path_close(path);
479     LV_PROFILER_DRAW_END;
480 }
481 
lv_vg_lite_path_append_circle(lv_vg_lite_path_t * path,float cx,float cy,float rx,float ry)482 void lv_vg_lite_path_append_circle(
483     lv_vg_lite_path_t * path,
484     float cx, float cy,
485     float rx, float ry)
486 {
487     LV_PROFILER_DRAW_BEGIN;
488     /* https://learn.microsoft.com/zh-cn/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers */
489     float rx_kappa = rx * PATH_KAPPA;
490     float ry_kappa = ry * PATH_KAPPA;
491 
492     lv_vg_lite_path_move_to(path, cx, cy - ry);
493     lv_vg_lite_path_cubic_to(path, cx + rx_kappa, cy - ry, cx + rx, cy - ry_kappa, cx + rx, cy);
494     lv_vg_lite_path_cubic_to(path, cx + rx, cy + ry_kappa, cx + rx_kappa, cy + ry, cx, cy + ry);
495     lv_vg_lite_path_cubic_to(path, cx - rx_kappa, cy + ry, cx - rx, cy + ry_kappa, cx - rx, cy);
496     lv_vg_lite_path_cubic_to(path, cx - rx, cy - ry_kappa, cx - rx_kappa, cy - ry, cx, cy - ry);
497     lv_vg_lite_path_close(path);
498     LV_PROFILER_DRAW_END;
499 }
500 
lv_vg_lite_path_append_arc_right_angle(lv_vg_lite_path_t * path,float start_x,float start_y,float center_x,float center_y,float end_x,float end_y)501 void lv_vg_lite_path_append_arc_right_angle(lv_vg_lite_path_t * path,
502                                             float start_x, float start_y,
503                                             float center_x, float center_y,
504                                             float end_x, float end_y)
505 {
506     LV_PROFILER_DRAW_BEGIN;
507     float dx1 = center_x - start_x;
508     float dy1 = center_y - start_y;
509     float dx2 = end_x - center_x;
510     float dy2 = end_y - center_y;
511 
512     float c = SIGN(dx1 * dy2 - dx2 * dy1) * PATH_ARC_MAGIC;
513 
514     lv_vg_lite_path_cubic_to(path,
515                              start_x - c * dy1, start_y + c * dx1,
516                              end_x - c * dy2, end_y + c * dx2,
517                              end_x, end_y);
518     LV_PROFILER_DRAW_END;
519 }
520 
lv_vg_lite_path_append_arc(lv_vg_lite_path_t * path,float cx,float cy,float radius,float start_angle,float sweep,bool pie)521 void lv_vg_lite_path_append_arc(lv_vg_lite_path_t * path,
522                                 float cx, float cy,
523                                 float radius,
524                                 float start_angle,
525                                 float sweep,
526                                 bool pie)
527 {
528     LV_PROFILER_DRAW_BEGIN;
529     /* just circle */
530     if(sweep >= 360.0f || sweep <= -360.0f) {
531         lv_vg_lite_path_append_circle(path, cx, cy, radius, radius);
532         LV_PROFILER_DRAW_END;
533         return;
534     }
535 
536     start_angle = MATH_RADIANS(start_angle);
537     sweep = MATH_RADIANS(sweep);
538 
539     int n_curves = (int)ceil(MATH_FABSF(sweep / MATH_HALF_PI));
540     float sweep_sign = sweep < 0 ? -1.f : 1.f;
541     float fract = fmodf(sweep, MATH_HALF_PI);
542     fract = (math_zero(fract)) ? MATH_HALF_PI * sweep_sign : fract;
543 
544     /* Start from here */
545     float start_x = radius * MATH_COSF(start_angle);
546     float start_y = radius * MATH_SINF(start_angle);
547 
548     if(pie) {
549         lv_vg_lite_path_move_to(path, cx, cy);
550         lv_vg_lite_path_line_to(path, start_x + cx, start_y + cy);
551     }
552 
553     for(int i = 0; i < n_curves; ++i) {
554         float end_angle = start_angle + ((i != n_curves - 1) ? MATH_HALF_PI * sweep_sign : fract);
555         float end_x = radius * MATH_COSF(end_angle);
556         float end_y = radius * MATH_SINF(end_angle);
557 
558         /* variables needed to calculate bezier control points */
559 
560         /** get bezier control points using article:
561          * (http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
562          */
563         float ax = start_x;
564         float ay = start_y;
565         float bx = end_x;
566         float by = end_y;
567         float q1 = ax * ax + ay * ay;
568         float q2 = ax * bx + ay * by + q1;
569         float k2 = (4.0f / 3.0f) * ((MATH_SQRTF(2 * q1 * q2) - q2) / (ax * by - ay * bx));
570 
571         /* Next start point is the current end point */
572         start_x = end_x;
573         start_y = end_y;
574 
575         end_x += cx;
576         end_y += cy;
577 
578         float ctrl1_x = ax - k2 * ay + cx;
579         float ctrl1_y = ay + k2 * ax + cy;
580         float ctrl2_x = bx + k2 * by + cx;
581         float ctrl2_y = by - k2 * bx + cy;
582 
583         lv_vg_lite_path_cubic_to(path, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, end_x, end_y);
584         start_angle = end_angle;
585     }
586 
587     if(pie) {
588         lv_vg_lite_path_close(path);
589     }
590 
591     LV_PROFILER_DRAW_END;
592 }
593 
lv_vg_lite_vlc_op_arg_len(uint8_t vlc_op)594 uint8_t lv_vg_lite_vlc_op_arg_len(uint8_t vlc_op)
595 {
596     switch(vlc_op) {
597             VLC_OP_ARG_LEN(END, 0);
598             VLC_OP_ARG_LEN(CLOSE, 0);
599             VLC_OP_ARG_LEN(MOVE, 2);
600             VLC_OP_ARG_LEN(MOVE_REL, 2);
601             VLC_OP_ARG_LEN(LINE, 2);
602             VLC_OP_ARG_LEN(LINE_REL, 2);
603             VLC_OP_ARG_LEN(QUAD, 4);
604             VLC_OP_ARG_LEN(QUAD_REL, 4);
605             VLC_OP_ARG_LEN(CUBIC, 6);
606             VLC_OP_ARG_LEN(CUBIC_REL, 6);
607             VLC_OP_ARG_LEN(SCCWARC, 5);
608             VLC_OP_ARG_LEN(SCCWARC_REL, 5);
609             VLC_OP_ARG_LEN(SCWARC, 5);
610             VLC_OP_ARG_LEN(SCWARC_REL, 5);
611             VLC_OP_ARG_LEN(LCCWARC, 5);
612             VLC_OP_ARG_LEN(LCCWARC_REL, 5);
613             VLC_OP_ARG_LEN(LCWARC, 5);
614             VLC_OP_ARG_LEN(LCWARC_REL, 5);
615         default:
616             LV_ASSERT_FORMAT_MSG(false, "Invalid op code: %d", vlc_op);
617             break;
618     }
619 
620     return 0;
621 }
622 
lv_vg_lite_path_format_len(vg_lite_format_t format)623 uint8_t lv_vg_lite_path_format_len(vg_lite_format_t format)
624 {
625     switch(format) {
626         case VG_LITE_FP32:
627             return sizeof(float);
628         case VG_LITE_S32:
629             return sizeof(int32_t);
630         case VG_LITE_S16:
631             return sizeof(int16_t);
632         case VG_LITE_S8:
633             return sizeof(int8_t);
634         default:
635             LV_ASSERT_FORMAT_MSG(false, "Invalid format: %d", format);
636             break;
637     }
638 
639     return 0;
640 }
641 
lv_vg_lite_path_for_each_data(const vg_lite_path_t * path,lv_vg_lite_path_iter_cb_t cb,void * user_data)642 void lv_vg_lite_path_for_each_data(const vg_lite_path_t * path, lv_vg_lite_path_iter_cb_t cb, void * user_data)
643 {
644     LV_ASSERT_NULL(path);
645     LV_ASSERT_NULL(cb);
646 
647     uint8_t fmt_len = lv_vg_lite_path_format_len(path->format);
648     uint8_t * cur = path->path;
649     uint8_t * end = cur + path->path_length;
650     float tmp_data[8];
651 
652     while(cur < end) {
653         /* get op code */
654         uint8_t op_code = LV_VG_LITE_PATH_GET_OP_CODE(cur);
655 
656         /* get arguments length */
657         uint8_t arg_len = lv_vg_lite_vlc_op_arg_len(op_code);
658 
659         /* skip op code */
660         cur += fmt_len;
661 
662         /* print arguments */
663         for(uint8_t i = 0; i < arg_len; i++) {
664             switch(path->format) {
665                 case VG_LITE_S8:
666                     tmp_data[i] = *((int8_t *)cur);
667                     break;
668                 case VG_LITE_S16:
669                     tmp_data[i] = *((int16_t *)cur);
670                     break;
671                 case VG_LITE_S32:
672                     tmp_data[i] = *((int32_t *)cur);
673                     break;
674                 case VG_LITE_FP32:
675                     tmp_data[i] = *((float *)cur);
676                     break;
677                 default:
678                     LV_ASSERT_FORMAT_MSG(false, "Invalid format: %d", path->format);
679                     break;
680             }
681 
682             cur += fmt_len;
683         }
684 
685         cb(user_data, op_code, tmp_data, arg_len);
686     }
687 }
688 
lv_vg_lite_path_append_path(lv_vg_lite_path_t * dest,const lv_vg_lite_path_t * src)689 void lv_vg_lite_path_append_path(lv_vg_lite_path_t * dest, const lv_vg_lite_path_t * src)
690 {
691     LV_ASSERT_NULL(dest);
692     LV_ASSERT_NULL(src);
693 
694     LV_ASSERT(dest->base.format == dest->base.format);
695     lv_vg_lite_path_append_data(dest, src->base.path, src->base.path_length);
696 }
697 
698 /**********************
699  *   STATIC FUNCTIONS
700  **********************/
701 
702 #endif /*LV_USE_DRAW_VG_LITE*/
703