Hasty Badger
Small UI library (a branch of Turbo Badger)
 All Classes Namespaces Functions Variables Enumerations Enumerator Friends Groups Pages
nanosvg.h
1 /*
2  * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  *
20  * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
21  * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
22  *
23  * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
24  *
25  * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
26  *
27  */
28 
29 #ifndef NANOSVG_H
30 #define NANOSVG_H
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
37 //
38 // The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
39 //
40 // NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
41 //
42 // The shapes in the SVG images are transformed by the viewBox and converted to specified units.
43 // That is, you should get the same looking data as your designed in your favorite app.
44 //
45 // NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
46 // to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
47 //
48 // The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
49 // DPI (dots-per-inch) controls how the unit conversion is done.
50 //
51 // If you don't know or care about the units stuff, "px" and 96 should get you going.
52 
53 
54 /* Example Usage:
55  // Load
56  NSVGImage* image;
57  image = nsvgParseFromFile("test.svg", "px", 96);
58  printf("size: %f x %f\n", image->width, image->height);
59  // Use...
60  for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
61  for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
62  for (int i = 0; i < path->npts-1; i += 3) {
63  float* p = &path->pts[i*2];
64  drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
65  }
66  }
67  }
68  // Delete
69  nsvgDelete(image);
70 */
71 
72 enum NSVGpaintType {
73  NSVG_PAINT_NONE = 0,
74  NSVG_PAINT_COLOR = 1,
75  NSVG_PAINT_LINEAR_GRADIENT = 2,
76  NSVG_PAINT_RADIAL_GRADIENT = 3
77 };
78 
79 enum NSVGspreadType {
80  NSVG_SPREAD_PAD = 0,
81  NSVG_SPREAD_REFLECT = 1,
82  NSVG_SPREAD_REPEAT = 2
83 };
84 
85 enum NSVGlineJoin {
86  NSVG_JOIN_MITER = 0,
87  NSVG_JOIN_ROUND = 1,
88  NSVG_JOIN_BEVEL = 2
89 };
90 
91 enum NSVGlineCap {
92  NSVG_CAP_BUTT = 0,
93  NSVG_CAP_ROUND = 1,
94  NSVG_CAP_SQUARE = 2
95 };
96 
97 enum NSVGfillRule {
98  NSVG_FILLRULE_NONZERO = 0,
99  NSVG_FILLRULE_EVENODD = 1
100 };
101 
102 enum NSVGflags {
103  NSVG_FLAGS_VISIBLE = 0x01
104 };
105 
106 typedef struct NSVGgradientStop {
107  unsigned int color;
108  float offset;
110 
111 typedef struct NSVGgradient {
112  float xform[6];
113  char spread;
114  float fx, fy;
115  int nstops;
116  NSVGgradientStop stops[1];
117 } NSVGgradient;
118 
119 typedef struct NSVGpaint {
120  char type;
121  union {
122  unsigned int color;
123  NSVGgradient* gradient;
124  };
125 } NSVGpaint;
126 
127 typedef struct NSVGpath
128 {
129  float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
130  int npts; // Total number of bezier points.
131  char closed; // Flag indicating if shapes should be treated as closed.
132  float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
133  struct NSVGpath* next; // Pointer to next path, or NULL if last element.
134 } NSVGpath;
135 
136 typedef struct NSVGshape
137 {
138  char id[64]; // Optional 'id' attr of the shape or its group
139  NSVGpaint fill; // Fill paint
140  NSVGpaint stroke; // Stroke paint
141  float opacity; // Opacity of the shape.
142  float strokeWidth; // Stroke width (scaled).
143  float strokeDashOffset; // Stroke dash offset (scaled).
144  float strokeDashArray[8]; // Stroke dash array (scaled).
145  char strokeDashCount; // Number of dash values in dash array.
146  char strokeLineJoin; // Stroke join type.
147  char strokeLineCap; // Stroke cap type.
148  float miterLimit; // Miter limit
149  char fillRule; // Fill rule, see NSVGfillRule.
150  unsigned char flags; // Logical or of NSVG_FLAGS_* flags
151  float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
152  NSVGpath* paths; // Linked list of paths in the image.
153  struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
154 } NSVGshape;
155 
156 typedef struct NSVGimage
157 {
158  float width; // Width of the image.
159  float height; // Height of the image.
160  NSVGshape* shapes; // Linked list of shapes in the image.
161 } NSVGimage;
162 
163 // Parses SVG file from a file, returns SVG image as paths.
164 NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
165 
166 // Parses SVG file from a null terminated string, returns SVG image as paths.
167 // Important note: changes the string.
168 NSVGimage* nsvgParse(char* input, const char* units, float dpi);
169 
170 // Deletes list of paths.
171 void nsvgDelete(NSVGimage* image);
172 
173 #ifdef __cplusplus
174 }
175 #endif
176 
177 #endif // NANOSVG_H
178 
179 #ifdef NANOSVG_IMPLEMENTATION
180 
181 #include <string.h>
182 #include <stdlib.h>
183 #include <stdio.h>
184 #include <math.h>
185 
186 #define NSVG_PI (3.14159265358979323846264338327f)
187 #define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
188 
189 #define NSVG_ALIGN_MIN 0
190 #define NSVG_ALIGN_MID 1
191 #define NSVG_ALIGN_MAX 2
192 #define NSVG_ALIGN_NONE 0
193 #define NSVG_ALIGN_MEET 1
194 #define NSVG_ALIGN_SLICE 2
195 
196 #define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
197 #define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
198 
199 #ifdef _MSC_VER
200  #pragma warning (disable: 4996) // Switch off security warnings
201  #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
202  #ifdef __cplusplus
203  #define NSVG_INLINE inline
204  #else
205  #define NSVG_INLINE
206  #endif
207 #else
208  #define NSVG_INLINE inline
209 #endif
210 
211 
212 static int nsvg__isspace(char c)
213 {
214  return strchr(" \t\n\v\f\r", c) != 0;
215 }
216 
217 static int nsvg__isdigit(char c)
218 {
219  return c >= '0' && c <= '9';
220 }
221 
222 static int nsvg__isnum(char c)
223 {
224  return strchr("0123456789+-.eE", c) != 0;
225 }
226 
227 static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
228 static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
229 
230 
231 // Simple XML parser
232 
233 #define NSVG_XML_TAG 1
234 #define NSVG_XML_CONTENT 2
235 #define NSVG_XML_MAX_ATTRIBS 256
236 
237 static void nsvg__parseContent(char* s,
238  void (*contentCb)(void* ud, const char* s),
239  void* ud)
240 {
241  // Trim start white spaces
242  while (*s && nsvg__isspace(*s)) s++;
243  if (!*s) return;
244 
245  if (contentCb)
246  (*contentCb)(ud, s);
247 }
248 
249 static void nsvg__parseElement(char* s,
250  void (*startelCb)(void* ud, const char* el, const char** attr),
251  void (*endelCb)(void* ud, const char* el),
252  void* ud)
253 {
254  const char* attr[NSVG_XML_MAX_ATTRIBS];
255  int nattr = 0;
256  char* name;
257  int start = 0;
258  int end = 0;
259  char quote;
260 
261  // Skip white space after the '<'
262  while (*s && nsvg__isspace(*s)) s++;
263 
264  // Check if the tag is end tag
265  if (*s == '/') {
266  s++;
267  end = 1;
268  } else {
269  start = 1;
270  }
271 
272  // Skip comments, data and preprocessor stuff.
273  if (!*s || *s == '?' || *s == '!')
274  return;
275 
276  // Get tag name
277  name = s;
278  while (*s && !nsvg__isspace(*s)) s++;
279  if (*s) { *s++ = '\0'; }
280 
281  // Get attribs
282  while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
283  char* name = NULL;
284  char* value = NULL;
285 
286  // Skip white space before the attrib name
287  while (*s && nsvg__isspace(*s)) s++;
288  if (!*s) break;
289  if (*s == '/') {
290  end = 1;
291  break;
292  }
293  name = s;
294  // Find end of the attrib name.
295  while (*s && !nsvg__isspace(*s) && *s != '=') s++;
296  if (*s) { *s++ = '\0'; }
297  // Skip until the beginning of the value.
298  while (*s && *s != '\"' && *s != '\'') s++;
299  if (!*s) break;
300  quote = *s;
301  s++;
302  // Store value and find the end of it.
303  value = s;
304  while (*s && *s != quote) s++;
305  if (*s) { *s++ = '\0'; }
306 
307  // Store only well formed attributes
308  if (name && value) {
309  attr[nattr++] = name;
310  attr[nattr++] = value;
311  }
312  }
313 
314  // List terminator
315  attr[nattr++] = 0;
316  attr[nattr++] = 0;
317 
318  // Call callbacks.
319  if (start && startelCb)
320  (*startelCb)(ud, name, attr);
321  if (end && endelCb)
322  (*endelCb)(ud, name);
323 }
324 
325 int nsvg__parseXML(char* input,
326  void (*startelCb)(void* ud, const char* el, const char** attr),
327  void (*endelCb)(void* ud, const char* el),
328  void (*contentCb)(void* ud, const char* s),
329  void* ud)
330 {
331  char* s = input;
332  char* mark = s;
333  int state = NSVG_XML_CONTENT;
334  while (*s) {
335  if (*s == '<' && state == NSVG_XML_CONTENT) {
336  // Start of a tag
337  *s++ = '\0';
338  nsvg__parseContent(mark, contentCb, ud);
339  mark = s;
340  state = NSVG_XML_TAG;
341  } else if (*s == '>' && state == NSVG_XML_TAG) {
342  // Start of a content or new tag.
343  *s++ = '\0';
344  nsvg__parseElement(mark, startelCb, endelCb, ud);
345  mark = s;
346  state = NSVG_XML_CONTENT;
347  } else {
348  s++;
349  }
350  }
351 
352  return 1;
353 }
354 
355 
356 /* Simple SVG parser. */
357 
358 #define NSVG_MAX_ATTR 128
359 
360 enum NSVGgradientUnits {
361  NSVG_USER_SPACE = 0,
362  NSVG_OBJECT_SPACE = 1
363 };
364 
365 #define NSVG_MAX_DASHES 8
366 
367 enum NSVGunits {
368  NSVG_UNITS_USER,
369  NSVG_UNITS_PX,
370  NSVG_UNITS_PT,
371  NSVG_UNITS_PC,
372  NSVG_UNITS_MM,
373  NSVG_UNITS_CM,
374  NSVG_UNITS_IN,
375  NSVG_UNITS_PERCENT,
376  NSVG_UNITS_EM,
377  NSVG_UNITS_EX
378 };
379 
380 typedef struct NSVGcoordinate {
381  float value;
382  int units;
383 } NSVGcoordinate;
384 
385 typedef struct NSVGlinearData {
386  NSVGcoordinate x1, y1, x2, y2;
387 } NSVGlinearData;
388 
389 typedef struct NSVGradialData {
390  NSVGcoordinate cx, cy, r, fx, fy;
391 } NSVGradialData;
392 
393 typedef struct NSVGgradientData
394 {
395  char id[64];
396  char ref[64];
397  char type;
398  union {
399  NSVGlinearData linear;
400  NSVGradialData radial;
401  };
402  char spread;
403  char units;
404  float xform[6];
405  int nstops;
406  NSVGgradientStop* stops;
407  struct NSVGgradientData* next;
408 } NSVGgradientData;
409 
410 typedef struct NSVGattrib
411 {
412  char id[64];
413  float xform[6];
414  unsigned int fillColor;
415  unsigned int strokeColor;
416  float opacity;
417  float fillOpacity;
418  float strokeOpacity;
419  char fillGradient[64];
420  char strokeGradient[64];
421  float strokeWidth;
422  float strokeDashOffset;
423  float strokeDashArray[NSVG_MAX_DASHES];
424  int strokeDashCount;
425  char strokeLineJoin;
426  char strokeLineCap;
427  float miterLimit;
428  char fillRule;
429  float fontSize;
430  unsigned int stopColor;
431  float stopOpacity;
432  float stopOffset;
433  char hasFill;
434  char hasStroke;
435  char visible;
436 } NSVGattrib;
437 
438 typedef struct NSVGparser
439 {
440  NSVGattrib attr[NSVG_MAX_ATTR];
441  int attrHead;
442  float* pts;
443  int npts;
444  int cpts;
445  NSVGpath* plist;
446  NSVGimage* image;
447  NSVGgradientData* gradients;
448  NSVGshape* shapesTail;
449  float viewMinx, viewMiny, viewWidth, viewHeight;
450  int alignX, alignY, alignType;
451  float dpi;
452  char pathFlag;
453  char defsFlag;
454 } NSVGparser;
455 
456 static void nsvg__xformIdentity(float* t)
457 {
458  t[0] = 1.0f; t[1] = 0.0f;
459  t[2] = 0.0f; t[3] = 1.0f;
460  t[4] = 0.0f; t[5] = 0.0f;
461 }
462 
463 static void nsvg__xformSetTranslation(float* t, float tx, float ty)
464 {
465  t[0] = 1.0f; t[1] = 0.0f;
466  t[2] = 0.0f; t[3] = 1.0f;
467  t[4] = tx; t[5] = ty;
468 }
469 
470 static void nsvg__xformSetScale(float* t, float sx, float sy)
471 {
472  t[0] = sx; t[1] = 0.0f;
473  t[2] = 0.0f; t[3] = sy;
474  t[4] = 0.0f; t[5] = 0.0f;
475 }
476 
477 static void nsvg__xformSetSkewX(float* t, float a)
478 {
479  t[0] = 1.0f; t[1] = 0.0f;
480  t[2] = tanf(a); t[3] = 1.0f;
481  t[4] = 0.0f; t[5] = 0.0f;
482 }
483 
484 static void nsvg__xformSetSkewY(float* t, float a)
485 {
486  t[0] = 1.0f; t[1] = tanf(a);
487  t[2] = 0.0f; t[3] = 1.0f;
488  t[4] = 0.0f; t[5] = 0.0f;
489 }
490 
491 static void nsvg__xformSetRotation(float* t, float a)
492 {
493  float cs = cosf(a), sn = sinf(a);
494  t[0] = cs; t[1] = sn;
495  t[2] = -sn; t[3] = cs;
496  t[4] = 0.0f; t[5] = 0.0f;
497 }
498 
499 static void nsvg__xformMultiply(float* t, float* s)
500 {
501  float t0 = t[0] * s[0] + t[1] * s[2];
502  float t2 = t[2] * s[0] + t[3] * s[2];
503  float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
504  t[1] = t[0] * s[1] + t[1] * s[3];
505  t[3] = t[2] * s[1] + t[3] * s[3];
506  t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
507  t[0] = t0;
508  t[2] = t2;
509  t[4] = t4;
510 }
511 
512 static void nsvg__xformInverse(float* inv, float* t)
513 {
514  double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
515  if (det > -1e-6 && det < 1e-6) {
516  nsvg__xformIdentity(t);
517  return;
518  }
519  invdet = 1.0 / det;
520  inv[0] = (float)(t[3] * invdet);
521  inv[2] = (float)(-t[2] * invdet);
522  inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
523  inv[1] = (float)(-t[1] * invdet);
524  inv[3] = (float)(t[0] * invdet);
525  inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
526 }
527 
528 static void nsvg__xformPremultiply(float* t, float* s)
529 {
530  float s2[6];
531  memcpy(s2, s, sizeof(float)*6);
532  nsvg__xformMultiply(s2, t);
533  memcpy(t, s2, sizeof(float)*6);
534 }
535 
536 static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
537 {
538  *dx = x*t[0] + y*t[2] + t[4];
539  *dy = x*t[1] + y*t[3] + t[5];
540 }
541 
542 static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
543 {
544  *dx = x*t[0] + y*t[2];
545  *dy = x*t[1] + y*t[3];
546 }
547 
548 #define NSVG_EPSILON (1e-12)
549 
550 static int nsvg__ptInBounds(float* pt, float* bounds)
551 {
552  return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
553 }
554 
555 
556 static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
557 {
558  double it = 1.0-t;
559  return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
560 }
561 
562 static void nsvg__curveBounds(float* bounds, float* curve)
563 {
564  int i, j, count;
565  double roots[2], a, b, c, b2ac, t, v;
566  float* v0 = &curve[0];
567  float* v1 = &curve[2];
568  float* v2 = &curve[4];
569  float* v3 = &curve[6];
570 
571  // Start the bounding box by end points
572  bounds[0] = nsvg__minf(v0[0], v3[0]);
573  bounds[1] = nsvg__minf(v0[1], v3[1]);
574  bounds[2] = nsvg__maxf(v0[0], v3[0]);
575  bounds[3] = nsvg__maxf(v0[1], v3[1]);
576 
577  // Bezier curve fits inside the convex hull of it's control points.
578  // If control points are inside the bounds, we're done.
579  if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
580  return;
581 
582  // Add bezier curve inflection points in X and Y.
583  for (i = 0; i < 2; i++) {
584  a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
585  b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
586  c = 3.0 * v1[i] - 3.0 * v0[i];
587  count = 0;
588  if (fabs(a) < NSVG_EPSILON) {
589  if (fabs(b) > NSVG_EPSILON) {
590  t = -c / b;
591  if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
592  roots[count++] = t;
593  }
594  } else {
595  b2ac = b*b - 4.0*c*a;
596  if (b2ac > NSVG_EPSILON) {
597  t = (-b + sqrt(b2ac)) / (2.0 * a);
598  if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
599  roots[count++] = t;
600  t = (-b - sqrt(b2ac)) / (2.0 * a);
601  if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
602  roots[count++] = t;
603  }
604  }
605  for (j = 0; j < count; j++) {
606  v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
607  bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
608  bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
609  }
610  }
611 }
612 
613 static NSVGparser* nsvg__createParser()
614 {
615  NSVGparser* p;
616  p = (NSVGparser*)malloc(sizeof(NSVGparser));
617  if (p == NULL) goto error;
618  memset(p, 0, sizeof(NSVGparser));
619 
620  p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
621  if (p->image == NULL) goto error;
622  memset(p->image, 0, sizeof(NSVGimage));
623 
624  // Init style
625  nsvg__xformIdentity(p->attr[0].xform);
626  memset(p->attr[0].id, 0, sizeof p->attr[0].id);
627  p->attr[0].fillColor = NSVG_RGB(0,0,0);
628  p->attr[0].strokeColor = NSVG_RGB(0,0,0);
629  p->attr[0].opacity = 1;
630  p->attr[0].fillOpacity = 1;
631  p->attr[0].strokeOpacity = 1;
632  p->attr[0].stopOpacity = 1;
633  p->attr[0].strokeWidth = 1;
634  p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
635  p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
636  p->attr[0].miterLimit = 4;
637  p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
638  p->attr[0].hasFill = 1;
639  p->attr[0].visible = 1;
640 
641  return p;
642 
643 error:
644  if (p) {
645  if (p->image) free(p->image);
646  free(p);
647  }
648  return NULL;
649 }
650 
651 static void nsvg__deletePaths(NSVGpath* path)
652 {
653  while (path) {
654  NSVGpath *next = path->next;
655  if (path->pts != NULL)
656  free(path->pts);
657  free(path);
658  path = next;
659  }
660 }
661 
662 static void nsvg__deletePaint(NSVGpaint* paint)
663 {
664  if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
665  free(paint->gradient);
666 }
667 
668 static void nsvg__deleteGradientData(NSVGgradientData* grad)
669 {
670  NSVGgradientData* next;
671  while (grad != NULL) {
672  next = grad->next;
673  free(grad->stops);
674  free(grad);
675  grad = next;
676  }
677 }
678 
679 static void nsvg__deleteParser(NSVGparser* p)
680 {
681  if (p != NULL) {
682  nsvg__deletePaths(p->plist);
683  nsvg__deleteGradientData(p->gradients);
684  nsvgDelete(p->image);
685  free(p->pts);
686  free(p);
687  }
688 }
689 
690 static void nsvg__resetPath(NSVGparser* p)
691 {
692  p->npts = 0;
693 }
694 
695 static void nsvg__addPoint(NSVGparser* p, float x, float y)
696 {
697  if (p->npts+1 > p->cpts) {
698  p->cpts = p->cpts ? p->cpts*2 : 8;
699  p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
700  if (!p->pts) return;
701  }
702  p->pts[p->npts*2+0] = x;
703  p->pts[p->npts*2+1] = y;
704  p->npts++;
705 }
706 
707 static void nsvg__moveTo(NSVGparser* p, float x, float y)
708 {
709  if (p->npts > 0) {
710  p->pts[(p->npts-1)*2+0] = x;
711  p->pts[(p->npts-1)*2+1] = y;
712  } else {
713  nsvg__addPoint(p, x, y);
714  }
715 }
716 
717 static void nsvg__lineTo(NSVGparser* p, float x, float y)
718 {
719  float px,py, dx,dy;
720  if (p->npts > 0) {
721  px = p->pts[(p->npts-1)*2+0];
722  py = p->pts[(p->npts-1)*2+1];
723  dx = x - px;
724  dy = y - py;
725  nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
726  nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
727  nsvg__addPoint(p, x, y);
728  }
729 }
730 
731 static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
732 {
733  nsvg__addPoint(p, cpx1, cpy1);
734  nsvg__addPoint(p, cpx2, cpy2);
735  nsvg__addPoint(p, x, y);
736 }
737 
738 static NSVGattrib* nsvg__getAttr(NSVGparser* p)
739 {
740  return &p->attr[p->attrHead];
741 }
742 
743 static void nsvg__pushAttr(NSVGparser* p)
744 {
745  if (p->attrHead < NSVG_MAX_ATTR-1) {
746  p->attrHead++;
747  memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
748  }
749 }
750 
751 static void nsvg__popAttr(NSVGparser* p)
752 {
753  if (p->attrHead > 0)
754  p->attrHead--;
755 }
756 
757 static float nsvg__actualOrigX(NSVGparser* p)
758 {
759  return p->viewMinx;
760 }
761 
762 static float nsvg__actualOrigY(NSVGparser* p)
763 {
764  return p->viewMiny;
765 }
766 
767 static float nsvg__actualWidth(NSVGparser* p)
768 {
769  return p->viewWidth;
770 }
771 
772 static float nsvg__actualHeight(NSVGparser* p)
773 {
774  return p->viewHeight;
775 }
776 
777 static float nsvg__actualLength(NSVGparser* p)
778 {
779  float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
780  return sqrtf(w*w + h*h) / sqrtf(2.0f);
781 }
782 
783 static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
784 {
785  NSVGattrib* attr = nsvg__getAttr(p);
786  switch (c.units) {
787  case NSVG_UNITS_USER: return c.value;
788  case NSVG_UNITS_PX: return c.value;
789  case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi;
790  case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi;
791  case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi;
792  case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi;
793  case NSVG_UNITS_IN: return c.value * p->dpi;
794  case NSVG_UNITS_EM: return c.value * attr->fontSize;
795  case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
796  case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length;
797  default: return c.value;
798  }
799  return c.value;
800 }
801 
802 static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
803 {
804  NSVGgradientData* grad = p->gradients;
805  while (grad) {
806  if (strcmp(grad->id, id) == 0)
807  return grad;
808  grad = grad->next;
809  }
810  return NULL;
811 }
812 
813 static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
814 {
815  NSVGattrib* attr = nsvg__getAttr(p);
816  NSVGgradientData* data = NULL;
817  NSVGgradientData* ref = NULL;
818  NSVGgradientStop* stops = NULL;
819  NSVGgradient* grad;
820  float ox, oy, sw, sh, sl;
821  int nstops = 0;
822 
823  data = nsvg__findGradientData(p, id);
824  if (data == NULL) return NULL;
825 
826  // TODO: use ref to fill in all unset values too.
827  ref = data;
828  while (ref != NULL) {
829  if (stops == NULL && ref->stops != NULL) {
830  stops = ref->stops;
831  nstops = ref->nstops;
832  break;
833  }
834  ref = nsvg__findGradientData(p, ref->ref);
835  }
836  if (stops == NULL) return NULL;
837 
838  grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
839  if (grad == NULL) return NULL;
840 
841  // The shape width and height.
842  if (data->units == NSVG_OBJECT_SPACE) {
843  ox = localBounds[0];
844  oy = localBounds[1];
845  sw = localBounds[2] - localBounds[0];
846  sh = localBounds[3] - localBounds[1];
847  } else {
848  ox = nsvg__actualOrigX(p);
849  oy = nsvg__actualOrigY(p);
850  sw = nsvg__actualWidth(p);
851  sh = nsvg__actualHeight(p);
852  }
853  sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
854 
855  if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
856  float x1, y1, x2, y2, dx, dy;
857  x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
858  y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
859  x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
860  y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
861  // Calculate transform aligned to the line
862  dx = x2 - x1;
863  dy = y2 - y1;
864  grad->xform[0] = dy; grad->xform[1] = -dx;
865  grad->xform[2] = dx; grad->xform[3] = dy;
866  grad->xform[4] = x1; grad->xform[5] = y1;
867  } else {
868  float cx, cy, fx, fy, r;
869  cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
870  cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
871  fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
872  fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
873  r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
874  // Calculate transform aligned to the circle
875  grad->xform[0] = r; grad->xform[1] = 0;
876  grad->xform[2] = 0; grad->xform[3] = r;
877  grad->xform[4] = cx; grad->xform[5] = cy;
878  grad->fx = fx / r;
879  grad->fy = fy / r;
880  }
881 
882  nsvg__xformMultiply(grad->xform, data->xform);
883  nsvg__xformMultiply(grad->xform, attr->xform);
884 
885  grad->spread = data->spread;
886  memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
887  grad->nstops = nstops;
888 
889  *paintType = data->type;
890 
891  return grad;
892 }
893 
894 static float nsvg__getAverageScale(float* t)
895 {
896  float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
897  float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
898  return (sx + sy) * 0.5f;
899 }
900 
901 static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
902 {
903  NSVGpath* path;
904  float curve[4*2], curveBounds[4];
905  int i, first = 1;
906  for (path = shape->paths; path != NULL; path = path->next) {
907  nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
908  for (i = 0; i < path->npts-1; i += 3) {
909  nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
910  nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
911  nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
912  nsvg__curveBounds(curveBounds, curve);
913  if (first) {
914  bounds[0] = curveBounds[0];
915  bounds[1] = curveBounds[1];
916  bounds[2] = curveBounds[2];
917  bounds[3] = curveBounds[3];
918  first = 0;
919  } else {
920  bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
921  bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
922  bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
923  bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
924  }
925  curve[0] = curve[6];
926  curve[1] = curve[7];
927  }
928  }
929 }
930 
931 static void nsvg__addShape(NSVGparser* p)
932 {
933  NSVGattrib* attr = nsvg__getAttr(p);
934  float scale = 1.0f;
935  NSVGshape* shape;
936  NSVGpath* path;
937  int i;
938 
939  if (p->plist == NULL)
940  return;
941 
942  shape = (NSVGshape*)malloc(sizeof(NSVGshape));
943  if (shape == NULL) goto error;
944  memset(shape, 0, sizeof(NSVGshape));
945 
946  memcpy(shape->id, attr->id, sizeof shape->id);
947  scale = nsvg__getAverageScale(attr->xform);
948  shape->strokeWidth = attr->strokeWidth * scale;
949  shape->strokeDashOffset = attr->strokeDashOffset * scale;
950  shape->strokeDashCount = (char)attr->strokeDashCount;
951  for (i = 0; i < attr->strokeDashCount; i++)
952  shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
953  shape->strokeLineJoin = attr->strokeLineJoin;
954  shape->strokeLineCap = attr->strokeLineCap;
955  shape->miterLimit = attr->miterLimit;
956  shape->fillRule = attr->fillRule;
957  shape->opacity = attr->opacity;
958 
959  shape->paths = p->plist;
960  p->plist = NULL;
961 
962  // Calculate shape bounds
963  shape->bounds[0] = shape->paths->bounds[0];
964  shape->bounds[1] = shape->paths->bounds[1];
965  shape->bounds[2] = shape->paths->bounds[2];
966  shape->bounds[3] = shape->paths->bounds[3];
967  for (path = shape->paths->next; path != NULL; path = path->next) {
968  shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
969  shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
970  shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
971  shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
972  }
973 
974  // Set fill
975  if (attr->hasFill == 0) {
976  shape->fill.type = NSVG_PAINT_NONE;
977  } else if (attr->hasFill == 1) {
978  shape->fill.type = NSVG_PAINT_COLOR;
979  shape->fill.color = attr->fillColor;
980  shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
981  } else if (attr->hasFill == 2) {
982  float inv[6], localBounds[4];
983  nsvg__xformInverse(inv, attr->xform);
984  nsvg__getLocalBounds(localBounds, shape, inv);
985  shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
986  if (shape->fill.gradient == NULL) {
987  shape->fill.type = NSVG_PAINT_NONE;
988  }
989  }
990 
991  // Set stroke
992  if (attr->hasStroke == 0) {
993  shape->stroke.type = NSVG_PAINT_NONE;
994  } else if (attr->hasStroke == 1) {
995  shape->stroke.type = NSVG_PAINT_COLOR;
996  shape->stroke.color = attr->strokeColor;
997  shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
998  } else if (attr->hasStroke == 2) {
999  float inv[6], localBounds[4];
1000  nsvg__xformInverse(inv, attr->xform);
1001  nsvg__getLocalBounds(localBounds, shape, inv);
1002  shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
1003  if (shape->stroke.gradient == NULL)
1004  shape->stroke.type = NSVG_PAINT_NONE;
1005  }
1006 
1007  // Set flags
1008  shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
1009 
1010  // Add to tail
1011  if (p->image->shapes == NULL)
1012  p->image->shapes = shape;
1013  else
1014  p->shapesTail->next = shape;
1015  p->shapesTail = shape;
1016 
1017  return;
1018 
1019 error:
1020  if (shape) free(shape);
1021 }
1022 
1023 static void nsvg__addPath(NSVGparser* p, char closed)
1024 {
1025  NSVGattrib* attr = nsvg__getAttr(p);
1026  NSVGpath* path = NULL;
1027  float bounds[4];
1028  float* curve;
1029  int i;
1030 
1031  if (p->npts < 4)
1032  return;
1033 
1034  if (closed)
1035  nsvg__lineTo(p, p->pts[0], p->pts[1]);
1036 
1037  path = (NSVGpath*)malloc(sizeof(NSVGpath));
1038  if (path == NULL) goto error;
1039  memset(path, 0, sizeof(NSVGpath));
1040 
1041  path->pts = (float*)malloc(p->npts*2*sizeof(float));
1042  if (path->pts == NULL) goto error;
1043  path->closed = closed;
1044  path->npts = p->npts;
1045 
1046  // Transform path.
1047  for (i = 0; i < p->npts; ++i)
1048  nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
1049 
1050  // Find bounds
1051  for (i = 0; i < path->npts-1; i += 3) {
1052  curve = &path->pts[i*2];
1053  nsvg__curveBounds(bounds, curve);
1054  if (i == 0) {
1055  path->bounds[0] = bounds[0];
1056  path->bounds[1] = bounds[1];
1057  path->bounds[2] = bounds[2];
1058  path->bounds[3] = bounds[3];
1059  } else {
1060  path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
1061  path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
1062  path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
1063  path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
1064  }
1065  }
1066 
1067  path->next = p->plist;
1068  p->plist = path;
1069 
1070  return;
1071 
1072 error:
1073  if (path != NULL) {
1074  if (path->pts != NULL) free(path->pts);
1075  free(path);
1076  }
1077 }
1078 
1079 // We roll our own string to float because the std library one uses locale and messes things up.
1080 static double nsvg__atof(const char* s)
1081 {
1082  char* cur = (char*)s;
1083  char* end = NULL;
1084  double res = 0.0, sign = 1.0;
1085  long long intPart = 0, fracPart = 0;
1086  char hasIntPart = 0, hasFracPart = 0;
1087 
1088  // Parse optional sign
1089  if (*cur == '+') {
1090  cur++;
1091  } else if (*cur == '-') {
1092  sign = -1;
1093  cur++;
1094  }
1095 
1096  // Parse integer part
1097  if (nsvg__isdigit(*cur)) {
1098  // Parse digit sequence
1099  intPart = (double)strtoll(cur, &end, 10);
1100  if (cur != end) {
1101  res = (double)intPart;
1102  hasIntPart = 1;
1103  cur = end;
1104  }
1105  }
1106 
1107  // Parse fractional part.
1108  if (*cur == '.') {
1109  cur++; // Skip '.'
1110  if (nsvg__isdigit(*cur)) {
1111  // Parse digit sequence
1112  fracPart = strtoll(cur, &end, 10);
1113  if (cur != end) {
1114  res += (double)fracPart / pow(10.0, (double)(end - cur));
1115  hasFracPart = 1;
1116  cur = end;
1117  }
1118  }
1119  }
1120 
1121  // A valid number should have integer or fractional part.
1122  if (!hasIntPart && !hasFracPart)
1123  return 0.0;
1124 
1125  // Parse optional exponent
1126  if (*cur == 'e' || *cur == 'E') {
1127  int expPart = 0;
1128  cur++; // skip 'E'
1129  expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
1130  if (cur != end) {
1131  res *= pow(10.0, (double)expPart);
1132  }
1133  }
1134 
1135  return res * sign;
1136 }
1137 
1138 
1139 static const char* nsvg__parseNumber(const char* s, char* it, const int size)
1140 {
1141  const int last = size-1;
1142  int i = 0;
1143 
1144  // sign
1145  if (*s == '-' || *s == '+') {
1146  if (i < last) it[i++] = *s;
1147  s++;
1148  }
1149  // integer part
1150  while (*s && nsvg__isdigit(*s)) {
1151  if (i < last) it[i++] = *s;
1152  s++;
1153  }
1154  if (*s == '.') {
1155  // decimal point
1156  if (i < last) it[i++] = *s;
1157  s++;
1158  // fraction part
1159  while (*s && nsvg__isdigit(*s)) {
1160  if (i < last) it[i++] = *s;
1161  s++;
1162  }
1163  }
1164  // exponent
1165  if (*s == 'e' || *s == 'E') {
1166  if (i < last) it[i++] = *s;
1167  s++;
1168  if (*s == '-' || *s == '+') {
1169  if (i < last) it[i++] = *s;
1170  s++;
1171  }
1172  while (*s && nsvg__isdigit(*s)) {
1173  if (i < last) it[i++] = *s;
1174  s++;
1175  }
1176  }
1177  it[i] = '\0';
1178 
1179  return s;
1180 }
1181 
1182 static const char* nsvg__getNextPathItem(const char* s, char* it)
1183 {
1184  it[0] = '\0';
1185  // Skip white spaces and commas
1186  while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
1187  if (!*s) return s;
1188  if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
1189  s = nsvg__parseNumber(s, it, 64);
1190  } else {
1191  // Parse command
1192  it[0] = *s++;
1193  it[1] = '\0';
1194  return s;
1195  }
1196 
1197  return s;
1198 }
1199 
1200 static unsigned int nsvg__parseColorHex(const char* str)
1201 {
1202  unsigned int c = 0, r = 0, g = 0, b = 0;
1203  int n = 0;
1204  str++; // skip #
1205  // Calculate number of characters.
1206  while(str[n] && !nsvg__isspace(str[n]))
1207  n++;
1208  if (n == 6) {
1209  sscanf(str, "%x", &c);
1210  } else if (n == 3) {
1211  sscanf(str, "%x", &c);
1212  c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
1213  c |= c<<4;
1214  }
1215  r = (c >> 16) & 0xff;
1216  g = (c >> 8) & 0xff;
1217  b = c & 0xff;
1218  return NSVG_RGB(r,g,b);
1219 }
1220 
1221 static unsigned int nsvg__parseColorRGB(const char* str)
1222 {
1223  int r = -1, g = -1, b = -1;
1224  char s1[32]="", s2[32]="";
1225  sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
1226  if (strchr(s1, '%')) {
1227  return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
1228  } else {
1229  return NSVG_RGB(r,g,b);
1230  }
1231 }
1232 
1233 typedef struct NSVGNamedColor {
1234  const char* name;
1235  unsigned int color;
1236 } NSVGNamedColor;
1237 
1238 NSVGNamedColor nsvg__colors[] = {
1239 
1240  { "red", NSVG_RGB(255, 0, 0) },
1241  { "green", NSVG_RGB( 0, 128, 0) },
1242  { "blue", NSVG_RGB( 0, 0, 255) },
1243  { "yellow", NSVG_RGB(255, 255, 0) },
1244  { "cyan", NSVG_RGB( 0, 255, 255) },
1245  { "magenta", NSVG_RGB(255, 0, 255) },
1246  { "black", NSVG_RGB( 0, 0, 0) },
1247  { "grey", NSVG_RGB(128, 128, 128) },
1248  { "gray", NSVG_RGB(128, 128, 128) },
1249  { "white", NSVG_RGB(255, 255, 255) },
1250 
1251 #ifdef NANOSVG_ALL_COLOR_KEYWORDS
1252  { "aliceblue", NSVG_RGB(240, 248, 255) },
1253  { "antiquewhite", NSVG_RGB(250, 235, 215) },
1254  { "aqua", NSVG_RGB( 0, 255, 255) },
1255  { "aquamarine", NSVG_RGB(127, 255, 212) },
1256  { "azure", NSVG_RGB(240, 255, 255) },
1257  { "beige", NSVG_RGB(245, 245, 220) },
1258  { "bisque", NSVG_RGB(255, 228, 196) },
1259  { "blanchedalmond", NSVG_RGB(255, 235, 205) },
1260  { "blueviolet", NSVG_RGB(138, 43, 226) },
1261  { "brown", NSVG_RGB(165, 42, 42) },
1262  { "burlywood", NSVG_RGB(222, 184, 135) },
1263  { "cadetblue", NSVG_RGB( 95, 158, 160) },
1264  { "chartreuse", NSVG_RGB(127, 255, 0) },
1265  { "chocolate", NSVG_RGB(210, 105, 30) },
1266  { "coral", NSVG_RGB(255, 127, 80) },
1267  { "cornflowerblue", NSVG_RGB(100, 149, 237) },
1268  { "cornsilk", NSVG_RGB(255, 248, 220) },
1269  { "crimson", NSVG_RGB(220, 20, 60) },
1270  { "darkblue", NSVG_RGB( 0, 0, 139) },
1271  { "darkcyan", NSVG_RGB( 0, 139, 139) },
1272  { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
1273  { "darkgray", NSVG_RGB(169, 169, 169) },
1274  { "darkgreen", NSVG_RGB( 0, 100, 0) },
1275  { "darkgrey", NSVG_RGB(169, 169, 169) },
1276  { "darkkhaki", NSVG_RGB(189, 183, 107) },
1277  { "darkmagenta", NSVG_RGB(139, 0, 139) },
1278  { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
1279  { "darkorange", NSVG_RGB(255, 140, 0) },
1280  { "darkorchid", NSVG_RGB(153, 50, 204) },
1281  { "darkred", NSVG_RGB(139, 0, 0) },
1282  { "darksalmon", NSVG_RGB(233, 150, 122) },
1283  { "darkseagreen", NSVG_RGB(143, 188, 143) },
1284  { "darkslateblue", NSVG_RGB( 72, 61, 139) },
1285  { "darkslategray", NSVG_RGB( 47, 79, 79) },
1286  { "darkslategrey", NSVG_RGB( 47, 79, 79) },
1287  { "darkturquoise", NSVG_RGB( 0, 206, 209) },
1288  { "darkviolet", NSVG_RGB(148, 0, 211) },
1289  { "deeppink", NSVG_RGB(255, 20, 147) },
1290  { "deepskyblue", NSVG_RGB( 0, 191, 255) },
1291  { "dimgray", NSVG_RGB(105, 105, 105) },
1292  { "dimgrey", NSVG_RGB(105, 105, 105) },
1293  { "dodgerblue", NSVG_RGB( 30, 144, 255) },
1294  { "firebrick", NSVG_RGB(178, 34, 34) },
1295  { "floralwhite", NSVG_RGB(255, 250, 240) },
1296  { "forestgreen", NSVG_RGB( 34, 139, 34) },
1297  { "fuchsia", NSVG_RGB(255, 0, 255) },
1298  { "gainsboro", NSVG_RGB(220, 220, 220) },
1299  { "ghostwhite", NSVG_RGB(248, 248, 255) },
1300  { "gold", NSVG_RGB(255, 215, 0) },
1301  { "goldenrod", NSVG_RGB(218, 165, 32) },
1302  { "greenyellow", NSVG_RGB(173, 255, 47) },
1303  { "honeydew", NSVG_RGB(240, 255, 240) },
1304  { "hotpink", NSVG_RGB(255, 105, 180) },
1305  { "indianred", NSVG_RGB(205, 92, 92) },
1306  { "indigo", NSVG_RGB( 75, 0, 130) },
1307  { "ivory", NSVG_RGB(255, 255, 240) },
1308  { "khaki", NSVG_RGB(240, 230, 140) },
1309  { "lavender", NSVG_RGB(230, 230, 250) },
1310  { "lavenderblush", NSVG_RGB(255, 240, 245) },
1311  { "lawngreen", NSVG_RGB(124, 252, 0) },
1312  { "lemonchiffon", NSVG_RGB(255, 250, 205) },
1313  { "lightblue", NSVG_RGB(173, 216, 230) },
1314  { "lightcoral", NSVG_RGB(240, 128, 128) },
1315  { "lightcyan", NSVG_RGB(224, 255, 255) },
1316  { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
1317  { "lightgray", NSVG_RGB(211, 211, 211) },
1318  { "lightgreen", NSVG_RGB(144, 238, 144) },
1319  { "lightgrey", NSVG_RGB(211, 211, 211) },
1320  { "lightpink", NSVG_RGB(255, 182, 193) },
1321  { "lightsalmon", NSVG_RGB(255, 160, 122) },
1322  { "lightseagreen", NSVG_RGB( 32, 178, 170) },
1323  { "lightskyblue", NSVG_RGB(135, 206, 250) },
1324  { "lightslategray", NSVG_RGB(119, 136, 153) },
1325  { "lightslategrey", NSVG_RGB(119, 136, 153) },
1326  { "lightsteelblue", NSVG_RGB(176, 196, 222) },
1327  { "lightyellow", NSVG_RGB(255, 255, 224) },
1328  { "lime", NSVG_RGB( 0, 255, 0) },
1329  { "limegreen", NSVG_RGB( 50, 205, 50) },
1330  { "linen", NSVG_RGB(250, 240, 230) },
1331  { "maroon", NSVG_RGB(128, 0, 0) },
1332  { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
1333  { "mediumblue", NSVG_RGB( 0, 0, 205) },
1334  { "mediumorchid", NSVG_RGB(186, 85, 211) },
1335  { "mediumpurple", NSVG_RGB(147, 112, 219) },
1336  { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
1337  { "mediumslateblue", NSVG_RGB(123, 104, 238) },
1338  { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
1339  { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
1340  { "mediumvioletred", NSVG_RGB(199, 21, 133) },
1341  { "midnightblue", NSVG_RGB( 25, 25, 112) },
1342  { "mintcream", NSVG_RGB(245, 255, 250) },
1343  { "mistyrose", NSVG_RGB(255, 228, 225) },
1344  { "moccasin", NSVG_RGB(255, 228, 181) },
1345  { "navajowhite", NSVG_RGB(255, 222, 173) },
1346  { "navy", NSVG_RGB( 0, 0, 128) },
1347  { "oldlace", NSVG_RGB(253, 245, 230) },
1348  { "olive", NSVG_RGB(128, 128, 0) },
1349  { "olivedrab", NSVG_RGB(107, 142, 35) },
1350  { "orange", NSVG_RGB(255, 165, 0) },
1351  { "orangered", NSVG_RGB(255, 69, 0) },
1352  { "orchid", NSVG_RGB(218, 112, 214) },
1353  { "palegoldenrod", NSVG_RGB(238, 232, 170) },
1354  { "palegreen", NSVG_RGB(152, 251, 152) },
1355  { "paleturquoise", NSVG_RGB(175, 238, 238) },
1356  { "palevioletred", NSVG_RGB(219, 112, 147) },
1357  { "papayawhip", NSVG_RGB(255, 239, 213) },
1358  { "peachpuff", NSVG_RGB(255, 218, 185) },
1359  { "peru", NSVG_RGB(205, 133, 63) },
1360  { "pink", NSVG_RGB(255, 192, 203) },
1361  { "plum", NSVG_RGB(221, 160, 221) },
1362  { "powderblue", NSVG_RGB(176, 224, 230) },
1363  { "purple", NSVG_RGB(128, 0, 128) },
1364  { "rosybrown", NSVG_RGB(188, 143, 143) },
1365  { "royalblue", NSVG_RGB( 65, 105, 225) },
1366  { "saddlebrown", NSVG_RGB(139, 69, 19) },
1367  { "salmon", NSVG_RGB(250, 128, 114) },
1368  { "sandybrown", NSVG_RGB(244, 164, 96) },
1369  { "seagreen", NSVG_RGB( 46, 139, 87) },
1370  { "seashell", NSVG_RGB(255, 245, 238) },
1371  { "sienna", NSVG_RGB(160, 82, 45) },
1372  { "silver", NSVG_RGB(192, 192, 192) },
1373  { "skyblue", NSVG_RGB(135, 206, 235) },
1374  { "slateblue", NSVG_RGB(106, 90, 205) },
1375  { "slategray", NSVG_RGB(112, 128, 144) },
1376  { "slategrey", NSVG_RGB(112, 128, 144) },
1377  { "snow", NSVG_RGB(255, 250, 250) },
1378  { "springgreen", NSVG_RGB( 0, 255, 127) },
1379  { "steelblue", NSVG_RGB( 70, 130, 180) },
1380  { "tan", NSVG_RGB(210, 180, 140) },
1381  { "teal", NSVG_RGB( 0, 128, 128) },
1382  { "thistle", NSVG_RGB(216, 191, 216) },
1383  { "tomato", NSVG_RGB(255, 99, 71) },
1384  { "turquoise", NSVG_RGB( 64, 224, 208) },
1385  { "violet", NSVG_RGB(238, 130, 238) },
1386  { "wheat", NSVG_RGB(245, 222, 179) },
1387  { "whitesmoke", NSVG_RGB(245, 245, 245) },
1388  { "yellowgreen", NSVG_RGB(154, 205, 50) },
1389 #endif
1390 };
1391 
1392 static unsigned int nsvg__parseColorName(const char* str)
1393 {
1394  int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
1395 
1396  for (i = 0; i < ncolors; i++) {
1397  if (strcmp(nsvg__colors[i].name, str) == 0) {
1398  return nsvg__colors[i].color;
1399  }
1400  }
1401 
1402  return NSVG_RGB(128, 128, 128);
1403 }
1404 
1405 static unsigned int nsvg__parseColor(const char* str)
1406 {
1407  size_t len = 0;
1408  while(*str == ' ') ++str;
1409  len = strlen(str);
1410  if (len >= 1 && *str == '#')
1411  return nsvg__parseColorHex(str);
1412  else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
1413  return nsvg__parseColorRGB(str);
1414  return nsvg__parseColorName(str);
1415 }
1416 
1417 static float nsvg__parseOpacity(const char* str)
1418 {
1419  float val = 0;
1420  sscanf(str, "%f", &val);
1421  if (val < 0.0f) val = 0.0f;
1422  if (val > 1.0f) val = 1.0f;
1423  return val;
1424 }
1425 
1426 static float nsvg__parseMiterLimit(const char* str)
1427 {
1428  float val = 0;
1429  sscanf(str, "%f", &val);
1430  if (val < 0.0f) val = 0.0f;
1431  return val;
1432 }
1433 
1434 static int nsvg__parseUnits(const char* units)
1435 {
1436  if (units[0] == 'p' && units[1] == 'x')
1437  return NSVG_UNITS_PX;
1438  else if (units[0] == 'p' && units[1] == 't')
1439  return NSVG_UNITS_PT;
1440  else if (units[0] == 'p' && units[1] == 'c')
1441  return NSVG_UNITS_PC;
1442  else if (units[0] == 'm' && units[1] == 'm')
1443  return NSVG_UNITS_MM;
1444  else if (units[0] == 'c' && units[1] == 'm')
1445  return NSVG_UNITS_CM;
1446  else if (units[0] == 'i' && units[1] == 'n')
1447  return NSVG_UNITS_IN;
1448  else if (units[0] == '%')
1449  return NSVG_UNITS_PERCENT;
1450  else if (units[0] == 'e' && units[1] == 'm')
1451  return NSVG_UNITS_EM;
1452  else if (units[0] == 'e' && units[1] == 'x')
1453  return NSVG_UNITS_EX;
1454  return NSVG_UNITS_USER;
1455 }
1456 
1457 static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
1458 {
1459  NSVGcoordinate coord = {0, NSVG_UNITS_USER};
1460  char units[32]="";
1461  sscanf(str, "%f%31s", &coord.value, units);
1462  coord.units = nsvg__parseUnits(units);
1463  return coord;
1464 }
1465 
1466 static NSVGcoordinate nsvg__coord(float v, int units)
1467 {
1468  NSVGcoordinate coord = {v, units};
1469  return coord;
1470 }
1471 
1472 static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
1473 {
1474  NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
1475  return nsvg__convertToPixels(p, coord, orig, length);
1476 }
1477 
1478 static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
1479 {
1480  const char* end;
1481  const char* ptr;
1482  char it[64];
1483 
1484  *na = 0;
1485  ptr = str;
1486  while (*ptr && *ptr != '(') ++ptr;
1487  if (*ptr == 0)
1488  return 1;
1489  end = ptr;
1490  while (*end && *end != ')') ++end;
1491  if (*end == 0)
1492  return 1;
1493 
1494  while (ptr < end) {
1495  if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
1496  if (*na >= maxNa) return 0;
1497  ptr = nsvg__parseNumber(ptr, it, 64);
1498  args[(*na)++] = (float)nsvg__atof(it);
1499  } else {
1500  ++ptr;
1501  }
1502  }
1503  return (int)(end - str);
1504 }
1505 
1506 
1507 static int nsvg__parseMatrix(float* xform, const char* str)
1508 {
1509  float t[6];
1510  int na = 0;
1511  int len = nsvg__parseTransformArgs(str, t, 6, &na);
1512  if (na != 6) return len;
1513  memcpy(xform, t, sizeof(float)*6);
1514  return len;
1515 }
1516 
1517 static int nsvg__parseTranslate(float* xform, const char* str)
1518 {
1519  float args[2];
1520  float t[6];
1521  int na = 0;
1522  int len = nsvg__parseTransformArgs(str, args, 2, &na);
1523  if (na == 1) args[1] = 0.0;
1524 
1525  nsvg__xformSetTranslation(t, args[0], args[1]);
1526  memcpy(xform, t, sizeof(float)*6);
1527  return len;
1528 }
1529 
1530 static int nsvg__parseScale(float* xform, const char* str)
1531 {
1532  float args[2];
1533  int na = 0;
1534  float t[6];
1535  int len = nsvg__parseTransformArgs(str, args, 2, &na);
1536  if (na == 1) args[1] = args[0];
1537  nsvg__xformSetScale(t, args[0], args[1]);
1538  memcpy(xform, t, sizeof(float)*6);
1539  return len;
1540 }
1541 
1542 static int nsvg__parseSkewX(float* xform, const char* str)
1543 {
1544  float args[1];
1545  int na = 0;
1546  float t[6];
1547  int len = nsvg__parseTransformArgs(str, args, 1, &na);
1548  nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
1549  memcpy(xform, t, sizeof(float)*6);
1550  return len;
1551 }
1552 
1553 static int nsvg__parseSkewY(float* xform, const char* str)
1554 {
1555  float args[1];
1556  int na = 0;
1557  float t[6];
1558  int len = nsvg__parseTransformArgs(str, args, 1, &na);
1559  nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
1560  memcpy(xform, t, sizeof(float)*6);
1561  return len;
1562 }
1563 
1564 static int nsvg__parseRotate(float* xform, const char* str)
1565 {
1566  float args[3];
1567  int na = 0;
1568  float m[6];
1569  float t[6];
1570  int len = nsvg__parseTransformArgs(str, args, 3, &na);
1571  if (na == 1)
1572  args[1] = args[2] = 0.0f;
1573  nsvg__xformIdentity(m);
1574 
1575  if (na > 1) {
1576  nsvg__xformSetTranslation(t, -args[1], -args[2]);
1577  nsvg__xformMultiply(m, t);
1578  }
1579 
1580  nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
1581  nsvg__xformMultiply(m, t);
1582 
1583  if (na > 1) {
1584  nsvg__xformSetTranslation(t, args[1], args[2]);
1585  nsvg__xformMultiply(m, t);
1586  }
1587 
1588  memcpy(xform, m, sizeof(float)*6);
1589 
1590  return len;
1591 }
1592 
1593 static void nsvg__parseTransform(float* xform, const char* str)
1594 {
1595  float t[6];
1596  nsvg__xformIdentity(xform);
1597  while (*str)
1598  {
1599  if (strncmp(str, "matrix", 6) == 0)
1600  str += nsvg__parseMatrix(t, str);
1601  else if (strncmp(str, "translate", 9) == 0)
1602  str += nsvg__parseTranslate(t, str);
1603  else if (strncmp(str, "scale", 5) == 0)
1604  str += nsvg__parseScale(t, str);
1605  else if (strncmp(str, "rotate", 6) == 0)
1606  str += nsvg__parseRotate(t, str);
1607  else if (strncmp(str, "skewX", 5) == 0)
1608  str += nsvg__parseSkewX(t, str);
1609  else if (strncmp(str, "skewY", 5) == 0)
1610  str += nsvg__parseSkewY(t, str);
1611  else{
1612  ++str;
1613  continue;
1614  }
1615 
1616  nsvg__xformPremultiply(xform, t);
1617  }
1618 }
1619 
1620 static void nsvg__parseUrl(char* id, const char* str)
1621 {
1622  int i = 0;
1623  str += 4; // "url(";
1624  if (*str == '#')
1625  str++;
1626  while (i < 63 && *str != ')') {
1627  id[i] = *str++;
1628  i++;
1629  }
1630  id[i] = '\0';
1631 }
1632 
1633 static char nsvg__parseLineCap(const char* str)
1634 {
1635  if (strcmp(str, "butt") == 0)
1636  return NSVG_CAP_BUTT;
1637  else if (strcmp(str, "round") == 0)
1638  return NSVG_CAP_ROUND;
1639  else if (strcmp(str, "square") == 0)
1640  return NSVG_CAP_SQUARE;
1641  // TODO: handle inherit.
1642  return NSVG_CAP_BUTT;
1643 }
1644 
1645 static char nsvg__parseLineJoin(const char* str)
1646 {
1647  if (strcmp(str, "miter") == 0)
1648  return NSVG_JOIN_MITER;
1649  else if (strcmp(str, "round") == 0)
1650  return NSVG_JOIN_ROUND;
1651  else if (strcmp(str, "bevel") == 0)
1652  return NSVG_JOIN_BEVEL;
1653  // TODO: handle inherit.
1654  return NSVG_CAP_BUTT;
1655 }
1656 
1657 static char nsvg__parseFillRule(const char* str)
1658 {
1659  if (strcmp(str, "nonzero") == 0)
1660  return NSVG_FILLRULE_NONZERO;
1661  else if (strcmp(str, "evenodd") == 0)
1662  return NSVG_FILLRULE_EVENODD;
1663  // TODO: handle inherit.
1664  return NSVG_FILLRULE_NONZERO;
1665 }
1666 
1667 static const char* nsvg__getNextDashItem(const char* s, char* it)
1668 {
1669  int n = 0;
1670  it[0] = '\0';
1671  // Skip white spaces and commas
1672  while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
1673  // Advance until whitespace, comma or end.
1674  while (*s && (!nsvg__isspace(*s) && *s != ',')) {
1675  if (n < 63)
1676  it[n++] = *s;
1677  s++;
1678  }
1679  it[n++] = '\0';
1680  return s;
1681 }
1682 
1683 static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
1684 {
1685  char item[64];
1686  int count = 0, i;
1687  float sum = 0.0f;
1688 
1689  // Handle "none"
1690  if (str[0] == 'n')
1691  return 0;
1692 
1693  // Parse dashes
1694  while (*str) {
1695  str = nsvg__getNextDashItem(str, item);
1696  if (!*item) break;
1697  if (count < NSVG_MAX_DASHES)
1698  strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
1699  }
1700 
1701  for (i = 0; i < count; i++)
1702  sum += strokeDashArray[i];
1703  if (sum <= 1e-6f)
1704  count = 0;
1705 
1706  return count;
1707 }
1708 
1709 static void nsvg__parseStyle(NSVGparser* p, const char* str);
1710 
1711 static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
1712 {
1713  float xform[6];
1714  NSVGattrib* attr = nsvg__getAttr(p);
1715  if (!attr) return 0;
1716 
1717  if (strcmp(name, "style") == 0) {
1718  nsvg__parseStyle(p, value);
1719  } else if (strcmp(name, "display") == 0) {
1720  if (strcmp(value, "none") == 0)
1721  attr->visible = 0;
1722  // Don't reset ->visible on display:inline, one display:none hides the whole subtree
1723 
1724  } else if (strcmp(name, "fill") == 0) {
1725  if (strcmp(value, "none") == 0) {
1726  attr->hasFill = 0;
1727  } else if (strncmp(value, "url(", 4) == 0) {
1728  attr->hasFill = 2;
1729  nsvg__parseUrl(attr->fillGradient, value);
1730  } else {
1731  attr->hasFill = 1;
1732  attr->fillColor = nsvg__parseColor(value);
1733  }
1734  } else if (strcmp(name, "opacity") == 0) {
1735  attr->opacity = nsvg__parseOpacity(value);
1736  } else if (strcmp(name, "fill-opacity") == 0) {
1737  attr->fillOpacity = nsvg__parseOpacity(value);
1738  } else if (strcmp(name, "stroke") == 0) {
1739  if (strcmp(value, "none") == 0) {
1740  attr->hasStroke = 0;
1741  } else if (strncmp(value, "url(", 4) == 0) {
1742  attr->hasStroke = 2;
1743  nsvg__parseUrl(attr->strokeGradient, value);
1744  } else {
1745  attr->hasStroke = 1;
1746  attr->strokeColor = nsvg__parseColor(value);
1747  }
1748  } else if (strcmp(name, "stroke-width") == 0) {
1749  attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
1750  } else if (strcmp(name, "stroke-dasharray") == 0) {
1751  attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
1752  } else if (strcmp(name, "stroke-dashoffset") == 0) {
1753  attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
1754  } else if (strcmp(name, "stroke-opacity") == 0) {
1755  attr->strokeOpacity = nsvg__parseOpacity(value);
1756  } else if (strcmp(name, "stroke-linecap") == 0) {
1757  attr->strokeLineCap = nsvg__parseLineCap(value);
1758  } else if (strcmp(name, "stroke-linejoin") == 0) {
1759  attr->strokeLineJoin = nsvg__parseLineJoin(value);
1760  } else if (strcmp(name, "stroke-miterlimit") == 0) {
1761  attr->miterLimit = nsvg__parseMiterLimit(value);
1762  } else if (strcmp(name, "fill-rule") == 0) {
1763  attr->fillRule = nsvg__parseFillRule(value);
1764  } else if (strcmp(name, "font-size") == 0) {
1765  attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
1766  } else if (strcmp(name, "transform") == 0) {
1767  nsvg__parseTransform(xform, value);
1768  nsvg__xformPremultiply(attr->xform, xform);
1769  } else if (strcmp(name, "stop-color") == 0) {
1770  attr->stopColor = nsvg__parseColor(value);
1771  } else if (strcmp(name, "stop-opacity") == 0) {
1772  attr->stopOpacity = nsvg__parseOpacity(value);
1773  } else if (strcmp(name, "offset") == 0) {
1774  attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
1775  } else if (strcmp(name, "id") == 0) {
1776  strncpy(attr->id, value, 63);
1777  attr->id[63] = '\0';
1778  } else {
1779  return 0;
1780  }
1781  return 1;
1782 }
1783 
1784 static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
1785 {
1786  const char* str;
1787  const char* val;
1788  char name[512];
1789  char value[512];
1790  int n;
1791 
1792  str = start;
1793  while (str < end && *str != ':') ++str;
1794 
1795  val = str;
1796 
1797  // Right Trim
1798  while (str > start && (*str == ':' || nsvg__isspace(*str))) --str;
1799  ++str;
1800 
1801  n = (int)(str - start);
1802  if (n > 511) n = 511;
1803  if (n) memcpy(name, start, n);
1804  name[n] = 0;
1805 
1806  while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
1807 
1808  n = (int)(end - val);
1809  if (n > 511) n = 511;
1810  if (n) memcpy(value, val, n);
1811  value[n] = 0;
1812 
1813  return nsvg__parseAttr(p, name, value);
1814 }
1815 
1816 static void nsvg__parseStyle(NSVGparser* p, const char* str)
1817 {
1818  const char* start;
1819  const char* end;
1820 
1821  while (*str) {
1822  // Left Trim
1823  while(*str && nsvg__isspace(*str)) ++str;
1824  start = str;
1825  while(*str && *str != ';') ++str;
1826  end = str;
1827 
1828  // Right Trim
1829  while (end > start && (*end == ';' || nsvg__isspace(*end))) --end;
1830  ++end;
1831 
1832  nsvg__parseNameValue(p, start, end);
1833  if (*str) ++str;
1834  }
1835 }
1836 
1837 static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
1838 {
1839  int i;
1840  for (i = 0; attr[i]; i += 2)
1841  {
1842  if (strcmp(attr[i], "style") == 0)
1843  nsvg__parseStyle(p, attr[i + 1]);
1844  else
1845  nsvg__parseAttr(p, attr[i], attr[i + 1]);
1846  }
1847 }
1848 
1849 static int nsvg__getArgsPerElement(char cmd)
1850 {
1851  switch (cmd) {
1852  case 'v':
1853  case 'V':
1854  case 'h':
1855  case 'H':
1856  return 1;
1857  case 'm':
1858  case 'M':
1859  case 'l':
1860  case 'L':
1861  case 't':
1862  case 'T':
1863  return 2;
1864  case 'q':
1865  case 'Q':
1866  case 's':
1867  case 'S':
1868  return 4;
1869  case 'c':
1870  case 'C':
1871  return 6;
1872  case 'a':
1873  case 'A':
1874  return 7;
1875  }
1876  return 0;
1877 }
1878 
1879 static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1880 {
1881  if (rel) {
1882  *cpx += args[0];
1883  *cpy += args[1];
1884  } else {
1885  *cpx = args[0];
1886  *cpy = args[1];
1887  }
1888  nsvg__moveTo(p, *cpx, *cpy);
1889 }
1890 
1891 static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1892 {
1893  if (rel) {
1894  *cpx += args[0];
1895  *cpy += args[1];
1896  } else {
1897  *cpx = args[0];
1898  *cpy = args[1];
1899  }
1900  nsvg__lineTo(p, *cpx, *cpy);
1901 }
1902 
1903 static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1904 {
1905  if (rel)
1906  *cpx += args[0];
1907  else
1908  *cpx = args[0];
1909  nsvg__lineTo(p, *cpx, *cpy);
1910 }
1911 
1912 static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1913 {
1914  if (rel)
1915  *cpy += args[0];
1916  else
1917  *cpy = args[0];
1918  nsvg__lineTo(p, *cpx, *cpy);
1919 }
1920 
1921 static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
1922  float* cpx2, float* cpy2, float* args, int rel)
1923 {
1924  float x2, y2, cx1, cy1, cx2, cy2;
1925 
1926  if (rel) {
1927  cx1 = *cpx + args[0];
1928  cy1 = *cpy + args[1];
1929  cx2 = *cpx + args[2];
1930  cy2 = *cpy + args[3];
1931  x2 = *cpx + args[4];
1932  y2 = *cpy + args[5];
1933  } else {
1934  cx1 = args[0];
1935  cy1 = args[1];
1936  cx2 = args[2];
1937  cy2 = args[3];
1938  x2 = args[4];
1939  y2 = args[5];
1940  }
1941 
1942  nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1943 
1944  *cpx2 = cx2;
1945  *cpy2 = cy2;
1946  *cpx = x2;
1947  *cpy = y2;
1948 }
1949 
1950 static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
1951  float* cpx2, float* cpy2, float* args, int rel)
1952 {
1953  float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
1954 
1955  x1 = *cpx;
1956  y1 = *cpy;
1957  if (rel) {
1958  cx2 = *cpx + args[0];
1959  cy2 = *cpy + args[1];
1960  x2 = *cpx + args[2];
1961  y2 = *cpy + args[3];
1962  } else {
1963  cx2 = args[0];
1964  cy2 = args[1];
1965  x2 = args[2];
1966  y2 = args[3];
1967  }
1968 
1969  cx1 = 2*x1 - *cpx2;
1970  cy1 = 2*y1 - *cpy2;
1971 
1972  nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1973 
1974  *cpx2 = cx2;
1975  *cpy2 = cy2;
1976  *cpx = x2;
1977  *cpy = y2;
1978 }
1979 
1980 static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
1981  float* cpx2, float* cpy2, float* args, int rel)
1982 {
1983  float x1, y1, x2, y2, cx, cy;
1984  float cx1, cy1, cx2, cy2;
1985 
1986  x1 = *cpx;
1987  y1 = *cpy;
1988  if (rel) {
1989  cx = *cpx + args[0];
1990  cy = *cpy + args[1];
1991  x2 = *cpx + args[2];
1992  y2 = *cpy + args[3];
1993  } else {
1994  cx = args[0];
1995  cy = args[1];
1996  x2 = args[2];
1997  y2 = args[3];
1998  }
1999 
2000  // Convert to cubic bezier
2001  cx1 = x1 + 2.0f/3.0f*(cx - x1);
2002  cy1 = y1 + 2.0f/3.0f*(cy - y1);
2003  cx2 = x2 + 2.0f/3.0f*(cx - x2);
2004  cy2 = y2 + 2.0f/3.0f*(cy - y2);
2005 
2006  nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
2007 
2008  *cpx2 = cx;
2009  *cpy2 = cy;
2010  *cpx = x2;
2011  *cpy = y2;
2012 }
2013 
2014 static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
2015  float* cpx2, float* cpy2, float* args, int rel)
2016 {
2017  float x1, y1, x2, y2, cx, cy;
2018  float cx1, cy1, cx2, cy2;
2019 
2020  x1 = *cpx;
2021  y1 = *cpy;
2022  if (rel) {
2023  x2 = *cpx + args[0];
2024  y2 = *cpy + args[1];
2025  } else {
2026  x2 = args[0];
2027  y2 = args[1];
2028  }
2029 
2030  cx = 2*x1 - *cpx2;
2031  cy = 2*y1 - *cpy2;
2032 
2033  // Convert to cubix bezier
2034  cx1 = x1 + 2.0f/3.0f*(cx - x1);
2035  cy1 = y1 + 2.0f/3.0f*(cy - y1);
2036  cx2 = x2 + 2.0f/3.0f*(cx - x2);
2037  cy2 = y2 + 2.0f/3.0f*(cy - y2);
2038 
2039  nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
2040 
2041  *cpx2 = cx;
2042  *cpy2 = cy;
2043  *cpx = x2;
2044  *cpy = y2;
2045 }
2046 
2047 static float nsvg__sqr(float x) { return x*x; }
2048 static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
2049 
2050 static float nsvg__vecrat(float ux, float uy, float vx, float vy)
2051 {
2052  return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
2053 }
2054 
2055 static float nsvg__vecang(float ux, float uy, float vx, float vy)
2056 {
2057  float r = nsvg__vecrat(ux,uy, vx,vy);
2058  if (r < -1.0f) r = -1.0f;
2059  if (r > 1.0f) r = 1.0f;
2060  return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
2061 }
2062 
2063 static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
2064 {
2065  // Ported from canvg (https://code.google.com/p/canvg/)
2066  float rx, ry, rotx;
2067  float x1, y1, x2, y2, cx, cy, dx, dy, d;
2068  float x1p, y1p, cxp, cyp, s, sa, sb;
2069  float ux, uy, vx, vy, a1, da;
2070  float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
2071  float sinrx, cosrx;
2072  int fa, fs;
2073  int i, ndivs;
2074  float hda, kappa;
2075 
2076  rx = fabsf(args[0]); // y radius
2077  ry = fabsf(args[1]); // x radius
2078  rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle
2079  fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
2080  fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
2081  x1 = *cpx; // start point
2082  y1 = *cpy;
2083  if (rel) { // end point
2084  x2 = *cpx + args[5];
2085  y2 = *cpy + args[6];
2086  } else {
2087  x2 = args[5];
2088  y2 = args[6];
2089  }
2090 
2091  dx = x1 - x2;
2092  dy = y1 - y2;
2093  d = sqrtf(dx*dx + dy*dy);
2094  if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
2095  // The arc degenerates to a line
2096  nsvg__lineTo(p, x2, y2);
2097  *cpx = x2;
2098  *cpy = y2;
2099  return;
2100  }
2101 
2102  sinrx = sinf(rotx);
2103  cosrx = cosf(rotx);
2104 
2105  // Convert to center point parameterization.
2106  // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
2107  // 1) Compute x1', y1'
2108  x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
2109  y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
2110  d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
2111  if (d > 1) {
2112  d = sqrtf(d);
2113  rx *= d;
2114  ry *= d;
2115  }
2116  // 2) Compute cx', cy'
2117  s = 0.0f;
2118  sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
2119  sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
2120  if (sa < 0.0f) sa = 0.0f;
2121  if (sb > 0.0f)
2122  s = sqrtf(sa / sb);
2123  if (fa == fs)
2124  s = -s;
2125  cxp = s * rx * y1p / ry;
2126  cyp = s * -ry * x1p / rx;
2127 
2128  // 3) Compute cx,cy from cx',cy'
2129  cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
2130  cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
2131 
2132  // 4) Calculate theta1, and delta theta.
2133  ux = (x1p - cxp) / rx;
2134  uy = (y1p - cyp) / ry;
2135  vx = (-x1p - cxp) / rx;
2136  vy = (-y1p - cyp) / ry;
2137  a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle
2138  da = nsvg__vecang(ux,uy, vx,vy); // Delta angle
2139 
2140 // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
2141 // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
2142 
2143  if (fs == 0 && da > 0)
2144  da -= 2 * NSVG_PI;
2145  else if (fs == 1 && da < 0)
2146  da += 2 * NSVG_PI;
2147 
2148  // Approximate the arc using cubic spline segments.
2149  t[0] = cosrx; t[1] = sinrx;
2150  t[2] = -sinrx; t[3] = cosrx;
2151  t[4] = cx; t[5] = cy;
2152 
2153  // Split arc into max 90 degree segments.
2154  // The loop assumes an iteration per end point (including start and end), this +1.
2155  ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
2156  hda = (da / (float)ndivs) / 2.0f;
2157  kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
2158  if (da < 0.0f)
2159  kappa = -kappa;
2160 
2161  for (i = 0; i <= ndivs; i++) {
2162  a = a1 + da * ((float)i/(float)ndivs);
2163  dx = cosf(a);
2164  dy = sinf(a);
2165  nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
2166  nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
2167  if (i > 0)
2168  nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
2169  px = x;
2170  py = y;
2171  ptanx = tanx;
2172  ptany = tany;
2173  }
2174 
2175  *cpx = x2;
2176  *cpy = y2;
2177 }
2178 
2179 static void nsvg__parsePath(NSVGparser* p, const char** attr)
2180 {
2181  const char* s = NULL;
2182  char cmd = '\0';
2183  float args[10];
2184  int nargs;
2185  int rargs = 0;
2186  float cpx, cpy, cpx2, cpy2;
2187  const char* tmp[4];
2188  char closedFlag;
2189  int i;
2190  char item[64];
2191 
2192  for (i = 0; attr[i]; i += 2) {
2193  if (strcmp(attr[i], "d") == 0) {
2194  s = attr[i + 1];
2195  } else {
2196  tmp[0] = attr[i];
2197  tmp[1] = attr[i + 1];
2198  tmp[2] = 0;
2199  tmp[3] = 0;
2200  nsvg__parseAttribs(p, tmp);
2201  }
2202  }
2203 
2204  if (s) {
2205  nsvg__resetPath(p);
2206  cpx = 0; cpy = 0;
2207  cpx2 = 0; cpy2 = 0;
2208  closedFlag = 0;
2209  nargs = 0;
2210 
2211  while (*s) {
2212  s = nsvg__getNextPathItem(s, item);
2213  if (!*item) break;
2214  if (nsvg__isnum(item[0])) {
2215  if (nargs < 10)
2216  args[nargs++] = (float)nsvg__atof(item);
2217  if (nargs >= rargs) {
2218  switch (cmd) {
2219  case 'm':
2220  case 'M':
2221  nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
2222  // Moveto can be followed by multiple coordinate pairs,
2223  // which should be treated as linetos.
2224  cmd = (cmd == 'm') ? 'l' : 'L';
2225  rargs = nsvg__getArgsPerElement(cmd);
2226  cpx2 = cpx; cpy2 = cpy;
2227  break;
2228  case 'l':
2229  case 'L':
2230  nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
2231  cpx2 = cpx; cpy2 = cpy;
2232  break;
2233  case 'H':
2234  case 'h':
2235  nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
2236  cpx2 = cpx; cpy2 = cpy;
2237  break;
2238  case 'V':
2239  case 'v':
2240  nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
2241  cpx2 = cpx; cpy2 = cpy;
2242  break;
2243  case 'C':
2244  case 'c':
2245  nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
2246  break;
2247  case 'S':
2248  case 's':
2249  nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
2250  break;
2251  case 'Q':
2252  case 'q':
2253  nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
2254  break;
2255  case 'T':
2256  case 't':
2257  nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
2258  break;
2259  case 'A':
2260  case 'a':
2261  nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
2262  cpx2 = cpx; cpy2 = cpy;
2263  break;
2264  default:
2265  if (nargs >= 2) {
2266  cpx = args[nargs-2];
2267  cpy = args[nargs-1];
2268  cpx2 = cpx; cpy2 = cpy;
2269  }
2270  break;
2271  }
2272  nargs = 0;
2273  }
2274  } else {
2275  cmd = item[0];
2276  rargs = nsvg__getArgsPerElement(cmd);
2277  if (cmd == 'M' || cmd == 'm') {
2278  // Commit path.
2279  if (p->npts > 0)
2280  nsvg__addPath(p, closedFlag);
2281  // Start new subpath.
2282  nsvg__resetPath(p);
2283  closedFlag = 0;
2284  nargs = 0;
2285  } else if (cmd == 'Z' || cmd == 'z') {
2286  closedFlag = 1;
2287  // Commit path.
2288  if (p->npts > 0) {
2289  // Move current point to first point
2290  cpx = p->pts[0];
2291  cpy = p->pts[1];
2292  cpx2 = cpx; cpy2 = cpy;
2293  nsvg__addPath(p, closedFlag);
2294  }
2295  // Start new subpath.
2296  nsvg__resetPath(p);
2297  nsvg__moveTo(p, cpx, cpy);
2298  closedFlag = 0;
2299  nargs = 0;
2300  }
2301  }
2302  }
2303  // Commit path.
2304  if (p->npts)
2305  nsvg__addPath(p, closedFlag);
2306  }
2307 
2308  nsvg__addShape(p);
2309 }
2310 
2311 static void nsvg__parseRect(NSVGparser* p, const char** attr)
2312 {
2313  float x = 0.0f;
2314  float y = 0.0f;
2315  float w = 0.0f;
2316  float h = 0.0f;
2317  float rx = -1.0f; // marks not set
2318  float ry = -1.0f;
2319  int i;
2320 
2321  for (i = 0; attr[i]; i += 2) {
2322  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2323  if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
2324  if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
2325  if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
2326  if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
2327  if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
2328  if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
2329  }
2330  }
2331 
2332  if (rx < 0.0f && ry > 0.0f) rx = ry;
2333  if (ry < 0.0f && rx > 0.0f) ry = rx;
2334  if (rx < 0.0f) rx = 0.0f;
2335  if (ry < 0.0f) ry = 0.0f;
2336  if (rx > w/2.0f) rx = w/2.0f;
2337  if (ry > h/2.0f) ry = h/2.0f;
2338 
2339  if (w != 0.0f && h != 0.0f) {
2340  nsvg__resetPath(p);
2341 
2342  if (rx < 0.00001f || ry < 0.0001f) {
2343  nsvg__moveTo(p, x, y);
2344  nsvg__lineTo(p, x+w, y);
2345  nsvg__lineTo(p, x+w, y+h);
2346  nsvg__lineTo(p, x, y+h);
2347  } else {
2348  // Rounded rectangle
2349  nsvg__moveTo(p, x+rx, y);
2350  nsvg__lineTo(p, x+w-rx, y);
2351  nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
2352  nsvg__lineTo(p, x+w, y+h-ry);
2353  nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
2354  nsvg__lineTo(p, x+rx, y+h);
2355  nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
2356  nsvg__lineTo(p, x, y+ry);
2357  nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
2358  }
2359 
2360  nsvg__addPath(p, 1);
2361 
2362  nsvg__addShape(p);
2363  }
2364 }
2365 
2366 static void nsvg__parseCircle(NSVGparser* p, const char** attr)
2367 {
2368  float cx = 0.0f;
2369  float cy = 0.0f;
2370  float r = 0.0f;
2371  int i;
2372 
2373  for (i = 0; attr[i]; i += 2) {
2374  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2375  if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
2376  if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
2377  if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
2378  }
2379  }
2380 
2381  if (r > 0.0f) {
2382  nsvg__resetPath(p);
2383 
2384  nsvg__moveTo(p, cx+r, cy);
2385  nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
2386  nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
2387  nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
2388  nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
2389 
2390  nsvg__addPath(p, 1);
2391 
2392  nsvg__addShape(p);
2393  }
2394 }
2395 
2396 static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
2397 {
2398  float cx = 0.0f;
2399  float cy = 0.0f;
2400  float rx = 0.0f;
2401  float ry = 0.0f;
2402  int i;
2403 
2404  for (i = 0; attr[i]; i += 2) {
2405  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2406  if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
2407  if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
2408  if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
2409  if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
2410  }
2411  }
2412 
2413  if (rx > 0.0f && ry > 0.0f) {
2414 
2415  nsvg__resetPath(p);
2416 
2417  nsvg__moveTo(p, cx+rx, cy);
2418  nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
2419  nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
2420  nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
2421  nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
2422 
2423  nsvg__addPath(p, 1);
2424 
2425  nsvg__addShape(p);
2426  }
2427 }
2428 
2429 static void nsvg__parseLine(NSVGparser* p, const char** attr)
2430 {
2431  float x1 = 0.0;
2432  float y1 = 0.0;
2433  float x2 = 0.0;
2434  float y2 = 0.0;
2435  int i;
2436 
2437  for (i = 0; attr[i]; i += 2) {
2438  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2439  if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
2440  if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
2441  if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
2442  if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
2443  }
2444  }
2445 
2446  nsvg__resetPath(p);
2447 
2448  nsvg__moveTo(p, x1, y1);
2449  nsvg__lineTo(p, x2, y2);
2450 
2451  nsvg__addPath(p, 0);
2452 
2453  nsvg__addShape(p);
2454 }
2455 
2456 static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
2457 {
2458  int i;
2459  const char* s;
2460  float args[2];
2461  int nargs, npts = 0;
2462  char item[64];
2463 
2464  nsvg__resetPath(p);
2465 
2466  for (i = 0; attr[i]; i += 2) {
2467  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2468  if (strcmp(attr[i], "points") == 0) {
2469  s = attr[i + 1];
2470  nargs = 0;
2471  while (*s) {
2472  s = nsvg__getNextPathItem(s, item);
2473  args[nargs++] = (float)nsvg__atof(item);
2474  if (nargs >= 2) {
2475  if (npts == 0)
2476  nsvg__moveTo(p, args[0], args[1]);
2477  else
2478  nsvg__lineTo(p, args[0], args[1]);
2479  nargs = 0;
2480  npts++;
2481  }
2482  }
2483  }
2484  }
2485  }
2486 
2487  nsvg__addPath(p, (char)closeFlag);
2488 
2489  nsvg__addShape(p);
2490 }
2491 
2492 static void nsvg__parseSVG(NSVGparser* p, const char** attr)
2493 {
2494  int i;
2495  for (i = 0; attr[i]; i += 2) {
2496  if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2497  if (strcmp(attr[i], "width") == 0) {
2498  p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
2499  } else if (strcmp(attr[i], "height") == 0) {
2500  p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
2501  } else if (strcmp(attr[i], "viewBox") == 0) {
2502  sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight);
2503  } else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
2504  if (strstr(attr[i + 1], "none") != 0) {
2505  // No uniform scaling
2506  p->alignType = NSVG_ALIGN_NONE;
2507  } else {
2508  // Parse X align
2509  if (strstr(attr[i + 1], "xMin") != 0)
2510  p->alignX = NSVG_ALIGN_MIN;
2511  else if (strstr(attr[i + 1], "xMid") != 0)
2512  p->alignX = NSVG_ALIGN_MID;
2513  else if (strstr(attr[i + 1], "xMax") != 0)
2514  p->alignX = NSVG_ALIGN_MAX;
2515  // Parse X align
2516  if (strstr(attr[i + 1], "yMin") != 0)
2517  p->alignY = NSVG_ALIGN_MIN;
2518  else if (strstr(attr[i + 1], "yMid") != 0)
2519  p->alignY = NSVG_ALIGN_MID;
2520  else if (strstr(attr[i + 1], "yMax") != 0)
2521  p->alignY = NSVG_ALIGN_MAX;
2522  // Parse meet/slice
2523  p->alignType = NSVG_ALIGN_MEET;
2524  if (strstr(attr[i + 1], "slice") != 0)
2525  p->alignType = NSVG_ALIGN_SLICE;
2526  }
2527  }
2528  }
2529  }
2530 }
2531 
2532 static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
2533 {
2534  int i;
2535  NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
2536  if (grad == NULL) return;
2537  memset(grad, 0, sizeof(NSVGgradientData));
2538  grad->units = NSVG_OBJECT_SPACE;
2539  grad->type = type;
2540  if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
2541  grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
2542  grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
2543  grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
2544  grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
2545  } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
2546  grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
2547  grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
2548  grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
2549  }
2550 
2551  nsvg__xformIdentity(grad->xform);
2552 
2553  for (i = 0; attr[i]; i += 2) {
2554  if (strcmp(attr[i], "id") == 0) {
2555  strncpy(grad->id, attr[i+1], 63);
2556  grad->id[63] = '\0';
2557  } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2558  if (strcmp(attr[i], "gradientUnits") == 0) {
2559  if (strcmp(attr[i+1], "objectBoundingBox") == 0)
2560  grad->units = NSVG_OBJECT_SPACE;
2561  else
2562  grad->units = NSVG_USER_SPACE;
2563  } else if (strcmp(attr[i], "gradientTransform") == 0) {
2564  nsvg__parseTransform(grad->xform, attr[i + 1]);
2565  } else if (strcmp(attr[i], "cx") == 0) {
2566  grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
2567  } else if (strcmp(attr[i], "cy") == 0) {
2568  grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
2569  } else if (strcmp(attr[i], "r") == 0) {
2570  grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
2571  } else if (strcmp(attr[i], "fx") == 0) {
2572  grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
2573  } else if (strcmp(attr[i], "fy") == 0) {
2574  grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
2575  } else if (strcmp(attr[i], "x1") == 0) {
2576  grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
2577  } else if (strcmp(attr[i], "y1") == 0) {
2578  grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
2579  } else if (strcmp(attr[i], "x2") == 0) {
2580  grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
2581  } else if (strcmp(attr[i], "y2") == 0) {
2582  grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
2583  } else if (strcmp(attr[i], "spreadMethod") == 0) {
2584  if (strcmp(attr[i+1], "pad") == 0)
2585  grad->spread = NSVG_SPREAD_PAD;
2586  else if (strcmp(attr[i+1], "reflect") == 0)
2587  grad->spread = NSVG_SPREAD_REFLECT;
2588  else if (strcmp(attr[i+1], "repeat") == 0)
2589  grad->spread = NSVG_SPREAD_REPEAT;
2590  } else if (strcmp(attr[i], "xlink:href") == 0) {
2591  const char *href = attr[i+1];
2592  strncpy(grad->ref, href+1, 62);
2593  grad->ref[62] = '\0';
2594  }
2595  }
2596  }
2597 
2598  grad->next = p->gradients;
2599  p->gradients = grad;
2600 }
2601 
2602 static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
2603 {
2604  NSVGattrib* curAttr = nsvg__getAttr(p);
2605  NSVGgradientData* grad;
2606  NSVGgradientStop* stop;
2607  int i, idx;
2608 
2609  curAttr->stopOffset = 0;
2610  curAttr->stopColor = 0;
2611  curAttr->stopOpacity = 1.0f;
2612 
2613  for (i = 0; attr[i]; i += 2) {
2614  nsvg__parseAttr(p, attr[i], attr[i + 1]);
2615  }
2616 
2617  // Add stop to the last gradient.
2618  grad = p->gradients;
2619  if (grad == NULL) return;
2620 
2621  grad->nstops++;
2622  grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
2623  if (grad->stops == NULL) return;
2624 
2625  // Insert
2626  idx = grad->nstops-1;
2627  for (i = 0; i < grad->nstops-1; i++) {
2628  if (curAttr->stopOffset < grad->stops[i].offset) {
2629  idx = i;
2630  break;
2631  }
2632  }
2633  if (idx != grad->nstops-1) {
2634  for (i = grad->nstops-1; i > idx; i--)
2635  grad->stops[i] = grad->stops[i-1];
2636  }
2637 
2638  stop = &grad->stops[idx];
2639  stop->color = curAttr->stopColor;
2640  stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
2641  stop->offset = curAttr->stopOffset;
2642 }
2643 
2644 static void nsvg__startElement(void* ud, const char* el, const char** attr)
2645 {
2646  NSVGparser* p = (NSVGparser*)ud;
2647 
2648  if (p->defsFlag) {
2649  // Skip everything but gradients in defs
2650  if (strcmp(el, "linearGradient") == 0) {
2651  nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
2652  } else if (strcmp(el, "radialGradient") == 0) {
2653  nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
2654  } else if (strcmp(el, "stop") == 0) {
2655  nsvg__parseGradientStop(p, attr);
2656  }
2657  return;
2658  }
2659 
2660  if (strcmp(el, "g") == 0) {
2661  nsvg__pushAttr(p);
2662  nsvg__parseAttribs(p, attr);
2663  } else if (strcmp(el, "path") == 0) {
2664  if (p->pathFlag) // Do not allow nested paths.
2665  return;
2666  nsvg__pushAttr(p);
2667  nsvg__parsePath(p, attr);
2668  nsvg__popAttr(p);
2669  } else if (strcmp(el, "rect") == 0) {
2670  nsvg__pushAttr(p);
2671  nsvg__parseRect(p, attr);
2672  nsvg__popAttr(p);
2673  } else if (strcmp(el, "circle") == 0) {
2674  nsvg__pushAttr(p);
2675  nsvg__parseCircle(p, attr);
2676  nsvg__popAttr(p);
2677  } else if (strcmp(el, "ellipse") == 0) {
2678  nsvg__pushAttr(p);
2679  nsvg__parseEllipse(p, attr);
2680  nsvg__popAttr(p);
2681  } else if (strcmp(el, "line") == 0) {
2682  nsvg__pushAttr(p);
2683  nsvg__parseLine(p, attr);
2684  nsvg__popAttr(p);
2685  } else if (strcmp(el, "polyline") == 0) {
2686  nsvg__pushAttr(p);
2687  nsvg__parsePoly(p, attr, 0);
2688  nsvg__popAttr(p);
2689  } else if (strcmp(el, "polygon") == 0) {
2690  nsvg__pushAttr(p);
2691  nsvg__parsePoly(p, attr, 1);
2692  nsvg__popAttr(p);
2693  } else if (strcmp(el, "linearGradient") == 0) {
2694  nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
2695  } else if (strcmp(el, "radialGradient") == 0) {
2696  nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
2697  } else if (strcmp(el, "stop") == 0) {
2698  nsvg__parseGradientStop(p, attr);
2699  } else if (strcmp(el, "defs") == 0) {
2700  p->defsFlag = 1;
2701  } else if (strcmp(el, "svg") == 0) {
2702  nsvg__parseSVG(p, attr);
2703  }
2704 }
2705 
2706 static void nsvg__endElement(void* ud, const char* el)
2707 {
2708  NSVGparser* p = (NSVGparser*)ud;
2709 
2710  if (strcmp(el, "g") == 0) {
2711  nsvg__popAttr(p);
2712  } else if (strcmp(el, "path") == 0) {
2713  p->pathFlag = 0;
2714  } else if (strcmp(el, "defs") == 0) {
2715  p->defsFlag = 0;
2716  }
2717 }
2718 
2719 static void nsvg__content(void* ud, const char* s)
2720 {
2721  NSVG_NOTUSED(ud);
2722  NSVG_NOTUSED(s);
2723  // empty
2724 }
2725 
2726 static void nsvg__imageBounds(NSVGparser* p, float* bounds)
2727 {
2728  NSVGshape* shape;
2729  shape = p->image->shapes;
2730  if (shape == NULL) {
2731  bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
2732  return;
2733  }
2734  bounds[0] = shape->bounds[0];
2735  bounds[1] = shape->bounds[1];
2736  bounds[2] = shape->bounds[2];
2737  bounds[3] = shape->bounds[3];
2738  for (shape = shape->next; shape != NULL; shape = shape->next) {
2739  bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
2740  bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
2741  bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
2742  bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
2743  }
2744 }
2745 
2746 static float nsvg__viewAlign(float content, float container, int type)
2747 {
2748  if (type == NSVG_ALIGN_MIN)
2749  return 0;
2750  else if (type == NSVG_ALIGN_MAX)
2751  return container - content;
2752  // mid
2753  return (container - content) * 0.5f;
2754 }
2755 
2756 static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
2757 {
2758  float t[6];
2759  nsvg__xformSetTranslation(t, tx, ty);
2760  nsvg__xformMultiply (grad->xform, t);
2761 
2762  nsvg__xformSetScale(t, sx, sy);
2763  nsvg__xformMultiply (grad->xform, t);
2764 }
2765 
2766 static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
2767 {
2768  NSVGshape* shape;
2769  NSVGpath* path;
2770  float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
2771  int i;
2772  float* pt;
2773 
2774  // Guess image size if not set completely.
2775  nsvg__imageBounds(p, bounds);
2776 
2777  if (p->viewWidth == 0) {
2778  if (p->image->width > 0) {
2779  p->viewWidth = p->image->width;
2780  } else {
2781  p->viewMinx = bounds[0];
2782  p->viewWidth = bounds[2] - bounds[0];
2783  }
2784  }
2785  if (p->viewHeight == 0) {
2786  if (p->image->height > 0) {
2787  p->viewHeight = p->image->height;
2788  } else {
2789  p->viewMiny = bounds[1];
2790  p->viewHeight = bounds[3] - bounds[1];
2791  }
2792  }
2793  if (p->image->width == 0)
2794  p->image->width = p->viewWidth;
2795  if (p->image->height == 0)
2796  p->image->height = p->viewHeight;
2797 
2798  tx = -p->viewMinx;
2799  ty = -p->viewMiny;
2800  sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
2801  sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
2802  // Unit scaling
2803  us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
2804 
2805  // Fix aspect ratio
2806  if (p->alignType == NSVG_ALIGN_MEET) {
2807  // fit whole image into viewbox
2808  sx = sy = nsvg__minf(sx, sy);
2809  tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
2810  ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
2811  } else if (p->alignType == NSVG_ALIGN_SLICE) {
2812  // fill whole viewbox with image
2813  sx = sy = nsvg__maxf(sx, sy);
2814  tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
2815  ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
2816  }
2817 
2818  // Transform
2819  sx *= us;
2820  sy *= us;
2821  avgs = (sx+sy) / 2.0f;
2822  for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
2823  shape->bounds[0] = (shape->bounds[0] + tx) * sx;
2824  shape->bounds[1] = (shape->bounds[1] + ty) * sy;
2825  shape->bounds[2] = (shape->bounds[2] + tx) * sx;
2826  shape->bounds[3] = (shape->bounds[3] + ty) * sy;
2827  for (path = shape->paths; path != NULL; path = path->next) {
2828  path->bounds[0] = (path->bounds[0] + tx) * sx;
2829  path->bounds[1] = (path->bounds[1] + ty) * sy;
2830  path->bounds[2] = (path->bounds[2] + tx) * sx;
2831  path->bounds[3] = (path->bounds[3] + ty) * sy;
2832  for (i =0; i < path->npts; i++) {
2833  pt = &path->pts[i*2];
2834  pt[0] = (pt[0] + tx) * sx;
2835  pt[1] = (pt[1] + ty) * sy;
2836  }
2837  }
2838 
2839  if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
2840  nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
2841  memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
2842  nsvg__xformInverse(shape->fill.gradient->xform, t);
2843  }
2844  if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
2845  nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
2846  memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
2847  nsvg__xformInverse(shape->stroke.gradient->xform, t);
2848  }
2849 
2850  shape->strokeWidth *= avgs;
2851  shape->strokeDashOffset *= avgs;
2852  for (i = 0; i < shape->strokeDashCount; i++)
2853  shape->strokeDashArray[i] *= avgs;
2854  }
2855 }
2856 
2857 NSVGimage* nsvgParse(char* input, const char* units, float dpi)
2858 {
2859  NSVGparser* p;
2860  NSVGimage* ret = 0;
2861 
2862  p = nsvg__createParser();
2863  if (p == NULL) {
2864  return NULL;
2865  }
2866  p->dpi = dpi;
2867 
2868  nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
2869 
2870  // Scale to viewBox
2871  nsvg__scaleToViewbox(p, units);
2872 
2873  ret = p->image;
2874  p->image = NULL;
2875 
2876  nsvg__deleteParser(p);
2877 
2878  return ret;
2879 }
2880 
2881 NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
2882 {
2883  FILE* fp = NULL;
2884  size_t size;
2885  char* data = NULL;
2886  NSVGimage* image = NULL;
2887 
2888  fp = fopen(filename, "rb");
2889  if (!fp) goto error;
2890  fseek(fp, 0, SEEK_END);
2891  size = ftell(fp);
2892  fseek(fp, 0, SEEK_SET);
2893  data = (char*)malloc(size+1);
2894  if (data == NULL) goto error;
2895  if (fread(data, 1, size, fp) != size) goto error;
2896  data[size] = '\0'; // Must be null terminated.
2897  fclose(fp);
2898  image = nsvgParse(data, units, dpi);
2899  free(data);
2900 
2901  return image;
2902 
2903 error:
2904  if (fp) fclose(fp);
2905  if (data) free(data);
2906  if (image) nsvgDelete(image);
2907  return NULL;
2908 }
2909 
2910 void nsvgDelete(NSVGimage* image)
2911 {
2912  NSVGshape *snext, *shape;
2913  if (image == NULL) return;
2914  shape = image->shapes;
2915  while (shape != NULL) {
2916  snext = shape->next;
2917  nsvg__deletePaths(shape->paths);
2918  nsvg__deletePaint(&shape->fill);
2919  nsvg__deletePaint(&shape->stroke);
2920  free(shape);
2921  shape = snext;
2922  }
2923  free(image);
2924 }
2925 
2926 #endif
Definition: nanosvg.h:156
Definition: nanosvg.h:127
Definition: nanosvg.h:136
Definition: nanosvg.h:111
Definition: nanosvg.h:106
Definition: nanosvg.h:119