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