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