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