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