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