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