2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2008 James Bursa <james@semichrome.net>
15 #include "svgtiny_internal.h"
19 static svgtiny_code
svgtiny_parse_linear_gradient(dom_element
*linear
,
20 struct svgtiny_parse_state_gradient
*grad
,
21 struct svgtiny_parse_state
*state
);
22 static float svgtiny_parse_gradient_offset(const char *s
);
23 static void svgtiny_path_bbox(float *p
, unsigned int n
,
24 float *x0
, float *y0
, float *x1
, float *y1
);
25 static void svgtiny_invert_matrix(float *m
, float *inv
);
29 * Find a gradient by id and parse it.
32 void svgtiny_find_gradient(const char *id
,
33 struct svgtiny_parse_state_gradient
*grad
,
34 struct svgtiny_parse_state
*state
)
36 dom_element
*gradient
;
37 dom_string
*id_str
, *name
;
41 fprintf(stderr
, "svgtiny_find_gradient: id \"%s\"\n", id
);
44 grad
->linear_gradient_stop_count
= 0;
45 if (grad
->gradient_x1
!= NULL
)
46 dom_string_unref(grad
->gradient_x1
);
47 if (grad
->gradient_y1
!= NULL
)
48 dom_string_unref(grad
->gradient_y1
);
49 if (grad
->gradient_x2
!= NULL
)
50 dom_string_unref(grad
->gradient_x2
);
51 if (grad
->gradient_y2
!= NULL
)
52 dom_string_unref(grad
->gradient_y2
);
53 grad
->gradient_x1
= dom_string_ref(state
->interned_zero_percent
);
54 grad
->gradient_y1
= dom_string_ref(state
->interned_zero_percent
);
55 grad
->gradient_x2
= dom_string_ref(state
->interned_hundred_percent
);
56 grad
->gradient_y2
= dom_string_ref(state
->interned_zero_percent
);
57 grad
->gradient_user_space_on_use
= false;
58 grad
->gradient_transform
.a
= 1;
59 grad
->gradient_transform
.b
= 0;
60 grad
->gradient_transform
.c
= 0;
61 grad
->gradient_transform
.d
= 1;
62 grad
->gradient_transform
.e
= 0;
63 grad
->gradient_transform
.f
= 0;
65 exc
= dom_string_create_interned((const uint8_t *) id
,
67 if (exc
!= DOM_NO_ERR
)
70 exc
= dom_document_get_element_by_id(state
->document
, id_str
,
72 dom_string_unref(id_str
);
73 if (exc
!= DOM_NO_ERR
|| gradient
== NULL
) {
75 fprintf(stderr
, "gradient \"%s\" not found\n", id
);
80 exc
= dom_node_get_node_name(gradient
, &name
);
81 if (exc
!= DOM_NO_ERR
) {
82 dom_node_unref(gradient
);
86 if (dom_string_isequal(name
, state
->interned_linearGradient
))
87 svgtiny_parse_linear_gradient(gradient
, grad
, state
);
89 dom_node_unref(gradient
);
90 dom_string_unref(name
);
93 fprintf(stderr
, "linear_gradient_stop_count %i\n",
94 grad
->linear_gradient_stop_count
);
100 * Parse a <linearGradient> element node.
102 * http://www.w3.org/TR/SVG11/pservers#LinearGradients
105 svgtiny_code
svgtiny_parse_linear_gradient(dom_element
*linear
,
106 struct svgtiny_parse_state_gradient
*grad
,
107 struct svgtiny_parse_state
*state
)
114 exc
= dom_element_get_attribute(linear
, state
->interned_href
, &attr
);
115 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
116 if (dom_string_data(attr
)[0] == (uint8_t) '#') {
117 char *s
= strndup(dom_string_data(attr
) + 1,
118 dom_string_byte_length(attr
) - 1);
119 svgtiny_find_gradient(s
, grad
, state
);
122 dom_string_unref(attr
);
125 exc
= dom_element_get_attribute(linear
, state
->interned_x1
, &attr
);
126 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
127 dom_string_unref(grad
->gradient_x1
);
128 grad
->gradient_x1
= attr
;
132 exc
= dom_element_get_attribute(linear
, state
->interned_y1
, &attr
);
133 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
134 dom_string_unref(grad
->gradient_y1
);
135 grad
->gradient_y1
= attr
;
139 exc
= dom_element_get_attribute(linear
, state
->interned_x2
, &attr
);
140 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
141 dom_string_unref(grad
->gradient_x2
);
142 grad
->gradient_x2
= attr
;
146 exc
= dom_element_get_attribute(linear
, state
->interned_y2
, &attr
);
147 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
148 dom_string_unref(grad
->gradient_y2
);
149 grad
->gradient_y2
= attr
;
153 exc
= dom_element_get_attribute(linear
, state
->interned_gradientUnits
,
155 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
156 grad
->gradient_user_space_on_use
=
157 dom_string_isequal(attr
,
158 state
->interned_userSpaceOnUse
);
159 dom_string_unref(attr
);
162 exc
= dom_element_get_attribute(linear
,
163 state
->interned_gradientTransform
,
165 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
166 float a
= 1, b
= 0, c
= 0, d
= 1, e
= 0, f
= 0;
167 char *s
= strndup(dom_string_data(attr
),
168 dom_string_byte_length(attr
));
170 dom_string_unref(attr
);
171 return svgtiny_OUT_OF_MEMORY
;
173 svgtiny_parse_transform(s
, &a
, &b
, &c
, &d
, &e
, &f
);
175 #ifdef GRADIENT_DEBUG
176 fprintf(stderr
, "transform %g %g %g %g %g %g\n",
179 grad
->gradient_transform
.a
= a
;
180 grad
->gradient_transform
.b
= b
;
181 grad
->gradient_transform
.c
= c
;
182 grad
->gradient_transform
.d
= d
;
183 grad
->gradient_transform
.e
= e
;
184 grad
->gradient_transform
.f
= f
;
185 dom_string_unref(attr
);
188 exc
= dom_element_get_elements_by_tag_name(linear
,
189 state
->interned_stop
,
191 if (exc
== DOM_NO_ERR
&& stops
!= NULL
) {
192 uint32_t listlen
, stopnr
;
193 exc
= dom_nodelist_get_length(stops
, &listlen
);
194 if (exc
!= DOM_NO_ERR
) {
195 dom_nodelist_unref(stops
);
199 for (stopnr
= 0; stopnr
< listlen
; ++stopnr
) {
202 svgtiny_colour color
= svgtiny_TRANSPARENT
;
203 exc
= dom_nodelist_item(stops
, stopnr
,
204 (dom_node
**) (void *) &stop
);
205 if (exc
!= DOM_NO_ERR
)
207 exc
= dom_element_get_attribute(stop
,
208 state
->interned_offset
,
210 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
211 char *s
= strndup(dom_string_data(attr
),
212 dom_string_byte_length(attr
));
213 offset
= svgtiny_parse_gradient_offset(s
);
215 dom_string_unref(attr
);
217 exc
= dom_element_get_attribute(stop
,
218 state
->interned_stop_color
,
220 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
221 svgtiny_parse_color(attr
, &color
, NULL
, state
);
222 dom_string_unref(attr
);
224 exc
= dom_element_get_attribute(stop
,
225 state
->interned_style
,
227 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
228 char *content
= strndup(dom_string_data(attr
),
229 dom_string_byte_length(attr
));
232 if ((s
= strstr(content
, "stop-color:"))) {
236 exc
= dom_string_create_interned(
240 if (exc
== DOM_NO_ERR
&&
242 svgtiny_parse_color(value
,
246 dom_string_unref(value
);
250 dom_string_unref(attr
);
252 if (offset
!= -1 && color
!= svgtiny_TRANSPARENT
) {
253 #ifdef GRADIENT_DEBUG
254 fprintf(stderr
, "stop %g %x\n", offset
, color
);
256 grad
->gradient_stop
[i
].offset
= offset
;
257 grad
->gradient_stop
[i
].color
= color
;
260 dom_node_unref(stop
);
261 if (i
== svgtiny_MAX_STOPS
)
265 dom_nodelist_unref(stops
);
269 grad
->linear_gradient_stop_count
= i
;
275 float svgtiny_parse_gradient_offset(const char *s
)
277 int num_length
= strspn(s
, "0123456789+-.");
278 const char *unit
= s
+ num_length
;
279 float n
= atof((const char *) s
);
283 else if (unit
[0] == '%')
297 * Add a path with a linear gradient fill to the svgtiny_diagram.
300 svgtiny_code
svgtiny_add_path_linear_gradient(float *p
, unsigned int n
,
301 struct svgtiny_parse_state
*state
)
306 float object_x0
, object_y0
, object_x1
, object_y1
;
307 float gradient_x0
, gradient_y0
, gradient_x1
, gradient_y1
,
308 gradient_dx
, gradient_dy
;
310 unsigned int steps
= 10;
311 float x0
= 0, y0
= 0, x0_trans
, y0_trans
, r0
; /* segment start point */
312 float x1
, y1
, x1_trans
, y1_trans
, r1
; /* segment end point */
313 /* segment control points (beziers only) */
314 float c0x
= 0, c0y
= 0, c1x
= 0, c1y
= 0;
315 float gradient_norm_squared
;
316 struct svgtiny_list
*pts
;
318 unsigned int min_pt
= 0;
320 unsigned int stop_count
;
321 unsigned int current_stop
;
323 float current_stop_r
;
324 int red0
, green0
, blue0
, red1
, green1
, blue1
;
325 unsigned int t
, a
, b
;
326 struct svgtiny_parse_state_gradient
*grad
;
328 assert(state
->fill
== svgtiny_LINEAR_GRADIENT
);
329 grad
= &state
->fill_grad
;
331 /* determine object bounding box */
332 svgtiny_path_bbox(p
, n
, &object_x0
, &object_y0
, &object_x1
, &object_y1
);
333 #ifdef GRADIENT_DEBUG
334 fprintf(stderr
, "object bbox: (%g %g) (%g %g)\n",
335 object_x0
, object_y0
, object_x1
, object_y1
);
338 if (!grad
->gradient_user_space_on_use
) {
339 gradient_x0
= object_x0
+
340 svgtiny_parse_length(grad
->gradient_x1
,
341 object_x1
- object_x0
, *state
);
342 gradient_y0
= object_y0
+
343 svgtiny_parse_length(grad
->gradient_y1
,
344 object_y1
- object_y0
, *state
);
345 gradient_x1
= object_x0
+
346 svgtiny_parse_length(grad
->gradient_x2
,
347 object_x1
- object_x0
, *state
);
348 gradient_y1
= object_y0
+
349 svgtiny_parse_length(grad
->gradient_y2
,
350 object_y1
- object_y0
, *state
);
352 gradient_x0
= svgtiny_parse_length(grad
->gradient_x1
,
353 state
->viewport_width
, *state
);
354 gradient_y0
= svgtiny_parse_length(grad
->gradient_y1
,
355 state
->viewport_height
, *state
);
356 gradient_x1
= svgtiny_parse_length(grad
->gradient_x2
,
357 state
->viewport_width
, *state
);
358 gradient_y1
= svgtiny_parse_length(grad
->gradient_y2
,
359 state
->viewport_height
, *state
);
361 gradient_dx
= gradient_x1
- gradient_x0
;
362 gradient_dy
= gradient_y1
- gradient_y0
;
363 #ifdef GRADIENT_DEBUG
364 fprintf(stderr
, "gradient vector: (%g %g) => (%g %g)\n",
365 gradient_x0
, gradient_y0
, gradient_x1
, gradient_y1
);
368 /* show theoretical gradient strips for debugging */
369 /*unsigned int strips = 10;
370 for (unsigned int z = 0; z != strips; z++) {
371 float f0, fd, strip_x0, strip_y0, strip_dx, strip_dy;
372 f0 = (float) z / (float) strips;
373 fd = (float) 1 / (float) strips;
374 strip_x0 = gradient_x0 + f0 * gradient_dx;
375 strip_y0 = gradient_y0 + f0 * gradient_dy;
376 strip_dx = fd * gradient_dx;
377 strip_dy = fd * gradient_dy;
378 fprintf(stderr, "strip %i vector: (%g %g) + (%g %g)\n",
379 z, strip_x0, strip_y0, strip_dx, strip_dy);
381 float *p = malloc(13 * sizeof p[0]);
383 return svgtiny_OUT_OF_MEMORY;
384 p[0] = svgtiny_PATH_MOVE;
385 p[1] = strip_x0 + (strip_dy * 3);
386 p[2] = strip_y0 - (strip_dx * 3);
387 p[3] = svgtiny_PATH_LINE;
388 p[4] = p[1] + strip_dx;
389 p[5] = p[2] + strip_dy;
390 p[6] = svgtiny_PATH_LINE;
391 p[7] = p[4] - (strip_dy * 6);
392 p[8] = p[5] + (strip_dx * 6);
393 p[9] = svgtiny_PATH_LINE;
394 p[10] = p[7] - strip_dx;
395 p[11] = p[8] - strip_dy;
396 p[12] = svgtiny_PATH_CLOSE;
397 svgtiny_transform_path(p, 13, state);
398 struct svgtiny_shape *shape = svgtiny_add_shape(state);
401 return svgtiny_OUT_OF_MEMORY;
404 shape->path_length = 13;
405 shape->fill = svgtiny_TRANSPARENT;
406 shape->stroke = svgtiny_RGB(0, 0xff, 0);
407 state->diagram->shape_count++;
410 /* invert gradient transform for applying to vertices */
411 svgtiny_invert_matrix(&grad
->gradient_transform
.a
, trans
);
412 #ifdef GRADIENT_DEBUG
413 fprintf(stderr
, "inverse transform %g %g %g %g %g %g\n",
414 trans
[0], trans
[1], trans
[2], trans
[3],
418 /* compute points on the path for triangle vertices */
419 /* r, r0, r1 are distance along gradient vector */
420 gradient_norm_squared
= gradient_dx
* gradient_dx
+
421 gradient_dy
* gradient_dy
;
422 pts
= svgtiny_list_create(sizeof (struct grad_point
));
425 return svgtiny_OUT_OF_MEMORY
;
427 for (j
= 0; j
!= n
; ) {
428 int segment_type
= (int) p
[j
];
429 struct grad_point
*point
;
432 if (segment_type
== svgtiny_PATH_MOVE
) {
439 assert(segment_type
== svgtiny_PATH_CLOSE
||
440 segment_type
== svgtiny_PATH_LINE
||
441 segment_type
== svgtiny_PATH_BEZIER
);
443 /* start point (x0, y0) */
444 x0_trans
= trans
[0]*x0
+ trans
[2]*y0
+ trans
[4];
445 y0_trans
= trans
[1]*x0
+ trans
[3]*y0
+ trans
[5];
446 r0
= ((x0_trans
- gradient_x0
) * gradient_dx
+
447 (y0_trans
- gradient_y0
) * gradient_dy
) /
448 gradient_norm_squared
;
449 point
= svgtiny_list_push(pts
);
452 svgtiny_list_free(pts
);
453 return svgtiny_OUT_OF_MEMORY
;
460 min_pt
= svgtiny_list_size(pts
) - 1;
463 /* end point (x1, y1) */
464 if (segment_type
== svgtiny_PATH_LINE
) {
468 } else if (segment_type
== svgtiny_PATH_CLOSE
) {
472 } else /* svgtiny_PATH_BEZIER */ {
481 x1_trans
= trans
[0]*x1
+ trans
[2]*y1
+ trans
[4];
482 y1_trans
= trans
[1]*x1
+ trans
[3]*y1
+ trans
[5];
483 r1
= ((x1_trans
- gradient_x0
) * gradient_dx
+
484 (y1_trans
- gradient_y0
) * gradient_dy
) /
485 gradient_norm_squared
;
487 /* determine steps from change in r */
489 if(isnan(r0
) || isnan(r1
)) {
492 steps
= ceilf(fabsf(r1
- r0
) / 0.05);
497 #ifdef GRADIENT_DEBUG
498 fprintf(stderr
, "r0 %g, r1 %g, steps %i\n",
502 /* loop through intermediate points */
503 for (z
= 1; z
!= steps
; z
++) {
504 float t
, x
, y
, x_trans
, y_trans
, r
;
505 struct grad_point
*point
;
506 t
= (float) z
/ (float) steps
;
507 if (segment_type
== svgtiny_PATH_BEZIER
) {
508 x
= (1-t
) * (1-t
) * (1-t
) * x0
+
509 3 * t
* (1-t
) * (1-t
) * c0x
+
510 3 * t
* t
* (1-t
) * c1x
+
512 y
= (1-t
) * (1-t
) * (1-t
) * y0
+
513 3 * t
* (1-t
) * (1-t
) * c0y
+
514 3 * t
* t
* (1-t
) * c1y
+
517 x
= (1-t
) * x0
+ t
* x1
;
518 y
= (1-t
) * y0
+ t
* y1
;
520 x_trans
= trans
[0]*x
+ trans
[2]*y
+ trans
[4];
521 y_trans
= trans
[1]*x
+ trans
[3]*y
+ trans
[5];
522 r
= ((x_trans
- gradient_x0
) * gradient_dx
+
523 (y_trans
- gradient_y0
) * gradient_dy
) /
524 gradient_norm_squared
;
525 #ifdef GRADIENT_DEBUG
526 fprintf(stderr
, "(%g %g [%g]) ", x
, y
, r
);
528 point
= svgtiny_list_push(pts
);
531 svgtiny_list_free(pts
);
532 return svgtiny_OUT_OF_MEMORY
;
539 min_pt
= svgtiny_list_size(pts
) - 1;
542 #ifdef GRADIENT_DEBUG
543 fprintf(stderr
, "\n");
546 /* next segment start point is this segment end point */
550 #ifdef GRADIENT_DEBUG
551 fprintf(stderr
, "pts size %i, min_pt %i, min_r %.3f\n",
552 svgtiny_list_size(pts
), min_pt
, min_r
);
555 /* There must be at least a single point for the gradient */
556 if (svgtiny_list_size(pts
) == 0) {
557 svgtiny_list_free(pts
);
563 /* render triangles */
564 stop_count
= grad
->linear_gradient_stop_count
;
565 assert(2 <= stop_count
);
568 current_stop_r
= grad
->gradient_stop
[0].offset
;
569 red0
= red1
= svgtiny_RED(grad
->gradient_stop
[0].color
);
570 green0
= green1
= svgtiny_GREEN(grad
->gradient_stop
[0].color
);
571 blue0
= blue1
= svgtiny_BLUE(grad
->gradient_stop
[0].color
);
573 a
= (min_pt
+ 1) % svgtiny_list_size(pts
);
574 b
= min_pt
== 0 ? svgtiny_list_size(pts
) - 1 : min_pt
- 1;
576 struct grad_point
*point_t
= svgtiny_list_get(pts
, t
);
577 struct grad_point
*point_a
= svgtiny_list_get(pts
, a
);
578 struct grad_point
*point_b
= svgtiny_list_get(pts
, b
);
579 float mean_r
= (point_t
->r
+ point_a
->r
+ point_b
->r
) / 3;
581 struct svgtiny_shape
*shape
;
582 /*fprintf(stderr, "triangle: t %i %.3f a %i %.3f b %i %.3f "
584 t, pts[t].r, a, pts[a].r, b, pts[b].r,
586 while (current_stop
!= stop_count
&& current_stop_r
< mean_r
) {
588 if (current_stop
== stop_count
)
593 red1
= svgtiny_RED(grad
->
594 gradient_stop
[current_stop
].color
);
595 green1
= svgtiny_GREEN(grad
->
596 gradient_stop
[current_stop
].color
);
597 blue1
= svgtiny_BLUE(grad
->
598 gradient_stop
[current_stop
].color
);
599 last_stop_r
= current_stop_r
;
600 current_stop_r
= grad
->
601 gradient_stop
[current_stop
].offset
;
603 p
= malloc(10 * sizeof p
[0]);
605 return svgtiny_OUT_OF_MEMORY
;
606 p
[0] = svgtiny_PATH_MOVE
;
609 p
[3] = svgtiny_PATH_LINE
;
612 p
[6] = svgtiny_PATH_LINE
;
615 p
[9] = svgtiny_PATH_CLOSE
;
616 svgtiny_transform_path(p
, 10, state
);
617 shape
= svgtiny_add_shape(state
);
620 return svgtiny_OUT_OF_MEMORY
;
623 shape
->path_length
= 10;
624 /*shape->fill = svgtiny_TRANSPARENT;*/
625 if (current_stop
== 0)
626 shape
->fill
= grad
->gradient_stop
[0].color
;
627 else if (current_stop
== stop_count
)
628 shape
->fill
= grad
->gradient_stop
[stop_count
- 1].color
;
630 float stop_r
= (mean_r
- last_stop_r
) /
631 (current_stop_r
- last_stop_r
);
632 shape
->fill
= svgtiny_RGB(
633 (int) ((1 - stop_r
) * red0
+ stop_r
* red1
),
634 (int) ((1 - stop_r
) * green0
+ stop_r
* green1
),
635 (int) ((1 - stop_r
) * blue0
+ stop_r
* blue1
));
637 shape
->stroke
= svgtiny_TRANSPARENT
;
638 #ifdef GRADIENT_DEBUG
639 shape
->stroke
= svgtiny_RGB(0, 0, 0xff);
641 state
->diagram
->shape_count
++;
642 if (point_a
->r
< point_b
->r
) {
644 a
= (a
+ 1) % svgtiny_list_size(pts
);
647 b
= b
== 0 ? svgtiny_list_size(pts
) - 1 : b
- 1;
651 /* render gradient vector for debugging */
652 #ifdef GRADIENT_DEBUG
654 float *p
= malloc(7 * sizeof p
[0]);
656 return svgtiny_OUT_OF_MEMORY
;
657 p
[0] = svgtiny_PATH_MOVE
;
660 p
[3] = svgtiny_PATH_LINE
;
663 p
[6] = svgtiny_PATH_CLOSE
;
664 svgtiny_transform_path(p
, 7, state
);
665 struct svgtiny_shape
*shape
= svgtiny_add_shape(state
);
668 return svgtiny_OUT_OF_MEMORY
;
671 shape
->path_length
= 7;
672 shape
->fill
= svgtiny_TRANSPARENT
;
673 shape
->stroke
= svgtiny_RGB(0xff, 0, 0);
674 state
->diagram
->shape_count
++;
678 /* render triangle vertices with r values for debugging */
679 #ifdef GRADIENT_DEBUG
680 for (unsigned int i
= 0; i
!= svgtiny_list_size(pts
); i
++) {
681 struct grad_point
*point
= svgtiny_list_get(pts
, i
);
682 struct svgtiny_shape
*shape
= svgtiny_add_shape(state
);
684 return svgtiny_OUT_OF_MEMORY
;
685 char *text
= malloc(20);
687 return svgtiny_OUT_OF_MEMORY
;
688 sprintf(text
, "%i=%.3f", i
, point
->r
);
690 shape
->text_x
= state
->ctm
.a
* point
->x
+
691 state
->ctm
.c
* point
->y
+ state
->ctm
.e
;
692 shape
->text_y
= state
->ctm
.b
* point
->x
+
693 state
->ctm
.d
* point
->y
+ state
->ctm
.f
;
694 shape
->fill
= svgtiny_RGB(0, 0, 0);
695 shape
->stroke
= svgtiny_TRANSPARENT
;
696 state
->diagram
->shape_count
++;
700 /* plot actual path outline */
701 if (state
->stroke
!= svgtiny_TRANSPARENT
) {
702 struct svgtiny_shape
*shape
;
703 svgtiny_transform_path(p
, n
, state
);
705 shape
= svgtiny_add_shape(state
);
708 return svgtiny_OUT_OF_MEMORY
;
711 shape
->path_length
= n
;
712 shape
->fill
= svgtiny_TRANSPARENT
;
713 state
->diagram
->shape_count
++;
718 svgtiny_list_free(pts
);
725 * Get the bounding box of path.
728 void svgtiny_path_bbox(float *p
, unsigned int n
,
729 float *x0
, float *y0
, float *x1
, float *y1
)
736 for (j
= 0; j
!= n
; ) {
737 unsigned int points
= 0;
739 switch ((int) p
[j
]) {
740 case svgtiny_PATH_MOVE
:
741 case svgtiny_PATH_LINE
:
744 case svgtiny_PATH_CLOSE
:
747 case svgtiny_PATH_BEZIER
:
754 for (k
= 0; k
!= points
; k
++) {
755 float x
= p
[j
], y
= p
[j
+ 1];
771 * Invert a transformation matrix.
773 void svgtiny_invert_matrix(float *m
, float *inv
)
775 float determinant
= m
[0]*m
[3] - m
[1]*m
[2];
776 inv
[0] = m
[3] / determinant
;
777 inv
[1] = -m
[1] / determinant
;
778 inv
[2] = -m
[2] / determinant
;
779 inv
[3] = m
[0] / determinant
;
780 inv
[4] = (m
[2]*m
[5] - m
[3]*m
[4]) / determinant
;
781 inv
[5] = (m
[1]*m
[4] - m
[0]*m
[5]) / determinant
;