NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
path.h
Go to the documentation of this file.
1#pragma once
2
3#include "private.h"
4#include "utils.h"
5
6#include <assert.h>
7
8namespace novasvg {
9namespace render {
10
12{
14 it->size = path->elements.size;
15 it->index = 0;
16}
17
19{
20 return it->index < it->size;
21}
22
24{
25 const path_element_t* elements = it->elements + it->index;
26 switch(elements[0].header.command) {
30 points[0] = elements[1].point;
31 break;
33 points[0] = elements[1].point;
34 points[1] = elements[2].point;
35 points[2] = elements[3].point;
36 break;
37 }
38
39 it->index += elements[0].header.length;
40 return elements[0].header.command;
41}
42
44{
45 path_t* path = static_cast<path_t*>(malloc(sizeof(path_t)));
47 path->num_points = 0;
48 path->num_contours = 0;
49 path->num_curves = 0;
52 return path;
53}
54
60
68
73
75{
76 if(elements)
77 *elements = path->elements.data;
78 return path->elements.size;
79}
80
82{
83 const int length = npoints + 1;
86 elements->header.command = command;
87 elements->header.length = length;
88 path->elements.size += length;
89 path->num_points += npoints;
90 return elements + 1;
91}
92
93NOVASVG_INLINE void path_move_to(path_t* path, float x, float y)
94{
96 elements[0].point = NOVASVG_MAKE_POINT(x, y);
98 path->num_contours += 1;
99}
100
101NOVASVG_INLINE void path_line_to(path_t* path, float x, float y)
102{
103 if(path->elements.size == 0)
104 path_move_to(path, 0, 0);
106 elements[0].point = NOVASVG_MAKE_POINT(x, y);
107}
108
109NOVASVG_INLINE void path_quad_to(path_t* path, float x1, float y1, float x2, float y2)
110{
111 float current_x, current_y;
112 path_get_current_point(path, &current_x, &current_y);
113 float cp1x = 2.f / 3.f * x1 + 1.f / 3.f * current_x;
114 float cp1y = 2.f / 3.f * y1 + 1.f / 3.f * current_y;
115 float cp2x = 2.f / 3.f * x1 + 1.f / 3.f * x2;
116 float cp2y = 2.f / 3.f * y1 + 1.f / 3.f * y2;
117 path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, x2, y2);
118}
119
120NOVASVG_INLINE void path_cubic_to(path_t* path, float x1, float y1, float x2, float y2, float x3, float y3)
121{
122 if(path->elements.size == 0)
123 path_move_to(path, 0, 0);
125 elements[0].point = NOVASVG_MAKE_POINT(x1, y1);
126 elements[1].point = NOVASVG_MAKE_POINT(x2, y2);
127 elements[2].point = NOVASVG_MAKE_POINT(x3, y3);
128 path->num_curves += 1;
129}
130
131NOVASVG_INLINE void path_arc_to(path_t* path, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
132{
133 float current_x, current_y;
134 path_get_current_point(path, &current_x, &current_y);
135 if(rx == 0.f || ry == 0.f || (current_x == x && current_y == y)) {
136 path_line_to(path, x, y);
137 return;
138 }
139
140 if(rx < 0.f) rx = -rx;
141 if(ry < 0.f) ry = -ry;
142
143 float dx = (current_x - x) * 0.5f;
144 float dy = (current_y - y) * 0.5f;
145
147 matrix_init_rotate(&matrix, -angle);
148 matrix_map(&matrix, dx, dy, &dx, &dy);
149
150 float rxrx = rx * rx;
151 float ryry = ry * ry;
152 float dxdx = dx * dx;
153 float dydy = dy * dy;
154 float radius = dxdx / rxrx + dydy / ryry;
155 if(radius > 1.f) {
156 rx *= sqrtf(radius);
157 ry *= sqrtf(radius);
158 }
159
160 matrix_init_scale(&matrix, 1.f / rx, 1.f / ry);
161 matrix_rotate(&matrix, -angle);
162
163 float x1, y1;
164 float x2, y2;
165 matrix_map(&matrix, current_x, current_y, &x1, &y1);
166 matrix_map(&matrix, x, y, &x2, &y2);
167
168 float dx1 = x2 - x1;
169 float dy1 = y2 - y1;
170 float d = dx1 * dx1 + dy1 * dy1;
171 float scale_sq = 1.f / d - 0.25f;
172 if(scale_sq < 0.f) scale_sq = 0.f;
173 float scale = sqrtf(scale_sq);
174 if(sweep_flag == large_arc_flag)
175 scale = -scale;
176 dx1 *= scale;
177 dy1 *= scale;
178
179 float cx1 = 0.5f * (x1 + x2) - dy1;
180 float cy1 = 0.5f * (y1 + y2) + dx1;
181
182 float th1 = atan2f(y1 - cy1, x1 - cx1);
183 float th2 = atan2f(y2 - cy1, x2 - cx1);
184 float th_arc = th2 - th1;
185 if(th_arc < 0.f && sweep_flag)
186 th_arc += NOVASVG_TWO_PI;
187 else if(th_arc > 0.f && !sweep_flag)
188 th_arc -= NOVASVG_TWO_PI;
189 matrix_init_rotate(&matrix, angle);
190 matrix_scale(&matrix, rx, ry);
191 int segments = (int)(ceilf(fabsf(th_arc / (NOVASVG_HALF_PI + 0.001f))));
192 for(int i = 0; i < segments; i++) {
193 float th_start = th1 + i * th_arc / segments;
194 float th_end = th1 + (i + 1) * th_arc / segments;
195 float t = (8.f / 6.f) * tanf(0.25f * (th_end - th_start));
196
197 float x3 = cosf(th_end) + cx1;
198 float y3 = sinf(th_end) + cy1;
199
200 float cp2x = x3 + t * sinf(th_end);
201 float cp2y = y3 - t * cosf(th_end);
202
203 float cp1x = cosf(th_start) - t * sinf(th_start);
204 float cp1y = sinf(th_start) + t * cosf(th_start);
205
206 cp1x += cx1;
207 cp1y += cy1;
208
209 matrix_map(&matrix, cp1x, cp1y, &cp1x, &cp1y);
210 matrix_map(&matrix, cp2x, cp2y, &cp2x, &cp2y);
211 matrix_map(&matrix, x3, y3, &x3, &y3);
212
213 path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, x3, y3);
214 }
215}
216
218{
219 if(path->elements.size == 0)
220 return;
222 elements[0].point = path->start_point;
223}
224
225NOVASVG_INLINE void path_get_current_point(const path_t* path, float* x, float* y)
226{
227 float xx = 0.f;
228 float yy = 0.f;
229 if(path->num_points > 0) {
232 }
233
234 if(x) *x = xx;
235 if(y) *y = yy;
236}
237
239{
241}
242
251
252NOVASVG_INLINE void path_add_rect(path_t* path, float x, float y, float w, float h)
253{
254 path_reserve(path, 6 * 2);
255 path_move_to(path, x, y);
256 path_line_to(path, x + w, y);
257 path_line_to(path, x + w, y + h);
258 path_line_to(path, x, y + h);
259 path_line_to(path, x, y);
261}
262
263NOVASVG_INLINE void path_add_round_rect(path_t* path, float x, float y, float w, float h, float rx, float ry)
264{
265 rx = NOVASVG_MIN(rx, w * 0.5f);
266 ry = NOVASVG_MIN(ry, h * 0.5f);
267 if(rx == 0.f && ry == 0.f) {
268 path_add_rect(path, x, y, w, h);
269 return;
270 }
271
272 float right = x + w;
273 float bottom = y + h;
274
275 float cpx = rx * NOVASVG_KAPPA;
276 float cpy = ry * NOVASVG_KAPPA;
277
278 path_reserve(path, 6 * 2 + 4 * 4);
279 path_move_to(path, x, y+ry);
280 path_cubic_to(path, x, y+ry-cpy, x+rx-cpx, y, x+rx, y);
281 path_line_to(path, right-rx, y);
282 path_cubic_to(path, right-rx+cpx, y, right, y+ry-cpy, right, y+ry);
283 path_line_to(path, right, bottom-ry);
284 path_cubic_to(path, right, bottom-ry+cpy, right-rx+cpx, bottom, right-rx, bottom);
285 path_line_to(path, x+rx, bottom);
286 path_cubic_to(path, x+rx-cpx, bottom, x, bottom-ry+cpy, x, bottom-ry);
287 path_line_to(path, x, y+ry);
289}
290
291NOVASVG_INLINE void path_add_ellipse(path_t* path, float cx, float cy, float rx, float ry)
292{
293 float left = cx - rx;
294 float top = cy - ry;
295 float right = cx + rx;
296 float bottom = cy + ry;
297
298 float cpx = rx * NOVASVG_KAPPA;
299 float cpy = ry * NOVASVG_KAPPA;
300
301 path_reserve(path, 2 * 2 + 4 * 4);
302 path_move_to(path, cx, top);
303 path_cubic_to(path, cx+cpx, top, right, cy-cpy, right, cy);
304 path_cubic_to(path, right, cy+cpy, cx+cpx, bottom, cx, bottom);
305 path_cubic_to(path, cx-cpx, bottom, left, cy+cpy, left, cy);
306 path_cubic_to(path, left, cy-cpy, cx-cpx, top, cx, top);
308}
309
310NOVASVG_INLINE void path_add_circle(path_t* path, float cx, float cy, float r)
311{
312 path_add_ellipse(path, cx, cy, r, r);
313}
314
315NOVASVG_INLINE void path_add_arc(path_t* path, float cx, float cy, float r, float a0, float a1, bool ccw)
316{
317 float da = a1 - a0;
318 if(fabsf(da) > NOVASVG_TWO_PI) {
319 da = NOVASVG_TWO_PI;
320 } else if(da != 0.f && ccw != (da < 0.f)) {
321 da += NOVASVG_TWO_PI * (ccw ? -1 : 1);
322 }
323
324 int seg_n = (int)(ceilf(fabsf(da) / NOVASVG_HALF_PI));
325 if(seg_n == 0)
326 return;
327 float a = a0;
328 float ax = cx + cosf(a) * r;
329 float ay = cy + sinf(a) * r;
330
331 float seg_a = da / seg_n;
332 float d = (seg_a / NOVASVG_HALF_PI) * NOVASVG_KAPPA * r;
333 float dx = -sinf(a) * d;
334 float dy = cosf(a) * d;
335
336 path_reserve(path, 2 + 4 * seg_n);
337 if(path->elements.size == 0) {
338 path_move_to(path, ax, ay);
339 } else {
340 path_line_to(path, ax, ay);
341 }
342
343 for(int i = 0; i < seg_n; i++) {
344 float cp1x = ax + dx;
345 float cp1y = ay + dy;
346
347 a += seg_a;
348 ax = cx + cosf(a) * r;
349 ay = cy + sinf(a) * r;
350
351 dx = -sinf(a) * d;
352 dy = cosf(a) * d;
353
354 float cp2x = ax - dx;
355 float cp2y = ay - dy;
356
357 path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, ax, ay);
358 }
359}
360
362{
363 path_element_t* elements = path->elements.data;
364 for(int i = 0; i < path->elements.size; i += elements[i].header.length) {
365 switch(elements[i].header.command) {
369 matrix_map_point(matrix, &elements[i + 1].point, &elements[i + 1].point);
370 break;
372 matrix_map_point(matrix, &elements[i + 1].point, &elements[i + 1].point);
373 matrix_map_point(matrix, &elements[i + 2].point, &elements[i + 2].point);
374 matrix_map_point(matrix, &elements[i + 3].point, &elements[i + 3].point);
375 break;
376 }
377 }
378}
379
381{
382 if(matrix == NULL) {
384 path->start_point = source->start_point;
385 path->num_points += source->num_points;
386 path->num_contours += source->num_contours;
387 path->num_curves += source->num_curves;
388 return;
389 }
390
392 path_iterator_init(&it, source);
393
394 point_t points[3];
396 while(path_iterator_has_next(&it)) {
397 switch(path_iterator_next(&it, points)) {
399 matrix_map_points(matrix, points, points, 1);
400 path_move_to(path, points[0].x, points[0].y);
401 break;
403 matrix_map_points(matrix, points, points, 1);
404 path_line_to(path, points[0].x, points[0].y);
405 break;
407 matrix_map_points(matrix, points, points, 3);
408 path_cubic_to(path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
409 break;
412 break;
413 }
414 }
415}
416
417NOVASVG_INLINE void path_traverse(const path_t* path, path_traverse_func_t traverse_func, void* closure)
418{
421
422 point_t points[3];
423 while(path_iterator_has_next(&it)) {
424 switch(path_iterator_next(&it, points)) {
426 traverse_func(closure, NOVASVG_PATH_COMMAND_MOVE_TO, points, 1);
427 break;
429 traverse_func(closure, NOVASVG_PATH_COMMAND_LINE_TO, points, 1);
430 break;
432 traverse_func(closure, NOVASVG_PATH_COMMAND_CUBIC_TO, points, 3);
433 break;
435 traverse_func(closure, NOVASVG_PATH_COMMAND_CLOSE, points, 1);
436 break;
437 }
438 }
439}
440
441typedef struct {
442 float x1; float y1;
443 float x2; float y2;
444 float x3; float y3;
445 float x4; float y4;
446} bezier_t;
447
448NOVASVG_INLINE void split_bezier(const bezier_t* b, bezier_t* first, bezier_t* second)
449{
450 float c = (b->x2 + b->x3) * 0.5f;
451 first->x2 = (b->x1 + b->x2) * 0.5f;
452 second->x3 = (b->x3 + b->x4) * 0.5f;
453 first->x1 = b->x1;
454 second->x4 = b->x4;
455 first->x3 = (first->x2 + c) * 0.5f;
456 second->x2 = (second->x3 + c) * 0.5f;
457 first->x4 = second->x1 = (first->x3 + second->x2) * 0.5f;
458
459 c = (b->y2 + b->y3) * 0.5f;
460 first->y2 = (b->y1 + b->y2) * 0.5f;
461 second->y3 = (b->y3 + b->y4) * 0.5f;
462 first->y1 = b->y1;
463 second->y4 = b->y4;
464 first->y3 = (first->y2 + c) * 0.5f;
465 second->y2 = (second->y3 + c) * 0.5f;
466 first->y4 = second->y1 = (first->y3 + second->y2) * 0.5f;
467}
468
469NOVASVG_INLINE void path_traverse_flatten(const path_t* path, path_traverse_func_t traverse_func, void* closure)
470{
471 if(path->num_curves == 0) {
472 path_traverse(path, traverse_func, closure);
473 return;
474 }
475
476 const float threshold = 0.25f;
477
480
481 bezier_t beziers[32];
482 point_t points[3];
483 point_t current_point = {0, 0};
484 while(path_iterator_has_next(&it)) {
485 path_command_t command = path_iterator_next(&it, points);
486 switch(command) {
490 traverse_func(closure, command, points, 1);
491 current_point = points[0];
492 break;
494 beziers[0].x1 = current_point.x;
495 beziers[0].y1 = current_point.y;
496 beziers[0].x2 = points[0].x;
497 beziers[0].y2 = points[0].y;
498 beziers[0].x3 = points[1].x;
499 beziers[0].y3 = points[1].y;
500 beziers[0].x4 = points[2].x;
501 beziers[0].y4 = points[2].y;
502 bezier_t* b = beziers;
503 while(b >= beziers) {
504 float y4y1 = b->y4 - b->y1;
505 float x4x1 = b->x4 - b->x1;
506 float l = fabsf(x4x1) + fabsf(y4y1);
507 float d;
508 if(l > 1.f) {
509 d = fabsf((x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2)) + fabsf((x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3));
510 } else {
511 d = fabsf(b->x1 - b->x2) + fabsf(b->y1 - b->y2) + fabsf(b->x1 - b->x3) + fabsf(b->y1 - b->y3);
512 l = 1.f;
513 }
514
515 if(d < threshold*l || b == beziers + 31) {
516 point_t p = { b->x4, b->y4 };
517 traverse_func(closure, NOVASVG_PATH_COMMAND_LINE_TO, &p, 1);
518 --b;
519 } else {
520 split_bezier(b, b + 1, b);
521 ++b;
522 }
523 }
524
525 current_point = points[2];
526 break;
527 }
528 }
529}
530
531typedef struct {
532 const float* dashes; int ndashes;
533 float start_phase; float phase;
534 int start_index; int index;
535 bool start_toggle; bool toggle;
538 void* closure;
539} dasher_t;
540
541NOVASVG_INLINE void dash_traverse_func(void* closure, path_command_t command, const point_t* points, int npoints)
542{
543 dasher_t* dasher = (dasher_t*)(closure);
544 if(command == NOVASVG_PATH_COMMAND_MOVE_TO) {
545 if(dasher->start_toggle)
546 dasher->traverse_func(dasher->closure, NOVASVG_PATH_COMMAND_MOVE_TO, points, npoints);
547 dasher->current_point = points[0];
548 dasher->phase = dasher->start_phase;
549 dasher->index = dasher->start_index;
550 dasher->toggle = dasher->start_toggle;
551 return;
552 }
553
554 assert(command == NOVASVG_PATH_COMMAND_LINE_TO || command == NOVASVG_PATH_COMMAND_CLOSE);
555 point_t p0 = dasher->current_point;
556 point_t p1 = points[0];
557 float dx = p1.x - p0.x;
558 float dy = p1.y - p0.y;
559 float dist0 = sqrtf(dx*dx + dy*dy);
560 float dist1 = 0.f;
561 while(dist0 - dist1 > dasher->dashes[dasher->index % dasher->ndashes] - dasher->phase) {
562 dist1 += dasher->dashes[dasher->index % dasher->ndashes] - dasher->phase;
563 float a = dist1 / dist0;
564 point_t p = { p0.x + a * dx, p0.y + a * dy };
565 if(dasher->toggle) {
566 dasher->traverse_func(dasher->closure, NOVASVG_PATH_COMMAND_LINE_TO, &p, 1);
567 } else {
568 dasher->traverse_func(dasher->closure, NOVASVG_PATH_COMMAND_MOVE_TO, &p, 1);
569 }
570
571 dasher->phase = 0.f;
572 dasher->toggle = !dasher->toggle;
573 dasher->index++;
574 }
575
576 if(dasher->toggle) {
577 dasher->traverse_func(dasher->closure, NOVASVG_PATH_COMMAND_LINE_TO, &p1, 1);
578 }
579
580 dasher->phase += dist0 - dist1;
581 dasher->current_point = p1;
582}
583
584NOVASVG_INLINE void path_traverse_dashed(const path_t* path, float offset, const float* dashes, int ndashes, path_traverse_func_t traverse_func, void* closure)
585{
586 float dash_sum = 0.f;
587 for(int i = 0; i < ndashes; ++i)
588 dash_sum += dashes[i];
589 if(ndashes % 2 == 1)
590 dash_sum *= 2.f;
591 if(dash_sum <= 0.f) {
592 path_traverse(path, traverse_func, closure);
593 return;
594 }
595
596 dasher_t dasher;
597 dasher.dashes = dashes;
598 dasher.ndashes = ndashes;
599 dasher.start_phase = fmodf(offset, dash_sum);
600 if(dasher.start_phase < 0.f)
601 dasher.start_phase += dash_sum;
602 dasher.start_index = 0;
603 dasher.start_toggle = true;
604 while(dasher.start_phase > 0.f && dasher.start_phase >= dasher.dashes[dasher.start_index % dasher.ndashes]) {
605 dasher.start_phase -= dashes[dasher.start_index % dasher.ndashes];
606 dasher.start_toggle = !dasher.start_toggle;
607 dasher.start_index++;
608 }
609
610 dasher.phase = dasher.start_phase;
611 dasher.index = dasher.start_index;
612 dasher.toggle = dasher.start_toggle;
614 dasher.traverse_func = traverse_func;
615 dasher.closure = closure;
617}
618
620{
621 path_t* clone = path_create();
623 clone->start_point = path->start_point;
624 clone->num_points = path->num_points;
626 clone->num_curves = path->num_curves;
627 return clone;
628}
629
630NOVASVG_INLINE void clone_traverse_func(void* closure, path_command_t command, const point_t* points, int npoints)
631{
632 path_t* path = (path_t*)(closure);
633 switch(command) {
635 path_move_to(path, points[0].x, points[0].y);
636 break;
638 path_line_to(path, points[0].x, points[0].y);
639 break;
641 path_cubic_to(path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
642 break;
645 break;
646 }
647}
648
650{
651 path_t* clone = path_create();
652 path_reserve(clone, path->elements.size + path->num_curves * 32);
654 return clone;
655}
656
657NOVASVG_INLINE path_t* path_clone_dashed(const path_t* path, float offset, const float* dashes, int ndashes)
658{
659 path_t* clone = path_create();
660 path_reserve(clone, path->elements.size + path->num_curves * 32);
661 path_traverse_dashed(path, offset, dashes, ndashes, clone_traverse_func, clone);
662 return clone;
663}
664
665typedef struct {
668 float length;
669 float x1;
670 float y1;
671 float x2;
672 float y2;
674
675NOVASVG_INLINE void extents_traverse_func(void* closure, path_command_t command, const point_t* points, int npoints)
676{
677 extents_calculator_t* calculator = (extents_calculator_t*)(closure);
678 if(calculator->is_first_point) {
679 assert(command == NOVASVG_PATH_COMMAND_MOVE_TO);
680 calculator->is_first_point = false;
681 calculator->current_point = points[0];
682 calculator->x1 = points[0].x;
683 calculator->y1 = points[0].y;
684 calculator->x2 = points[0].x;
685 calculator->y2 = points[0].y;
686 calculator->length = 0;
687 return;
688 }
689
690 for(int i = 0; i < npoints; ++i) {
691 calculator->x1 = NOVASVG_MIN(calculator->x1, points[i].x);
692 calculator->y1 = NOVASVG_MIN(calculator->y1, points[i].y);
693 calculator->x2 = NOVASVG_MAX(calculator->x2, points[i].x);
694 calculator->y2 = NOVASVG_MAX(calculator->y2, points[i].y);
695 if(command != NOVASVG_PATH_COMMAND_MOVE_TO)
696 calculator->length += hypotf(points[i].x - calculator->current_point.x, points[i].y - calculator->current_point.y);
697 calculator->current_point = points[i];
698 }
699}
700
701NOVASVG_INLINE float path_extents(const path_t* path, rect_t* extents, bool tight)
702{
703 extents_calculator_t calculator = {{0, 0}, true, 0, 0, 0, 0, 0};
704 if(tight) {
706 } else {
708 }
709
710 if(extents) {
711 extents->x = calculator.x1;
712 extents->y = calculator.y1;
713 extents->w = calculator.x2 - calculator.x1;
714 extents->h = calculator.y2 - calculator.y1;
715 }
716
717 return calculator.length;
718}
719
721{
722 return path_extents(path, NULL, true);
723}
724
725NOVASVG_INLINE bool parse_arc_flag(const char** begin, const char* end, bool* flag)
726{
727 if(skip_delim(begin, end, '0'))
728 *flag = 0;
729 else if(skip_delim(begin, end, '1'))
730 *flag = 1;
731 else
732 return false;
733 skip_ws_or_comma(begin, end, NULL);
734 return true;
735}
736
737NOVASVG_INLINE bool parse_path_coordinates(const char** begin, const char* end, float values[6], int offset, int count)
738{
739 for(int i = 0; i < count; i++) {
740 if(!parse_number(begin, end, values + offset + i))
741 return false;
742 skip_ws_or_comma(begin, end, NULL);
743 }
744
745 return true;
746}
747
748NOVASVG_INLINE bool path_parse(path_t* path, const char* data, int length)
749{
750 if(length == -1)
751 length = strlen(data);
752 const char* it = data;
753 const char* end = it + length;
754
755 float values[6];
756 bool flags[2];
757
758 float start_x = 0;
759 float start_y = 0;
760 float current_x = 0;
761 float current_y = 0;
762 float last_control_x = 0;
763 float last_control_y = 0;
764
765 char command = 0;
766 char last_command = 0;
767 skip_ws(&it, end);
768 while(it < end) {
769 if(NOVASVG_IS_ALPHA(*it)) {
770 command = *it++;
771 skip_ws(&it, end);
772 }
773
774 if(!last_command && !(command == 'M' || command == 'm'))
775 return false;
776 if(command == 'M' || command == 'm') {
777 if(!parse_path_coordinates(&it, end, values, 0, 2))
778 return false;
779 if(command == 'm') {
780 values[0] += current_x;
781 values[1] += current_y;
782 }
783
784 path_move_to(path, values[0], values[1]);
785 current_x = start_x = values[0];
786 current_y = start_y = values[1];
787 command = command == 'm' ? 'l' : 'L';
788 } else if(command == 'L' || command == 'l') {
789 if(!parse_path_coordinates(&it, end, values, 0, 2))
790 return false;
791 if(command == 'l') {
792 values[0] += current_x;
793 values[1] += current_y;
794 }
795
796 path_line_to(path, values[0], values[1]);
797 current_x = values[0];
798 current_y = values[1];
799 } else if(command == 'H' || command == 'h') {
800 if(!parse_path_coordinates(&it, end, values, 0, 1))
801 return false;
802 if(command == 'h') {
803 values[0] += current_x;
804 }
805
806 path_line_to(path, values[0], current_y);
807 current_x = values[0];
808 } else if(command == 'V' || command == 'v') {
809 if(!parse_path_coordinates(&it, end, values, 1, 1))
810 return false;
811 if(command == 'v') {
812 values[1] += current_y;
813 }
814
815 path_line_to(path, current_x, values[1]);
816 current_y = values[1];
817 } else if(command == 'Q' || command == 'q') {
818 if(!parse_path_coordinates(&it, end, values, 0, 4))
819 return false;
820 if(command == 'q') {
821 values[0] += current_x;
822 values[1] += current_y;
823 values[2] += current_x;
824 values[3] += current_y;
825 }
826
827 path_quad_to(path, values[0], values[1], values[2], values[3]);
828 last_control_x = values[0];
829 last_control_y = values[1];
830 current_x = values[2];
831 current_y = values[3];
832 } else if(command == 'C' || command == 'c') {
833 if(!parse_path_coordinates(&it, end, values, 0, 6))
834 return false;
835 if(command == 'c') {
836 values[0] += current_x;
837 values[1] += current_y;
838 values[2] += current_x;
839 values[3] += current_y;
840 values[4] += current_x;
841 values[5] += current_y;
842 }
843
844 path_cubic_to(path, values[0], values[1], values[2], values[3], values[4], values[5]);
845 last_control_x = values[2];
846 last_control_y = values[3];
847 current_x = values[4];
848 current_y = values[5];
849 } else if(command == 'T' || command == 't') {
850 if(last_command != 'Q' && last_command != 'q' && last_command != 'T' && last_command != 't') {
851 values[0] = current_x;
852 values[1] = current_y;
853 } else {
854 values[0] = 2 * current_x - last_control_x;
855 values[1] = 2 * current_y - last_control_y;
856 }
857
858 if(!parse_path_coordinates(&it, end, values, 2, 2))
859 return false;
860 if(command == 't') {
861 values[2] += current_x;
862 values[3] += current_y;
863 }
864
865 path_quad_to(path, values[0], values[1], values[2], values[3]);
866 last_control_x = values[0];
867 last_control_y = values[1];
868 current_x = values[2];
869 current_y = values[3];
870 } else if(command == 'S' || command == 's') {
871 if(last_command != 'C' && last_command != 'c' && last_command != 'S' && last_command != 's') {
872 values[0] = current_x;
873 values[1] = current_y;
874 } else {
875 values[0] = 2 * current_x - last_control_x;
876 values[1] = 2 * current_y - last_control_y;
877 }
878
879 if(!parse_path_coordinates(&it, end, values, 2, 4))
880 return false;
881 if(command == 's') {
882 values[2] += current_x;
883 values[3] += current_y;
884 values[4] += current_x;
885 values[5] += current_y;
886 }
887
888 path_cubic_to(path, values[0], values[1], values[2], values[3], values[4], values[5]);
889 last_control_x = values[2];
890 last_control_y = values[3];
891 current_x = values[4];
892 current_y = values[5];
893 } else if(command == 'A' || command == 'a') {
894 if(!parse_path_coordinates(&it, end, values, 0, 3)
895 || !parse_arc_flag(&it, end, &flags[0])
896 || !parse_arc_flag(&it, end, &flags[1])
897 || !parse_path_coordinates(&it, end, values, 3, 2)) {
898 return false;
899 }
900
901 if(command == 'a') {
902 values[3] += current_x;
903 values[4] += current_y;
904 }
905
906 path_arc_to(path, values[0], values[1], NOVASVG_DEG2RAD(values[2]), flags[0], flags[1], values[3], values[4]);
907 current_x = values[3];
908 current_y = values[4];
909 } else if(command == 'Z' || command == 'z') {
910 if(last_command == 'Z' || last_command == 'z')
911 return false;
913 current_x = start_x;
914 current_y = start_y;
915 } else {
916 return false;
917 }
918
919 last_command = command;
920 }
921
922 return true;
923}
924
925
926} // namespace render
927} // namespace novasvg
#define NOVASVG_MAX(a, b)
Definition color.h:11
#define NOVASVG_MIN(a, b)
Definition color.h:14
NOVASVG_INLINE void split_bezier(const bezier_t *b, bezier_t *first, bezier_t *second)
Definition path.h:448
NOVASVG_INLINE void matrix_map_point(const matrix_t *matrix, const point_t *src, point_t *dst)
Transforms the src point using matrix and stores the result in dst.
Definition matrix.h:106
NOVASVG_INLINE void matrix_map(const matrix_t *matrix, float x, float y, float *xx, float *yy)
Transforms the point (x, y) using matrix and stores the result in (xx, yy).
Definition matrix.h:100
NOVASVG_INLINE bool parse_number(const char **begin, const char *end, float *number)
Definition utils.h:88
NOVASVG_INLINE path_t * path_create(void)
Creates a new path object.
Definition path.h:43
NOVASVG_INLINE bool skip_ws_or_comma(const char **begin, const char *end, bool *has_comma)
Definition utils.h:222
@ NOVASVG_PATH_COMMAND_CUBIC_TO
Draws a cubic Bézier curve to a new point.
Definition render.h:313
@ NOVASVG_PATH_COMMAND_CLOSE
Closes the current path by drawing a line to the starting point.
Definition render.h:314
@ NOVASVG_PATH_COMMAND_LINE_TO
Draws a straight line to a new point.
Definition render.h:312
@ NOVASVG_PATH_COMMAND_MOVE_TO
Moves the current point to a new position.
Definition render.h:311
NOVASVG_INLINE void dash_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
Definition path.h:541
NOVASVG_INLINE void clone_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
Definition path.h:630
NOVASVG_INLINE void path_iterator_init(path_iterator_t *it, const path_t *path)
Initializes a path iterator for a given path.
Definition path.h:11
NOVASVG_INLINE void path_move_to(path_t *path, float x, float y)
Moves the current point to a new position.
Definition path.h:93
enum novasvg::render::path_command path_command_t
Enumeration defining path commands.
NOVASVG_INLINE void matrix_map_points(const matrix_t *matrix, const point_t *src, point_t *dst, int count)
Transforms an array of src points using matrix and stores the results in dst.
Definition matrix.h:111
NOVASVG_INLINE void matrix_init_rotate(matrix_t *matrix, float angle)
Initializes a 2D transformation matrix for rotation.
Definition matrix.h:30
NOVASVG_INLINE void path_arc_to(path_t *path, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
Adds an elliptical arc to the path.
Definition path.h:131
NOVASVG_INLINE void path_cubic_to(path_t *path, float x1, float y1, float x2, float y2, float x3, float y3)
Adds a cubic Bézier curve to the path.
Definition path.h:120
NOVASVG_INLINE float path_extents(const path_t *path, rect_t *extents, bool tight)
Computes the bounding box and total length of the path.
Definition path.h:701
NOVASVG_INLINE void path_add_path(path_t *path, const path_t *source, const matrix_t *matrix)
Adds a sub-path to the path.
Definition path.h:380
NOVASVG_INLINE bool parse_path_coordinates(const char **begin, const char *end, float values[6], int offset, int count)
Definition path.h:737
NOVASVG_INLINE path_element_t * path_add_command(path_t *path, path_command_t command, int npoints)
Definition path.h:81
NOVASVG_INLINE void path_get_current_point(const path_t *path, float *x, float *y)
Retrieves the current point of the path.
Definition path.h:225
void(* path_traverse_func_t)(void *closure, path_command_t command, const point_t *points, int npoints)
Callback function type for traversing a path.
Definition render.h:658
NOVASVG_INLINE void path_destroy(path_t *path)
Decreases the reference count of a path object.
Definition path.h:61
NOVASVG_INLINE void path_close(path_t *path)
Closes the current sub-path.
Definition path.h:217
NOVASVG_INLINE bool path_iterator_has_next(const path_iterator_t *it)
Checks if there are more elements to iterate over.
Definition path.h:18
NOVASVG_INLINE void path_reserve(path_t *path, int count)
Reserves space for path elements.
Definition path.h:238
NOVASVG_INLINE bool path_parse(path_t *path, const char *data, int length)
Parses SVG path data into a path_t object.
Definition path.h:748
NOVASVG_INLINE bool parse_arc_flag(const char **begin, const char *end, bool *flag)
Definition path.h:725
NOVASVG_INLINE bool skip_delim(const char **begin, const char *end, const char delim)
Definition utils.h:147
NOVASVG_INLINE bool skip_ws(const char **begin, const char *end)
Definition utils.h:174
NOVASVG_INLINE path_t * path_reference(path_t *path)
Increases the reference count of a path object.
Definition path.h:55
NOVASVG_INLINE void matrix_init_scale(matrix_t *matrix, float sx, float sy)
Initializes a 2D transformation matrix for scaling.
Definition matrix.h:25
NOVASVG_INLINE void path_traverse(const path_t *path, path_traverse_func_t traverse_func, void *closure)
Traverses the path and calls the callback for each element.
Definition path.h:417
NOVASVG_INLINE void path_add_ellipse(path_t *path, float cx, float cy, float rx, float ry)
Adds an ellipse to the path.
Definition path.h:291
NOVASVG_INLINE void path_add_arc(path_t *path, float cx, float cy, float r, float a0, float a1, bool ccw)
Adds an arc to the path.
Definition path.h:315
NOVASVG_INLINE path_t * path_clone_flatten(const path_t *path)
Creates a copy of the path with Bézier curves flattened to line segments.
Definition path.h:649
NOVASVG_INLINE float path_length(const path_t *path)
Calculates the total length of the path.
Definition path.h:720
NOVASVG_INLINE void path_quad_to(path_t *path, float x1, float y1, float x2, float y2)
Adds a quadratic Bézier curve to the path.
Definition path.h:109
NOVASVG_INLINE void path_reset(path_t *path)
Resets the path.
Definition path.h:243
NOVASVG_INLINE void path_add_round_rect(path_t *path, float x, float y, float w, float h, float rx, float ry)
Adds a rounded rectangle to the path.
Definition path.h:263
NOVASVG_INLINE int path_get_reference_count(const path_t *path)
Retrieves the reference count of a path object.
Definition path.h:69
NOVASVG_INLINE void matrix_scale(matrix_t *matrix, float sx, float sy)
Scales the matrix by factors sx and sy
Definition matrix.h:49
NOVASVG_INLINE void extents_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
Definition path.h:675
NOVASVG_INLINE void path_line_to(path_t *path, float x, float y)
Adds a straight line segment to the path.
Definition path.h:101
NOVASVG_INLINE path_t * path_clone_dashed(const path_t *path, float offset, const float *dashes, int ndashes)
Creates a copy of the path with a dashed pattern applied.
Definition path.h:657
NOVASVG_INLINE void path_traverse_flatten(const path_t *path, path_traverse_func_t traverse_func, void *closure)
Traverses the path with Bézier curves flattened to line segments.
Definition path.h:469
NOVASVG_INLINE void matrix_rotate(matrix_t *matrix, float angle)
Rotates the matrix by the specified angle (in radians).
Definition matrix.h:56
NOVASVG_INLINE path_command_t path_iterator_next(path_iterator_t *it, point_t points[3])
Retrieves the current command and its associated points, then advances the iterator.
Definition path.h:23
NOVASVG_INLINE void path_add_circle(path_t *path, float cx, float cy, float r)
Adds a circle to the path.
Definition path.h:310
NOVASVG_INLINE path_t * path_clone(const path_t *path)
Creates a copy of the path.
Definition path.h:619
NOVASVG_INLINE void path_traverse_dashed(const path_t *path, float offset, const float *dashes, int ndashes, path_traverse_func_t traverse_func, void *closure)
Traverses the path with a dashed pattern and calls the callback for each segment.
Definition path.h:584
NOVASVG_INLINE int path_get_elements(const path_t *path, const path_element_t **elements)
Retrieves the elements of a path.
Definition path.h:74
NOVASVG_INLINE void path_add_rect(path_t *path, float x, float y, float w, float h)
Adds a rectangle to the path.
Definition path.h:252
NOVASVG_INLINE void path_transform(path_t *path, const matrix_t *matrix)
Applies a transformation matrix to the path.
Definition path.h:361
Main namespace for the NovaSVG library.
Definition bitmap.h:23
#define NOVASVG_INLINE
Definition novasvg.h:42
#define novasvg_get_reference_count(ob)
Definition private.h:48
#define novasvg_destroy_reference(ob)
Definition private.h:47
#define novasvg_init_reference(ob)
Definition private.h:45
#define novasvg_increment_reference(ob)
Definition private.h:46
#define NOVASVG_DEG2RAD(x)
Definition render.h:108
#define NOVASVG_MAKE_POINT(x, y)
Definition render.h:119
#define NOVASVG_KAPPA
Definition render.h:106
#define NOVASVG_TWO_PI
Definition render.h:103
#define NOVASVG_EMPTY_POINT
Definition render.h:120
#define NOVASVG_HALF_PI
Definition render.h:104
path_traverse_func_t traverse_func
Definition path.h:537
const float * dashes
Definition path.h:532
A structure representing a 2D transformation matrix.
Definition render.h:138
Iterator for traversing path elements in a path.
Definition render.h:362
const path_element_t * elements
Pointer to the array of path elements.
Definition render.h:363
int index
Current position in the array.
Definition render.h:365
int size
Total number of elements in the array.
Definition render.h:364
path_element_t * data
Definition private.h:67
struct novasvg::render::path::@11 elements
A structure representing a point in 2D space.
Definition render.h:114
float y
The y-coordinate of the point.
Definition render.h:116
float x
The x-coordinate of the point.
Definition render.h:115
A structure representing a rectangle in 2D space.
Definition render.h:125
float x
The x-coordinate of the top-left corner of the rectangle.
Definition render.h:126
float w
The width of the rectangle.
Definition render.h:128
float h
The height of the rectangle.
Definition render.h:129
float y
The y-coordinate of the top-left corner of the rectangle.
Definition render.h:127
point_t point
A coordinate point in the path.
Definition render.h:356
struct novasvg::render::path_element::@14 header
Header for path commands.
int length
Number of elements including the header.
Definition render.h:354
path_command_t command
The path command.
Definition render.h:353
#define NOVASVG_IS_ALPHA(c)
Definition utils.h:19
#define novasvg_array_ensure(array, count)
Definition utils.h:49
#define novasvg_array_destroy(array)
Definition utils.h:71
#define novasvg_array_append(array, other)
Definition utils.h:69
#define novasvg_array_clear(array)
Definition utils.h:70
#define novasvg_array_init(array)
Definition utils.h:36