1
2
3 #include <string.h>
4 #include <stdlib.h>
5 #include <math.h>
6
7 #include <QTextStream>
8 #include <QFile>
9
10 #include "tiio_svg.h"
11 #include "tvectorimage.h"
12 #include "tstroke.h"
13 #include "tstrokeoutline.h"
14 #include "tregion.h"
15 #include "tcurves.h"
16 #include "tpalette.h"
17
18 //=------------------------------------------------------------------------------------------------------------------------------
19 //=------------------------------------------------------------------------------------------------------------------------------
20 //=------------------------------------------------------------------------------------------------------------------------------
21 //=------------------------------------------------------------------------------------------------------------------------------
22
23 namespace // svg_parser
24 {
25
26 struct NSVGpath {
27 float *pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
28 int npts; // Total number of bezier points.
29 char closed; // Flag indicating if shapes should be treated as closed.
30 struct NSVGpath *next; // Pointer to next path, or NULL if last element.
31 };
32
33 struct NSVGshape {
34 unsigned int fillColor; // Fill color
35 unsigned int strokeColor; // Stroke color
36 float strokeWidth; // Stroke width (scaled)
37 char hasFill; // Flag indicating if fill exists.
38 char hasStroke; // Flag indicating id store exists
39 struct NSVGpath *paths; // Linked list of paths in the image.
40 struct NSVGshape *next; // Pointer to next shape, or NULL if last element.
41 };
42
43 struct NSVGimage {
44 float width; // Width of the image, or -1.0f of not set.
45 float height; // Height of the image, or -1.0f of not set.
46 char wunits[8]; // Units of the width attribute
47 char hunits[8]; // Units of the height attribute
48 struct NSVGshape *shapes; // Linked list of shapes in the image.
49 };
50
51 #define NSVG_PI 3.14159265358979323846264338327f
52 #define NSVG_KAPPA90 \
53 0.5522847493f // Length proportional to radius of a cubic bezier handle for
54 // 90deg arcs.
55
56 #ifdef _MSC_VER
57 #pragma warning(disable : 4996) // Switch off security warnings
58 #pragma warning( \
59 disable : 4100) // Switch off unreferenced formal parameter warnings
60 #ifdef __cplusplus
61 #define NSVG_INLINE inline
62 #else
63 #define NSVG_INLINE
64 #endif
65 #else
66 #define NSVG_INLINE inline
67 #endif
68
nsvg__isspace(char c)69 int nsvg__isspace(char c) { return strchr(" \t\n\v\f\r", c) != 0; }
70
nsvg__isdigit(char c)71 int nsvg__isdigit(char c) { return strchr("0123456789", c) != 0; }
72
nsvg__isnum(char c)73 int nsvg__isnum(char c) { return strchr("0123456789+-.eE", c) != 0; }
74
nsvg__maxf(float a,float b)75 NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
76
77 // Simple XML parser
78
79 #define NSVG_XML_TAG 1
80 #define NSVG_XML_CONTENT 2
81 #define NSVG_XML_MAX_ATTRIBS 256
82
nsvg__parseContent(char * s,void (* contentCb)(void * ud,const char * s),void * ud)83 void nsvg__parseContent(char *s, void (*contentCb)(void *ud, const char *s),
84 void *ud) {
85 // Trim start white spaces
86 while (*s && nsvg__isspace(*s)) s++;
87 if (!*s) return;
88
89 if (contentCb) (*contentCb)(ud, s);
90 }
91
nsvg__parseElement(char * s,void (* startelCb)(void * ud,const char * el,const char ** attr),void (* endelCb)(void * ud,const char * el),void * ud)92 void nsvg__parseElement(char *s, void (*startelCb)(void *ud, const char *el,
93 const char **attr),
94 void (*endelCb)(void *ud, const char *el), void *ud) {
95 const char *attr[NSVG_XML_MAX_ATTRIBS];
96 int nattr = 0;
97 char *name;
98 int start = 0;
99 int end = 0;
100
101 // Skip white space after the '<'
102 while (*s && nsvg__isspace(*s)) s++;
103
104 // Check if the tag is end tag
105 if (*s == '/') {
106 s++;
107 end = 1;
108 } else {
109 start = 1;
110 }
111
112 // Skip comments, data and preprocessor stuff.
113 if (!*s || *s == '?' || *s == '!') return;
114
115 // Get tag name
116 name = s;
117 while (*s && !nsvg__isspace(*s)) s++;
118 if (*s) {
119 *s++ = '\0';
120 }
121
122 // Get attribs
123 while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS - 3) {
124 // Skip white space before the attrib name
125 while (*s && nsvg__isspace(*s)) s++;
126 if (!*s) break;
127 if (*s == '/') {
128 end = 1;
129 break;
130 }
131 attr[nattr++] = s;
132 // Find end of the attrib name.
133 while (*s && !nsvg__isspace(*s) && *s != '=') s++;
134 if (*s) {
135 *s++ = '\0';
136 }
137 // Skip until the beginning of the value.
138 while (*s && *s != '\"') s++;
139 if (!*s) break;
140 s++;
141 // Store value and find the end of it.
142 attr[nattr++] = s;
143 while (*s && *s != '\"') s++;
144 if (*s) {
145 *s++ = '\0';
146 }
147 }
148
149 // List terminator
150 attr[nattr++] = 0;
151 attr[nattr++] = 0;
152
153 // Call callbacks.
154 if (start && startelCb) (*startelCb)(ud, name, attr);
155 if (end && endelCb) (*endelCb)(ud, name);
156 }
157
nsvg__parseXML(char * input,void (* startelCb)(void * ud,const char * el,const char ** attr),void (* endelCb)(void * ud,const char * el),void (* contentCb)(void * ud,const char * s),void * ud)158 int nsvg__parseXML(char *input, void (*startelCb)(void *ud, const char *el,
159 const char **attr),
160 void (*endelCb)(void *ud, const char *el),
161 void (*contentCb)(void *ud, const char *s), void *ud) {
162 char *s = input;
163 char *mark = s;
164 int state = NSVG_XML_CONTENT;
165 while (*s) {
166 if (*s == '<' && state == NSVG_XML_CONTENT) {
167 // Start of a tag
168 *s++ = '\0';
169 nsvg__parseContent(mark, contentCb, ud);
170 mark = s;
171 state = NSVG_XML_TAG;
172 } else if (*s == '>' && state == NSVG_XML_TAG) {
173 // Start of a content or new tag.
174 *s++ = '\0';
175 nsvg__parseElement(mark, startelCb, endelCb, ud);
176 mark = s;
177 state = NSVG_XML_CONTENT;
178 } else {
179 s++;
180 }
181 }
182
183 return 1;
184 }
185
186 /* Simple SVG parser. */
187
188 #define NSVG_MAX_ATTR 128
189
190 struct NSVGAttrib {
191 float xform[6];
192 unsigned int fillColor;
193 unsigned int strokeColor;
194 float fillOpacity;
195 float strokeOpacity;
196 float strokeWidth;
197 char hasFill;
198 char hasStroke;
199 char visible;
200 };
201
202 struct NSVGParser {
203 struct NSVGAttrib attr[NSVG_MAX_ATTR];
204 int attrHead;
205 float *pts;
206 int npts;
207 int cpts;
208 struct NSVGpath *plist;
209 struct NSVGimage *image;
210 char pathFlag;
211 char defsFlag;
212 };
213
nsvg__xformSetIdentity(float * t)214 void nsvg__xformSetIdentity(float *t) {
215 t[0] = 1.0f;
216 t[1] = 0.0f;
217 t[2] = 0.0f;
218 t[3] = 1.0f;
219 t[4] = 0.0f;
220 t[5] = 0.0f;
221 }
222
nsvg__xformSetTranslation(float * t,float tx,float ty)223 void nsvg__xformSetTranslation(float *t, float tx, float ty) {
224 t[0] = 1.0f;
225 t[1] = 0.0f;
226 t[2] = 0.0f;
227 t[3] = 1.0f;
228 t[4] = tx;
229 t[5] = ty;
230 }
231
nsvg__xformSetScale(float * t,float sx,float sy)232 void nsvg__xformSetScale(float *t, float sx, float sy) {
233 t[0] = sx;
234 t[1] = 0.0f;
235 t[2] = 0.0f;
236 t[3] = sy;
237 t[4] = 0.0f;
238 t[5] = 0.0f;
239 }
240
nsvg__xformSetSkewX(float * t,float a)241 void nsvg__xformSetSkewX(float *t, float a) {
242 t[0] = 1.0f;
243 t[1] = 0.0f;
244 t[2] = tanf(a);
245 t[3] = 1.0f;
246 t[4] = 0.0f;
247 t[5] = 0.0f;
248 }
249
nsvg__xformSetSkewY(float * t,float a)250 void nsvg__xformSetSkewY(float *t, float a) {
251 t[0] = 1.0f;
252 t[1] = tanf(a);
253 t[2] = 0.0f;
254 t[3] = 1.0f;
255 t[4] = 0.0f;
256 t[5] = 0.0f;
257 }
258
nsvg__xformSetRotation(float * t,float a)259 void nsvg__xformSetRotation(float *t, float a) {
260 float cs = cosf(a), sn = sinf(a);
261 t[0] = cs;
262 t[1] = sn;
263 t[2] = -sn;
264 t[3] = cs;
265 t[4] = 0.0f;
266 t[5] = 0.0f;
267 }
268
nsvg__xformMultiply(float * t,float * s)269 void nsvg__xformMultiply(float *t, float *s) {
270 float t0 = t[0] * s[0] + t[1] * s[2];
271 float t2 = t[2] * s[0] + t[3] * s[2];
272 float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
273 t[1] = t[0] * s[1] + t[1] * s[3];
274 t[3] = t[2] * s[1] + t[3] * s[3];
275 t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
276 t[0] = t0;
277 t[2] = t2;
278 t[4] = t4;
279 }
280
nsvg__xformPremultiply(float * t,float * s)281 void nsvg__xformPremultiply(float *t, float *s) {
282 float s2[6];
283 memcpy(s2, s, sizeof(float) * 6);
284 nsvg__xformMultiply(s2, t);
285 memcpy(t, s2, sizeof(float) * 6);
286 }
287
nsvg__xformPoint(float * dx,float * dy,float x,float y,float * t)288 void nsvg__xformPoint(float *dx, float *dy, float x, float y, float *t) {
289 *dx = x * t[0] + y * t[2] + t[4];
290 *dy = x * t[1] + y * t[3] + t[5];
291 }
292
nsvg__xformVec(float * dx,float * dy,float x,float y,float * t)293 void nsvg__xformVec(float *dx, float *dy, float x, float y, float *t) {
294 *dx = x * t[0] + y * t[2];
295 *dy = x * t[1] + y * t[3];
296 }
297
nsvg__createParser()298 struct NSVGParser *nsvg__createParser() {
299 struct NSVGParser *p;
300 p = (struct NSVGParser *)malloc(sizeof(struct NSVGParser));
301 if (p == NULL) goto error;
302 memset(p, 0, sizeof(struct NSVGParser));
303
304 p->image = (struct NSVGimage *)malloc(sizeof(struct NSVGimage));
305 if (p->image == NULL) goto error;
306 memset(p->image, 0, sizeof(struct NSVGimage));
307 p->image->width = -1.0f;
308 p->image->height = -1.0f;
309
310 // Init style
311 nsvg__xformSetIdentity(p->attr[0].xform);
312 p->attr[0].fillColor = 0;
313 p->attr[0].strokeColor = 0;
314 p->attr[0].fillOpacity = 1;
315 p->attr[0].strokeOpacity = 1;
316 p->attr[0].strokeWidth = 1;
317 p->attr[0].hasFill = 0;
318 p->attr[0].hasStroke = 0;
319 p->attr[0].visible = 1;
320
321 return p;
322
323 error:
324 if (p) {
325 if (p->image) free(p->image);
326 free(p);
327 }
328 return NULL;
329 }
330
nsvg__deletePaths(struct NSVGpath * path)331 void nsvg__deletePaths(struct NSVGpath *path) {
332 while (path) {
333 struct NSVGpath *next = path->next;
334 if (path->pts != NULL) free(path->pts);
335 free(path);
336 path = next;
337 }
338 }
339
nsvgDelete(struct NSVGimage * image)340 void nsvgDelete(struct NSVGimage *image) {
341 struct NSVGshape *next, *shape;
342 if (image == NULL) return;
343 shape = image->shapes;
344 while (shape != NULL) {
345 next = shape->next;
346 nsvg__deletePaths(shape->paths);
347 free(shape);
348 shape = next;
349 }
350 free(image);
351 }
352
nsvg__deleteParser(struct NSVGParser * p)353 void nsvg__deleteParser(struct NSVGParser *p) {
354 if (p != NULL) {
355 nsvg__deletePaths(p->plist);
356 nsvgDelete(p->image);
357 free(p->pts);
358 free(p);
359 }
360 }
361
nsvg__resetPath(struct NSVGParser * p)362 void nsvg__resetPath(struct NSVGParser *p) { p->npts = 0; }
363
nsvg__addPoint(struct NSVGParser * p,float x,float y)364 void nsvg__addPoint(struct NSVGParser *p, float x, float y) {
365 if (p->npts + 1 > p->cpts) {
366 p->cpts = p->cpts ? p->cpts * 2 : 8;
367 p->pts = (float *)realloc(p->pts, p->cpts * 2 * sizeof(float));
368 if (!p->pts) return;
369 }
370 p->pts[p->npts * 2 + 0] = x;
371 p->pts[p->npts * 2 + 1] = y;
372 p->npts++;
373 }
374
nsvg__moveTo(struct NSVGParser * p,float x,float y)375 void nsvg__moveTo(struct NSVGParser *p, float x, float y) {
376 nsvg__addPoint(p, x, y);
377 }
378
nsvg__lineTo(struct NSVGParser * p,float x,float y)379 void nsvg__lineTo(struct NSVGParser *p, float x, float y) {
380 float px, py, dx, dy;
381 if (p->npts > 0) {
382 px = p->pts[(p->npts - 1) * 2 + 0];
383 py = p->pts[(p->npts - 1) * 2 + 1];
384 dx = x - px;
385 dy = y - py;
386 nsvg__addPoint(p, px + dx / 3.0f, py + dy / 3.0f);
387 nsvg__addPoint(p, x - dx / 3.0f, y - dy / 3.0f);
388 nsvg__addPoint(p, x, y);
389 }
390 }
391
nsvg__cubicBezTo(struct NSVGParser * p,float cpx1,float cpy1,float cpx2,float cpy2,float x,float y)392 void nsvg__cubicBezTo(struct NSVGParser *p, float cpx1, float cpy1, float cpx2,
393 float cpy2, float x, float y) {
394 nsvg__addPoint(p, cpx1, cpy1);
395 nsvg__addPoint(p, cpx2, cpy2);
396 nsvg__addPoint(p, x, y);
397 }
398
nsvg__getAttr(struct NSVGParser * p)399 struct NSVGAttrib *nsvg__getAttr(struct NSVGParser *p) {
400 return &p->attr[p->attrHead];
401 }
402
nsvg__pushAttr(struct NSVGParser * p)403 void nsvg__pushAttr(struct NSVGParser *p) {
404 if (p->attrHead < NSVG_MAX_ATTR - 1) {
405 p->attrHead++;
406 memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead - 1],
407 sizeof(struct NSVGAttrib));
408 }
409 }
410
nsvg__popAttr(struct NSVGParser * p)411 void nsvg__popAttr(struct NSVGParser *p) {
412 if (p->attrHead > 0) p->attrHead--;
413 }
414
nsvg__addShape(struct NSVGParser * p)415 void nsvg__addShape(struct NSVGParser *p) {
416 struct NSVGAttrib *attr = nsvg__getAttr(p);
417 float scale = 1.0f;
418 struct NSVGshape *shape, *cur, *prev;
419
420 if (p->plist == NULL) return;
421
422 shape = (struct NSVGshape *)malloc(sizeof(struct NSVGshape));
423 if (shape == NULL) goto error;
424 memset(shape, 0, sizeof(struct NSVGshape));
425
426 scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3]));
427 shape->hasFill = attr->hasFill;
428 shape->hasStroke = attr->hasStroke;
429 shape->strokeWidth = attr->strokeWidth * scale;
430
431 shape->fillColor = attr->fillColor;
432 if (shape->hasFill)
433 shape->fillColor |= (unsigned int)(attr->fillOpacity * 255) << 24;
434
435 shape->strokeColor = attr->strokeColor;
436 if (shape->hasStroke)
437 shape->strokeColor |= (unsigned int)(attr->strokeOpacity * 255) << 24;
438
439 shape->paths = p->plist;
440 p->plist = NULL;
441
442 // Add to tail
443 prev = NULL;
444 cur = p->image->shapes;
445 while (cur != NULL) {
446 prev = cur;
447 cur = cur->next;
448 }
449 if (prev == NULL)
450 p->image->shapes = shape;
451 else
452 prev->next = shape;
453
454 return;
455
456 error:
457 if (shape) free(shape);
458 }
459
nsvg__addPath(struct NSVGParser * p,char closed)460 void nsvg__addPath(struct NSVGParser *p, char closed) {
461 struct NSVGAttrib *attr = nsvg__getAttr(p);
462 struct NSVGpath *path = NULL;
463 int i;
464
465 if (p->npts == 0) return;
466
467 if (closed) nsvg__lineTo(p, p->pts[0], p->pts[1]);
468
469 path = (struct NSVGpath *)malloc(sizeof(struct NSVGpath));
470 if (path == NULL) goto error;
471 memset(path, 0, sizeof(struct NSVGpath));
472
473 path->pts = (float *)malloc(p->npts * 2 * sizeof(float));
474 if (path->pts == NULL) goto error;
475 path->closed = closed;
476 path->npts = p->npts;
477
478 // Transform path.
479 for (i = 0; i < p->npts; ++i)
480 nsvg__xformPoint(&path->pts[i * 2], &path->pts[i * 2 + 1], p->pts[i * 2],
481 p->pts[i * 2 + 1], attr->xform);
482
483 path->next = p->plist;
484 p->plist = path;
485
486 return;
487
488 error:
489 if (path != NULL) {
490 if (path->pts != NULL) free(path->pts);
491 free(path);
492 }
493 }
494
nsvg__getNextPathItem(const char * s,char * it)495 const char *nsvg__getNextPathItem(const char *s, char *it) {
496 int i = 0;
497 it[0] = '\0';
498 // Skip white spaces and commas
499 while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
500 if (!*s) return s;
501 if (*s == '-' || *s == '+' || nsvg__isdigit(*s)) {
502 // sign
503 if (*s == '-' || *s == '+') {
504 if (i < 63) it[i++] = *s;
505 s++;
506 }
507 // integer part
508 while (*s && nsvg__isdigit(*s)) {
509 if (i < 63) it[i++] = *s;
510 s++;
511 }
512 if (*s == '.') {
513 // decimal point
514 if (i < 63) it[i++] = *s;
515 s++;
516 // fraction part
517 while (*s && nsvg__isdigit(*s)) {
518 if (i < 63) it[i++] = *s;
519 s++;
520 }
521 }
522 // exponent
523 if (*s == 'e' || *s == 'E') {
524 if (i < 63) it[i++] = *s;
525 s++;
526 if (*s == '-' || *s == '+') {
527 if (i < 63) it[i++] = *s;
528 s++;
529 }
530 while (*s && nsvg__isdigit(*s)) {
531 if (i < 63) it[i++] = *s;
532 s++;
533 }
534 }
535 it[i] = '\0';
536 } else {
537 // Parse command
538 it[0] = *s++;
539 it[1] = '\0';
540 return s;
541 }
542
543 return s;
544 }
545
546 #define NSVG_RGB(r, g, b) \
547 (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
548
nsvg__parseColorHex(const char * str)549 unsigned int nsvg__parseColorHex(const char *str) {
550 unsigned int c = 0, r = 0, g = 0, b = 0;
551 int n = 0;
552 str++; // skip #
553 // Calculate number of characters.
554 while (str[n] && !nsvg__isspace(str[n])) n++;
555 if (n == 6) {
556 sscanf(str, "%x", &c);
557 } else if (n == 3) {
558 sscanf(str, "%x", &c);
559 c = (c & 0xf) | ((c & 0xf0) << 4) | ((c & 0xf00) << 8);
560 c |= c << 4;
561 }
562 r = (c >> 16) & 0xff;
563 g = (c >> 8) & 0xff;
564 b = c & 0xff;
565 return NSVG_RGB(r, g, b);
566 }
567
nsvg__parseColorRGB(const char * str)568 unsigned int nsvg__parseColorRGB(const char *str) {
569 int r = -1, g = -1, b = -1;
570 char s1[32] = "", s2[32] = "";
571 sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
572 if (strchr(s1, '%')) {
573 return NSVG_RGB((r * 255) / 100, (g * 255) / 100, (b * 255) / 100);
574 } else {
575 return NSVG_RGB(r, g, b);
576 }
577 }
578
579 struct NSVGNamedColor {
580 const char *name;
581 unsigned int color;
582 };
583
584 struct NSVGNamedColor nsvg__colors[] = {
585
586 {"red", NSVG_RGB(255, 0, 0)},
587 {"green", NSVG_RGB(0, 128, 0)},
588 {"blue", NSVG_RGB(0, 0, 255)},
589 {"yellow", NSVG_RGB(255, 255, 0)},
590 {"cyan", NSVG_RGB(0, 255, 255)},
591 {"magenta", NSVG_RGB(255, 0, 255)},
592 {"black", NSVG_RGB(0, 0, 0)},
593 {"grey", NSVG_RGB(128, 128, 128)},
594 {"gray", NSVG_RGB(128, 128, 128)},
595 {"white", NSVG_RGB(255, 255, 255)},
596
597 {"aliceblue", NSVG_RGB(240, 248, 255)},
598 {"antiquewhite", NSVG_RGB(250, 235, 215)},
599 {"aqua", NSVG_RGB(0, 255, 255)},
600 {"aquamarine", NSVG_RGB(127, 255, 212)},
601 {"azure", NSVG_RGB(240, 255, 255)},
602 {"beige", NSVG_RGB(245, 245, 220)},
603 {"bisque", NSVG_RGB(255, 228, 196)},
604 {"blanchedalmond", NSVG_RGB(255, 235, 205)},
605 {"blueviolet", NSVG_RGB(138, 43, 226)},
606 {"brown", NSVG_RGB(165, 42, 42)},
607 {"burlywood", NSVG_RGB(222, 184, 135)},
608 {"cadetblue", NSVG_RGB(95, 158, 160)},
609 {"chartreuse", NSVG_RGB(127, 255, 0)},
610 {"chocolate", NSVG_RGB(210, 105, 30)},
611 {"coral", NSVG_RGB(255, 127, 80)},
612 {"cornflowerblue", NSVG_RGB(100, 149, 237)},
613 {"cornsilk", NSVG_RGB(255, 248, 220)},
614 {"crimson", NSVG_RGB(220, 20, 60)},
615 {"darkblue", NSVG_RGB(0, 0, 139)},
616 {"darkcyan", NSVG_RGB(0, 139, 139)},
617 {"darkgoldenrod", NSVG_RGB(184, 134, 11)},
618 {"darkgray", NSVG_RGB(169, 169, 169)},
619 {"darkgreen", NSVG_RGB(0, 100, 0)},
620 {"darkgrey", NSVG_RGB(169, 169, 169)},
621 {"darkkhaki", NSVG_RGB(189, 183, 107)},
622 {"darkmagenta", NSVG_RGB(139, 0, 139)},
623 {"darkolivegreen", NSVG_RGB(85, 107, 47)},
624 {"darkorange", NSVG_RGB(255, 140, 0)},
625 {"darkorchid", NSVG_RGB(153, 50, 204)},
626 {"darkred", NSVG_RGB(139, 0, 0)},
627 {"darksalmon", NSVG_RGB(233, 150, 122)},
628 {"darkseagreen", NSVG_RGB(143, 188, 143)},
629 {"darkslateblue", NSVG_RGB(72, 61, 139)},
630 {"darkslategray", NSVG_RGB(47, 79, 79)},
631 {"darkslategrey", NSVG_RGB(47, 79, 79)},
632 {"darkturquoise", NSVG_RGB(0, 206, 209)},
633 {"darkviolet", NSVG_RGB(148, 0, 211)},
634 {"deeppink", NSVG_RGB(255, 20, 147)},
635 {"deepskyblue", NSVG_RGB(0, 191, 255)},
636 {"dimgray", NSVG_RGB(105, 105, 105)},
637 {"dimgrey", NSVG_RGB(105, 105, 105)},
638 {"dodgerblue", NSVG_RGB(30, 144, 255)},
639 {"firebrick", NSVG_RGB(178, 34, 34)},
640 {"floralwhite", NSVG_RGB(255, 250, 240)},
641 {"forestgreen", NSVG_RGB(34, 139, 34)},
642 {"fuchsia", NSVG_RGB(255, 0, 255)},
643 {"gainsboro", NSVG_RGB(220, 220, 220)},
644 {"ghostwhite", NSVG_RGB(248, 248, 255)},
645 {"gold", NSVG_RGB(255, 215, 0)},
646 {"goldenrod", NSVG_RGB(218, 165, 32)},
647 {"greenyellow", NSVG_RGB(173, 255, 47)},
648 {"honeydew", NSVG_RGB(240, 255, 240)},
649 {"hotpink", NSVG_RGB(255, 105, 180)},
650 {"indianred", NSVG_RGB(205, 92, 92)},
651 {"indigo", NSVG_RGB(75, 0, 130)},
652 {"ivory", NSVG_RGB(255, 255, 240)},
653 {"khaki", NSVG_RGB(240, 230, 140)},
654 {"lavender", NSVG_RGB(230, 230, 250)},
655 {"lavenderblush", NSVG_RGB(255, 240, 245)},
656 {"lawngreen", NSVG_RGB(124, 252, 0)},
657 {"lemonchiffon", NSVG_RGB(255, 250, 205)},
658 {"lightblue", NSVG_RGB(173, 216, 230)},
659 {"lightcoral", NSVG_RGB(240, 128, 128)},
660 {"lightcyan", NSVG_RGB(224, 255, 255)},
661 {"lightgoldenrodyellow", NSVG_RGB(250, 250, 210)},
662 {"lightgray", NSVG_RGB(211, 211, 211)},
663 {"lightgreen", NSVG_RGB(144, 238, 144)},
664 {"lightgrey", NSVG_RGB(211, 211, 211)},
665 {"lightpink", NSVG_RGB(255, 182, 193)},
666 {"lightsalmon", NSVG_RGB(255, 160, 122)},
667 {"lightseagreen", NSVG_RGB(32, 178, 170)},
668 {"lightskyblue", NSVG_RGB(135, 206, 250)},
669 {"lightslategray", NSVG_RGB(119, 136, 153)},
670 {"lightslategrey", NSVG_RGB(119, 136, 153)},
671 {"lightsteelblue", NSVG_RGB(176, 196, 222)},
672 {"lightyellow", NSVG_RGB(255, 255, 224)},
673 {"lime", NSVG_RGB(0, 255, 0)},
674 {"limegreen", NSVG_RGB(50, 205, 50)},
675 {"linen", NSVG_RGB(250, 240, 230)},
676 {"maroon", NSVG_RGB(128, 0, 0)},
677 {"mediumaquamarine", NSVG_RGB(102, 205, 170)},
678 {"mediumblue", NSVG_RGB(0, 0, 205)},
679 {"mediumorchid", NSVG_RGB(186, 85, 211)},
680 {"mediumpurple", NSVG_RGB(147, 112, 219)},
681 {"mediumseagreen", NSVG_RGB(60, 179, 113)},
682 {"mediumslateblue", NSVG_RGB(123, 104, 238)},
683 {"mediumspringgreen", NSVG_RGB(0, 250, 154)},
684 {"mediumturquoise", NSVG_RGB(72, 209, 204)},
685 {"mediumvioletred", NSVG_RGB(199, 21, 133)},
686 {"midnightblue", NSVG_RGB(25, 25, 112)},
687 {"mintcream", NSVG_RGB(245, 255, 250)},
688 {"mistyrose", NSVG_RGB(255, 228, 225)},
689 {"moccasin", NSVG_RGB(255, 228, 181)},
690 {"navajowhite", NSVG_RGB(255, 222, 173)},
691 {"navy", NSVG_RGB(0, 0, 128)},
692 {"oldlace", NSVG_RGB(253, 245, 230)},
693 {"olive", NSVG_RGB(128, 128, 0)},
694 {"olivedrab", NSVG_RGB(107, 142, 35)},
695 {"orange", NSVG_RGB(255, 165, 0)},
696 {"orangered", NSVG_RGB(255, 69, 0)},
697 {"orchid", NSVG_RGB(218, 112, 214)},
698 {"palegoldenrod", NSVG_RGB(238, 232, 170)},
699 {"palegreen", NSVG_RGB(152, 251, 152)},
700 {"paleturquoise", NSVG_RGB(175, 238, 238)},
701 {"palevioletred", NSVG_RGB(219, 112, 147)},
702 {"papayawhip", NSVG_RGB(255, 239, 213)},
703 {"peachpuff", NSVG_RGB(255, 218, 185)},
704 {"peru", NSVG_RGB(205, 133, 63)},
705 {"pink", NSVG_RGB(255, 192, 203)},
706 {"plum", NSVG_RGB(221, 160, 221)},
707 {"powderblue", NSVG_RGB(176, 224, 230)},
708 {"purple", NSVG_RGB(128, 0, 128)},
709 {"rosybrown", NSVG_RGB(188, 143, 143)},
710 {"royalblue", NSVG_RGB(65, 105, 225)},
711 {"saddlebrown", NSVG_RGB(139, 69, 19)},
712 {"salmon", NSVG_RGB(250, 128, 114)},
713 {"sandybrown", NSVG_RGB(244, 164, 96)},
714 {"seagreen", NSVG_RGB(46, 139, 87)},
715 {"seashell", NSVG_RGB(255, 245, 238)},
716 {"sienna", NSVG_RGB(160, 82, 45)},
717 {"silver", NSVG_RGB(192, 192, 192)},
718 {"skyblue", NSVG_RGB(135, 206, 235)},
719 {"slateblue", NSVG_RGB(106, 90, 205)},
720 {"slategray", NSVG_RGB(112, 128, 144)},
721 {"slategrey", NSVG_RGB(112, 128, 144)},
722 {"snow", NSVG_RGB(255, 250, 250)},
723 {"springgreen", NSVG_RGB(0, 255, 127)},
724 {"steelblue", NSVG_RGB(70, 130, 180)},
725 {"tan", NSVG_RGB(210, 180, 140)},
726 {"teal", NSVG_RGB(0, 128, 128)},
727 {"thistle", NSVG_RGB(216, 191, 216)},
728 {"tomato", NSVG_RGB(255, 99, 71)},
729 {"turquoise", NSVG_RGB(64, 224, 208)},
730 {"violet", NSVG_RGB(238, 130, 238)},
731 {"wheat", NSVG_RGB(245, 222, 179)},
732 {"whitesmoke", NSVG_RGB(245, 245, 245)},
733 {"yellowgreen", NSVG_RGB(154, 205, 50)},
734 };
735
nsvg__parseColorName(const char * str)736 unsigned int nsvg__parseColorName(const char *str) {
737 int i, ncolors = sizeof(nsvg__colors) / sizeof(struct NSVGNamedColor);
738
739 for (i = 0; i < ncolors; i++) {
740 if (strcmp(nsvg__colors[i].name, str) == 0) {
741 return nsvg__colors[i].color;
742 }
743 }
744
745 return NSVG_RGB(128, 128, 128);
746 }
747
nsvg__parseColor(const char * str)748 unsigned int nsvg__parseColor(const char *str) {
749 int len = 0;
750 while (*str == ' ') ++str;
751 len = (int)strlen(str);
752 if (len >= 1 && *str == '#')
753 return nsvg__parseColorHex(str);
754 else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' &&
755 str[3] == '(')
756 return nsvg__parseColorRGB(str);
757 return nsvg__parseColorName(str);
758 }
759
nsvg__parseFloat(const char * str)760 float nsvg__parseFloat(const char *str) {
761 while (*str == ' ') ++str;
762 return (float)atof(str);
763 }
764
nsvg__parseTransformArgs(const char * str,float * args,int maxNa,int * na)765 int nsvg__parseTransformArgs(const char *str, float *args, int maxNa, int *na) {
766 const char *end;
767 const char *ptr;
768
769 *na = 0;
770 ptr = str;
771 while (*ptr && *ptr != '(') ++ptr;
772 if (*ptr == 0) return 1;
773 end = ptr;
774 while (*end && *end != ')') ++end;
775 if (*end == 0) return 1;
776
777 while (ptr < end) {
778 if (nsvg__isnum(*ptr)) {
779 if (*na >= maxNa) return 0;
780 args[(*na)++] = (float)atof(ptr);
781 while (ptr < end && nsvg__isnum(*ptr)) ++ptr;
782 } else {
783 ++ptr;
784 }
785 }
786 return (int)(end - str);
787 }
788
nsvg__parseMatrix(struct NSVGParser * p,const char * str)789 int nsvg__parseMatrix(struct NSVGParser *p, const char *str) {
790 float t[6];
791 int na = 0;
792 int len = nsvg__parseTransformArgs(str, t, 6, &na);
793 if (na != 6) return len;
794 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
795 return len;
796 }
797
nsvg__parseTranslate(struct NSVGParser * p,const char * str)798 int nsvg__parseTranslate(struct NSVGParser *p, const char *str) {
799 float args[2];
800 float t[6];
801 int na = 0;
802 int len = nsvg__parseTransformArgs(str, args, 2, &na);
803 if (na == 1) args[1] = 0.0;
804 nsvg__xformSetTranslation(t, args[0], args[1]);
805 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
806 return len;
807 }
808
nsvg__parseScale(struct NSVGParser * p,const char * str)809 int nsvg__parseScale(struct NSVGParser *p, const char *str) {
810 float args[2];
811 int na = 0;
812 float t[6];
813 int len = nsvg__parseTransformArgs(str, args, 2, &na);
814 if (na == 1) args[1] = args[0];
815 nsvg__xformSetScale(t, args[0], args[1]);
816 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
817 return len;
818 }
819
nsvg__parseSkewX(struct NSVGParser * p,const char * str)820 int nsvg__parseSkewX(struct NSVGParser *p, const char *str) {
821 float args[1];
822 int na = 0;
823 float t[6];
824 int len = nsvg__parseTransformArgs(str, args, 1, &na);
825 nsvg__xformSetSkewX(t, args[0] / 180.0f * NSVG_PI);
826 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
827 return len;
828 }
829
nsvg__parseSkewY(struct NSVGParser * p,const char * str)830 int nsvg__parseSkewY(struct NSVGParser *p, const char *str) {
831 float args[1];
832 int na = 0;
833 float t[6];
834 int len = nsvg__parseTransformArgs(str, args, 1, &na);
835 nsvg__xformSetSkewY(t, args[0] / 180.0f * NSVG_PI);
836 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
837 return len;
838 }
839
nsvg__parseRotate(struct NSVGParser * p,const char * str)840 int nsvg__parseRotate(struct NSVGParser *p, const char *str) {
841 float args[3];
842 int na = 0;
843 float t[6];
844 int len = nsvg__parseTransformArgs(str, args, 3, &na);
845 if (na == 1) args[1] = args[2] = 0.0f;
846
847 if (na > 1) {
848 nsvg__xformSetTranslation(t, -args[1], -args[2]);
849 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
850 }
851
852 nsvg__xformSetRotation(t, args[0] / 180.0f * NSVG_PI);
853 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
854
855 if (na > 1) {
856 nsvg__xformSetTranslation(t, args[1], args[2]);
857 nsvg__xformPremultiply(nsvg__getAttr(p)->xform, t);
858 }
859
860 return len;
861 }
862
nsvg__parseTransform(struct NSVGParser * p,const char * str)863 void nsvg__parseTransform(struct NSVGParser *p, const char *str) {
864 while (*str) {
865 if (strncmp(str, "matrix", 6) == 0)
866 str += nsvg__parseMatrix(p, str);
867 else if (strncmp(str, "translate", 9) == 0)
868 str += nsvg__parseTranslate(p, str);
869 else if (strncmp(str, "scale", 5) == 0)
870 str += nsvg__parseScale(p, str);
871 else if (strncmp(str, "rotate", 6) == 0)
872 str += nsvg__parseRotate(p, str);
873 else if (strncmp(str, "skewX", 5) == 0)
874 str += nsvg__parseSkewX(p, str);
875 else if (strncmp(str, "skewY", 5) == 0)
876 str += nsvg__parseSkewY(p, str);
877 else
878 ++str;
879 }
880 }
881
882 void nsvg__parseStyle(struct NSVGParser *p, const char *str);
883
nsvg__parseAttr(struct NSVGParser * p,const char * name,const char * value)884 int nsvg__parseAttr(struct NSVGParser *p, const char *name, const char *value) {
885 struct NSVGAttrib *attr = nsvg__getAttr(p);
886 if (!attr) return 0;
887
888 if (strcmp(name, "style") == 0) {
889 nsvg__parseStyle(p, value);
890 } else if (strcmp(name, "display") == 0) {
891 if (strcmp(value, "none") == 0)
892 attr->visible = 0;
893 else
894 attr->visible = 1;
895 } else if (strcmp(name, "fill") == 0) {
896 if (strcmp(value, "none") == 0) {
897 attr->hasFill = 0;
898 } else {
899 attr->hasFill = 1;
900 attr->fillColor = nsvg__parseColor(value);
901 }
902 } else if (strcmp(name, "fill-opacity") == 0) {
903 attr->fillOpacity = nsvg__parseFloat(value);
904 } else if (strcmp(name, "stroke") == 0) {
905 if (strcmp(value, "none") == 0) {
906 attr->hasStroke = 0;
907 } else {
908 attr->hasStroke = 1;
909 attr->strokeColor = nsvg__parseColor(value);
910 }
911 } else if (strcmp(name, "stroke-width") == 0) {
912 attr->strokeWidth = nsvg__parseFloat(value);
913 } else if (strcmp(name, "stroke-opacity") == 0) {
914 attr->strokeOpacity = nsvg__parseFloat(value);
915 } else if (strcmp(name, "transform") == 0) {
916 nsvg__parseTransform(p, value);
917 } else {
918 return 0;
919 }
920 return 1;
921 }
922
nsvg__parseNameValue(struct NSVGParser * p,const char * start,const char * end)923 int nsvg__parseNameValue(struct NSVGParser *p, const char *start,
924 const char *end) {
925 const char *str;
926 const char *val;
927 char name[512];
928 char value[512];
929 int n;
930
931 str = start;
932 while (str < end && *str != ':') ++str;
933
934 val = str;
935
936 // Right Trim
937 while (str > start && (*str == ':' || nsvg__isspace(*str))) --str;
938 ++str;
939
940 n = (int)(str - start);
941 if (n > 511) n = 511;
942 if (n) memcpy(name, start, n);
943 name[n] = 0;
944
945 while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
946
947 n = (int)(end - val);
948 if (n > 511) n = 511;
949 if (n) memcpy(value, val, n);
950 value[n] = 0;
951
952 return nsvg__parseAttr(p, name, value);
953 }
954
nsvg__parseStyle(struct NSVGParser * p,const char * str)955 void nsvg__parseStyle(struct NSVGParser *p, const char *str) {
956 const char *start;
957 const char *end;
958
959 while (*str) {
960 // Left Trim
961 while (*str && nsvg__isspace(*str)) ++str;
962 start = str;
963 while (*str && *str != ';') ++str;
964 end = str;
965
966 // Right Trim
967 while (end > start && (*end == ';' || nsvg__isspace(*end))) --end;
968 ++end;
969
970 nsvg__parseNameValue(p, start, end);
971 if (*str) ++str;
972 }
973 }
974
nsvg__parseAttribs(struct NSVGParser * p,const char ** attr)975 void nsvg__parseAttribs(struct NSVGParser *p, const char **attr) {
976 int i;
977 for (i = 0; attr[i]; i += 2) {
978 if (strcmp(attr[i], "style") == 0)
979 nsvg__parseStyle(p, attr[i + 1]);
980 else
981 nsvg__parseAttr(p, attr[i], attr[i + 1]);
982 }
983 }
984
nsvg__getArgsPerElement(char cmd)985 int nsvg__getArgsPerElement(char cmd) {
986 switch (cmd) {
987 case 'v':
988 case 'V':
989 case 'h':
990 case 'H':
991 return 1;
992 case 'm':
993 case 'M':
994 case 'l':
995 case 'L':
996 case 't':
997 case 'T':
998 return 2;
999 case 'q':
1000 case 'Q':
1001 case 's':
1002 case 'S':
1003 return 4;
1004 case 'c':
1005 case 'C':
1006 return 6;
1007 case 'a':
1008 case 'A':
1009 return 7;
1010 }
1011 return 0;
1012 }
1013
nsvg__pathMoveTo(struct NSVGParser * p,float * cpx,float * cpy,float * args,int rel)1014 void nsvg__pathMoveTo(struct NSVGParser *p, float *cpx, float *cpy, float *args,
1015 int rel) {
1016 if (rel) {
1017 *cpx += args[0];
1018 *cpy += args[1];
1019 } else {
1020 *cpx = args[0];
1021 *cpy = args[1];
1022 }
1023 nsvg__moveTo(p, *cpx, *cpy);
1024 }
1025
nsvg__pathLineTo(struct NSVGParser * p,float * cpx,float * cpy,float * args,int rel)1026 void nsvg__pathLineTo(struct NSVGParser *p, float *cpx, float *cpy, float *args,
1027 int rel) {
1028 if (rel) {
1029 *cpx += args[0];
1030 *cpy += args[1];
1031 } else {
1032 *cpx = args[0];
1033 *cpy = args[1];
1034 }
1035 nsvg__lineTo(p, *cpx, *cpy);
1036 }
1037
nsvg__pathHLineTo(struct NSVGParser * p,float * cpx,float * cpy,float * args,int rel)1038 void nsvg__pathHLineTo(struct NSVGParser *p, float *cpx, float *cpy,
1039 float *args, int rel) {
1040 if (rel)
1041 *cpx += args[0];
1042 else
1043 *cpx = args[0];
1044 nsvg__lineTo(p, *cpx, *cpy);
1045 }
1046
nsvg__pathVLineTo(struct NSVGParser * p,float * cpx,float * cpy,float * args,int rel)1047 void nsvg__pathVLineTo(struct NSVGParser *p, float *cpx, float *cpy,
1048 float *args, int rel) {
1049 if (rel)
1050 *cpy += args[0];
1051 else
1052 *cpy = args[0];
1053 nsvg__lineTo(p, *cpx, *cpy);
1054 }
1055
nsvg__pathCubicBezTo(struct NSVGParser * p,float * cpx,float * cpy,float * cpx2,float * cpy2,float * args,int rel)1056 void nsvg__pathCubicBezTo(struct NSVGParser *p, float *cpx, float *cpy,
1057 float *cpx2, float *cpy2, float *args, int rel) {
1058 float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
1059
1060 x1 = *cpx;
1061 y1 = *cpy;
1062 if (rel) {
1063 cx1 = *cpx + args[0];
1064 cy1 = *cpy + args[1];
1065 cx2 = *cpx + args[2];
1066 cy2 = *cpy + args[3];
1067 x2 = *cpx + args[4];
1068 y2 = *cpy + args[5];
1069 } else {
1070 cx1 = args[0];
1071 cy1 = args[1];
1072 cx2 = args[2];
1073 cy2 = args[3];
1074 x2 = args[4];
1075 y2 = args[5];
1076 }
1077
1078 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
1079
1080 *cpx2 = cx2;
1081 *cpy2 = cy2;
1082 *cpx = x2;
1083 *cpy = y2;
1084 }
1085
nsvg__pathCubicBezShortTo(struct NSVGParser * p,float * cpx,float * cpy,float * cpx2,float * cpy2,float * args,int rel)1086 void nsvg__pathCubicBezShortTo(struct NSVGParser *p, float *cpx, float *cpy,
1087 float *cpx2, float *cpy2, float *args, int rel) {
1088 float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
1089
1090 x1 = *cpx;
1091 y1 = *cpy;
1092 if (rel) {
1093 cx2 = *cpx + args[0];
1094 cy2 = *cpy + args[1];
1095 x2 = *cpx + args[2];
1096 y2 = *cpy + args[3];
1097 } else {
1098 cx2 = args[0];
1099 cy2 = args[1];
1100 x2 = args[2];
1101 y2 = args[3];
1102 }
1103
1104 cx1 = 2 * x1 - *cpx2;
1105 cy1 = 2 * y1 - *cpy2;
1106
1107 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
1108
1109 *cpx2 = cx2;
1110 *cpy2 = cy2;
1111 *cpx = x2;
1112 *cpy = y2;
1113 }
1114
nsvg__pathQuadBezTo(struct NSVGParser * p,float * cpx,float * cpy,float * cpx2,float * cpy2,float * args,int rel)1115 void nsvg__pathQuadBezTo(struct NSVGParser *p, float *cpx, float *cpy,
1116 float *cpx2, float *cpy2, float *args, int rel) {
1117 float x1, y1, x2, y2, cx, cy;
1118 float cx1, cy1, cx2, cy2;
1119
1120 x1 = *cpx;
1121 y1 = *cpy;
1122 if (rel) {
1123 cx = *cpx + args[0];
1124 cy = *cpy + args[1];
1125 x2 = *cpx + args[2];
1126 y2 = *cpy + args[3];
1127 } else {
1128 cx = args[0];
1129 cy = args[1];
1130 x2 = args[2];
1131 y2 = args[3];
1132 }
1133
1134 // Convert to cubix bezier
1135 cx1 = x1 + 2.0f / 3.0f * (cx - x1);
1136 cy1 = y1 + 2.0f / 3.0f * (cy - y1);
1137 cx2 = x2 + 2.0f / 3.0f * (cx - x2);
1138 cy2 = y2 + 2.0f / 3.0f * (cy - y2);
1139 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
1140
1141 *cpx2 = cx;
1142 *cpy2 = cy;
1143 *cpx = x2;
1144 *cpy = y2;
1145 }
1146
nsvg__pathQuadBezShortTo(struct NSVGParser * p,float * cpx,float * cpy,float * cpx2,float * cpy2,float * args,int rel)1147 void nsvg__pathQuadBezShortTo(struct NSVGParser *p, float *cpx, float *cpy,
1148 float *cpx2, float *cpy2, float *args, int rel) {
1149 float x1, y1, x2, y2, cx, cy;
1150 float cx1, cy1, cx2, cy2;
1151
1152 x1 = *cpx;
1153 y1 = *cpy;
1154 if (rel) {
1155 x2 = *cpx + args[0];
1156 y2 = *cpy + args[1];
1157 } else {
1158 x2 = args[0];
1159 y2 = args[1];
1160 }
1161
1162 cx = 2 * x1 - *cpx2;
1163 cy = 2 * y1 - *cpy2;
1164
1165 // Convert to cubix bezier
1166 cx1 = x1 + 2.0f / 3.0f * (cx - x1);
1167 cy1 = y1 + 2.0f / 3.0f * (cy - y1);
1168 cx2 = x2 + 2.0f / 3.0f * (cx - x2);
1169 cy2 = y2 + 2.0f / 3.0f * (cy - y2);
1170 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
1171
1172 *cpx2 = cx;
1173 *cpy2 = cy;
1174 *cpx = x2;
1175 *cpy = y2;
1176 }
1177
nsvg__sqr(float x)1178 float nsvg__sqr(float x) { return x * x; }
nsvg__vmag(float x,float y)1179 float nsvg__vmag(float x, float y) { return sqrtf(x * x + y * y); }
1180
nsvg__vecrat(float ux,float uy,float vx,float vy)1181 float nsvg__vecrat(float ux, float uy, float vx, float vy) {
1182 return (ux * vx + uy * vy) / (nsvg__vmag(ux, uy) * nsvg__vmag(vx, vy));
1183 }
1184
nsvg__vecang(float ux,float uy,float vx,float vy)1185 float nsvg__vecang(float ux, float uy, float vx, float vy) {
1186 float r = nsvg__vecrat(ux, uy, vx, vy);
1187 if (r < -1.0f) r = -1.0f;
1188 if (r > 1.0f) r = 1.0f;
1189 return ((ux * vy < uy * vx) ? -1.0f : 1.0f) * acosf(r);
1190 }
1191
nsvg__pathArcTo(struct NSVGParser * p,float * cpx,float * cpy,float * args,int rel)1192 void nsvg__pathArcTo(struct NSVGParser *p, float *cpx, float *cpy, float *args,
1193 int rel) {
1194 // Ported from canvg (https://code.google.com/p/canvg/)
1195 float rx, ry, rotx;
1196 float x1, y1, x2, y2, cx, cy, dx, dy, d;
1197 float x1p, y1p, cxp, cyp, s, sa, sb;
1198 float ux, uy, vx, vy, a1, da;
1199 float x, y, tanx, tany, a, px, py, ptanx, ptany, t[6];
1200 float sinrx, cosrx;
1201 int fa, fs;
1202 int i, ndivs;
1203 float hda, kappa;
1204
1205 rx = fabsf(args[0]); // y radius
1206 ry = fabsf(args[1]); // x radius
1207 rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle
1208 fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
1209 fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
1210 x1 = *cpx; // start point
1211 y1 = *cpy;
1212 if (rel) { // end point
1213 x2 = *cpx + args[5];
1214 y2 = *cpy + args[6];
1215 } else {
1216 x2 = args[5];
1217 y2 = args[6];
1218 }
1219
1220 dx = x1 - x2;
1221 dy = y1 - y2;
1222 d = sqrtf(dx * dx + dy * dy);
1223 if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
1224 // The arc degenerates to a line
1225 nsvg__lineTo(p, x2, y2);
1226 *cpx = x2;
1227 *cpy = y2;
1228 return;
1229 }
1230
1231 sinrx = sinf(rotx);
1232 cosrx = cosf(rotx);
1233
1234 // Convert to center point parameterization.
1235 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1236 // 1) Compute x1', y1'
1237 x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
1238 y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
1239 d = nsvg__sqr(x1p) / nsvg__sqr(rx) + nsvg__sqr(y1p) / nsvg__sqr(ry);
1240 if (d > 1) {
1241 d = sqrtf(d);
1242 rx *= d;
1243 ry *= d;
1244 }
1245 // 2) Compute cx', cy'
1246 s = 0.0f;
1247 sa = nsvg__sqr(rx) * nsvg__sqr(ry) - nsvg__sqr(rx) * nsvg__sqr(y1p) -
1248 nsvg__sqr(ry) * nsvg__sqr(x1p);
1249 sb = nsvg__sqr(rx) * nsvg__sqr(y1p) + nsvg__sqr(ry) * nsvg__sqr(x1p);
1250 if (sa < 0.0f) sa = 0.0f;
1251 if (sb > 0.0f) s = sqrtf(sa / sb);
1252 if (fa == fs) s = -s;
1253 cxp = s * rx * y1p / ry;
1254 cyp = s * -ry * x1p / rx;
1255
1256 // 3) Compute cx,cy from cx',cy'
1257 cx = (x1 + x2) / 2.0f + cosrx * cxp - sinrx * cyp;
1258 cy = (y1 + y2) / 2.0f + sinrx * cxp + cosrx * cyp;
1259
1260 // 4) Calculate theta1, and delta theta.
1261 ux = (x1p - cxp) / rx;
1262 uy = (y1p - cyp) / ry;
1263 vx = (-x1p - cxp) / rx;
1264 vy = (-y1p - cyp) / ry;
1265 a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle
1266 da = nsvg__vecang(ux, uy, vx, vy); // Delta angle
1267
1268 // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
1269 // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
1270
1271 if (fa) {
1272 // Choose large arc
1273 if (da > 0.0f)
1274 da = da - 2 * NSVG_PI;
1275 else
1276 da = 2 * NSVG_PI + da;
1277 }
1278
1279 // Approximate the arc using cubic spline segments.
1280 t[0] = cosrx;
1281 t[1] = sinrx;
1282 t[2] = -sinrx;
1283 t[3] = cosrx;
1284 t[4] = cx;
1285 t[5] = cy;
1286
1287 // Split arc into max 90 degree segments.
1288 ndivs = (int)(ceil(fabsf(da)) / (NSVG_PI * 0.5f) + 0.5f);
1289 hda = (da / (float)ndivs) / 2.0f;
1290 kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
1291 if (da < 0.0f) kappa = -kappa;
1292
1293 for (i = 0; i <= ndivs; i++) {
1294 a = a1 + da * (i / (float)ndivs);
1295 dx = cosf(a);
1296 dy = sinf(a);
1297 nsvg__xformPoint(&x, &y, dx * rx, dy * ry, t); // position
1298 nsvg__xformVec(&tanx, &tany, -dy * rx * kappa, dx * ry * kappa,
1299 t); // tangent
1300 if (i > 0)
1301 nsvg__cubicBezTo(p, px + ptanx, py + ptany, x - tanx, y - tany, x, y);
1302 px = x;
1303 py = y;
1304 ptanx = tanx;
1305 ptany = tany;
1306 }
1307
1308 *cpx = x2;
1309 *cpy = y2;
1310 }
1311
nsvg__parsePath(struct NSVGParser * p,const char ** attr)1312 void nsvg__parsePath(struct NSVGParser *p, const char **attr) {
1313 const char *s;
1314 char cmd;
1315 float args[10];
1316 int nargs;
1317 int rargs;
1318 float cpx, cpy, cpx2, cpy2;
1319 const char *tmp[4];
1320 char closedFlag;
1321 int i;
1322 char item[64];
1323 float prev_m_cpx, prev_m_cpy;
1324 bool prev_m_exists;
1325
1326 for (i = 0; attr[i]; i += 2) {
1327 if (strcmp(attr[i], "d") == 0) {
1328 s = attr[i + 1];
1329
1330 nsvg__resetPath(p);
1331 cpx = 0;
1332 cpy = 0;
1333 closedFlag = 0;
1334 nargs = 0;
1335 prev_m_exists = false;
1336
1337 while (*s) {
1338 s = nsvg__getNextPathItem(s, item);
1339 if (!*item) break;
1340 if (nsvg__isnum(item[0])) {
1341 if (nargs < 10) args[nargs++] = (float)atof(item);
1342 if (nargs >= rargs) {
1343 switch (cmd) {
1344 case 'm':
1345 case 'M':
1346
1347 // If moveto is relative it relative to previous moveto point
1348 if (cmd == 'm' && prev_m_exists) {
1349 cpx = prev_m_cpx;
1350 cpy = prev_m_cpy;
1351 }
1352
1353 nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
1354
1355 prev_m_cpx = cpx;
1356 prev_m_cpy = cpy;
1357 prev_m_exists = true;
1358
1359 // Moveto can be followed by multiple coordinate pairs,
1360 // which should be treated as linetos.
1361 cmd = (cmd == 'm') ? 'l' : 'L';
1362 rargs = nsvg__getArgsPerElement(cmd);
1363 break;
1364 case 'l':
1365 case 'L':
1366 nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
1367 break;
1368 case 'H':
1369 case 'h':
1370 nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
1371 break;
1372 case 'V':
1373 case 'v':
1374 nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
1375 break;
1376 case 'C':
1377 case 'c':
1378 nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args,
1379 cmd == 'c' ? 1 : 0);
1380 break;
1381 case 'S':
1382 case 's':
1383 nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args,
1384 cmd == 's' ? 1 : 0);
1385 break;
1386 case 'Q':
1387 case 'q':
1388 nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args,
1389 cmd == 'q' ? 1 : 0);
1390 break;
1391 case 'T':
1392 case 't':
1393 nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args,
1394 cmd == 's' ? 1 : 0);
1395 break;
1396 case 'A':
1397 case 'a':
1398 nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
1399 break;
1400 default:
1401 if (nargs >= 2) {
1402 cpx = args[nargs - 2];
1403 cpy = args[nargs - 1];
1404 }
1405 break;
1406 }
1407 nargs = 0;
1408 }
1409 } else {
1410 cmd = item[0];
1411 rargs = nsvg__getArgsPerElement(cmd);
1412 if (cmd == 'M' || cmd == 'm') {
1413 // Commit path.
1414 if (p->npts > 0) nsvg__addPath(p, closedFlag);
1415 // Start new subpath.
1416 nsvg__resetPath(p);
1417 closedFlag = 0;
1418 nargs = 0;
1419 } else if (cmd == 'Z' || cmd == 'z') {
1420 closedFlag = 1;
1421 // Commit path.
1422 if (p->npts > 0) nsvg__addPath(p, closedFlag);
1423 // Start new subpath.
1424 nsvg__resetPath(p);
1425 closedFlag = 0;
1426 nargs = 0;
1427 }
1428 }
1429 }
1430 // Commit path.
1431 if (p->npts) nsvg__addPath(p, closedFlag);
1432 } else {
1433 tmp[0] = attr[i];
1434 tmp[1] = attr[i + 1];
1435 tmp[2] = 0;
1436 tmp[3] = 0;
1437 nsvg__parseAttribs(p, tmp);
1438 }
1439 }
1440
1441 nsvg__addShape(p);
1442 }
1443
nsvg__parseRect(struct NSVGParser * p,const char ** attr)1444 void nsvg__parseRect(struct NSVGParser *p, const char **attr) {
1445 float x = 0.0f;
1446 float y = 0.0f;
1447 float w = 0.0f;
1448 float h = 0.0f;
1449 float rx = -1.0f; // marks not set
1450 float ry = -1.0f;
1451 int i;
1452
1453 for (i = 0; attr[i]; i += 2) {
1454 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1455 if (strcmp(attr[i], "x") == 0) x = nsvg__parseFloat(attr[i + 1]);
1456 if (strcmp(attr[i], "y") == 0) y = nsvg__parseFloat(attr[i + 1]);
1457 if (strcmp(attr[i], "width") == 0) w = nsvg__parseFloat(attr[i + 1]);
1458 if (strcmp(attr[i], "height") == 0) h = nsvg__parseFloat(attr[i + 1]);
1459 if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(attr[i + 1]));
1460 if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(attr[i + 1]));
1461 }
1462 }
1463
1464 if (rx < 0.0f && ry > 0.0f) rx = ry;
1465 if (ry < 0.0f && rx > 0.0f) ry = rx;
1466 if (rx < 0.0f) rx = 0.0f;
1467 if (ry < 0.0f) ry = 0.0f;
1468 if (rx > w / 2.0f) rx = w / 2.0f;
1469 if (ry > h / 2.0f) ry = h / 2.0f;
1470
1471 if (w != 0.0f && h != 0.0f) {
1472 nsvg__resetPath(p);
1473
1474 if (rx < 0.00001f || ry < 0.0001f) {
1475 nsvg__moveTo(p, x, y);
1476 nsvg__lineTo(p, x + w, y);
1477 nsvg__lineTo(p, x + w, y + h);
1478 nsvg__lineTo(p, x, y + h);
1479 } else {
1480 // Rounded rectangle
1481 nsvg__moveTo(p, x + rx, y);
1482 nsvg__lineTo(p, x + w - rx, y);
1483 nsvg__cubicBezTo(p, x + w - rx * (1 - NSVG_KAPPA90), y, x + w,
1484 y + ry * (1 - NSVG_KAPPA90), x + w, y + ry);
1485 nsvg__lineTo(p, x + w, y + h - ry);
1486 nsvg__cubicBezTo(p, x + w, y + h - ry * (1 - NSVG_KAPPA90),
1487 x + w - rx * (1 - NSVG_KAPPA90), y + h, x + w - rx,
1488 y + h);
1489 nsvg__lineTo(p, x + rx, y + h);
1490 nsvg__cubicBezTo(p, x + rx * (1 - NSVG_KAPPA90), y + h, x,
1491 y + h - ry * (1 - NSVG_KAPPA90), x, y + h - ry);
1492 nsvg__lineTo(p, x, y + ry);
1493 nsvg__cubicBezTo(p, x, y + ry * (1 - NSVG_KAPPA90),
1494 x + rx * (1 - NSVG_KAPPA90), y, x + rx, y);
1495 }
1496
1497 nsvg__addPath(p, 1);
1498
1499 nsvg__addShape(p);
1500 }
1501 }
1502
nsvg__parseCircle(struct NSVGParser * p,const char ** attr)1503 void nsvg__parseCircle(struct NSVGParser *p, const char **attr) {
1504 float cx = 0.0f;
1505 float cy = 0.0f;
1506 float r = 0.0f;
1507 int i;
1508
1509 for (i = 0; attr[i]; i += 2) {
1510 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1511 if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(attr[i + 1]);
1512 if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(attr[i + 1]);
1513 if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(attr[i + 1]));
1514 }
1515 }
1516
1517 if (r > 0.0f) {
1518 nsvg__resetPath(p);
1519
1520 nsvg__moveTo(p, cx + r, cy);
1521 nsvg__cubicBezTo(p, cx + r, cy + r * NSVG_KAPPA90, cx + r * NSVG_KAPPA90,
1522 cy + r, cx, cy + r);
1523 nsvg__cubicBezTo(p, cx - r * NSVG_KAPPA90, cy + r, cx - r,
1524 cy + r * NSVG_KAPPA90, cx - r, cy);
1525 nsvg__cubicBezTo(p, cx - r, cy - r * NSVG_KAPPA90, cx - r * NSVG_KAPPA90,
1526 cy - r, cx, cy - r);
1527 nsvg__cubicBezTo(p, cx + r * NSVG_KAPPA90, cy - r, cx + r,
1528 cy - r * NSVG_KAPPA90, cx + r, cy);
1529
1530 nsvg__addPath(p, 1);
1531
1532 nsvg__addShape(p);
1533 }
1534 }
1535
nsvg__parseEllipse(struct NSVGParser * p,const char ** attr)1536 void nsvg__parseEllipse(struct NSVGParser *p, const char **attr) {
1537 float cx = 0.0f;
1538 float cy = 0.0f;
1539 float rx = 0.0f;
1540 float ry = 0.0f;
1541 int i;
1542
1543 for (i = 0; attr[i]; i += 2) {
1544 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1545 if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(attr[i + 1]);
1546 if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(attr[i + 1]);
1547 if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(attr[i + 1]));
1548 if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(attr[i + 1]));
1549 }
1550 }
1551
1552 if (rx > 0.0f && ry > 0.0f) {
1553 nsvg__resetPath(p);
1554
1555 nsvg__moveTo(p, cx + rx, cy);
1556 nsvg__cubicBezTo(p, cx + rx, cy + ry * NSVG_KAPPA90, cx + rx * NSVG_KAPPA90,
1557 cy + ry, cx, cy + ry);
1558 nsvg__cubicBezTo(p, cx - rx * NSVG_KAPPA90, cy + ry, cx - rx,
1559 cy + ry * NSVG_KAPPA90, cx - rx, cy);
1560 nsvg__cubicBezTo(p, cx - rx, cy - ry * NSVG_KAPPA90, cx - rx * NSVG_KAPPA90,
1561 cy - ry, cx, cy - ry);
1562 nsvg__cubicBezTo(p, cx + rx * NSVG_KAPPA90, cy - ry, cx + rx,
1563 cy - ry * NSVG_KAPPA90, cx + rx, cy);
1564
1565 nsvg__addPath(p, 1);
1566
1567 nsvg__addShape(p);
1568 }
1569 }
1570
nsvg__parseLine(struct NSVGParser * p,const char ** attr)1571 void nsvg__parseLine(struct NSVGParser *p, const char **attr) {
1572 float x1 = 0.0;
1573 float y1 = 0.0;
1574 float x2 = 0.0;
1575 float y2 = 0.0;
1576 int i;
1577
1578 for (i = 0; attr[i]; i += 2) {
1579 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1580 if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseFloat(attr[i + 1]);
1581 if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseFloat(attr[i + 1]);
1582 if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseFloat(attr[i + 1]);
1583 if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(attr[i + 1]);
1584 }
1585 }
1586
1587 nsvg__resetPath(p);
1588
1589 nsvg__moveTo(p, x1, y1);
1590 nsvg__lineTo(p, x2, y2);
1591
1592 nsvg__addPath(p, 0);
1593
1594 nsvg__addShape(p);
1595 }
1596
nsvg__parsePoly(struct NSVGParser * p,const char ** attr,int closeFlag)1597 void nsvg__parsePoly(struct NSVGParser *p, const char **attr, int closeFlag) {
1598 int i;
1599 const char *s;
1600 float args[2];
1601 int nargs, npts = 0;
1602 char item[64];
1603
1604 nsvg__resetPath(p);
1605
1606 for (i = 0; attr[i]; i += 2) {
1607 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1608 if (strcmp(attr[i], "points") == 0) {
1609 s = attr[i + 1];
1610 nargs = 0;
1611 while (*s) {
1612 s = nsvg__getNextPathItem(s, item);
1613 args[nargs++] = (float)atof(item);
1614 if (nargs >= 2) {
1615 if (npts == 0)
1616 nsvg__moveTo(p, args[0], args[1]);
1617 else
1618 nsvg__lineTo(p, args[0], args[1]);
1619 nargs = 0;
1620 npts++;
1621 }
1622 }
1623 }
1624 }
1625 }
1626
1627 nsvg__addPath(p, (char)closeFlag);
1628
1629 nsvg__addShape(p);
1630 }
1631
nsvg__parseSVG(struct NSVGParser * p,const char ** attr)1632 void nsvg__parseSVG(struct NSVGParser *p, const char **attr) {
1633 int i;
1634 for (i = 0; attr[i]; i += 2) {
1635 if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1636 if (strcmp(attr[i], "width") == 0) {
1637 p->image->wunits[0] = '\0';
1638 sscanf(attr[i + 1], "%f%s", &p->image->width, p->image->wunits);
1639 } else if (strcmp(attr[i], "height") == 0) {
1640 p->image->hunits[0] = '\0';
1641 sscanf(attr[i + 1], "%f%s", &p->image->height, p->image->hunits);
1642 }
1643 }
1644 }
1645 }
1646
nsvg__startElement(void * ud,const char * el,const char ** attr)1647 void nsvg__startElement(void *ud, const char *el, const char **attr) {
1648 struct NSVGParser *p = (struct NSVGParser *)ud;
1649
1650 // Skip everything in defs
1651 if (p->defsFlag) return;
1652
1653 if (strcmp(el, "g") == 0) {
1654 nsvg__pushAttr(p);
1655 nsvg__parseAttribs(p, attr);
1656 } else if (strcmp(el, "path") == 0) {
1657 if (p->pathFlag) // Do not allow nested paths.
1658 return;
1659 nsvg__pushAttr(p);
1660 nsvg__parsePath(p, attr);
1661 nsvg__popAttr(p);
1662 } else if (strcmp(el, "rect") == 0) {
1663 nsvg__pushAttr(p);
1664 nsvg__parseRect(p, attr);
1665 nsvg__popAttr(p);
1666 } else if (strcmp(el, "circle") == 0) {
1667 nsvg__pushAttr(p);
1668 nsvg__parseCircle(p, attr);
1669 nsvg__popAttr(p);
1670 } else if (strcmp(el, "ellipse") == 0) {
1671 nsvg__pushAttr(p);
1672 nsvg__parseEllipse(p, attr);
1673 nsvg__popAttr(p);
1674 } else if (strcmp(el, "line") == 0) {
1675 nsvg__pushAttr(p);
1676 nsvg__parseLine(p, attr);
1677 nsvg__popAttr(p);
1678 } else if (strcmp(el, "polyline") == 0) {
1679 nsvg__pushAttr(p);
1680 nsvg__parsePoly(p, attr, 0);
1681 nsvg__popAttr(p);
1682 } else if (strcmp(el, "polygon") == 0) {
1683 nsvg__pushAttr(p);
1684 nsvg__parsePoly(p, attr, 1);
1685 nsvg__popAttr(p);
1686 } else if (strcmp(el, "defs") == 0) {
1687 p->defsFlag = 1;
1688 } else if (strcmp(el, "svg") == 0) {
1689 nsvg__parseSVG(p, attr);
1690 }
1691 }
1692
nsvg__endElement(void * ud,const char * el)1693 void nsvg__endElement(void *ud, const char *el) {
1694 struct NSVGParser *p = (struct NSVGParser *)ud;
1695
1696 if (strcmp(el, "g") == 0) {
1697 nsvg__popAttr(p);
1698 } else if (strcmp(el, "path") == 0) {
1699 p->pathFlag = 0;
1700 } else if (strcmp(el, "defs") == 0) {
1701 p->defsFlag = 0;
1702 }
1703 }
1704
nsvg__content(void * ud,const char * s)1705 void nsvg__content(void *ud, const char *s) {
1706 // empty
1707 }
1708
dump(struct NSVGimage * image)1709 void dump(struct NSVGimage *image) {
1710 struct NSVGshape *shape;
1711 if (image == NULL) return;
1712 shape = image->shapes;
1713 while (shape != NULL) {
1714 struct NSVGpath *path;
1715 path = shape->paths;
1716 while (path) path = path->next;
1717 shape = shape->next;
1718 }
1719 }
1720
nsvgParse(char * input)1721 struct NSVGimage *nsvgParse(char *input) {
1722 struct NSVGParser *p;
1723 struct NSVGimage *ret = 0;
1724
1725 p = nsvg__createParser();
1726 if (p == NULL) {
1727 return NULL;
1728 }
1729
1730 nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
1731
1732 ret = p->image;
1733 p->image = NULL;
1734
1735 dump(ret);
1736
1737 nsvg__deleteParser(p);
1738
1739 return ret;
1740 }
1741
nsvgParseFromFile(const char * filename)1742 struct NSVGimage *nsvgParseFromFile(const char *filename) {
1743 FILE *fp = NULL;
1744 int size;
1745 char *data = NULL;
1746 struct NSVGimage *image = NULL;
1747
1748 fp = fopen(filename, "rb");
1749 if (!fp) goto error;
1750 fseek(fp, 0, SEEK_END);
1751 size = ftell(fp);
1752 fseek(fp, 0, SEEK_SET);
1753 data = (char *)malloc(size + 1);
1754 if (data == NULL) goto error;
1755 fread(data, size, 1, fp);
1756 data[size] = '\0'; // Must be null terminated.
1757 fclose(fp);
1758 image = nsvgParse(data);
1759 free(data);
1760
1761 return image;
1762
1763 error:
1764 if (fp) fclose(fp);
1765 if (data) free(data);
1766 if (image) nsvgDelete(image);
1767 return NULL;
1768 }
1769
1770 } // namespace svg_parser
1771
1772 //=------------------------------------------------------------------------------------------------------------------------------
1773 //=------------------------------------------------------------------------------------------------------------------------------
1774 //=------------------------------------------------------------------------------------------------------------------------------
1775 //=------------------------------------------------------------------------------------------------------------------------------
1776
1777 class TImageWriterSvg final : public TImageWriter {
1778 public:
1779 TImageWriterSvg(const TFilePath &, TPropertyGroup *);
~TImageWriterSvg()1780 ~TImageWriterSvg() {}
1781
1782 private:
1783 // double m_maxThickness;
1784 // not implemented
1785 TImageWriterSvg(const TImageWriterSvg &);
1786 TImageWriterSvg &operator=(const TImageWriterSvg &src);
1787
1788 public:
1789 void save(const TImageP &) override;
1790 };
1791
1792 //-----------------------------------------------------------------------------
1793 class TImageReaderSvg final : public TImageReader {
1794 TLevelP m_level;
1795
1796 public:
TImageReaderSvg(const TFilePath & path,TLevelP & level)1797 TImageReaderSvg(const TFilePath &path, TLevelP &level)
1798 : TImageReader(path), m_level(level) {}
1799
1800 TImageP load() override;
1801 };
1802
getFrameWriter(TFrameId fid)1803 TImageWriterP TLevelWriterSvg::getFrameWriter(TFrameId fid) {
1804 TImageWriterSvg *iwm =
1805 new TImageWriterSvg(m_path.withFrame(fid), getProperties());
1806 return TImageWriterP(iwm);
1807 }
1808
1809 //-----------------------------------------------------------------------------
TImageWriterSvg(const TFilePath & f,TPropertyGroup * prop)1810 TImageWriterSvg::TImageWriterSvg(const TFilePath &f, TPropertyGroup *prop)
1811 : TImageWriter(f)
1812 //, m_maxThickness(0)
1813 {
1814 setProperties(prop);
1815 }
1816
1817 //-----------------------------------------------------------------------------
1818
TLevelWriterSvg(const TFilePath & path,TPropertyGroup * winfo)1819 TLevelWriterSvg::TLevelWriterSvg(const TFilePath &path, TPropertyGroup *winfo)
1820 : TLevelWriter(path, winfo)
1821 //, m_pli (0)
1822 //, m_frameNumber (0)
1823 {}
1824
1825 //-----------------------------------------------------------------------------
1826
writeRegion(TRegion * r,TPalette * plt,QTextStream & out,double ly)1827 static void writeRegion(TRegion *r, TPalette *plt, QTextStream &out,
1828 double ly) {
1829 if (r->getEdgeCount() == 0) return;
1830 std::vector<const TQuadratic *> quadsOutline;
1831
1832 for (int i = 0; i < (int)r->getEdgeCount(); i++) {
1833 TEdge *e = r->getEdge(i);
1834 TStroke *s = e->m_s;
1835 int index0, index1;
1836 double t0, t1;
1837 double w0 = e->m_w0, w1 = e->m_w1;
1838
1839 if (w0 > w1) {
1840 TStroke *s1 = new TStroke(*s);
1841 s1->changeDirection();
1842 double totalLength = s->getLength();
1843 w0 = s1->getParameterAtLength(totalLength - s->getLength(w0));
1844 w1 = s1->getParameterAtLength(totalLength - s->getLength(w1));
1845 s = s1;
1846
1847 assert(w0 <= w1);
1848 }
1849
1850 s->getChunkAndT(w0, index0, t0);
1851 s->getChunkAndT(w1, index1, t1);
1852
1853 for (int j = index0; j <= index1; j++) {
1854 const TQuadratic *q = s->getChunk(j);
1855 if (j == index0 && t0 != 0) {
1856 TQuadratic q1, *q2 = new TQuadratic();
1857 q->split(t0, q1, *q2);
1858 q = q2;
1859 }
1860 if (j == index1 && t1 != 1) {
1861 TQuadratic *q1 = new TQuadratic(), q2;
1862 q->split(t1, *q1, q2);
1863 q = q1;
1864 }
1865 quadsOutline.push_back(q);
1866 }
1867 }
1868
1869 if (quadsOutline.empty()) return;
1870
1871 out << "<path \n";
1872 TPixel32 col = plt->getStyle(r->getStyle())->getMainColor();
1873 if (col == TPixel::Transparent) col = TPixel::White;
1874
1875 out << "style=\"fill:rgb(" << col.r << "," << col.g << "," << col.b
1876 << ")\" \n";
1877 out << "d=\"M " << quadsOutline[0]->getP0().x << " "
1878 << ly - quadsOutline[0]->getP0().y << "\n";
1879
1880 for (int i = 0; i < quadsOutline.size(); i++)
1881 out << "Q " << quadsOutline[i]->getP1().x << ","
1882 << ly - quadsOutline[i]->getP1().y << "," << quadsOutline[i]->getP2().x
1883 << "," << ly - quadsOutline[i]->getP2().y << "\n";
1884 out << " \" /> \n";
1885 for (int i = 0; i < (int)r->getSubregionCount(); i++)
1886 writeRegion(r->getSubregion(i), plt, out, ly);
1887 }
1888
1889 //--------------------------------------------------------------------------------------
1890
writeOutlineStroke(TStroke * s,TPalette * plt,QTextStream & out,double ly,double quality)1891 static void writeOutlineStroke(TStroke *s, TPalette *plt, QTextStream &out,
1892 double ly, double quality) {
1893 if (s->getChunkCount() == 0) return;
1894 if (s->getMaxThickness() == 0) return;
1895
1896 std::vector<TQuadratic *> quadsOutline;
1897 computeOutlines(s, 0, s->getChunkCount() - 1, quadsOutline, quality);
1898 if (quadsOutline.empty()) return;
1899
1900 out << "<path \n";
1901 TPixel32 col = plt->getStyle(s->getStyle())->getMainColor();
1902
1903 out << "style=\"fill:rgb(" << col.r << "," << col.g << "," << col.b
1904 << ")\" \n";
1905 out << "d=\"M " << quadsOutline[0]->getP0().x << " "
1906 << ly - quadsOutline[0]->getP0().y << "\n";
1907
1908 for (int i = 0; i < quadsOutline.size(); i++)
1909 out << "Q " << quadsOutline[i]->getP1().x << ","
1910 << ly - quadsOutline[i]->getP1().y << "," << quadsOutline[i]->getP2().x
1911 << "," << ly - quadsOutline[i]->getP2().y << "\n";
1912 out << " \" /> \n";
1913 }
1914
1915 //----------------------------------------------------------
1916
computeAverageThickness(const TStroke * s)1917 static double computeAverageThickness(const TStroke *s) {
1918 int count = s->getControlPointCount();
1919
1920 double resThick = 0;
1921
1922 for (int i = 0; i < s->getControlPointCount(); i++) {
1923 double thick = s->getControlPoint(i).thick;
1924 if (i >= 2 && i < s->getControlPointCount() - 2) resThick += thick;
1925 }
1926
1927 if (count < 6) return s->getControlPoint(count / 2 + 1).thick;
1928 return resThick / (s->getControlPointCount() - 4);
1929 }
1930
1931 //----------------------------------------------------------------
1932
writeCenterlineStroke(TStroke * s,TPalette * plt,QTextStream & out,double ly)1933 static void writeCenterlineStroke(TStroke *s, TPalette *plt, QTextStream &out,
1934 double ly) {
1935 if (s->getChunkCount() == 0) return;
1936 if (s->getMaxThickness() == 0) return;
1937
1938 double thick = 2 * computeAverageThickness(s);
1939
1940 out << "<path \n";
1941 TPixel32 col = plt->getStyle(s->getStyle())->getMainColor();
1942
1943 out << "style=\"stroke:rgb(" << col.r << "," << col.g << "," << col.b
1944 << ")\" stroke-width=\"" << thick << " \" \n";
1945 out << "d=\"M " << s->getChunk(0)->getP0().x << " "
1946 << ly - s->getChunk(0)->getP0().y << "\n";
1947
1948 for (int i = 0; i < s->getChunkCount(); i++)
1949 out << "Q " << s->getChunk(i)->getP1().x << ","
1950 << ly - s->getChunk(i)->getP1().y << "," << s->getChunk(i)->getP2().x
1951 << "," << ly - s->getChunk(i)->getP2().y << "\n";
1952 out << " \" /> \n";
1953 }
1954
1955 //----------------------------------------------------------
1956
SvgWriterProperties()1957 Tiio::SvgWriterProperties::SvgWriterProperties()
1958 : m_strokeMode("Stroke Mode"), m_outlineQuality("Outline Quality") {
1959 m_strokeMode.addValue(L"Centerline");
1960 m_strokeMode.addValue(L"Outline");
1961 m_outlineQuality.addValue(L"High");
1962 m_outlineQuality.addValue(L"Medium");
1963 m_outlineQuality.addValue(L"Low");
1964 bind(m_strokeMode);
1965 bind(m_outlineQuality);
1966 }
1967
updateTranslation()1968 void Tiio::SvgWriterProperties::updateTranslation() {
1969 m_strokeMode.setQStringName(tr("Stroke Mode"));
1970 m_outlineQuality.setQStringName(tr("Outline Quality"));
1971 m_strokeMode.setItemUIName(L"Centerline", tr("Centerline"));
1972 m_strokeMode.setItemUIName(L"Outline", tr("Outline"));
1973 m_outlineQuality.setItemUIName(L"High", tr("High"));
1974 m_outlineQuality.setItemUIName(L"Medium", tr("Medium"));
1975 m_outlineQuality.setItemUIName(L"Low", tr("Low"));
1976 }
1977 //----------------------------------------------------------------------------
1978
1979 // void writeSvg(QString path, TVectorImageP v)
save(const TImageP & img)1980 void TImageWriterSvg::save(const TImageP &img) {
1981 const TVectorImageP v = (const TVectorImageP)img;
1982
1983 if (v->getStrokeCount() == 0) return;
1984
1985 TPalette *plt = v->getPalette();
1986
1987 TRectD r = v->getBBox();
1988 double ly = r.getP00().y + r.getP11().y;
1989
1990 QFile file(this->getFilePath().getQString());
1991 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
1992
1993 QTextStream out(&file);
1994 out.setRealNumberPrecision(1);
1995 out.setRealNumberNotation(QTextStream::FixedNotation);
1996
1997 out << "<?xml version=\"1.0\"?>\n";
1998 out << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
1999 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
2000 out << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n";
2001
2002 out << "<g transform=\"translate(" << -r.getP00().x << "," << -r.getP00().y
2003 << ")\" stroke-width=\"0\" fill=\"none\" >\n";
2004
2005 bool isCenterline =
2006 ((TEnumProperty *)(m_properties->getProperty("Stroke Mode")))
2007 ->getValue() == L"Centerline";
2008 double quality = 1;
2009
2010 if (!isCenterline) {
2011 if (((TEnumProperty *)(m_properties->getProperty("Outline Quality")))
2012 ->getValue() == L"Low")
2013 quality = 200;
2014 else if (((TEnumProperty *)(m_properties->getProperty("Outline Quality")))
2015 ->getValue() == L"Medium")
2016 quality = 10;
2017 }
2018
2019 for (int j = 0; j < (int)v->getRegionCount(); j++)
2020 writeRegion(v->getRegion(j), plt, out, ly);
2021 for (int j = 0; j < (int)v->getStrokeCount(); j++)
2022 if (isCenterline)
2023 writeCenterlineStroke(v->getStroke(j), plt, out, ly);
2024 else
2025 writeOutlineStroke(v->getStroke(j), plt, out, ly, quality);
2026 out << "</g> \n";
2027
2028 out << "</svg> \n";
2029 }
2030
2031 //-----------------------------------------------------------------------------
2032
2033 namespace {
addColorToPalette(TPalette * plt,unsigned int _color)2034 int addColorToPalette(TPalette *plt, unsigned int _color) {
2035 TPixel color(_color & 0xFF, (_color >> 8) & 0xFF, _color >> 16);
2036 for (int i = 0; i < plt->getStyleCount(); i++)
2037 if (plt->getStyle(i)->getMainColor() == color) return i;
2038 TPalette::Page *page = plt->getPage(0);
2039 int index = page->addStyle(color);
2040 return index; // plt->addStyle(color);
2041 }
2042
findColor(TPalette * plt,unsigned int _color)2043 int findColor(TPalette *plt, unsigned int _color) {
2044 TPixel color(_color & 0xFF, (_color >> 8) & 0xFF, _color >> 16);
2045 for (int i = 0; i < plt->getStyleCount(); i++)
2046 if (plt->getStyle(i)->getMainColor() == color) return i;
2047 assert(false);
2048 return -1;
2049 }
2050
2051 //-----------------------------------------------------------------------------
2052
buildStroke(NSVGpath * path,float width)2053 TStroke *buildStroke(NSVGpath *path, float width) {
2054 assert((path->npts - 1) % 3 == 0);
2055
2056 TThickPoint p0 = TThickPoint(path->pts[0], -path->pts[1], width);
2057 std::vector<TThickPoint> points;
2058
2059 points.push_back(p0);
2060
2061 for (int i = 1; i < path->npts; i += 3) {
2062 std::vector<TThickQuadratic *> chunkArray;
2063
2064 computeQuadraticsFromCubic(
2065 p0, TThickPoint(path->pts[2 * i], -path->pts[2 * i + 1], width),
2066 TThickPoint(path->pts[2 * i + 2], -path->pts[2 * i + 3], width),
2067 TThickPoint(path->pts[2 * i + 4], -path->pts[2 * i + 5], width), 0.01,
2068 chunkArray);
2069
2070 for (int j = 0; j < chunkArray.size(); j++) {
2071 points.push_back(chunkArray[j]->getP1());
2072 points.push_back(chunkArray[j]->getP2());
2073 }
2074 p0 = chunkArray.back()->getP2();
2075 }
2076
2077 if (points.empty()) return 0;
2078
2079 if (path->closed) {
2080 if (points.back() != points.front()) {
2081 points.push_back(0.5 * (points.back() + points.front()));
2082 points.push_back(points.front());
2083 } else {
2084 int gasp = 0;
2085 }
2086 }
2087 TStroke *s = new TStroke(points);
2088
2089 s->setSelfLoop(path->closed);
2090
2091 std::vector<TThickPoint> tpoints;
2092 s->getControlPoints(tpoints);
2093
2094 for (int j = 0; j < tpoints.size(); j++) {
2095 tpoints[j].thick = width;
2096 }
2097
2098 s->reshape(&tpoints[0], tpoints.size());
2099
2100 return s;
2101 }
2102
2103 } // namespace
2104
2105 //-----------------------------------------------------------------------------
2106
load()2107 TImageP TImageReaderSvg::load() {
2108 NSVGimage *svgImg =
2109 nsvgParseFromFile(m_path.getQString().toStdString().c_str());
2110 if (!svgImg) return TImageP();
2111
2112 TPalette *plt = m_level->getPalette();
2113 assert(plt);
2114
2115 TVectorImage *vimage = new TVectorImage();
2116 vimage->setPalette(plt);
2117
2118 for (NSVGshape *shape = svgImg->shapes; shape; shape = shape->next) {
2119 int inkIndex, paintIndex;
2120 NSVGpath *path = shape->paths;
2121 if (!path) continue;
2122
2123 // TVectorImageP vapp = new TVectorImage();
2124 // TPalette* appPlt = new TPalette();
2125 // vapp->setPalette(appPlt);
2126
2127 TPixel color(shape->fillColor & 0xFF, (shape->fillColor >> 8) & 0xFF,
2128 shape->fillColor >> 16);
2129 if (!shape->hasFill) {
2130 assert(color == TPixel::Black);
2131 shape->hasFill = true;
2132 }
2133 if (shape->hasStroke) inkIndex = findColor(plt, shape->strokeColor);
2134
2135 if (shape->hasFill) paintIndex = findColor(plt, shape->fillColor);
2136
2137 // vapp->setPalette(plt.getPointer());
2138 int startStrokeIndex = vimage->getStrokeCount();
2139 for (; path; path = path->next) {
2140 TStroke *s = buildStroke(path, shape->hasStroke ? shape->strokeWidth : 0);
2141 if (!s) continue;
2142 s->setStyle(shape->hasStroke ? inkIndex : 0);
2143 vimage->addStroke(s);
2144 }
2145 if (startStrokeIndex == vimage->getStrokeCount()) continue;
2146
2147 vimage->group(startStrokeIndex,
2148 vimage->getStrokeCount() - startStrokeIndex);
2149 if (shape->hasFill) {
2150 vimage->enterGroup(startStrokeIndex);
2151 vimage->selectFill(TRectD(-9999999, -9999999, 9999999, 9999999), 0,
2152 paintIndex, true, true, false);
2153 vimage->exitGroup();
2154 }
2155
2156 /* vapp->findRegions();
2157 if (paintIndex!=-1)
2158 for (int i=0; i<(int)vapp->getRegionCount(); i++)
2159 vapp->getRegion(i)->setStyle(paintIndex);
2160 std::vector<int> indexes(vapp->getStrokeCount());
2161 for (int i=0; i<(int)vapp->getStrokeCount() ;i++)
2162 indexes[i] = vimage->getStrokeCount()+i;
2163 vimage->insertImage(vapp, indexes);*/
2164 // delete appPlt;
2165 }
2166
2167 nsvgDelete(svgImg);
2168 // if (m_level)
2169 // m_level->setPalette(plt);
2170 return TImageP(vimage);
2171 }
2172
2173 //-----------------------------------------------------
2174
TLevelReaderSvg(const TFilePath & path)2175 TLevelReaderSvg::TLevelReaderSvg(const TFilePath &path) : TLevelReader(path) {}
2176
2177 //-----------------------------------------------------
2178
getFrameReader(TFrameId fid)2179 TImageReaderP TLevelReaderSvg::getFrameReader(TFrameId fid) {
2180 return new TImageReaderSvg(getFilePath().withFrame(fid), m_level);
2181 }
2182
2183 //-----------------------------------------------------
2184
loadInfo()2185 TLevelP TLevelReaderSvg::loadInfo() {
2186 m_level = TLevelReader::loadInfo();
2187 TPalette *plt = new TPalette();
2188 TLevel::Iterator it = m_level->begin();
2189
2190 for (; it != m_level->end(); ++it) {
2191 NSVGimage *svgImg = nsvgParseFromFile(
2192 m_path.withFrame(it->first).getQString().toStdString().c_str());
2193 if (!svgImg) continue;
2194
2195 for (NSVGshape *shape = svgImg->shapes; shape; shape = shape->next) {
2196 if (shape->hasStroke) addColorToPalette(plt, shape->strokeColor);
2197
2198 if (shape->hasFill) addColorToPalette(plt, shape->fillColor);
2199 }
2200
2201 nsvgDelete(svgImg);
2202 }
2203
2204 m_level->setPalette(plt);
2205 return m_level;
2206 }
2207