26 switch(elements[0].header.
command) {
30 points[0] = elements[1].
point;
33 points[0] = elements[1].
point;
34 points[1] = elements[2].
point;
35 points[2] = elements[3].
point;
83 const int length = npoints + 1;
111 float 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;
133 float current_x, current_y;
135 if(rx == 0.f || ry == 0.f || (current_x == x && current_y == y)) {
140 if(rx < 0.f) rx = -rx;
141 if(ry < 0.f) ry = -ry;
143 float dx = (current_x - x) * 0.5f;
144 float dy = (current_y - y) * 0.5f;
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;
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)
179 float cx1 = 0.5f * (x1 + x2) - dy1;
180 float cy1 = 0.5f * (y1 + y2) + dx1;
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)
187 else if(th_arc > 0.f && !sweep_flag)
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));
197 float x3 = cosf(th_end) + cx1;
198 float y3 = sinf(th_end) + cy1;
200 float cp2x = x3 + t * sinf(th_end);
201 float cp2y = y3 - t * cosf(th_end);
203 float cp1x = cosf(th_start) - t * sinf(th_start);
204 float cp1y = sinf(th_start) + t * cosf(th_start);
267 if(rx == 0.f && ry == 0.f) {
273 float bottom = y + h;
284 path_cubic_to(
path, right, bottom-ry+cpy, right-rx+cpx, bottom, right-rx, bottom);
293 float left = cx - rx;
295 float right = cx + rx;
296 float bottom = cy + ry;
320 }
else if(da != 0.f && ccw != (da < 0.f)) {
328 float ax = cx + cosf(a) * r;
329 float ay = cy + sinf(a) * r;
331 float seg_a = da / seg_n;
333 float dx = -sinf(a) * d;
334 float dy = cosf(a) * d;
343 for(
int i = 0; i < seg_n; i++) {
344 float cp1x = ax + dx;
345 float cp1y = ay + dy;
348 ax = cx + cosf(a) * r;
349 ay = cy + sinf(a) * r;
354 float cp2x = ax - dx;
355 float cp2y = ay - dy;
365 switch(elements[i].header.
command) {
408 path_cubic_to(
path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
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;
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;
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;
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;
476 const float threshold = 0.25f;
483 point_t current_point = {0, 0};
490 traverse_func(closure, command, points, 1);
491 current_point = points[0];
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;
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);
509 d = fabsf((x4x1)*(b->
y1 - b->
y2) - (y4y1)*(b->
x1 - b->
x2)) + fabsf((x4x1)*(b->
y1 - b->
y3) - (y4y1)*(b->
x1 - b->
x3));
511 d = fabsf(b->
x1 - b->
x2) + fabsf(b->
y1 - b->
y2) + fabsf(b->
x1 - b->
x3) + fabsf(b->
y1 - b->
y3);
515 if(d < threshold*l || b == beziers + 31) {
525 current_point = points[2];
557 float dx = p1.
x - p0.
x;
558 float dy = p1.
y - p0.
y;
559 float dist0 = sqrtf(dx*dx + dy*dy);
563 float a = dist1 / dist0;
564 point_t p = { p0.
x + a * dx, p0.
y + a * dy };
580 dasher->
phase += dist0 - dist1;
586 float dash_sum = 0.f;
587 for(
int i = 0; i < ndashes; ++i)
588 dash_sum += dashes[i];
591 if(dash_sum <= 0.f) {
641 path_cubic_to(
path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
682 calculator->
x1 = points[0].
x;
683 calculator->
y1 = points[0].
y;
684 calculator->
x2 = points[0].
x;
685 calculator->
y2 = points[0].
y;
690 for(
int i = 0; i < npoints; ++i) {
711 extents->
x = calculator.
x1;
712 extents->
y = calculator.
y1;
713 extents->
w = calculator.
x2 - calculator.
x1;
714 extents->
h = calculator.
y2 - calculator.
y1;
739 for(
int i = 0; i < count; i++) {
751 length = strlen(data);
752 const char* it = data;
753 const char* end = it + length;
762 float last_control_x = 0;
763 float last_control_y = 0;
766 char last_command = 0;
774 if(!last_command && !(command ==
'M' || command ==
'm'))
776 if(command ==
'M' || command ==
'm') {
780 values[0] += current_x;
781 values[1] += current_y;
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') {
792 values[0] += current_x;
793 values[1] += current_y;
797 current_x = values[0];
798 current_y = values[1];
799 }
else if(command ==
'H' || command ==
'h') {
803 values[0] += current_x;
807 current_x = values[0];
808 }
else if(command ==
'V' || command ==
'v') {
812 values[1] += current_y;
816 current_y = values[1];
817 }
else if(command ==
'Q' || command ==
'q') {
821 values[0] += current_x;
822 values[1] += current_y;
823 values[2] += current_x;
824 values[3] += current_y;
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') {
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;
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;
854 values[0] = 2 * current_x - last_control_x;
855 values[1] = 2 * current_y - last_control_y;
861 values[2] += current_x;
862 values[3] += current_y;
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;
875 values[0] = 2 * current_x - last_control_x;
876 values[1] = 2 * current_y - last_control_y;
882 values[2] += current_x;
883 values[3] += current_y;
884 values[4] += current_x;
885 values[5] += current_y;
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') {
902 values[3] += current_x;
903 values[4] += current_y;
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')
919 last_command = command;
#define NOVASVG_MAX(a, b)
#define NOVASVG_MIN(a, b)
NOVASVG_INLINE void split_bezier(const bezier_t *b, bezier_t *first, bezier_t *second)
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.
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).
NOVASVG_INLINE bool parse_number(const char **begin, const char *end, float *number)
NOVASVG_INLINE path_t * path_create(void)
Creates a new path object.
NOVASVG_INLINE bool skip_ws_or_comma(const char **begin, const char *end, bool *has_comma)
@ NOVASVG_PATH_COMMAND_CUBIC_TO
Draws a cubic Bézier curve to a new point.
@ NOVASVG_PATH_COMMAND_CLOSE
Closes the current path by drawing a line to the starting point.
@ NOVASVG_PATH_COMMAND_LINE_TO
Draws a straight line to a new point.
@ NOVASVG_PATH_COMMAND_MOVE_TO
Moves the current point to a new position.
NOVASVG_INLINE void dash_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
NOVASVG_INLINE void clone_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
NOVASVG_INLINE void path_iterator_init(path_iterator_t *it, const path_t *path)
Initializes a path iterator for a given path.
NOVASVG_INLINE void path_move_to(path_t *path, float x, float y)
Moves the current point to a new position.
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.
NOVASVG_INLINE void matrix_init_rotate(matrix_t *matrix, float angle)
Initializes a 2D transformation matrix for rotation.
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.
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.
NOVASVG_INLINE float path_extents(const path_t *path, rect_t *extents, bool tight)
Computes the bounding box and total length of the path.
NOVASVG_INLINE void path_add_path(path_t *path, const path_t *source, const matrix_t *matrix)
Adds a sub-path to the path.
NOVASVG_INLINE bool parse_path_coordinates(const char **begin, const char *end, float values[6], int offset, int count)
NOVASVG_INLINE path_element_t * path_add_command(path_t *path, path_command_t command, int npoints)
NOVASVG_INLINE void path_get_current_point(const path_t *path, float *x, float *y)
Retrieves the current point of the path.
void(* path_traverse_func_t)(void *closure, path_command_t command, const point_t *points, int npoints)
Callback function type for traversing a path.
NOVASVG_INLINE void path_destroy(path_t *path)
Decreases the reference count of a path object.
NOVASVG_INLINE void path_close(path_t *path)
Closes the current sub-path.
NOVASVG_INLINE bool path_iterator_has_next(const path_iterator_t *it)
Checks if there are more elements to iterate over.
NOVASVG_INLINE void path_reserve(path_t *path, int count)
Reserves space for path elements.
NOVASVG_INLINE bool path_parse(path_t *path, const char *data, int length)
Parses SVG path data into a path_t object.
NOVASVG_INLINE bool parse_arc_flag(const char **begin, const char *end, bool *flag)
NOVASVG_INLINE bool skip_delim(const char **begin, const char *end, const char delim)
NOVASVG_INLINE bool skip_ws(const char **begin, const char *end)
NOVASVG_INLINE path_t * path_reference(path_t *path)
Increases the reference count of a path object.
NOVASVG_INLINE void matrix_init_scale(matrix_t *matrix, float sx, float sy)
Initializes a 2D transformation matrix for scaling.
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.
NOVASVG_INLINE void path_add_ellipse(path_t *path, float cx, float cy, float rx, float ry)
Adds an ellipse to the path.
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.
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.
NOVASVG_INLINE float path_length(const path_t *path)
Calculates the total length of the path.
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.
NOVASVG_INLINE void path_reset(path_t *path)
Resets the path.
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.
NOVASVG_INLINE int path_get_reference_count(const path_t *path)
Retrieves the reference count of a path object.
NOVASVG_INLINE void matrix_scale(matrix_t *matrix, float sx, float sy)
Scales the matrix by factors sx and sy
NOVASVG_INLINE void extents_traverse_func(void *closure, path_command_t command, const point_t *points, int npoints)
NOVASVG_INLINE void path_line_to(path_t *path, float x, float y)
Adds a straight line segment to the path.
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.
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.
NOVASVG_INLINE void matrix_rotate(matrix_t *matrix, float angle)
Rotates the matrix by the specified angle (in radians).
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.
NOVASVG_INLINE void path_add_circle(path_t *path, float cx, float cy, float r)
Adds a circle to the path.
NOVASVG_INLINE path_t * path_clone(const path_t *path)
Creates a copy of the path.
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.
NOVASVG_INLINE int path_get_elements(const path_t *path, const path_element_t **elements)
Retrieves the elements of a path.
NOVASVG_INLINE void path_add_rect(path_t *path, float x, float y, float w, float h)
Adds a rectangle to the path.
NOVASVG_INLINE void path_transform(path_t *path, const matrix_t *matrix)
Applies a transformation matrix to the path.
Main namespace for the NovaSVG library.
#define novasvg_get_reference_count(ob)
#define novasvg_destroy_reference(ob)
#define novasvg_init_reference(ob)
#define novasvg_increment_reference(ob)
#define NOVASVG_DEG2RAD(x)
#define NOVASVG_MAKE_POINT(x, y)
#define NOVASVG_EMPTY_POINT
path_traverse_func_t traverse_func
A structure representing a 2D transformation matrix.
Iterator for traversing path elements in a path.
const path_element_t * elements
Pointer to the array of path elements.
int index
Current position in the array.
int size
Total number of elements in the array.
struct novasvg::render::path::@11 elements
A structure representing a point in 2D space.
float y
The y-coordinate of the point.
float x
The x-coordinate of the point.
A structure representing a rectangle in 2D space.
float x
The x-coordinate of the top-left corner of the rectangle.
float w
The width of the rectangle.
float h
The height of the rectangle.
float y
The y-coordinate of the top-left corner of the rectangle.
point_t point
A coordinate point in the path.
struct novasvg::render::path_element::@14 header
Header for path commands.
int length
Number of elements including the header.
path_command_t command
The path command.
#define NOVASVG_IS_ALPHA(c)
#define novasvg_array_ensure(array, count)
#define novasvg_array_destroy(array)
#define novasvg_array_append(array, other)
#define novasvg_array_clear(array)
#define novasvg_array_init(array)