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 * Fork developement, feature integration and new bugs: 28 * Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org> 29 * Contains code from various contributors. 30 */ 31 /** 32 NanoVega.SVG is a simple stupid SVG parser. The output of the parser is a list of drawing commands. 33 34 The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. 35 36 NanoVega.SVG supports a wide range of SVG features, but something may be missing. Your's Captain Obvious. 37 38 39 The shapes in the SVG images are transformed by the viewBox and converted to specified units. 40 That is, you should get the same looking data as your designed in your favorite app. 41 42 NanoVega.SVG can return the paths in few different units. For example if you want to render an image, you may choose 43 to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. 44 45 The units passed to NanoVega.SVG should be one of: 'px', 'pt', 'pc', 'mm', 'cm', 'in'. 46 DPI (dots-per-inch) controls how the unit conversion is done. 47 48 If you don't know or care about the units stuff, "px" and 96 should get you going. 49 50 Example Usage: 51 52 --- 53 // Load 54 NSVG* image = nsvgParseFromFile("test.svg", "px", 96); 55 printf("size: %f x %f\n", image.width, image.height); 56 // Use... 57 image.forEachShape((in ref NSVG.Shape shape) { 58 if (!shape.visible) return; 59 shape.forEachPath((in ref NSVG.Path path) { 60 // this will issue final `LineTo` for closed pathes 61 path.forEachCommand!true(delegate (NSVG.Command cmd, const(float)[] args) nothrow @trusted @nogc { 62 final switch (cmd) { 63 case NSVG.Command.MoveTo: nvg.moveTo(args); break; 64 case NSVG.Command.LineTo: nvg.lineTo(args); break; 65 case NSVG.Command.QuadTo: nvg.quadTo(args); break; 66 case NSVG.Command.BezierTo: nvg.bezierTo(args); break; 67 } 68 }); 69 }); 70 }); 71 72 NSVGrasterizer rast = nsvgCreateRasterizer(); 73 // Allocate memory for image 74 ubyte* img = malloc(w*h*4); 75 // Rasterize 76 rasterize(rast, image, 0, 0, 1, img, w, h, w*4); 77 78 // Delete 79 image.kill(); 80 --- 81 82 To turn a SVG into a png: 83 --- 84 import arsd.svg; 85 import arsd.png; 86 87 void main() { 88 // Load 89 NSVG* image = nsvgParseFromFile("test.svg", "px", 96); 90 91 int w = 200; 92 int h = 200; 93 94 NSVGrasterizer rast = nsvgCreateRasterizer(); 95 // Allocate memory for image 96 auto img = new TrueColorImage(w, h); 97 // Rasterize 98 rasterize(rast, image, 0, 0, 1, img.imageData.bytes.ptr, w, h, w*4); 99 100 // Delete 101 image.kill(); 102 103 writePng("test.png", img); 104 105 106 } 107 --- 108 */ 109 module arsd.svg; 110 111 private import core.stdc.math : fabs, fabsf, atan2f, acosf, cosf, sinf, tanf, sqrt, sqrtf, floorf, ceilf, fmodf; 112 //private import iv.vfs; 113 114 version(nanosvg_disable_vfs) { 115 enum NanoSVGHasIVVFS = false; 116 } else { 117 static if (is(typeof((){import iv.vfs;}))) { 118 enum NanoSVGHasIVVFS = true; 119 import iv.vfs; 120 } else { 121 enum NanoSVGHasIVVFS = false; 122 } 123 } 124 125 version(aliced) {} else { 126 private alias usize = size_t; 127 } 128 129 version = nanosvg_crappy_stylesheet_parser; 130 //version = nanosvg_debug_styles; 131 //version(rdmd) import iv.strex; 132 133 //version = nanosvg_use_beziers; // convert everything to beziers 134 //version = nanosvg_only_cubic_beziers; // convert everything to cubic beziers 135 136 /// 137 public enum NSVGDefaults { 138 CanvasWidth = 800, 139 CanvasHeight = 600, 140 } 141 142 143 // ////////////////////////////////////////////////////////////////////////// // 144 public alias NSVGrasterizer = NSVGrasterizerS*; /// 145 public alias NSVGRasterizer = NSVGrasterizer; /// 146 147 /// 148 struct NSVG { 149 @disable this (this); 150 151 /// 152 enum Command : int { 153 MoveTo, /// 154 LineTo, /// 155 QuadTo, /// 156 BezierTo, /// cubic bezier 157 } 158 159 /// 160 enum PaintType : ubyte { 161 None, /// 162 Color, /// 163 LinearGradient, /// 164 RadialGradient, /// 165 } 166 167 /// 168 enum SpreadType : ubyte { 169 Pad, /// 170 Reflect, /// 171 Repeat, /// 172 } 173 174 /// 175 enum LineJoin : ubyte { 176 Miter, /// 177 Round, /// 178 Bevel, /// 179 } 180 181 /// 182 enum LineCap : ubyte { 183 Butt, /// 184 Round, /// 185 Square, /// 186 } 187 188 /// 189 enum FillRule : ubyte { 190 NonZero, /// 191 EvenOdd, /// 192 } 193 194 alias Flags = ubyte; /// 195 enum : ubyte { 196 Visible = 0x01, /// 197 } 198 199 /// 200 static struct GradientStop { 201 uint color; /// 202 float offset; /// 203 } 204 205 /// 206 static struct Gradient { 207 float[6] xform; /// 208 SpreadType spread; /// 209 float fx, fy; /// 210 int nstops; /// 211 GradientStop[0] stops; /// 212 } 213 214 /// 215 static struct Paint { 216 pure nothrow @safe @nogc: 217 @disable this (this); 218 PaintType type; /// 219 union { 220 uint color; /// 221 Gradient* gradient; /// 222 } 223 static uint rgb (ubyte r, ubyte g, ubyte b) { pragma(inline, true); return (r|(g<<8)|(b<<16)); } /// 224 @property const { 225 bool isNone () { pragma(inline, true); return (type == PaintType.None); } /// 226 bool isColor () { pragma(inline, true); return (type == PaintType.Color); } /// 227 // gradient types 228 bool isLinear () { pragma(inline, true); return (type == PaintType.LinearGradient); } /// 229 bool isRadial () { pragma(inline, true); return (type == PaintType.RadialGradient); } /// 230 // color 231 ubyte r () { pragma(inline, true); return color&0xff; } /// 232 ubyte g () { pragma(inline, true); return (color>>8)&0xff; } /// 233 ubyte b () { pragma(inline, true); return (color>>16)&0xff; } /// 234 ubyte a () { pragma(inline, true); return (color>>24)&0xff; } /// 235 } 236 } 237 238 /// 239 static struct Path { 240 @disable this (this); 241 float* stream; /// Command, args...; Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... 242 int nsflts; /// Total number of floats in stream. 243 bool closed; /// Flag indicating if shapes should be treated as closed. 244 float[4] bounds; /// Tight bounding box of the shape [minx,miny,maxx,maxy]. 245 NSVG.Path* next; /// Pointer to next path, or null if last element. 246 247 /// 248 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (nsflts == 0); } 249 250 /// 251 float startX () const nothrow @trusted @nogc { 252 pragma(inline, true); 253 return (nsflts >= 3 && cast(Command)stream[0] == Command.MoveTo ? stream[1] : float.nan); 254 } 255 256 /// 257 float startY () const nothrow @trusted @nogc { 258 pragma(inline, true); 259 return (nsflts >= 3 && cast(Command)stream[0] == Command.MoveTo ? stream[2] : float.nan); 260 } 261 262 /// 263 bool startPoint (float* dx, float* dy) const nothrow @trusted @nogc { 264 if (nsflts >= 3 && cast(Command)stream[0] == Command.MoveTo) { 265 if (dx !is null) *dx = stream[1]; 266 if (dy !is null) *dy = stream[2]; 267 return true; 268 } else { 269 if (dx !is null) *dx = 0; 270 if (dy !is null) *dy = 0; 271 return false; 272 } 273 } 274 275 /// 276 int countCubics () const nothrow @trusted @nogc { 277 if (nsflts < 3) return 0; 278 int res = 0, argc; 279 for (int pidx = 0; pidx+3 <= nsflts; ) { 280 final switch (cast(Command)stream[pidx++]) { 281 case Command.MoveTo: argc = 2; break; 282 case Command.LineTo: argc = 2; ++res; break; 283 case Command.QuadTo: argc = 4; ++res; break; 284 case Command.BezierTo: argc = 6; ++res; break; 285 } 286 if (pidx+argc > nsflts) break; // just in case 287 pidx += argc; 288 } 289 return res; 290 } 291 292 /// 293 int countCommands(bool synthesizeCloseCommand=true) () const nothrow @trusted @nogc { 294 if (nsflts < 3) return 0; 295 int res = 0, argc; 296 for (int pidx = 0; pidx+3 <= nsflts; ) { 297 ++res; 298 final switch (cast(Command)stream[pidx++]) { 299 case Command.MoveTo: argc = 2; break; 300 case Command.LineTo: argc = 2; break; 301 case Command.QuadTo: argc = 4; break; 302 case Command.BezierTo: argc = 6; break; 303 } 304 if (pidx+argc > nsflts) break; // just in case 305 pidx += argc; 306 } 307 static if (synthesizeCloseCommand) { if (closed) ++res; } 308 return res; 309 } 310 311 /// emits cubic beziers. 312 /// if `withMoveTo` is `false`, issue 8-arg commands for cubic beziers (i.e. include starting point). 313 /// if `withMoveTo` is `true`, issue 2-arg command for `moveTo`, and 6-arg command for cubic beziers. 314 void asCubics(bool withMoveTo=false, DG) (scope DG dg) inout if (__traits(compiles, (){ DG xdg; float[] f; xdg(f); })) { 315 if (dg is null) return; 316 if (nsflts < 3) return; 317 enum HasRes = __traits(compiles, (){ DG xdg; float[] f; bool res = xdg(f); }); 318 float cx = 0, cy = 0; 319 float[8] cubic = void; 320 321 void synthLine (in float cx, in float cy, in float x, in float y) nothrow @trusted @nogc { 322 immutable float dx = x-cx; 323 immutable float dy = y-cy; 324 cubic.ptr[0] = cx; 325 cubic.ptr[1] = cy; 326 cubic.ptr[2] = cx+dx/3.0f; 327 cubic.ptr[3] = cy+dy/3.0f; 328 cubic.ptr[4] = x-dx/3.0f; 329 cubic.ptr[5] = y-dy/3.0f; 330 cubic.ptr[6] = x; 331 cubic.ptr[7] = y; 332 } 333 334 void synthQuad (in float cx, in float cy, in float x1, in float y1, in float x2, in float y2) nothrow @trusted @nogc { 335 immutable float cx1 = x1+2.0f/3.0f*(cx-x1); 336 immutable float cy1 = y1+2.0f/3.0f*(cy-y1); 337 immutable float cx2 = x2+2.0f/3.0f*(cx-x2); 338 immutable float cy2 = y2+2.0f/3.0f*(cy-y2); 339 cubic.ptr[0] = cx; 340 cubic.ptr[1] = cy; 341 cubic.ptr[2] = cx1; 342 cubic.ptr[3] = cy2; 343 cubic.ptr[4] = cx2; 344 cubic.ptr[5] = cy2; 345 cubic.ptr[6] = x2; 346 cubic.ptr[7] = y2; 347 } 348 349 for (int pidx = 0; pidx+3 <= nsflts; ) { 350 final switch (cast(Command)stream[pidx++]) { 351 case Command.MoveTo: 352 static if (withMoveTo) { 353 static if (HasRes) { if (dg(stream[pidx+0..pidx+2])) return; } else { dg(stream[pidx+0..pidx+2]); } 354 } 355 cx = stream[pidx++]; 356 cy = stream[pidx++]; 357 continue; 358 case Command.LineTo: 359 synthLine(cx, cy, stream[pidx+0], stream[pidx+1]); 360 pidx += 2; 361 break; 362 case Command.QuadTo: 363 synthQuad(cx, cy, stream[pidx+0], stream[pidx+1], stream[pidx+2], stream[pidx+3]); 364 pidx += 4; 365 break; 366 case Command.BezierTo: 367 cubic.ptr[0] = cx; 368 cubic.ptr[1] = cy; 369 cubic.ptr[2..8] = stream[pidx..pidx+6]; 370 pidx += 6; 371 break; 372 } 373 cx = cubic.ptr[6]; 374 cy = cubic.ptr[7]; 375 static if (withMoveTo) { 376 static if (HasRes) { if (dg(cubic[2..8])) return; } else { dg(cubic[2..8]); } 377 } else { 378 static if (HasRes) { if (dg(cubic[])) return; } else { dg(cubic[]); } 379 } 380 } 381 } 382 383 /// if `synthesizeCloseCommand` is true, and the path is closed, this emits line to the first point. 384 void forEachCommand(bool synthesizeCloseCommand=true, DG) (scope DG dg) inout 385 if (__traits(compiles, (){ DG xdg; Command c; const(float)[] f; xdg(c, f); })) 386 { 387 if (dg is null) return; 388 if (nsflts < 3) return; 389 enum HasRes = __traits(compiles, (){ DG xdg; Command c; const(float)[] f; bool res = xdg(c, f); }); 390 int argc; 391 Command cmd; 392 for (int pidx = 0; pidx+3 <= nsflts; ) { 393 cmd = cast(Command)stream[pidx++]; 394 final switch (cmd) { 395 case Command.MoveTo: argc = 2; break; 396 case Command.LineTo: argc = 2; break; 397 case Command.QuadTo: argc = 4; break; 398 case Command.BezierTo: argc = 6; break; 399 } 400 if (pidx+argc > nsflts) break; // just in case 401 static if (HasRes) { if (dg(cmd, stream[pidx..pidx+argc])) return; } else { dg(cmd, stream[pidx..pidx+argc]); } 402 pidx += argc; 403 } 404 static if (synthesizeCloseCommand) { 405 if (closed && cast(Command)stream[0] == Command.MoveTo) { 406 static if (HasRes) { if (dg(Command.LineTo, stream[1..3])) return; } else { dg(Command.LineTo, stream[1..3]); } 407 } 408 } 409 } 410 } 411 412 /// 413 static struct Shape { 414 @disable this (this); 415 char[64] id = 0; /// Optional 'id' attr of the shape or its group 416 NSVG.Paint fill; /// Fill paint 417 NSVG.Paint stroke; /// Stroke paint 418 float opacity; /// Opacity of the shape. 419 float strokeWidth; /// Stroke width (scaled). 420 float strokeDashOffset; /// Stroke dash offset (scaled). 421 float[8] strokeDashArray; /// Stroke dash array (scaled). 422 byte strokeDashCount; /// Number of dash values in dash array. 423 LineJoin strokeLineJoin; /// Stroke join type. 424 LineCap strokeLineCap; /// Stroke cap type. 425 float miterLimit; /// Miter limit 426 FillRule fillRule; /// Fill rule, see FillRule. 427 /*Flags*/ubyte flags; /// Logical or of NSVG_FLAGS_* flags 428 float[4] bounds; /// Tight bounding box of the shape [minx,miny,maxx,maxy]. 429 NSVG.Path* paths; /// Linked list of paths in the image. 430 NSVG.Shape* next; /// Pointer to next shape, or null if last element. 431 432 @property bool visible () const pure nothrow @safe @nogc { pragma(inline, true); return ((flags&Visible) != 0); } /// 433 434 /// delegate can accept: 435 /// NSVG.Path* 436 /// const(NSVG.Path)* 437 /// ref NSVG.Path 438 /// in ref NSVG.Path 439 /// delegate can return: 440 /// void 441 /// bool (true means `stop`) 442 void forEachPath(DG) (scope DG dg) inout 443 if (__traits(compiles, (){ DG xdg; NSVG.Path s; xdg(&s); }) || 444 __traits(compiles, (){ DG xdg; NSVG.Path s; xdg(s); })) 445 { 446 if (dg is null) return; 447 enum WantPtr = __traits(compiles, (){ DG xdg; NSVG.Path s; xdg(&s); }); 448 static if (WantPtr) { 449 enum HasRes = __traits(compiles, (){ DG xdg; NSVG.Path s; bool res = xdg(&s); }); 450 } else { 451 enum HasRes = __traits(compiles, (){ DG xdg; NSVG.Path s; bool res = xdg(s); }); 452 } 453 static if (__traits(compiles, (){ NSVG.Path* s = this.paths; })) { 454 alias TP = NSVG.Path*; 455 } else { 456 alias TP = const(NSVG.Path)*; 457 } 458 for (TP path = paths; path !is null; path = path.next) { 459 static if (HasRes) { 460 static if (WantPtr) { 461 if (dg(path)) return; 462 } else { 463 if (dg(*path)) return; 464 } 465 } else { 466 static if (WantPtr) dg(path); else dg(*path); 467 } 468 } 469 } 470 } 471 472 float width; /// Width of the image. 473 float height; /// Height of the image. 474 NSVG.Shape* shapes; /// Linked list of shapes in the image. 475 476 /// delegate can accept: 477 /// NSVG.Shape* 478 /// const(NSVG.Shape)* 479 /// ref NSVG.Shape 480 /// in ref NSVG.Shape 481 /// delegate can return: 482 /// void 483 /// bool (true means `stop`) 484 void forEachShape(DG) (scope DG dg) inout 485 if (__traits(compiles, (){ DG xdg; NSVG.Shape s; xdg(&s); }) || 486 __traits(compiles, (){ DG xdg; NSVG.Shape s; xdg(s); })) 487 { 488 if (dg is null) return; 489 enum WantPtr = __traits(compiles, (){ DG xdg; NSVG.Shape s; xdg(&s); }); 490 static if (WantPtr) { 491 enum HasRes = __traits(compiles, (){ DG xdg; NSVG.Shape s; bool res = xdg(&s); }); 492 } else { 493 enum HasRes = __traits(compiles, (){ DG xdg; NSVG.Shape s; bool res = xdg(s); }); 494 } 495 static if (__traits(compiles, (){ NSVG.Shape* s = this.shapes; })) { 496 alias TP = NSVG.Shape*; 497 } else { 498 alias TP = const(NSVG.Shape)*; 499 } 500 for (TP shape = shapes; shape !is null; shape = shape.next) { 501 static if (HasRes) { 502 static if (WantPtr) { 503 if (dg(shape)) return; 504 } else { 505 if (dg(*shape)) return; 506 } 507 } else { 508 static if (WantPtr) dg(shape); else dg(*shape); 509 } 510 } 511 } 512 } 513 514 515 // ////////////////////////////////////////////////////////////////////////// // 516 private: 517 nothrow @trusted @nogc { 518 519 // ////////////////////////////////////////////////////////////////////////// // 520 // sscanf replacement: just enough to replace all our cases 521 int xsscanf(A...) (const(char)[] str, const(char)[] fmt, ref A args) { 522 int spos; 523 while (spos < str.length && str.ptr[spos] <= ' ') ++spos; 524 525 static int hexdigit() (char c) { 526 pragma(inline, true); 527 return 528 (c >= '0' && c <= '9' ? c-'0' : 529 c >= 'A' && c <= 'F' ? c-'A'+10 : 530 c >= 'a' && c <= 'f' ? c-'a'+10 : 531 -1); 532 } 533 534 bool parseInt(T : ulong) (ref T res) { 535 res = 0; 536 debug(xsscanf_int) { import std.stdio; writeln("parseInt00: str=", str[spos..$].quote); } 537 bool neg = false; 538 if (spos < str.length && str.ptr[spos] == '+') ++spos; 539 else if (spos < str.length && str.ptr[spos] == '-') { neg = true; ++spos; } 540 if (spos >= str.length || str.ptr[spos] < '0' || str.ptr[spos] > '9') return false; 541 while (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') res = res*10+str.ptr[spos++]-'0'; 542 debug(xsscanf_int) { import std.stdio; writeln("parseInt10: str=", str[spos..$].quote); } 543 if (neg) res = -res; 544 return true; 545 } 546 547 bool parseHex(T : ulong) (ref T res) { 548 res = 0; 549 debug(xsscanf_int) { import std.stdio; writeln("parseHex00: str=", str[spos..$].quote); } 550 if (spos >= str.length || hexdigit(str.ptr[spos]) < 0) return false; 551 while (spos < str.length) { 552 auto d = hexdigit(str.ptr[spos]); 553 if (d < 0) break; 554 res = res*16+d; 555 ++spos; 556 } 557 debug(xsscanf_int) { import std.stdio; writeln("parseHex10: str=", str[spos..$].quote); } 558 return true; 559 } 560 561 bool parseFloat(T : real) (ref T res) { 562 res = 0.0; 563 debug(xsscanf_float) { import std.stdio; writeln("parseFloat00: str=", str[spos..$].quote); } 564 bool neg = false; 565 if (spos < str.length && str.ptr[spos] == '+') ++spos; 566 else if (spos < str.length && str.ptr[spos] == '-') { neg = true; ++spos; } 567 bool wasChar = false; 568 // integer part 569 debug(xsscanf_float) { import std.stdio; writeln("parseFloat01: str=", str[spos..$].quote); } 570 if (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') wasChar = true; 571 while (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') res = res*10+str.ptr[spos++]-'0'; 572 // fractional part 573 if (spos < str.length && str.ptr[spos] == '.') { 574 debug(xsscanf_float) { import std.stdio; writeln("parseFloat02: str=", str[spos..$].quote); } 575 T div = 1.0/10; 576 ++spos; 577 if (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') wasChar = true; 578 debug(xsscanf_float) { import std.stdio; writeln("parseFloat03: str=", str[spos..$].quote); } 579 while (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') { 580 res += div*(str.ptr[spos++]-'0'); 581 div /= 10.0; 582 } 583 debug(xsscanf_float) { import std.stdio; writeln("parseFloat04: str=", str[spos..$].quote); } 584 debug(xsscanf_float) { import std.stdio; writeln("div=", div, "; res=", res, "; str=", str[spos..$].quote); } 585 } 586 // '[Ee][+-]num' part 587 if (wasChar && spos < str.length && (str.ptr[spos] == 'E' || str.ptr[spos] == 'e')) { 588 debug(xsscanf_float) { import std.stdio; writeln("parseFloat05: str=", str[spos..$].quote); } 589 ++spos; 590 bool xneg = false; 591 if (spos < str.length && str.ptr[spos] == '+') ++spos; 592 else if (spos < str.length && str.ptr[spos] == '-') { xneg = true; ++spos; } 593 int n = 0; 594 if (spos >= str.length || str.ptr[spos] < '0' || str.ptr[spos] > '9') return false; // number expected 595 debug(xsscanf_float) { import std.stdio; writeln("parseFloat06: str=", str[spos..$].quote); } 596 while (spos < str.length && str.ptr[spos] >= '0' && str.ptr[spos] <= '9') n = n*10+str.ptr[spos++]-'0'; 597 if (xneg) { 598 while (n-- > 0) res /= 10; 599 } else { 600 while (n-- > 0) res *= 10; 601 } 602 debug(xsscanf_float) { import std.stdio; writeln("parseFloat07: str=", str[spos..$].quote); } 603 } 604 if (!wasChar) return false; 605 debug(xsscanf_float) { import std.stdio; writeln("parseFloat10: str=", str[spos..$].quote); } 606 if (neg) res = -res; 607 return true; 608 } 609 610 int fpos; 611 612 void skipXSpaces () { 613 if (fpos < fmt.length && fmt.ptr[fpos] <= ' ') { 614 while (fpos < fmt.length && fmt.ptr[fpos] <= ' ') ++fpos; 615 while (spos < str.length && str.ptr[spos] <= ' ') ++spos; 616 } 617 } 618 619 bool parseImpl(T/*, usize dummy*/) (ref T res) { 620 while (fpos < fmt.length) { 621 //{ import std.stdio; writeln("spos=", spos, "; fpos=", fpos, "\nfmt=", fmt[fpos..$].quote, "\nstr=", str[spos..$].quote); } 622 if (fmt.ptr[fpos] <= ' ') { 623 skipXSpaces(); 624 continue; 625 } 626 if (fmt.ptr[fpos] != '%') { 627 if (spos >= str.length || str.ptr[spos] != fmt.ptr[spos]) return false; 628 ++spos; 629 ++fpos; 630 continue; 631 } 632 if (fmt.length-fpos < 2) return false; // stray percent 633 fpos += 2; 634 bool skipAss = false; 635 if (fmt.ptr[fpos-1] == '*') { 636 ++fpos; 637 if (fpos >= fmt.length) return false; // stray star 638 skipAss = true; 639 } 640 switch (fmt.ptr[fpos-1]) { 641 case '%': 642 if (spos >= str.length || str.ptr[spos] != '%') return false; 643 ++spos; 644 break; 645 case 'd': 646 static if (is(T : ulong)) { 647 if (skipAss) { 648 long v; 649 if (!parseInt!long(v)) return false; 650 } else { 651 return parseInt!T(res); 652 } 653 } else { 654 if (!skipAss) assert(0, "invalid type"); 655 long v; 656 if (!parseInt!long(v)) return false; 657 } 658 break; 659 case 'x': 660 static if (is(T : ulong)) { 661 if (skipAss) { 662 long v; 663 if (!parseHex!long(v)) return false; 664 } else { 665 return parseHex!T(res); 666 } 667 } else { 668 if (!skipAss) assert(0, "invalid type"); 669 ulong v; 670 if (!parseHex!ulong(v)) return false; 671 } 672 break; 673 case 'f': 674 static if (is(T == float) || is(T == double) || is(T == real)) { 675 if (skipAss) { 676 double v; 677 if (!parseFloat!double(v)) return false; 678 } else { 679 return parseFloat!T(res); 680 } 681 } else { 682 if (!skipAss) assert(0, "invalid type"); 683 double v; 684 if (!parseFloat!double(v)) return false; 685 } 686 break; 687 case '[': 688 if (fmt.length-fpos < 1) return false; 689 auto stp = spos; 690 while (spos < str.length) { 691 bool ok = false; 692 foreach (immutable cidx, char c; fmt[fpos..$]) { 693 if (cidx != 0) { 694 if (c == '-') assert(0, "not yet"); 695 if (c == ']') break; 696 } 697 if (c == ' ') { 698 if (str.ptr[spos] <= ' ') { ok = true; break; } 699 } else { 700 if (str.ptr[spos] == c) { ok = true; break; } 701 } 702 } 703 //{ import std.stdio; writeln("** spos=", spos, "; fpos=", fpos, "\nfmt=", fmt[fpos..$].quote, "\nstr=", str[spos..$].quote, "\nok: ", ok); } 704 if (!ok) break; // not a match 705 ++spos; // skip match 706 } 707 ++fpos; 708 while (fpos < fmt.length && fmt[fpos] != ']') ++fpos; 709 if (fpos < fmt.length) ++fpos; 710 static if (is(T == const(char)[])) { 711 if (!skipAss) { 712 res = str[stp..spos]; 713 return true; 714 } 715 } else { 716 if (!skipAss) assert(0, "invalid type"); 717 } 718 break; 719 case 's': 720 auto stp = spos; 721 while (spos < str.length && str.ptr[spos] > ' ') ++spos; 722 static if (is(T == const(char)[])) { 723 if (!skipAss) { 724 res = str[stp..spos]; 725 return true; 726 } 727 } else { 728 // skip non-spaces 729 if (!skipAss) assert(0, "invalid type"); 730 } 731 break; 732 default: assert(0, "unknown format specifier"); 733 } 734 } 735 return false; 736 } 737 738 foreach (usize aidx, immutable T; A) { 739 //pragma(msg, "aidx=", aidx, "; T=", T); 740 if (!parseImpl!(T)(args[aidx])) return -(spos+1); 741 //{ import std.stdio; writeln("@@@ aidx=", aidx+3, "; spos=", spos, "; fpos=", fpos, "\nfmt=", fmt[fpos..$].quote, "\nstr=", str[spos..$].quote); } 742 } 743 skipXSpaces(); 744 return (fpos < fmt.length ? -(spos+1) : spos); 745 } 746 747 748 // ////////////////////////////////////////////////////////////////////////// // 749 T* xalloc(T) (usize addmem=0) if (!is(T == class)) { 750 import core.stdc.stdlib : malloc; 751 if (T.sizeof == 0 && addmem == 0) addmem = 1; 752 auto res = cast(ubyte*)malloc(T.sizeof+addmem+256); 753 if (res is null) assert(0, "NanoVega.SVG: out of memory"); 754 res[0..T.sizeof+addmem] = 0; 755 return cast(T*)res; 756 } 757 758 T* xcalloc(T) (usize count) if (!is(T == class) && !is(T == struct)) { 759 import core.stdc.stdlib : malloc; 760 usize sz = T.sizeof*count; 761 if (sz == 0) sz = 1; 762 auto res = cast(ubyte*)malloc(sz+256); 763 if (res is null) assert(0, "NanoVega.SVG: out of memory"); 764 res[0..sz] = 0; 765 return cast(T*)res; 766 } 767 768 void xfree(T) (ref T* p) { 769 if (p !is null) { 770 import core.stdc.stdlib : free; 771 free(p); 772 p = null; 773 } 774 } 775 776 777 alias AttrList = const(const(char)[])[]; 778 779 public enum NSVG_PI = 3.14159265358979323846264338327f; /// 780 enum NSVG_KAPPA90 = 0.5522847493f; // Lenght proportional to radius of a cubic bezier handle for 90deg arcs. 781 782 enum NSVG_ALIGN_MIN = 0; 783 enum NSVG_ALIGN_MID = 1; 784 enum NSVG_ALIGN_MAX = 2; 785 enum NSVG_ALIGN_NONE = 0; 786 enum NSVG_ALIGN_MEET = 1; 787 enum NSVG_ALIGN_SLICE = 2; 788 789 790 int nsvg__isspace() (char c) { pragma(inline, true); return (c && c <= ' '); } // because 791 int nsvg__isdigit() (char c) { pragma(inline, true); return (c >= '0' && c <= '9'); } 792 int nsvg__isnum() (char c) { pragma(inline, true); return ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.' || c == 'e' || c == 'E'); } 793 794 int nsvg__hexdigit() (char c) { 795 pragma(inline, true); 796 return 797 (c >= '0' && c <= '9' ? c-'0' : 798 c >= 'A' && c <= 'F' ? c-'A'+10 : 799 c >= 'a' && c <= 'f' ? c-'a'+10 : 800 -1); 801 } 802 803 float nsvg__minf() (float a, float b) { pragma(inline, true); return (a < b ? a : b); } 804 float nsvg__maxf() (float a, float b) { pragma(inline, true); return (a > b ? a : b); } 805 806 807 // Simple XML parser 808 enum NSVG_XML_TAG = 1; 809 enum NSVG_XML_CONTENT = 2; 810 enum NSVG_XML_MAX_ATTRIBS = 256; 811 812 void nsvg__parseContent (const(char)[] s, scope void function (void* ud, const(char)[] s) nothrow @nogc contentCb, void* ud) { 813 // Trim start white spaces 814 while (s.length && nsvg__isspace(s[0])) s = s[1..$]; 815 if (s.length == 0) return; 816 //{ import std.stdio; writeln("s=", s.quote); } 817 if (contentCb !is null) contentCb(ud, s); 818 } 819 820 static void nsvg__parseElement (const(char)[] s, 821 scope void function (void* ud, const(char)[] el, AttrList attr) nothrow @nogc startelCb, 822 scope void function (void* ud, const(char)[] el) nothrow @nogc endelCb, 823 void* ud) 824 { 825 const(char)[][NSVG_XML_MAX_ATTRIBS] attr; 826 int nattr = 0; 827 const(char)[] name; 828 int start = 0; 829 int end = 0; 830 char quote; 831 832 // Skip white space after the '<' 833 while (s.length && nsvg__isspace(s[0])) s = s[1..$]; 834 835 // Check if the tag is end tag 836 if (s.length && s[0] == '/') { 837 s = s[1..$]; 838 end = 1; 839 } else { 840 start = 1; 841 } 842 843 // Skip comments, data and preprocessor stuff. 844 if (s.length == 0 || s[0] == '?' || s[0] == '!') return; 845 846 // Get tag name 847 //{ import std.stdio; writeln("bs=", s.quote); } 848 { 849 usize pos = 0; 850 while (pos < s.length && !nsvg__isspace(s[pos])) ++pos; 851 name = s[0..pos]; 852 s = s[pos..$]; 853 } 854 //{ import std.stdio; writeln("name=", name.quote); } 855 //{ import std.stdio; writeln("as=", s.quote); } 856 857 // Get attribs 858 while (!end && s.length && attr.length-nattr >= 2) { 859 // skip white space before the attrib name 860 while (s.length && nsvg__isspace(s[0])) s = s[1..$]; 861 if (s.length == 0) break; 862 if (s[0] == '/') { end = 1; break; } 863 // find end of the attrib name 864 { 865 usize pos = 0; 866 while (pos < s.length && !nsvg__isspace(s[pos]) && s[pos] != '=') ++pos; 867 attr[nattr++] = s[0..pos]; 868 s = s[pos..$]; 869 } 870 // skip until the beginning of the value 871 while (s.length && s[0] != '\"' && s[0] != '\'') s = s[1..$]; 872 if (s.length == 0) break; 873 // store value and find the end of it 874 quote = s[0]; 875 s = s[1..$]; 876 { 877 usize pos = 0; 878 while (pos < s.length && s[pos] != quote) ++pos; 879 attr[nattr++] = s[0..pos]; 880 s = s[pos+(pos < s.length ? 1 : 0)..$]; 881 } 882 //{ import std.stdio; writeln("n=", attr[nattr-2].quote, "\nv=", attr[nattr-1].quote, "\n"); } 883 } 884 885 debug(nanosvg) { 886 import std.stdio; 887 writeln("==========================="); 888 foreach (immutable idx, const(char)[] v; attr[0..nattr]) writeln(" #", idx, ": ", v.quote); 889 } 890 891 // Call callbacks. 892 if (start && startelCb !is null) startelCb(ud, name, attr[0..nattr]); 893 if (end && endelCb !is null) endelCb(ud, name); 894 } 895 896 void nsvg__parseXML (const(char)[] input, 897 scope void function (void* ud, const(char)[] el, AttrList attr) nothrow @nogc startelCb, 898 scope void function (void* ud, const(char)[] el) nothrow @nogc endelCb, 899 scope void function (void* ud, const(char)[] s) nothrow @nogc contentCb, 900 void* ud) 901 { 902 usize cpos = 0; 903 int state = NSVG_XML_CONTENT; 904 while (cpos < input.length) { 905 if (state == NSVG_XML_CONTENT && input[cpos] == '<') { 906 if (input.length-cpos >= 9 && input[cpos..cpos+9] == "<![CDATA[") { 907 cpos += 9; 908 while (cpos < input.length) { 909 if (input.length-cpos > 1 && input.ptr[cpos] == ']' && input.ptr[cpos+1] == ']') { 910 cpos += 2; 911 while (cpos < input.length && input.ptr[cpos] <= ' ') ++cpos; 912 if (cpos < input.length && input.ptr[cpos] == '>') { ++cpos; break; } 913 } else { 914 ++cpos; 915 } 916 } 917 continue; 918 } 919 // start of a tag 920 //{ import std.stdio; writeln("ctx: ", input[0..cpos].quote); } 921 ////version(nanosvg_debug_styles) { import std.stdio; writeln("ctx: ", input[0..cpos].quote); } 922 nsvg__parseContent(input[0..cpos], contentCb, ud); 923 input = input[cpos+1..$]; 924 if (input.length > 2 && input.ptr[0] == '!' && input.ptr[1] == '-' && input.ptr[2] == '-') { 925 //{ import std.stdio; writeln("ctx0: ", input.quote); } 926 // skip comments 927 cpos = 3; 928 while (cpos < input.length) { 929 if (input.length-cpos > 2 && input.ptr[cpos] == '-' && input.ptr[cpos+1] == '-' && input.ptr[cpos+2] == '>') { 930 cpos += 3; 931 break; 932 } 933 ++cpos; 934 } 935 input = input[cpos..$]; 936 //{ import std.stdio; writeln("ctx1: ", input.quote); } 937 } else { 938 state = NSVG_XML_TAG; 939 } 940 cpos = 0; 941 } else if (state == NSVG_XML_TAG && input[cpos] == '>') { 942 // start of a content or new tag 943 //{ import std.stdio; writeln("tag: ", input[0..cpos].quote); } 944 nsvg__parseElement(input[0..cpos], startelCb, endelCb, ud); 945 input = input[cpos+1..$]; 946 cpos = 0; 947 state = NSVG_XML_CONTENT; 948 } else { 949 ++cpos; 950 } 951 } 952 } 953 954 955 /* Simple SVG parser. */ 956 957 enum NSVG_MAX_ATTR = 128; 958 959 enum GradientUnits : ubyte { 960 User, 961 Object, 962 } 963 964 enum NSVG_MAX_DASHES = 8; 965 966 enum Units : ubyte { 967 user, 968 px, 969 pt, 970 pc, 971 mm, 972 cm, 973 in_, 974 percent, 975 em, 976 ex, 977 } 978 979 struct Coordinate { 980 float value; 981 Units units; 982 } 983 984 struct LinearData { 985 Coordinate x1, y1, x2, y2; 986 } 987 988 struct RadialData { 989 Coordinate cx, cy, r, fx, fy; 990 } 991 992 struct GradientData { 993 char[64] id = 0; 994 char[64] ref_ = 0; 995 NSVG.PaintType type; 996 union { 997 LinearData linear; 998 RadialData radial; 999 } 1000 NSVG.SpreadType spread; 1001 GradientUnits units; 1002 float[6] xform; 1003 int nstops; 1004 NSVG.GradientStop* stops; 1005 GradientData* next; 1006 } 1007 1008 struct Attrib { 1009 char[64] id = 0; 1010 float[6] xform; 1011 uint fillColor; 1012 uint strokeColor; 1013 float opacity; 1014 float fillOpacity; 1015 float strokeOpacity; 1016 char[64] fillGradient = 0; 1017 char[64] strokeGradient = 0; 1018 float strokeWidth; 1019 float strokeDashOffset; 1020 float[NSVG_MAX_DASHES] strokeDashArray; 1021 int strokeDashCount; 1022 NSVG.LineJoin strokeLineJoin; 1023 NSVG.LineCap strokeLineCap; 1024 float miterLimit; 1025 NSVG.FillRule fillRule; 1026 float fontSize; 1027 uint stopColor; 1028 float stopOpacity; 1029 float stopOffset; 1030 ubyte hasFill; 1031 ubyte hasStroke; 1032 ubyte visible; 1033 } 1034 1035 version(nanosvg_crappy_stylesheet_parser) { 1036 struct Style { 1037 const(char)[] name; 1038 const(char)[] value; 1039 } 1040 } 1041 1042 struct Parser { 1043 Attrib[NSVG_MAX_ATTR] attr; 1044 int attrHead; 1045 float* stream; 1046 int nsflts; 1047 int csflts; 1048 NSVG.Path* plist; 1049 NSVG* image; 1050 GradientData* gradients; 1051 NSVG.Shape* shapesTail; 1052 float viewMinx, viewMiny, viewWidth, viewHeight; 1053 int alignX, alignY, alignType; 1054 float dpi; 1055 bool pathFlag; 1056 bool defsFlag; 1057 int canvaswdt = -1; 1058 int canvashgt = -1; 1059 version(nanosvg_crappy_stylesheet_parser) { 1060 Style* styles; 1061 uint styleCount; 1062 bool inStyle; 1063 } 1064 } 1065 1066 const(char)[] fromAsciiz (const(char)[] s) { 1067 //foreach (immutable idx, char ch; s) if (!ch) return s[0..idx]; 1068 //return s; 1069 if (s.length) { 1070 import core.stdc.string : memchr; 1071 if (auto zp = cast(const(char)*)memchr(s.ptr, 0, s.length)) return s[0..cast(usize)(zp-s.ptr)]; 1072 } 1073 return s; 1074 } 1075 1076 // ////////////////////////////////////////////////////////////////////////// // 1077 // matrix operations made public for the sake of... something. 1078 1079 /// 1080 public void nsvg__xformIdentity (float* t) { 1081 t[0] = 1.0f; t[1] = 0.0f; 1082 t[2] = 0.0f; t[3] = 1.0f; 1083 t[4] = 0.0f; t[5] = 0.0f; 1084 } 1085 1086 /// 1087 public void nsvg__xformSetTranslation (float* t, in float tx, in float ty) { 1088 t[0] = 1.0f; t[1] = 0.0f; 1089 t[2] = 0.0f; t[3] = 1.0f; 1090 t[4] = tx; t[5] = ty; 1091 } 1092 1093 /// 1094 public void nsvg__xformSetScale (float* t, in float sx, in float sy) { 1095 t[0] = sx; t[1] = 0.0f; 1096 t[2] = 0.0f; t[3] = sy; 1097 t[4] = 0.0f; t[5] = 0.0f; 1098 } 1099 1100 /// 1101 public void nsvg__xformSetSkewX (float* t, in float a) { 1102 t[0] = 1.0f; t[1] = 0.0f; 1103 t[2] = tanf(a); t[3] = 1.0f; 1104 t[4] = 0.0f; t[5] = 0.0f; 1105 } 1106 1107 /// 1108 public void nsvg__xformSetSkewY (float* t, in float a) { 1109 t[0] = 1.0f; t[1] = tanf(a); 1110 t[2] = 0.0f; t[3] = 1.0f; 1111 t[4] = 0.0f; t[5] = 0.0f; 1112 } 1113 1114 /// 1115 public void nsvg__xformSetRotation (float* t, in float a) { 1116 immutable cs = cosf(a), sn = sinf(a); 1117 t[0] = cs; t[1] = sn; 1118 t[2] = -sn; t[3] = cs; 1119 t[4] = 0.0f; t[5] = 0.0f; 1120 } 1121 1122 /// 1123 public void nsvg__xformMultiply (float* t, const(float)* s) { 1124 immutable t0 = t[0]*s[0]+t[1]*s[2]; 1125 immutable t2 = t[2]*s[0]+t[3]*s[2]; 1126 immutable t4 = t[4]*s[0]+t[5]*s[2]+s[4]; 1127 t[1] = t[0]*s[1]+t[1]*s[3]; 1128 t[3] = t[2]*s[1]+t[3]*s[3]; 1129 t[5] = t[4]*s[1]+t[5]*s[3]+s[5]; 1130 t[0] = t0; 1131 t[2] = t2; 1132 t[4] = t4; 1133 } 1134 1135 /// 1136 public void nsvg__xformInverse (float* inv, const(float)* t) { 1137 immutable double det = cast(double)t[0]*t[3]-cast(double)t[2]*t[1]; 1138 if (det > -1e-6 && det < 1e-6) { 1139 nsvg__xformIdentity(inv); 1140 return; 1141 } 1142 immutable double invdet = 1.0/det; 1143 inv[0] = cast(float)(t[3]*invdet); 1144 inv[2] = cast(float)(-t[2]*invdet); 1145 inv[4] = cast(float)((cast(double)t[2]*t[5]-cast(double)t[3]*t[4])*invdet); 1146 inv[1] = cast(float)(-t[1]*invdet); 1147 inv[3] = cast(float)(t[0]*invdet); 1148 inv[5] = cast(float)((cast(double)t[1]*t[4]-cast(double)t[0]*t[5])*invdet); 1149 } 1150 1151 /// 1152 public void nsvg__xformPremultiply (float* t, const(float)* s) { 1153 float[6] s2 = s[0..6]; 1154 //memcpy(s2.ptr, s, float.sizeof*6); 1155 nsvg__xformMultiply(s2.ptr, t); 1156 //memcpy(t, s2.ptr, float.sizeof*6); 1157 t[0..6] = s2[]; 1158 } 1159 1160 /// 1161 public void nsvg__xformPoint (float* dx, float* dy, in float x, in float y, const(float)* t) { 1162 if (dx !is null) *dx = x*t[0]+y*t[2]+t[4]; 1163 if (dy !is null) *dy = x*t[1]+y*t[3]+t[5]; 1164 } 1165 1166 /// 1167 public void nsvg__xformVec (float* dx, float* dy, in float x, in float y, const(float)* t) { 1168 if (dx !is null) *dx = x*t[0]+y*t[2]; 1169 if (dy !is null) *dy = x*t[1]+y*t[3]; 1170 } 1171 1172 /// 1173 public enum NSVG_EPSILON = (1e-12); 1174 1175 /// 1176 public int nsvg__ptInBounds (const(float)* pt, const(float)* bounds) { 1177 pragma(inline, true); 1178 return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; 1179 } 1180 1181 /// 1182 public double nsvg__evalBezier (double t, double p0, double p1, double p2, double p3) { 1183 pragma(inline, true); 1184 double it = 1.0-t; 1185 return it*it*it*p0+3.0*it*it*t*p1+3.0*it*t*t*p2+t*t*t*p3; 1186 } 1187 1188 /// 1189 public void nsvg__curveBounds (float* bounds, const(float)* curve) { 1190 const float* v0 = &curve[0]; 1191 const float* v1 = &curve[2]; 1192 const float* v2 = &curve[4]; 1193 const float* v3 = &curve[6]; 1194 1195 // Start the bounding box by end points 1196 bounds[0] = nsvg__minf(v0[0], v3[0]); 1197 bounds[1] = nsvg__minf(v0[1], v3[1]); 1198 bounds[2] = nsvg__maxf(v0[0], v3[0]); 1199 bounds[3] = nsvg__maxf(v0[1], v3[1]); 1200 1201 // Bezier curve fits inside the convex hull of it's control points. 1202 // If control points are inside the bounds, we're done. 1203 if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) return; 1204 1205 // Add bezier curve inflection points in X and Y. 1206 double[2] roots = void; 1207 foreach (int i; 0..2) { 1208 immutable double a = -3.0*v0[i]+9.0*v1[i]-9.0*v2[i]+3.0*v3[i]; 1209 immutable double b = 6.0*v0[i]-12.0*v1[i]+6.0*v2[i]; 1210 immutable double c = 3.0*v1[i]-3.0*v0[i]; 1211 int count = 0; 1212 if (fabs(a) < NSVG_EPSILON) { 1213 if (fabs(b) > NSVG_EPSILON) { 1214 immutable double t = -c/b; 1215 if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots.ptr[count++] = t; 1216 } 1217 } else { 1218 immutable double b2ac = b*b-4.0*c*a; 1219 if (b2ac > NSVG_EPSILON) { 1220 double t = (-b+sqrt(b2ac))/(2.0*a); 1221 if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots.ptr[count++] = t; 1222 t = (-b-sqrt(b2ac))/(2.0*a); 1223 if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots.ptr[count++] = t; 1224 } 1225 } 1226 foreach (int j; 0..count) { 1227 immutable double v = nsvg__evalBezier(roots.ptr[j], v0[i], v1[i], v2[i], v3[i]); 1228 bounds[0+i] = nsvg__minf(bounds[0+i], cast(float)v); 1229 bounds[2+i] = nsvg__maxf(bounds[2+i], cast(float)v); 1230 } 1231 } 1232 } 1233 1234 1235 // ////////////////////////////////////////////////////////////////////////// // 1236 Parser* nsvg__createParser () { 1237 Parser* p = xalloc!Parser; 1238 if (p is null) goto error; 1239 1240 p.image = xalloc!NSVG; 1241 if (p.image is null) goto error; 1242 1243 // Init style 1244 nsvg__xformIdentity(p.attr[0].xform.ptr); 1245 p.attr[0].id[] = 0; 1246 p.attr[0].fillColor = NSVG.Paint.rgb(0, 0, 0); 1247 p.attr[0].strokeColor = NSVG.Paint.rgb(0, 0, 0); 1248 p.attr[0].opacity = 1; 1249 p.attr[0].fillOpacity = 1; 1250 p.attr[0].strokeOpacity = 1; 1251 p.attr[0].stopOpacity = 1; 1252 p.attr[0].strokeWidth = 1; 1253 p.attr[0].strokeLineJoin = NSVG.LineJoin.Miter; 1254 p.attr[0].strokeLineCap = NSVG.LineCap.Butt; 1255 p.attr[0].miterLimit = 4; 1256 p.attr[0].fillRule = NSVG.FillRule.EvenOdd; 1257 p.attr[0].hasFill = 1; 1258 p.attr[0].visible = 1; 1259 1260 return p; 1261 1262 error: 1263 if (p !is null) { 1264 xfree(p.image); 1265 xfree(p); 1266 } 1267 return null; 1268 } 1269 1270 void nsvg__deletePaths (NSVG.Path* path) { 1271 while (path !is null) { 1272 NSVG.Path* next = path.next; 1273 xfree(path.stream); 1274 xfree(path); 1275 path = next; 1276 } 1277 } 1278 1279 void nsvg__deletePaint (NSVG.Paint* paint) { 1280 if (paint.type == NSVG.PaintType.LinearGradient || paint.type == NSVG.PaintType.RadialGradient) xfree(paint.gradient); 1281 } 1282 1283 void nsvg__deleteGradientData (GradientData* grad) { 1284 GradientData* next; 1285 while (grad !is null) { 1286 next = grad.next; 1287 xfree(grad.stops); 1288 xfree(grad); 1289 grad = next; 1290 } 1291 } 1292 1293 void nsvg__deleteParser (Parser* p) { 1294 if (p !is null) { 1295 nsvg__deletePaths(p.plist); 1296 nsvg__deleteGradientData(p.gradients); 1297 kill(p.image); 1298 xfree(p.stream); 1299 version(nanosvg_crappy_stylesheet_parser) xfree(p.styles); 1300 xfree(p); 1301 } 1302 } 1303 1304 void nsvg__resetPath (Parser* p) { 1305 p.nsflts = 0; 1306 } 1307 1308 void nsvg__addToStream (Parser* p, in float v) { 1309 if (p.nsflts+1 > p.csflts) { 1310 import core.stdc.stdlib : realloc; 1311 p.csflts = (p.csflts == 0 ? 32 : p.csflts < 16384 ? p.csflts*2 : p.csflts+4096); //k8: arbitrary 1312 p.stream = cast(float*)realloc(p.stream, p.csflts*float.sizeof); 1313 if (p.stream is null) assert(0, "nanosvg: out of memory"); 1314 } 1315 p.stream[p.nsflts++] = v; 1316 } 1317 1318 void nsvg__addCommand (Parser* p, NSVG.Command c) { 1319 nsvg__addToStream(p, cast(float)c); 1320 } 1321 1322 void nsvg__addPoint (Parser* p, in float x, in float y) { 1323 nsvg__addToStream(p, x); 1324 nsvg__addToStream(p, y); 1325 } 1326 1327 void nsvg__moveTo (Parser* p, in float x, in float y) { 1328 // this is always called right after `nsvg__resetPath()` 1329 if (p.nsflts != 0) assert(0, "internal error in NanoVega.SVG"); 1330 nsvg__addCommand(p, NSVG.Command.MoveTo); 1331 nsvg__addPoint(p, x, y); 1332 /* 1333 if (p.npts > 0) { 1334 p.pts[(p.npts-1)*2+0] = x; 1335 p.pts[(p.npts-1)*2+1] = y; 1336 } else { 1337 nsvg__addPoint(p, x, y); 1338 } 1339 */ 1340 } 1341 1342 void nsvg__lineTo (Parser* p, in float x, in float y) { 1343 if (p.nsflts > 0) { 1344 version(nanosvg_use_beziers) { 1345 immutable float px = p.pts[(p.npts-1)*2+0]; 1346 immutable float py = p.pts[(p.npts-1)*2+1]; 1347 immutable float dx = x-px; 1348 immutable float dy = y-py; 1349 nsvg__addCommand(NSVG.Command.BezierTo); 1350 nsvg__addPoint(p, px+dx/3.0f, py+dy/3.0f); 1351 nsvg__addPoint(p, x-dx/3.0f, y-dy/3.0f); 1352 nsvg__addPoint(p, x, y); 1353 } else { 1354 nsvg__addCommand(p, NSVG.Command.LineTo); 1355 nsvg__addPoint(p, x, y); 1356 } 1357 } 1358 } 1359 1360 void nsvg__cubicBezTo (Parser* p, in float cpx1, in float cpy1, in float cpx2, in float cpy2, in float x, in float y) { 1361 nsvg__addCommand(p, NSVG.Command.BezierTo); 1362 nsvg__addPoint(p, cpx1, cpy1); 1363 nsvg__addPoint(p, cpx2, cpy2); 1364 nsvg__addPoint(p, x, y); 1365 } 1366 1367 void nsvg__quadBezTo (Parser* p, in float cpx1, in float cpy1, in float x, in float y) { 1368 nsvg__addCommand(p, NSVG.Command.QuadTo); 1369 nsvg__addPoint(p, cpx1, cpy1); 1370 nsvg__addPoint(p, x, y); 1371 } 1372 1373 Attrib* nsvg__getAttr (Parser* p) { 1374 return p.attr.ptr+p.attrHead; 1375 } 1376 1377 void nsvg__pushAttr (Parser* p) { 1378 if (p.attrHead < NSVG_MAX_ATTR-1) { 1379 import core.stdc.string : memmove; 1380 ++p.attrHead; 1381 memmove(p.attr.ptr+p.attrHead, p.attr.ptr+(p.attrHead-1), Attrib.sizeof); 1382 } 1383 } 1384 1385 void nsvg__popAttr (Parser* p) { 1386 if (p.attrHead > 0) --p.attrHead; 1387 } 1388 1389 float nsvg__actualOrigX (Parser* p) { pragma(inline, true); return p.viewMinx; } 1390 float nsvg__actualOrigY (Parser* p) { pragma(inline, true); return p.viewMiny; } 1391 float nsvg__actualWidth (Parser* p) { pragma(inline, true); return p.viewWidth; } 1392 float nsvg__actualHeight (Parser* p) { pragma(inline, true); return p.viewHeight; } 1393 1394 float nsvg__actualLength (Parser* p) { 1395 immutable float w = nsvg__actualWidth(p); 1396 immutable float h = nsvg__actualHeight(p); 1397 return sqrtf(w*w+h*h)/sqrtf(2.0f); 1398 } 1399 1400 float nsvg__convertToPixels (Parser* p, Coordinate c, float orig, float length) { 1401 Attrib* attr = nsvg__getAttr(p); 1402 switch (c.units) { 1403 case Units.user: return c.value; 1404 case Units.px: return c.value; 1405 case Units.pt: return c.value/72.0f*p.dpi; 1406 case Units.pc: return c.value/6.0f*p.dpi; 1407 case Units.mm: return c.value/25.4f*p.dpi; 1408 case Units.cm: return c.value/2.54f*p.dpi; 1409 case Units.in_: return c.value*p.dpi; 1410 case Units.em: return c.value*attr.fontSize; 1411 case Units.ex: return c.value*attr.fontSize*0.52f; // x-height of Helvetica. 1412 case Units.percent: return orig+c.value/100.0f*length; 1413 default: return c.value; 1414 } 1415 assert(0); 1416 //return c.value; 1417 } 1418 1419 GradientData* nsvg__findGradientData (Parser* p, const(char)[] id) { 1420 GradientData* grad = p.gradients; 1421 id = id.fromAsciiz; 1422 while (grad !is null) { 1423 if (grad.id.fromAsciiz == id) return grad; 1424 grad = grad.next; 1425 } 1426 return null; 1427 } 1428 1429 NSVG.Gradient* nsvg__createGradient (Parser* p, const(char)[] id, const(float)* localBounds, NSVG.PaintType* paintType) { 1430 Attrib* attr = nsvg__getAttr(p); 1431 GradientData* data = null; 1432 GradientData* ref_ = null; 1433 NSVG.GradientStop* stops = null; 1434 NSVG.Gradient* grad; 1435 float ox = void, oy = void, sw = void, sh = void; 1436 int nstops = 0; 1437 1438 id = id.fromAsciiz; 1439 data = nsvg__findGradientData(p, id); 1440 if (data is null) return null; 1441 1442 // TODO: use ref_ to fill in all unset values too. 1443 ref_ = data; 1444 while (ref_ !is null) { 1445 if (stops is null && ref_.stops !is null) { 1446 stops = ref_.stops; 1447 nstops = ref_.nstops; 1448 break; 1449 } 1450 ref_ = nsvg__findGradientData(p, ref_.ref_[]); 1451 } 1452 if (stops is null) return null; 1453 1454 grad = xalloc!(NSVG.Gradient)(NSVG.GradientStop.sizeof*nstops); 1455 if (grad is null) return null; 1456 1457 // The shape width and height. 1458 if (data.units == GradientUnits.Object) { 1459 ox = localBounds[0]; 1460 oy = localBounds[1]; 1461 sw = localBounds[2]-localBounds[0]; 1462 sh = localBounds[3]-localBounds[1]; 1463 } else { 1464 ox = nsvg__actualOrigX(p); 1465 oy = nsvg__actualOrigY(p); 1466 sw = nsvg__actualWidth(p); 1467 sh = nsvg__actualHeight(p); 1468 } 1469 immutable float sl = sqrtf(sw*sw+sh*sh)/sqrtf(2.0f); 1470 1471 if (data.type == NSVG.PaintType.LinearGradient) { 1472 immutable float x1 = nsvg__convertToPixels(p, data.linear.x1, ox, sw); 1473 immutable float y1 = nsvg__convertToPixels(p, data.linear.y1, oy, sh); 1474 immutable float x2 = nsvg__convertToPixels(p, data.linear.x2, ox, sw); 1475 immutable float y2 = nsvg__convertToPixels(p, data.linear.y2, oy, sh); 1476 // Calculate transform aligned to the line 1477 immutable float dx = x2-x1; 1478 immutable float dy = y2-y1; 1479 grad.xform[0] = dy; grad.xform[1] = -dx; 1480 grad.xform[2] = dx; grad.xform[3] = dy; 1481 grad.xform[4] = x1; grad.xform[5] = y1; 1482 } else { 1483 immutable float cx = nsvg__convertToPixels(p, data.radial.cx, ox, sw); 1484 immutable float cy = nsvg__convertToPixels(p, data.radial.cy, oy, sh); 1485 immutable float fx = nsvg__convertToPixels(p, data.radial.fx, ox, sw); 1486 immutable float fy = nsvg__convertToPixels(p, data.radial.fy, oy, sh); 1487 immutable float r = nsvg__convertToPixels(p, data.radial.r, 0, sl); 1488 // Calculate transform aligned to the circle 1489 grad.xform[0] = r; grad.xform[1] = 0; 1490 grad.xform[2] = 0; grad.xform[3] = r; 1491 grad.xform[4] = cx; grad.xform[5] = cy; 1492 // fix from https://github.com/memononen/nanosvg/issues/26#issuecomment-278713651 1493 grad.fx = (fx-cx)/r; // was fx/r; 1494 grad.fy = (fy-cy)/r; // was fy/r; 1495 } 1496 1497 nsvg__xformMultiply(grad.xform.ptr, data.xform.ptr); 1498 nsvg__xformMultiply(grad.xform.ptr, attr.xform.ptr); 1499 1500 grad.spread = data.spread; 1501 //memcpy(grad.stops.ptr, stops, nstops*NSVG.GradientStop.sizeof); 1502 grad.stops.ptr[0..nstops] = stops[0..nstops]; 1503 grad.nstops = nstops; 1504 1505 *paintType = data.type; 1506 1507 return grad; 1508 } 1509 1510 float nsvg__getAverageScale (float* t) { 1511 float sx = sqrtf(t[0]*t[0]+t[2]*t[2]); 1512 float sy = sqrtf(t[1]*t[1]+t[3]*t[3]); 1513 return (sx+sy)*0.5f; 1514 } 1515 1516 void nsvg__quadBounds (float* bounds, const(float)* curve) nothrow @trusted @nogc { 1517 // cheat: convert quadratic bezier to cubic bezier 1518 immutable float cx = curve[0]; 1519 immutable float cy = curve[1]; 1520 immutable float x1 = curve[2]; 1521 immutable float y1 = curve[3]; 1522 immutable float x2 = curve[4]; 1523 immutable float y2 = curve[5]; 1524 immutable float cx1 = x1+2.0f/3.0f*(cx-x1); 1525 immutable float cy1 = y1+2.0f/3.0f*(cy-y1); 1526 immutable float cx2 = x2+2.0f/3.0f*(cx-x2); 1527 immutable float cy2 = y2+2.0f/3.0f*(cy-y2); 1528 float[8] cubic = void; 1529 cubic.ptr[0] = cx; 1530 cubic.ptr[1] = cy; 1531 cubic.ptr[2] = cx1; 1532 cubic.ptr[3] = cy1; 1533 cubic.ptr[4] = cx2; 1534 cubic.ptr[5] = cy2; 1535 cubic.ptr[6] = x2; 1536 cubic.ptr[7] = y2; 1537 nsvg__curveBounds(bounds, cubic.ptr); 1538 } 1539 1540 void nsvg__getLocalBounds (float* bounds, NSVG.Shape* shape, const(float)* xform) { 1541 bool first = true; 1542 1543 void addPoint (in float x, in float y) nothrow @trusted @nogc { 1544 if (!first) { 1545 bounds[0] = nsvg__minf(bounds[0], x); 1546 bounds[1] = nsvg__minf(bounds[1], y); 1547 bounds[2] = nsvg__maxf(bounds[2], x); 1548 bounds[3] = nsvg__maxf(bounds[3], y); 1549 } else { 1550 bounds[0] = bounds[2] = x; 1551 bounds[1] = bounds[3] = y; 1552 first = false; 1553 } 1554 } 1555 1556 void addRect (in float x0, in float y0, in float x1, in float y1) nothrow @trusted @nogc { 1557 addPoint(x0, y0); 1558 addPoint(x1, y0); 1559 addPoint(x1, y1); 1560 addPoint(x0, y1); 1561 } 1562 1563 float cx = 0, cy = 0; 1564 for (NSVG.Path* path = shape.paths; path !is null; path = path.next) { 1565 path.forEachCommand!false(delegate (NSVG.Command cmd, const(float)[] args) nothrow @trusted @nogc { 1566 import core.stdc.string : memmove; 1567 assert(args.length <= 6); 1568 float[8] xpt = void; 1569 // transform points 1570 foreach (immutable n; 0..args.length/2) { 1571 nsvg__xformPoint(&xpt.ptr[n*2+0], &xpt.ptr[n*2+1], args.ptr[n*2+0], args.ptr[n*2+1], xform); 1572 } 1573 // add to bounds 1574 final switch (cmd) { 1575 case NSVG.Command.MoveTo: 1576 cx = xpt.ptr[0]; 1577 cy = xpt.ptr[1]; 1578 break; 1579 case NSVG.Command.LineTo: 1580 addPoint(cx, cy); 1581 addPoint(xpt.ptr[0], xpt.ptr[1]); 1582 cx = xpt.ptr[0]; 1583 cy = xpt.ptr[1]; 1584 break; 1585 case NSVG.Command.QuadTo: 1586 memmove(xpt.ptr+2, xpt.ptr, 4); // make room for starting point 1587 xpt.ptr[0] = cx; 1588 xpt.ptr[1] = cy; 1589 float[4] curveBounds = void; 1590 nsvg__quadBounds(curveBounds.ptr, xpt.ptr); 1591 addRect(curveBounds.ptr[0], curveBounds.ptr[1], curveBounds.ptr[2], curveBounds.ptr[3]); 1592 cx = xpt.ptr[4]; 1593 cy = xpt.ptr[5]; 1594 break; 1595 case NSVG.Command.BezierTo: 1596 memmove(xpt.ptr+2, xpt.ptr, 6); // make room for starting point 1597 xpt.ptr[0] = cx; 1598 xpt.ptr[1] = cy; 1599 float[4] curveBounds = void; 1600 nsvg__curveBounds(curveBounds.ptr, xpt.ptr); 1601 addRect(curveBounds.ptr[0], curveBounds.ptr[1], curveBounds.ptr[2], curveBounds.ptr[3]); 1602 cx = xpt.ptr[6]; 1603 cy = xpt.ptr[7]; 1604 break; 1605 } 1606 }); 1607 /* 1608 nsvg__xformPoint(&curve.ptr[0], &curve.ptr[1], path.pts[0], path.pts[1], xform); 1609 for (int i = 0; i < path.npts-1; i += 3) { 1610 nsvg__xformPoint(&curve.ptr[2], &curve.ptr[3], path.pts[(i+1)*2], path.pts[(i+1)*2+1], xform); 1611 nsvg__xformPoint(&curve.ptr[4], &curve.ptr[5], path.pts[(i+2)*2], path.pts[(i+2)*2+1], xform); 1612 nsvg__xformPoint(&curve.ptr[6], &curve.ptr[7], path.pts[(i+3)*2], path.pts[(i+3)*2+1], xform); 1613 nsvg__curveBounds(curveBounds.ptr, curve.ptr); 1614 if (first) { 1615 bounds[0] = curveBounds.ptr[0]; 1616 bounds[1] = curveBounds.ptr[1]; 1617 bounds[2] = curveBounds.ptr[2]; 1618 bounds[3] = curveBounds.ptr[3]; 1619 first = false; 1620 } else { 1621 bounds[0] = nsvg__minf(bounds[0], curveBounds.ptr[0]); 1622 bounds[1] = nsvg__minf(bounds[1], curveBounds.ptr[1]); 1623 bounds[2] = nsvg__maxf(bounds[2], curveBounds.ptr[2]); 1624 bounds[3] = nsvg__maxf(bounds[3], curveBounds.ptr[3]); 1625 } 1626 curve.ptr[0] = curve.ptr[6]; 1627 curve.ptr[1] = curve.ptr[7]; 1628 } 1629 */ 1630 } 1631 } 1632 1633 void nsvg__addShape (Parser* p) { 1634 Attrib* attr = nsvg__getAttr(p); 1635 float scale = 1.0f; 1636 NSVG.Shape* shape; 1637 NSVG.Path* path; 1638 int i; 1639 1640 if (p.plist is null) return; 1641 1642 shape = xalloc!(NSVG.Shape); 1643 if (shape is null) goto error; 1644 //memset(shape, 0, NSVG.Shape.sizeof); 1645 1646 shape.id[] = attr.id[]; 1647 scale = nsvg__getAverageScale(attr.xform.ptr); 1648 shape.strokeWidth = attr.strokeWidth*scale; 1649 shape.strokeDashOffset = attr.strokeDashOffset*scale; 1650 shape.strokeDashCount = cast(char)attr.strokeDashCount; 1651 for (i = 0; i < attr.strokeDashCount; i++) shape.strokeDashArray[i] = attr.strokeDashArray[i]*scale; 1652 shape.strokeLineJoin = attr.strokeLineJoin; 1653 shape.strokeLineCap = attr.strokeLineCap; 1654 shape.miterLimit = attr.miterLimit; 1655 shape.fillRule = attr.fillRule; 1656 shape.opacity = attr.opacity; 1657 1658 shape.paths = p.plist; 1659 p.plist = null; 1660 1661 // Calculate shape bounds 1662 shape.bounds.ptr[0] = shape.paths.bounds.ptr[0]; 1663 shape.bounds.ptr[1] = shape.paths.bounds.ptr[1]; 1664 shape.bounds.ptr[2] = shape.paths.bounds.ptr[2]; 1665 shape.bounds.ptr[3] = shape.paths.bounds.ptr[3]; 1666 for (path = shape.paths.next; path !is null; path = path.next) { 1667 shape.bounds.ptr[0] = nsvg__minf(shape.bounds.ptr[0], path.bounds[0]); 1668 shape.bounds.ptr[1] = nsvg__minf(shape.bounds.ptr[1], path.bounds[1]); 1669 shape.bounds.ptr[2] = nsvg__maxf(shape.bounds.ptr[2], path.bounds[2]); 1670 shape.bounds.ptr[3] = nsvg__maxf(shape.bounds.ptr[3], path.bounds[3]); 1671 } 1672 1673 // Set fill 1674 if (attr.hasFill == 0) { 1675 shape.fill.type = NSVG.PaintType.None; 1676 } else if (attr.hasFill == 1) { 1677 shape.fill.type = NSVG.PaintType.Color; 1678 shape.fill.color = attr.fillColor; 1679 shape.fill.color |= cast(uint)(attr.fillOpacity*255)<<24; 1680 } else if (attr.hasFill == 2) { 1681 float[6] inv; 1682 float[4] localBounds; 1683 nsvg__xformInverse(inv.ptr, attr.xform.ptr); 1684 nsvg__getLocalBounds(localBounds.ptr, shape, inv.ptr); 1685 shape.fill.gradient = nsvg__createGradient(p, attr.fillGradient[], localBounds.ptr, &shape.fill.type); 1686 if (shape.fill.gradient is null) shape.fill.type = NSVG.PaintType.None; 1687 } 1688 1689 // Set stroke 1690 if (attr.hasStroke == 0) { 1691 shape.stroke.type = NSVG.PaintType.None; 1692 } else if (attr.hasStroke == 1) { 1693 shape.stroke.type = NSVG.PaintType.Color; 1694 shape.stroke.color = attr.strokeColor; 1695 shape.stroke.color |= cast(uint)(attr.strokeOpacity*255)<<24; 1696 } else if (attr.hasStroke == 2) { 1697 float[6] inv; 1698 float[4] localBounds; 1699 nsvg__xformInverse(inv.ptr, attr.xform.ptr); 1700 nsvg__getLocalBounds(localBounds.ptr, shape, inv.ptr); 1701 shape.stroke.gradient = nsvg__createGradient(p, attr.strokeGradient[], localBounds.ptr, &shape.stroke.type); 1702 if (shape.stroke.gradient is null) shape.stroke.type = NSVG.PaintType.None; 1703 } 1704 1705 // Set flags 1706 shape.flags = (attr.visible ? NSVG.Visible : 0x00); 1707 1708 // Add to tail 1709 if (p.image.shapes is null) 1710 p.image.shapes = shape; 1711 else 1712 p.shapesTail.next = shape; 1713 1714 p.shapesTail = shape; 1715 1716 return; 1717 1718 error: 1719 if (shape) xfree(shape); 1720 } 1721 1722 void nsvg__addPath (Parser* p, bool closed) { 1723 Attrib* attr = nsvg__getAttr(p); 1724 1725 if (p.nsflts < 4) return; 1726 1727 if (closed) { 1728 auto cmd = cast(NSVG.Command)p.stream[0]; 1729 if (cmd != NSVG.Command.MoveTo) assert(0, "NanoVega.SVG: invalid path"); 1730 nsvg__lineTo(p, p.stream[1], p.stream[2]); 1731 } 1732 1733 float cx = 0, cy = 0; 1734 float[4] bounds = void; 1735 bool first = true; 1736 1737 NSVG.Path* path = xalloc!(NSVG.Path); 1738 if (path is null) goto error; 1739 //memset(path, 0, NSVG.Path.sizeof); 1740 1741 path.stream = xcalloc!float(p.nsflts); 1742 if (path.stream is null) goto error; 1743 path.closed = closed; 1744 path.nsflts = p.nsflts; 1745 1746 // transform path and calculate bounds 1747 void addPoint (in float x, in float y) nothrow @trusted @nogc { 1748 if (!first) { 1749 bounds[0] = nsvg__minf(bounds[0], x); 1750 bounds[1] = nsvg__minf(bounds[1], y); 1751 bounds[2] = nsvg__maxf(bounds[2], x); 1752 bounds[3] = nsvg__maxf(bounds[3], y); 1753 } else { 1754 bounds[0] = bounds[2] = x; 1755 bounds[1] = bounds[3] = y; 1756 first = false; 1757 } 1758 } 1759 1760 void addRect (in float x0, in float y0, in float x1, in float y1) nothrow @trusted @nogc { 1761 addPoint(x0, y0); 1762 addPoint(x1, y0); 1763 addPoint(x1, y1); 1764 addPoint(x0, y1); 1765 } 1766 1767 version(none) { 1768 foreach (immutable idx, float f; p.stream[0..p.nsflts]) { 1769 import core.stdc.stdio; 1770 printf("idx=%u; f=%g\n", cast(uint)idx, cast(double)f); 1771 } 1772 } 1773 1774 for (int i = 0; i+3 <= p.nsflts; ) { 1775 int argc = 0; // pair of coords 1776 NSVG.Command cmd = cast(NSVG.Command)p.stream[i]; 1777 final switch (cmd) { 1778 case NSVG.Command.MoveTo: argc = 1; break; 1779 case NSVG.Command.LineTo: argc = 1; break; 1780 case NSVG.Command.QuadTo: argc = 2; break; 1781 case NSVG.Command.BezierTo: argc = 3; break; 1782 } 1783 // copy command 1784 path.stream[i] = p.stream[i]; 1785 ++i; 1786 auto starti = i; 1787 // transform points 1788 while (argc-- > 0) { 1789 nsvg__xformPoint(&path.stream[i+0], &path.stream[i+1], p.stream[i+0], p.stream[i+1], attr.xform.ptr); 1790 i += 2; 1791 } 1792 // do bounds 1793 final switch (cmd) { 1794 case NSVG.Command.MoveTo: 1795 cx = path.stream[starti+0]; 1796 cy = path.stream[starti+1]; 1797 break; 1798 case NSVG.Command.LineTo: 1799 addPoint(cx, cy); 1800 cx = path.stream[starti+0]; 1801 cy = path.stream[starti+1]; 1802 addPoint(cx, cy); 1803 break; 1804 case NSVG.Command.QuadTo: 1805 float[6] curve = void; 1806 curve.ptr[0] = cx; 1807 curve.ptr[1] = cy; 1808 curve.ptr[2..6] = path.stream[starti+0..starti+4]; 1809 cx = path.stream[starti+2]; 1810 cy = path.stream[starti+3]; 1811 float[4] curveBounds = void; 1812 nsvg__quadBounds(curveBounds.ptr, curve.ptr); 1813 addRect(curveBounds.ptr[0], curveBounds.ptr[1], curveBounds.ptr[2], curveBounds.ptr[3]); 1814 break; 1815 case NSVG.Command.BezierTo: 1816 float[8] curve = void; 1817 curve.ptr[0] = cx; 1818 curve.ptr[1] = cy; 1819 curve.ptr[2..8] = path.stream[starti+0..starti+6]; 1820 cx = path.stream[starti+4]; 1821 cy = path.stream[starti+5]; 1822 float[4] curveBounds = void; 1823 nsvg__curveBounds(curveBounds.ptr, curve.ptr); 1824 addRect(curveBounds.ptr[0], curveBounds.ptr[1], curveBounds.ptr[2], curveBounds.ptr[3]); 1825 break; 1826 } 1827 } 1828 path.bounds[0..4] = bounds[0..4]; 1829 1830 path.next = p.plist; 1831 p.plist = path; 1832 1833 return; 1834 1835 error: 1836 if (path !is null) { 1837 if (path.stream !is null) xfree(path.stream); 1838 xfree(path); 1839 } 1840 } 1841 1842 // We roll our own string to float because the std library one uses locale and messes things up. 1843 // special hack: stop at '\0' (actually, it stops on any non-digit, so no special code is required) 1844 float nsvg__atof (const(char)[] s) nothrow @trusted @nogc { 1845 if (s.length == 0) return 0; // oops 1846 1847 const(char)* cur = s.ptr; 1848 auto left = s.length; 1849 double res = 0.0, sign = 1.0; 1850 bool hasIntPart = false, hasFracPart = false; 1851 1852 char peekChar () nothrow @trusted @nogc { pragma(inline, true); return (left > 0 ? *cur : '\x00'); } 1853 char getChar () nothrow @trusted @nogc { if (left > 0) { --left; return *cur++; } else return '\x00'; } 1854 1855 // Parse optional sign 1856 switch (peekChar) { 1857 case '-': sign = -1; goto case; 1858 case '+': getChar(); break; 1859 default: break; 1860 } 1861 1862 // Parse integer part 1863 if (nsvg__isdigit(peekChar)) { 1864 // Parse digit sequence 1865 hasIntPart = true; 1866 while (nsvg__isdigit(peekChar)) res = res*10.0+(getChar()-'0'); 1867 } 1868 1869 // Parse fractional part. 1870 if (peekChar == '.') { 1871 getChar(); // Skip '.' 1872 if (nsvg__isdigit(peekChar)) { 1873 // Parse digit sequence 1874 hasFracPart = true; 1875 int divisor = 1; 1876 long num = 0; 1877 while (nsvg__isdigit(peekChar)) { 1878 divisor *= 10; 1879 num = num*10+(getChar()-'0'); 1880 } 1881 res += cast(double)num/divisor; 1882 } 1883 } 1884 1885 // A valid number should have integer or fractional part. 1886 if (!hasIntPart && !hasFracPart) return 0; 1887 1888 // Parse optional exponent 1889 if (peekChar == 'e' || peekChar == 'E') { 1890 getChar(); // skip 'E' 1891 // parse optional sign 1892 bool epositive = true; 1893 switch (peekChar) { 1894 case '-': epositive = false; goto case; 1895 case '+': getChar(); break; 1896 default: break; 1897 } 1898 int expPart = 0; 1899 while (nsvg__isdigit(peekChar)) expPart = expPart*10+(getChar()-'0'); 1900 if (epositive) { 1901 foreach (immutable _; 0..expPart) res *= 10.0; 1902 } else { 1903 foreach (immutable _; 0..expPart) res /= 10.0; 1904 } 1905 } 1906 1907 return cast(float)(res*sign); 1908 } 1909 1910 // `it` should be big enough 1911 // returns number of chars eaten 1912 int nsvg__parseNumber (const(char)[] s, char[] it) { 1913 int i = 0; 1914 it[] = 0; 1915 1916 const(char)[] os = s; 1917 1918 // sign 1919 if (s.length && (s[0] == '-' || s[0] == '+')) { 1920 if (it.length-i > 1) it[i++] = s[0]; 1921 s = s[1..$]; 1922 } 1923 // integer part 1924 while (s.length && nsvg__isdigit(s[0])) { 1925 if (it.length-i > 1) it[i++] = s[0]; 1926 s = s[1..$]; 1927 } 1928 if (s.length && s[0] == '.') { 1929 // decimal point 1930 if (it.length-i > 1) it[i++] = s[0]; 1931 s = s[1..$]; 1932 // fraction part 1933 while (s.length && nsvg__isdigit(s[0])) { 1934 if (it.length-i > 1) it[i++] = s[0]; 1935 s = s[1..$]; 1936 } 1937 } 1938 // exponent 1939 if (s.length && (s[0] == 'e' || s[0] == 'E')) { 1940 if (it.length-i > 1) it[i++] = s[0]; 1941 s = s[1..$]; 1942 if (s.length && (s[0] == '-' || s[0] == '+')) { 1943 if (it.length-i > 1) it[i++] = s[0]; 1944 s = s[1..$]; 1945 } 1946 while (s.length && nsvg__isdigit(s[0])) { 1947 if (it.length-i > 1) it[i++] = s[0]; 1948 s = s[1..$]; 1949 } 1950 } 1951 1952 return cast(int)(s.ptr-os.ptr); 1953 } 1954 1955 // `it` should be big enough 1956 int nsvg__getNextPathItem (const(char)[] s, char[] it) { 1957 int res = 0; 1958 it[] = '\0'; 1959 // skip white spaces and commas 1960 while (res < s.length && (nsvg__isspace(s[res]) || s[res] == ',')) ++res; 1961 if (res >= s.length) return cast(int)s.length; 1962 if (s[res] == '-' || s[res] == '+' || s[res] == '.' || nsvg__isdigit(s[res])) { 1963 res += nsvg__parseNumber(s[res..$], it); 1964 } else if (s.length) { 1965 // Parse command 1966 it[0] = s[res++]; 1967 } 1968 return res; 1969 } 1970 1971 uint nsvg__parseColorHex (const(char)[] str) { 1972 char[12] tmp = 0; 1973 uint c = 0; 1974 ubyte r = 0, g = 0, b = 0; 1975 int n = 0; 1976 if (str.length) str = str[1..$]; // skip # 1977 // calculate number of characters 1978 while (n < str.length && !nsvg__isspace(str[n])) ++n; 1979 if (n == 3 || n == 6) { 1980 foreach (char ch; str[0..n]) { 1981 auto d0 = nsvg__hexdigit(ch); 1982 if (d0 < 0) break; 1983 c = c*16+d0; 1984 } 1985 if (n == 3) { 1986 c = (c&0xf)|((c&0xf0)<<4)|((c&0xf00)<<8); 1987 c |= c<<4; 1988 } 1989 } 1990 r = (c>>16)&0xff; 1991 g = (c>>8)&0xff; 1992 b = c&0xff; 1993 return NSVG.Paint.rgb(r, g, b); 1994 } 1995 1996 uint nsvg__parseColorRGB (const(char)[] str) { 1997 int r = -1, g = -1, b = -1; 1998 const(char)[] s1, s2; 1999 assert(str.length > 4); 2000 xsscanf(str[4..$], "%d%[%%, \t]%d%[%%, \t]%d", r, s1, g, s2, b); 2001 if (s1[].xindexOf('%') >= 0) { 2002 return NSVG.Paint.rgb(cast(ubyte)((r*255)/100), cast(ubyte)((g*255)/100), cast(ubyte)((b*255)/100)); 2003 } else { 2004 return NSVG.Paint.rgb(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); 2005 } 2006 } 2007 2008 struct NSVGNamedColor { 2009 string name; 2010 uint color; 2011 } 2012 2013 static immutable NSVGNamedColor[147] nsvg__colors = [ 2014 NSVGNamedColor("aliceblue", NSVG.Paint.rgb(240, 248, 255)), 2015 NSVGNamedColor("antiquewhite", NSVG.Paint.rgb(250, 235, 215)), 2016 NSVGNamedColor("aqua", NSVG.Paint.rgb( 0, 255, 255)), 2017 NSVGNamedColor("aquamarine", NSVG.Paint.rgb(127, 255, 212)), 2018 NSVGNamedColor("azure", NSVG.Paint.rgb(240, 255, 255)), 2019 NSVGNamedColor("beige", NSVG.Paint.rgb(245, 245, 220)), 2020 NSVGNamedColor("bisque", NSVG.Paint.rgb(255, 228, 196)), 2021 NSVGNamedColor("black", NSVG.Paint.rgb( 0, 0, 0)), // basic color 2022 NSVGNamedColor("blanchedalmond", NSVG.Paint.rgb(255, 235, 205)), 2023 NSVGNamedColor("blue", NSVG.Paint.rgb( 0, 0, 255)), // basic color 2024 NSVGNamedColor("blueviolet", NSVG.Paint.rgb(138, 43, 226)), 2025 NSVGNamedColor("brown", NSVG.Paint.rgb(165, 42, 42)), 2026 NSVGNamedColor("burlywood", NSVG.Paint.rgb(222, 184, 135)), 2027 NSVGNamedColor("cadetblue", NSVG.Paint.rgb( 95, 158, 160)), 2028 NSVGNamedColor("chartreuse", NSVG.Paint.rgb(127, 255, 0)), 2029 NSVGNamedColor("chocolate", NSVG.Paint.rgb(210, 105, 30)), 2030 NSVGNamedColor("coral", NSVG.Paint.rgb(255, 127, 80)), 2031 NSVGNamedColor("cornflowerblue", NSVG.Paint.rgb(100, 149, 237)), 2032 NSVGNamedColor("cornsilk", NSVG.Paint.rgb(255, 248, 220)), 2033 NSVGNamedColor("crimson", NSVG.Paint.rgb(220, 20, 60)), 2034 NSVGNamedColor("cyan", NSVG.Paint.rgb( 0, 255, 255)), // basic color 2035 NSVGNamedColor("darkblue", NSVG.Paint.rgb( 0, 0, 139)), 2036 NSVGNamedColor("darkcyan", NSVG.Paint.rgb( 0, 139, 139)), 2037 NSVGNamedColor("darkgoldenrod", NSVG.Paint.rgb(184, 134, 11)), 2038 NSVGNamedColor("darkgray", NSVG.Paint.rgb(169, 169, 169)), 2039 NSVGNamedColor("darkgreen", NSVG.Paint.rgb( 0, 100, 0)), 2040 NSVGNamedColor("darkgrey", NSVG.Paint.rgb(169, 169, 169)), 2041 NSVGNamedColor("darkkhaki", NSVG.Paint.rgb(189, 183, 107)), 2042 NSVGNamedColor("darkmagenta", NSVG.Paint.rgb(139, 0, 139)), 2043 NSVGNamedColor("darkolivegreen", NSVG.Paint.rgb( 85, 107, 47)), 2044 NSVGNamedColor("darkorange", NSVG.Paint.rgb(255, 140, 0)), 2045 NSVGNamedColor("darkorchid", NSVG.Paint.rgb(153, 50, 204)), 2046 NSVGNamedColor("darkred", NSVG.Paint.rgb(139, 0, 0)), 2047 NSVGNamedColor("darksalmon", NSVG.Paint.rgb(233, 150, 122)), 2048 NSVGNamedColor("darkseagreen", NSVG.Paint.rgb(143, 188, 143)), 2049 NSVGNamedColor("darkslateblue", NSVG.Paint.rgb( 72, 61, 139)), 2050 NSVGNamedColor("darkslategray", NSVG.Paint.rgb( 47, 79, 79)), 2051 NSVGNamedColor("darkslategrey", NSVG.Paint.rgb( 47, 79, 79)), 2052 NSVGNamedColor("darkturquoise", NSVG.Paint.rgb( 0, 206, 209)), 2053 NSVGNamedColor("darkviolet", NSVG.Paint.rgb(148, 0, 211)), 2054 NSVGNamedColor("deeppink", NSVG.Paint.rgb(255, 20, 147)), 2055 NSVGNamedColor("deepskyblue", NSVG.Paint.rgb( 0, 191, 255)), 2056 NSVGNamedColor("dimgray", NSVG.Paint.rgb(105, 105, 105)), 2057 NSVGNamedColor("dimgrey", NSVG.Paint.rgb(105, 105, 105)), 2058 NSVGNamedColor("dodgerblue", NSVG.Paint.rgb( 30, 144, 255)), 2059 NSVGNamedColor("firebrick", NSVG.Paint.rgb(178, 34, 34)), 2060 NSVGNamedColor("floralwhite", NSVG.Paint.rgb(255, 250, 240)), 2061 NSVGNamedColor("forestgreen", NSVG.Paint.rgb( 34, 139, 34)), 2062 NSVGNamedColor("fuchsia", NSVG.Paint.rgb(255, 0, 255)), 2063 NSVGNamedColor("gainsboro", NSVG.Paint.rgb(220, 220, 220)), 2064 NSVGNamedColor("ghostwhite", NSVG.Paint.rgb(248, 248, 255)), 2065 NSVGNamedColor("gold", NSVG.Paint.rgb(255, 215, 0)), 2066 NSVGNamedColor("goldenrod", NSVG.Paint.rgb(218, 165, 32)), 2067 NSVGNamedColor("gray", NSVG.Paint.rgb(128, 128, 128)), // basic color 2068 NSVGNamedColor("green", NSVG.Paint.rgb( 0, 128, 0)), // basic color 2069 NSVGNamedColor("greenyellow", NSVG.Paint.rgb(173, 255, 47)), 2070 NSVGNamedColor("grey", NSVG.Paint.rgb(128, 128, 128)), // basic color 2071 NSVGNamedColor("honeydew", NSVG.Paint.rgb(240, 255, 240)), 2072 NSVGNamedColor("hotpink", NSVG.Paint.rgb(255, 105, 180)), 2073 NSVGNamedColor("indianred", NSVG.Paint.rgb(205, 92, 92)), 2074 NSVGNamedColor("indigo", NSVG.Paint.rgb( 75, 0, 130)), 2075 NSVGNamedColor("ivory", NSVG.Paint.rgb(255, 255, 240)), 2076 NSVGNamedColor("khaki", NSVG.Paint.rgb(240, 230, 140)), 2077 NSVGNamedColor("lavender", NSVG.Paint.rgb(230, 230, 250)), 2078 NSVGNamedColor("lavenderblush", NSVG.Paint.rgb(255, 240, 245)), 2079 NSVGNamedColor("lawngreen", NSVG.Paint.rgb(124, 252, 0)), 2080 NSVGNamedColor("lemonchiffon", NSVG.Paint.rgb(255, 250, 205)), 2081 NSVGNamedColor("lightblue", NSVG.Paint.rgb(173, 216, 230)), 2082 NSVGNamedColor("lightcoral", NSVG.Paint.rgb(240, 128, 128)), 2083 NSVGNamedColor("lightcyan", NSVG.Paint.rgb(224, 255, 255)), 2084 NSVGNamedColor("lightgoldenrodyellow", NSVG.Paint.rgb(250, 250, 210)), 2085 NSVGNamedColor("lightgray", NSVG.Paint.rgb(211, 211, 211)), 2086 NSVGNamedColor("lightgreen", NSVG.Paint.rgb(144, 238, 144)), 2087 NSVGNamedColor("lightgrey", NSVG.Paint.rgb(211, 211, 211)), 2088 NSVGNamedColor("lightpink", NSVG.Paint.rgb(255, 182, 193)), 2089 NSVGNamedColor("lightsalmon", NSVG.Paint.rgb(255, 160, 122)), 2090 NSVGNamedColor("lightseagreen", NSVG.Paint.rgb( 32, 178, 170)), 2091 NSVGNamedColor("lightskyblue", NSVG.Paint.rgb(135, 206, 250)), 2092 NSVGNamedColor("lightslategray", NSVG.Paint.rgb(119, 136, 153)), 2093 NSVGNamedColor("lightslategrey", NSVG.Paint.rgb(119, 136, 153)), 2094 NSVGNamedColor("lightsteelblue", NSVG.Paint.rgb(176, 196, 222)), 2095 NSVGNamedColor("lightyellow", NSVG.Paint.rgb(255, 255, 224)), 2096 NSVGNamedColor("lime", NSVG.Paint.rgb( 0, 255, 0)), 2097 NSVGNamedColor("limegreen", NSVG.Paint.rgb( 50, 205, 50)), 2098 NSVGNamedColor("linen", NSVG.Paint.rgb(250, 240, 230)), 2099 NSVGNamedColor("magenta", NSVG.Paint.rgb(255, 0, 255)), // basic color 2100 NSVGNamedColor("maroon", NSVG.Paint.rgb(128, 0, 0)), 2101 NSVGNamedColor("mediumaquamarine", NSVG.Paint.rgb(102, 205, 170)), 2102 NSVGNamedColor("mediumblue", NSVG.Paint.rgb( 0, 0, 205)), 2103 NSVGNamedColor("mediumorchid", NSVG.Paint.rgb(186, 85, 211)), 2104 NSVGNamedColor("mediumpurple", NSVG.Paint.rgb(147, 112, 219)), 2105 NSVGNamedColor("mediumseagreen", NSVG.Paint.rgb( 60, 179, 113)), 2106 NSVGNamedColor("mediumslateblue", NSVG.Paint.rgb(123, 104, 238)), 2107 NSVGNamedColor("mediumspringgreen", NSVG.Paint.rgb( 0, 250, 154)), 2108 NSVGNamedColor("mediumturquoise", NSVG.Paint.rgb( 72, 209, 204)), 2109 NSVGNamedColor("mediumvioletred", NSVG.Paint.rgb(199, 21, 133)), 2110 NSVGNamedColor("midnightblue", NSVG.Paint.rgb( 25, 25, 112)), 2111 NSVGNamedColor("mintcream", NSVG.Paint.rgb(245, 255, 250)), 2112 NSVGNamedColor("mistyrose", NSVG.Paint.rgb(255, 228, 225)), 2113 NSVGNamedColor("moccasin", NSVG.Paint.rgb(255, 228, 181)), 2114 NSVGNamedColor("navajowhite", NSVG.Paint.rgb(255, 222, 173)), 2115 NSVGNamedColor("navy", NSVG.Paint.rgb( 0, 0, 128)), 2116 NSVGNamedColor("oldlace", NSVG.Paint.rgb(253, 245, 230)), 2117 NSVGNamedColor("olive", NSVG.Paint.rgb(128, 128, 0)), 2118 NSVGNamedColor("olivedrab", NSVG.Paint.rgb(107, 142, 35)), 2119 NSVGNamedColor("orange", NSVG.Paint.rgb(255, 165, 0)), 2120 NSVGNamedColor("orangered", NSVG.Paint.rgb(255, 69, 0)), 2121 NSVGNamedColor("orchid", NSVG.Paint.rgb(218, 112, 214)), 2122 NSVGNamedColor("palegoldenrod", NSVG.Paint.rgb(238, 232, 170)), 2123 NSVGNamedColor("palegreen", NSVG.Paint.rgb(152, 251, 152)), 2124 NSVGNamedColor("paleturquoise", NSVG.Paint.rgb(175, 238, 238)), 2125 NSVGNamedColor("palevioletred", NSVG.Paint.rgb(219, 112, 147)), 2126 NSVGNamedColor("papayawhip", NSVG.Paint.rgb(255, 239, 213)), 2127 NSVGNamedColor("peachpuff", NSVG.Paint.rgb(255, 218, 185)), 2128 NSVGNamedColor("peru", NSVG.Paint.rgb(205, 133, 63)), 2129 NSVGNamedColor("pink", NSVG.Paint.rgb(255, 192, 203)), 2130 NSVGNamedColor("plum", NSVG.Paint.rgb(221, 160, 221)), 2131 NSVGNamedColor("powderblue", NSVG.Paint.rgb(176, 224, 230)), 2132 NSVGNamedColor("purple", NSVG.Paint.rgb(128, 0, 128)), 2133 NSVGNamedColor("red", NSVG.Paint.rgb(255, 0, 0)), // basic color 2134 NSVGNamedColor("rosybrown", NSVG.Paint.rgb(188, 143, 143)), 2135 NSVGNamedColor("royalblue", NSVG.Paint.rgb( 65, 105, 225)), 2136 NSVGNamedColor("saddlebrown", NSVG.Paint.rgb(139, 69, 19)), 2137 NSVGNamedColor("salmon", NSVG.Paint.rgb(250, 128, 114)), 2138 NSVGNamedColor("sandybrown", NSVG.Paint.rgb(244, 164, 96)), 2139 NSVGNamedColor("seagreen", NSVG.Paint.rgb( 46, 139, 87)), 2140 NSVGNamedColor("seashell", NSVG.Paint.rgb(255, 245, 238)), 2141 NSVGNamedColor("sienna", NSVG.Paint.rgb(160, 82, 45)), 2142 NSVGNamedColor("silver", NSVG.Paint.rgb(192, 192, 192)), 2143 NSVGNamedColor("skyblue", NSVG.Paint.rgb(135, 206, 235)), 2144 NSVGNamedColor("slateblue", NSVG.Paint.rgb(106, 90, 205)), 2145 NSVGNamedColor("slategray", NSVG.Paint.rgb(112, 128, 144)), 2146 NSVGNamedColor("slategrey", NSVG.Paint.rgb(112, 128, 144)), 2147 NSVGNamedColor("snow", NSVG.Paint.rgb(255, 250, 250)), 2148 NSVGNamedColor("springgreen", NSVG.Paint.rgb( 0, 255, 127)), 2149 NSVGNamedColor("steelblue", NSVG.Paint.rgb( 70, 130, 180)), 2150 NSVGNamedColor("tan", NSVG.Paint.rgb(210, 180, 140)), 2151 NSVGNamedColor("teal", NSVG.Paint.rgb( 0, 128, 128)), 2152 NSVGNamedColor("thistle", NSVG.Paint.rgb(216, 191, 216)), 2153 NSVGNamedColor("tomato", NSVG.Paint.rgb(255, 99, 71)), 2154 NSVGNamedColor("turquoise", NSVG.Paint.rgb( 64, 224, 208)), 2155 NSVGNamedColor("violet", NSVG.Paint.rgb(238, 130, 238)), 2156 NSVGNamedColor("wheat", NSVG.Paint.rgb(245, 222, 179)), 2157 NSVGNamedColor("white", NSVG.Paint.rgb(255, 255, 255)), // basic color 2158 NSVGNamedColor("whitesmoke", NSVG.Paint.rgb(245, 245, 245)), 2159 NSVGNamedColor("yellow", NSVG.Paint.rgb(255, 255, 0)), // basic color 2160 NSVGNamedColor("yellowgreen", NSVG.Paint.rgb(154, 205, 50)), 2161 ]; 2162 2163 enum nsvg__color_name_maxlen = () { 2164 int res = 0; 2165 foreach (const ref known; nsvg__colors) if (res < known.name.length) res = cast(int)known.name.length; 2166 return res; 2167 }(); 2168 2169 2170 // `s0` and `s1` are never empty here 2171 // `s0` is always lowercased 2172 int xstrcmp (const(char)[] s0, const(char)[] s1) { 2173 /* 2174 const(char)* sp0 = s0.ptr; 2175 const(char)* sp1 = s1.ptr; 2176 foreach (; 0..(s0.length < s1.length ? s0.length : s1.length)) { 2177 int c1 = cast(int)(*sp1++); 2178 if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man's tolower 2179 if (auto diff = cast(int)(*sp0++)-c1) return diff; 2180 } 2181 // equals so far 2182 if (s0.length < s1.length) return -1; 2183 if (s0.length > s1.length) return 1; 2184 return 0; 2185 */ 2186 import core.stdc.string : memcmp; 2187 if (auto diff = memcmp(s0.ptr, s1.ptr, (s0.length < s1.length ? s0.length : s1.length))) return diff; 2188 // equals so far 2189 if (s0.length < s1.length) return -1; 2190 if (s0.length > s1.length) return 1; 2191 return 0; 2192 } 2193 2194 2195 uint nsvg__parseColorName (const(char)[] str) { 2196 if (str.length == 0 || str.length > nsvg__color_name_maxlen) return NSVG.Paint.rgb(128, 128, 128); 2197 // check if `str` contains only letters, and convert it to lowercase 2198 char[nsvg__color_name_maxlen] slow = void; 2199 foreach (immutable cidx, char ch; str) { 2200 if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower 2201 if (ch < 'a' || ch > 'z') return NSVG.Paint.rgb(128, 128, 128); // alas 2202 slow.ptr[cidx] = ch; 2203 } 2204 int low = 0; 2205 int high = cast(int)nsvg__colors.length-1; 2206 while (low <= high) { 2207 int med = (low+high)/2; 2208 assert(med >= 0 && med < nsvg__colors.length); 2209 int res = xstrcmp(nsvg__colors.ptr[med].name, str); 2210 if (res < 0) low = med+1; 2211 else if (res > 0) high = med-1; 2212 else return nsvg__colors.ptr[med].color; 2213 } 2214 return NSVG.Paint.rgb(128, 128, 128); 2215 } 2216 2217 uint nsvg__parseColor (const(char)[] str) { 2218 while (str.length && str[0] <= ' ') str = str[1..$]; 2219 if (str.length >= 1 && str[0] == '#') return nsvg__parseColorHex(str); 2220 if (str.length >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') return nsvg__parseColorRGB(str); 2221 return nsvg__parseColorName(str); 2222 } 2223 2224 float nsvg__parseOpacity (const(char)[] str) { 2225 float val = 0; 2226 xsscanf(str, "%f", val); 2227 if (val < 0.0f) val = 0.0f; 2228 if (val > 1.0f) val = 1.0f; 2229 return val; 2230 } 2231 2232 float nsvg__parseMiterLimit (const(char)[] str) { 2233 float val = 0; 2234 xsscanf(str, "%f", val); 2235 if (val < 0.0f) val = 0.0f; 2236 return val; 2237 } 2238 2239 Units nsvg__parseUnits (const(char)[] units) { 2240 if (units.length && units.ptr[0] == '%') return Units.percent; 2241 if (units.length == 2) { 2242 if (units.ptr[0] == 'p' && units.ptr[1] == 'x') return Units.px; 2243 if (units.ptr[0] == 'p' && units.ptr[1] == 't') return Units.pt; 2244 if (units.ptr[0] == 'p' && units.ptr[1] == 'c') return Units.pc; 2245 if (units.ptr[0] == 'm' && units.ptr[1] == 'm') return Units.mm; 2246 if (units.ptr[0] == 'c' && units.ptr[1] == 'm') return Units.cm; 2247 if (units.ptr[0] == 'i' && units.ptr[1] == 'n') return Units.in_; 2248 if (units.ptr[0] == 'e' && units.ptr[1] == 'm') return Units.em; 2249 if (units.ptr[0] == 'e' && units.ptr[1] == 'x') return Units.ex; 2250 } 2251 return Units.user; 2252 } 2253 2254 Coordinate nsvg__parseCoordinateRaw (const(char)[] str) { 2255 Coordinate coord = Coordinate(0, Units.user); 2256 const(char)[] units; 2257 xsscanf(str, "%f%s", coord.value, units); 2258 coord.units = nsvg__parseUnits(units); 2259 return coord; 2260 } 2261 2262 Coordinate nsvg__coord (float v, Units units) { 2263 Coordinate coord = Coordinate(v, units); 2264 return coord; 2265 } 2266 2267 float nsvg__parseCoordinate (Parser* p, const(char)[] str, float orig, float length) { 2268 Coordinate coord = nsvg__parseCoordinateRaw(str); 2269 return nsvg__convertToPixels(p, coord, orig, length); 2270 } 2271 2272 int nsvg__parseTransformArgs (const(char)[] str, float* args, int maxNa, int* na) { 2273 usize end, ptr; 2274 char[65] it = void; 2275 2276 assert(str.length); 2277 *na = 0; 2278 2279 ptr = 0; 2280 while (ptr < str.length && str[ptr] != '(') ++ptr; 2281 if (ptr >= str.length) return 1; 2282 2283 end = ptr; 2284 while (end < str.length && str[end] != ')') ++end; 2285 if (end >= str.length) return 1; 2286 2287 while (ptr < end) { 2288 if (str[ptr] == '-' || str[ptr] == '+' || str[ptr] == '.' || nsvg__isdigit(str[ptr])) { 2289 if (*na >= maxNa) return 0; 2290 ptr += nsvg__parseNumber(str[ptr..end], it[]); 2291 args[(*na)++] = nsvg__atof(it[]); // `it` is guaranteed to be asciiz, and `nsvg__atof()` will stop 2292 } else { 2293 ++ptr; 2294 } 2295 } 2296 return cast(int)end; // fuck off, 64bit 2297 } 2298 2299 2300 int nsvg__parseMatrix (float* xform, const(char)[] str) { 2301 float[6] t = void; 2302 int na = 0; 2303 int len = nsvg__parseTransformArgs(str, t.ptr, 6, &na); 2304 if (na != 6) return len; 2305 xform[0..6] = t[]; 2306 return len; 2307 } 2308 2309 int nsvg__parseTranslate (float* xform, const(char)[] str) { 2310 float[2] args = void; 2311 float[6] t = void; 2312 int na = 0; 2313 int len = nsvg__parseTransformArgs(str, args.ptr, 2, &na); 2314 if (na == 1) args[1] = 0.0; 2315 nsvg__xformSetTranslation(t.ptr, args.ptr[0], args.ptr[1]); 2316 xform[0..6] = t[]; 2317 return len; 2318 } 2319 2320 int nsvg__parseScale (float* xform, const(char)[] str) { 2321 float[2] args = void; 2322 int na = 0; 2323 float[6] t = void; 2324 int len = nsvg__parseTransformArgs(str, args.ptr, 2, &na); 2325 if (na == 1) args.ptr[1] = args.ptr[0]; 2326 nsvg__xformSetScale(t.ptr, args.ptr[0], args.ptr[1]); 2327 xform[0..6] = t[]; 2328 return len; 2329 } 2330 2331 int nsvg__parseSkewX (float* xform, const(char)[] str) { 2332 float[1] args = void; 2333 int na = 0; 2334 float[6] t = void; 2335 int len = nsvg__parseTransformArgs(str, args.ptr, 1, &na); 2336 nsvg__xformSetSkewX(t.ptr, args.ptr[0]/180.0f*NSVG_PI); 2337 xform[0..6] = t[]; 2338 return len; 2339 } 2340 2341 int nsvg__parseSkewY (float* xform, const(char)[] str) { 2342 float[1] args = void; 2343 int na = 0; 2344 float[6] t = void; 2345 int len = nsvg__parseTransformArgs(str, args.ptr, 1, &na); 2346 nsvg__xformSetSkewY(t.ptr, args.ptr[0]/180.0f*NSVG_PI); 2347 xform[0..6] = t[]; 2348 return len; 2349 } 2350 2351 int nsvg__parseRotate (float* xform, const(char)[] str) { 2352 float[3] args = void; 2353 int na = 0; 2354 float[6] m = void; 2355 float[6] t = void; 2356 int len = nsvg__parseTransformArgs(str, args.ptr, 3, &na); 2357 if (na == 1) args.ptr[1] = args.ptr[2] = 0.0f; 2358 nsvg__xformIdentity(m.ptr); 2359 2360 if (na > 1) { 2361 nsvg__xformSetTranslation(t.ptr, -args.ptr[1], -args.ptr[2]); 2362 nsvg__xformMultiply(m.ptr, t.ptr); 2363 } 2364 2365 nsvg__xformSetRotation(t.ptr, args.ptr[0]/180.0f*NSVG_PI); 2366 nsvg__xformMultiply(m.ptr, t.ptr); 2367 2368 if (na > 1) { 2369 nsvg__xformSetTranslation(t.ptr, args.ptr[1], args.ptr[2]); 2370 nsvg__xformMultiply(m.ptr, t.ptr); 2371 } 2372 2373 xform[0..6] = m[]; 2374 2375 return len; 2376 } 2377 2378 bool startsWith (const(char)[] str, const(char)[] sw) { 2379 pragma(inline, true); 2380 return (sw.length <= str.length && str[0..sw.length] == sw[]); 2381 } 2382 2383 void nsvg__parseTransform (float* xform, const(char)[] str) { 2384 float[6] t = void; 2385 nsvg__xformIdentity(xform); 2386 while (str.length) { 2387 int len; 2388 if (startsWith(str, "matrix")) len = nsvg__parseMatrix(t.ptr, str); 2389 else if (startsWith(str, "translate")) len = nsvg__parseTranslate(t.ptr, str); 2390 else if (startsWith(str, "scale")) len = nsvg__parseScale(t.ptr, str); 2391 else if (startsWith(str, "rotate")) len = nsvg__parseRotate(t.ptr, str); 2392 else if (startsWith(str, "skewX")) len = nsvg__parseSkewX(t.ptr, str); 2393 else if (startsWith(str, "skewY")) len = nsvg__parseSkewY(t.ptr, str); 2394 else { str = str[1..$]; continue; } 2395 str = str[len..$]; 2396 nsvg__xformPremultiply(xform, t.ptr); 2397 } 2398 } 2399 2400 // `id` should be prealloced 2401 void nsvg__parseUrl (char[] id, const(char)[] str) { 2402 int i = 0; 2403 if (str.length >= 4) { 2404 str = str[4..$]; // "url("; 2405 if (str.length && str[0] == '#') str = str[1..$]; 2406 while (str.length && str[0] != ')') { 2407 if (id.length-i > 1) id[i++] = str[0]; 2408 str = str[1..$]; 2409 } 2410 } 2411 if (id.length-i > 0) id[i] = '\0'; 2412 } 2413 2414 NSVG.LineCap nsvg__parseLineCap (const(char)[] str) { 2415 if (str == "butt") return NSVG.LineCap.Butt; 2416 if (str == "round") return NSVG.LineCap.Round; 2417 if (str == "square") return NSVG.LineCap.Square; 2418 // TODO: handle inherit. 2419 return NSVG.LineCap.Butt; 2420 } 2421 2422 NSVG.LineJoin nsvg__parseLineJoin (const(char)[] str) { 2423 if (str == "miter") return NSVG.LineJoin.Miter; 2424 if (str == "round") return NSVG.LineJoin.Round; 2425 if (str == "bevel") return NSVG.LineJoin.Bevel; 2426 // TODO: handle inherit. 2427 return NSVG.LineJoin.Miter; 2428 } 2429 2430 NSVG.FillRule nsvg__parseFillRule (const(char)[] str) { 2431 if (str == "nonzero") return NSVG.FillRule.NonZero; 2432 if (str == "evenodd") return NSVG.FillRule.EvenOdd; 2433 // TODO: handle inherit. 2434 return NSVG.FillRule.EvenOdd; 2435 } 2436 2437 2438 int nsvg__parseStrokeDashArray (Parser* p, const(char)[] str, float* strokeDashArray) { 2439 char[65] item = 0; 2440 int count = 0; 2441 float sum = 0.0f; 2442 2443 int nsvg__getNextDashItem () { 2444 int n = 0; 2445 item[] = '\0'; 2446 // skip white spaces and commas 2447 while (str.length && (nsvg__isspace(str[0]) || str[0] == ',')) str = str[1..$]; 2448 // advance until whitespace, comma or end 2449 while (str.length && (!nsvg__isspace(str[0]) && str[0] != ',')) { 2450 if (item.length-n > 1) item[n++] = str[0]; 2451 str = str[1..$]; 2452 } 2453 return n; 2454 } 2455 2456 // Handle "none" 2457 if (!str.length || str[0] == 'n') return 0; 2458 2459 // Parse dashes 2460 while (str.length) { 2461 auto len = nsvg__getNextDashItem(); 2462 if (len < 1) break; 2463 if (count < NSVG_MAX_DASHES) strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item[0..len], 0.0f, nsvg__actualLength(p))); 2464 } 2465 2466 foreach (int i; 0..count) sum += strokeDashArray[i]; 2467 if (sum <= 1e-6f) count = 0; 2468 2469 return count; 2470 } 2471 2472 const(char)[] trimLeft (const(char)[] s, char ech=0) { 2473 usize pos = 0; 2474 while (pos < s.length) { 2475 if (s.ptr[pos] <= ' ') { ++pos; continue; } 2476 if (ech && s.ptr[pos] == ech) { ++pos; continue; } 2477 if (s.ptr[pos] == '/' && s.length-pos > 1 && s.ptr[pos+1] == '*') { 2478 pos += 2; 2479 while (s.length-pos > 1 && !(s.ptr[pos] == '*' && s.ptr[pos+1] == '/')) ++pos; 2480 if ((pos += 2) > s.length) pos = s.length; 2481 continue; 2482 } 2483 break; 2484 } 2485 return s[pos..$]; 2486 } 2487 2488 static const(char)[] trimRight (const(char)[] s, char ech=0) { 2489 usize pos = 0; 2490 while (pos < s.length) { 2491 if (s.ptr[pos] <= ' ' || (ech && s.ptr[pos] == ech)) { 2492 if (s[pos..$].trimLeft(ech).length == 0) return s[0..pos]; 2493 } else if (s.ptr[pos] == '/' && s.length-pos > 1 && s.ptr[pos+1] == '*') { 2494 if (s[pos..$].trimLeft(ech).length == 0) return s[0..pos]; 2495 } 2496 ++pos; 2497 } 2498 return s; 2499 } 2500 2501 version(nanosvg_crappy_stylesheet_parser) { 2502 Style* findStyle (Parser* p, char fch, const(char)[] name) { 2503 if (name.length == 0) return null; 2504 foreach (ref st; p.styles[0..p.styleCount]) { 2505 if (st.name.length < 2 || st.name.ptr[0] != fch) continue; 2506 if (st.name[1..$] == name) return &st; 2507 } 2508 return null; 2509 } 2510 2511 void nsvg__parseClassOrId (Parser* p, char lch, const(char)[] str) { 2512 while (str.length) { 2513 while (str.length && str.ptr[0] <= ' ') str = str[1..$]; 2514 if (str.length == 0) break; 2515 usize pos = 1; 2516 while (pos < str.length && str.ptr[pos] > ' ') ++pos; 2517 version(nanosvg_debug_styles) { import std.stdio; writeln("class to find: ", lch, str[0..pos].quote); } 2518 if (auto st = p.findStyle(lch, str[0..pos])) { 2519 version(nanosvg_debug_styles) { import std.stdio; writeln("class: [", str[0..pos], "]; value: ", st.value.quote); } 2520 nsvg__parseStyle(p, st.value); 2521 } 2522 str = str[pos..$]; 2523 } 2524 } 2525 } 2526 2527 bool nsvg__parseAttr (Parser* p, const(char)[] name, const(char)[] value) { 2528 float[6] xform = void; 2529 Attrib* attr = nsvg__getAttr(p); 2530 if (attr is null) return false; //??? 2531 2532 if (name == "style") { 2533 nsvg__parseStyle(p, value); 2534 } else if (name == "display") { 2535 if (value == "none") attr.visible = 0; 2536 // Don't reset .visible on display:inline, one display:none hides the whole subtree 2537 } else if (name == "fill") { 2538 if (value == "none") { 2539 attr.hasFill = 0; 2540 } else if (startsWith(value, "url(")) { 2541 attr.hasFill = 2; 2542 nsvg__parseUrl(attr.fillGradient[], value); 2543 } else { 2544 attr.hasFill = 1; 2545 attr.fillColor = nsvg__parseColor(value); 2546 } 2547 } else if (name == "opacity") { 2548 attr.opacity = nsvg__parseOpacity(value); 2549 } else if (name == "fill-opacity") { 2550 attr.fillOpacity = nsvg__parseOpacity(value); 2551 } else if (name == "stroke") { 2552 if (value == "none") { 2553 attr.hasStroke = 0; 2554 } else if (startsWith(value, "url(")) { 2555 attr.hasStroke = 2; 2556 nsvg__parseUrl(attr.strokeGradient[], value); 2557 } else { 2558 attr.hasStroke = 1; 2559 attr.strokeColor = nsvg__parseColor(value); 2560 } 2561 } else if (name == "stroke-width") { 2562 attr.strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 2563 } else if (name == "stroke-dasharray") { 2564 attr.strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr.strokeDashArray.ptr); 2565 } else if (name == "stroke-dashoffset") { 2566 attr.strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 2567 } else if (name == "stroke-opacity") { 2568 attr.strokeOpacity = nsvg__parseOpacity(value); 2569 } else if (name == "stroke-linecap") { 2570 attr.strokeLineCap = nsvg__parseLineCap(value); 2571 } else if (name == "stroke-linejoin") { 2572 attr.strokeLineJoin = nsvg__parseLineJoin(value); 2573 } else if (name == "stroke-miterlimit") { 2574 attr.miterLimit = nsvg__parseMiterLimit(value); 2575 } else if (name == "fill-rule") { 2576 attr.fillRule = nsvg__parseFillRule(value); 2577 } else if (name == "font-size") { 2578 attr.fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); 2579 } else if (name == "transform") { 2580 nsvg__parseTransform(xform.ptr, value); 2581 nsvg__xformPremultiply(attr.xform.ptr, xform.ptr); 2582 } else if (name == "stop-color") { 2583 attr.stopColor = nsvg__parseColor(value); 2584 } else if (name == "stop-opacity") { 2585 attr.stopOpacity = nsvg__parseOpacity(value); 2586 } else if (name == "offset") { 2587 attr.stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); 2588 } else if (name == "class") { 2589 version(nanosvg_crappy_stylesheet_parser) nsvg__parseClassOrId(p, '.', value); 2590 } else if (name == "id") { 2591 // apply classes here too 2592 version(nanosvg_crappy_stylesheet_parser) nsvg__parseClassOrId(p, '#', value); 2593 attr.id[] = 0; 2594 if (value.length > attr.id.length-1) value = value[0..attr.id.length-1]; 2595 attr.id[0..value.length] = value[]; 2596 } else { 2597 return false; 2598 } 2599 return true; 2600 } 2601 2602 bool nsvg__parseNameValue (Parser* p, const(char)[] str) { 2603 const(char)[] name; 2604 2605 str = str.trimLeft; 2606 usize pos = 0; 2607 while (pos < str.length && str.ptr[pos] != ':') { 2608 if (str.length-pos > 1 && str.ptr[pos] == '/' && str.ptr[pos+1] == '*') { 2609 pos += 2; 2610 while (str.length-pos > 1 && !(str.ptr[pos] == '*' && str.ptr[pos+1] == '/')) ++pos; 2611 if ((pos += 2) > str.length) pos = str.length; 2612 } else { 2613 ++pos; 2614 } 2615 } 2616 2617 name = str[0..pos].trimLeft.trimRight; 2618 if (name.length == 0) return false; 2619 2620 str = str[pos+(pos < str.length ? 1 : 0)..$].trimLeft.trimRight(';'); 2621 2622 version(nanosvg_debug_styles) { import std.stdio; writeln("** name=", name.quote, "; value=", str.quote); } 2623 2624 return nsvg__parseAttr(p, name, str); 2625 } 2626 2627 void nsvg__parseStyle (Parser* p, const(char)[] str) { 2628 while (str.length) { 2629 str = str.trimLeft; 2630 usize pos = 0; 2631 while (pos < str.length && str[pos] != ';') { 2632 if (str.length-pos > 1 && str.ptr[pos] == '/' && str.ptr[pos+1] == '*') { 2633 pos += 2; 2634 while (str.length-pos > 1 && !(str.ptr[pos] == '*' && str.ptr[pos+1] == '/')) ++pos; 2635 if ((pos += 2) > str.length) pos = str.length; 2636 } else { 2637 ++pos; 2638 } 2639 } 2640 const(char)[] val = trimRight(str[0..pos]); 2641 version(nanosvg_debug_styles) { import std.stdio; writeln("style: ", val.quote); } 2642 str = str[pos+(pos < str.length ? 1 : 0)..$]; 2643 if (val.length > 0) nsvg__parseNameValue(p, val); 2644 } 2645 } 2646 2647 void nsvg__parseAttribs (Parser* p, AttrList attr) { 2648 for (usize i = 0; attr.length-i >= 2; i += 2) { 2649 if (attr[i] == "style") nsvg__parseStyle(p, attr[i+1]); 2650 else if (attr[i] == "class") { version(nanosvg_crappy_stylesheet_parser) nsvg__parseClassOrId(p, '.', attr[i+1]); } 2651 else nsvg__parseAttr(p, attr[i], attr[i+1]); 2652 } 2653 } 2654 2655 int nsvg__getArgsPerElement (char cmd) { 2656 switch (cmd) { 2657 case 'v': case 'V': 2658 case 'h': case 'H': 2659 return 1; 2660 case 'm': case 'M': 2661 case 'l': case 'L': 2662 case 't': case 'T': 2663 return 2; 2664 case 'q': case 'Q': 2665 case 's': case 'S': 2666 return 4; 2667 case 'c': case 'C': 2668 return 6; 2669 case 'a': case 'A': 2670 return 7; 2671 default: 2672 } 2673 return 0; 2674 } 2675 2676 void nsvg__pathMoveTo (Parser* p, float* cpx, float* cpy, const(float)* args, bool rel) { 2677 debug(nanosvg) { import std.stdio; writeln("nsvg__pathMoveTo: args=", args[0..2]); } 2678 if (rel) { *cpx += args[0]; *cpy += args[1]; } else { *cpx = args[0]; *cpy = args[1]; } 2679 nsvg__moveTo(p, *cpx, *cpy); 2680 } 2681 2682 void nsvg__pathLineTo (Parser* p, float* cpx, float* cpy, const(float)* args, bool rel) { 2683 debug(nanosvg) { import std.stdio; writeln("nsvg__pathLineTo: args=", args[0..2]); } 2684 if (rel) { *cpx += args[0]; *cpy += args[1]; } else { *cpx = args[0]; *cpy = args[1]; } 2685 nsvg__lineTo(p, *cpx, *cpy); 2686 } 2687 2688 void nsvg__pathHLineTo (Parser* p, float* cpx, float* cpy, const(float)* args, bool rel) { 2689 debug(nanosvg) { import std.stdio; writeln("nsvg__pathHLineTo: args=", args[0..1]); } 2690 if (rel) *cpx += args[0]; else *cpx = args[0]; 2691 nsvg__lineTo(p, *cpx, *cpy); 2692 } 2693 2694 void nsvg__pathVLineTo (Parser* p, float* cpx, float* cpy, const(float)* args, bool rel) { 2695 debug(nanosvg) { import std.stdio; writeln("nsvg__pathVLineTo: args=", args[0..1]); } 2696 if (rel) *cpy += args[0]; else *cpy = args[0]; 2697 nsvg__lineTo(p, *cpx, *cpy); 2698 } 2699 2700 void nsvg__pathCubicBezTo (Parser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, const(float)* args, bool rel) { 2701 debug(nanosvg) { import std.stdio; writeln("nsvg__pathCubicBezTo: args=", args[0..6]); } 2702 float cx1 = args[0]; 2703 float cy1 = args[1]; 2704 float cx2 = args[2]; 2705 float cy2 = args[3]; 2706 float x2 = args[4]; 2707 float y2 = args[5]; 2708 2709 if (rel) { 2710 cx1 += *cpx; 2711 cy1 += *cpy; 2712 cx2 += *cpx; 2713 cy2 += *cpy; 2714 x2 += *cpx; 2715 y2 += *cpy; 2716 } 2717 2718 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); 2719 2720 *cpx2 = cx2; 2721 *cpy2 = cy2; 2722 *cpx = x2; 2723 *cpy = y2; 2724 } 2725 2726 void nsvg__pathCubicBezShortTo (Parser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, const(float)* args, bool rel) { 2727 debug(nanosvg) { import std.stdio; writeln("nsvg__pathCubicBezShortTo: args=", args[0..4]); } 2728 2729 float cx2 = args[0]; 2730 float cy2 = args[1]; 2731 float x2 = args[2]; 2732 float y2 = args[3]; 2733 immutable float x1 = *cpx; 2734 immutable float y1 = *cpy; 2735 2736 if (rel) { 2737 cx2 += *cpx; 2738 cy2 += *cpy; 2739 x2 += *cpx; 2740 y2 += *cpy; 2741 } 2742 2743 immutable float cx1 = 2*x1-*cpx2; 2744 immutable float cy1 = 2*y1-*cpy2; 2745 2746 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); 2747 2748 *cpx2 = cx2; 2749 *cpy2 = cy2; 2750 *cpx = x2; 2751 *cpy = y2; 2752 } 2753 2754 void nsvg__pathQuadBezTo (Parser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, const(float)* args, bool rel) { 2755 debug(nanosvg) { import std.stdio; writeln("nsvg__pathQuadBezTo: args=", args[0..4]); } 2756 2757 float cx = args[0]; 2758 float cy = args[1]; 2759 float x2 = args[2]; 2760 float y2 = args[3]; 2761 immutable float x1 = *cpx; 2762 immutable float y1 = *cpy; 2763 2764 if (rel) { 2765 cx += *cpx; 2766 cy += *cpy; 2767 x2 += *cpx; 2768 y2 += *cpy; 2769 } 2770 2771 version(nanosvg_only_cubic_beziers) { 2772 // convert to cubic bezier 2773 immutable float cx1 = x1+2.0f/3.0f*(cx-x1); 2774 immutable float cy1 = y1+2.0f/3.0f*(cy-y1); 2775 immutable float cx2 = x2+2.0f/3.0f*(cx-x2); 2776 immutable float cy2 = y2+2.0f/3.0f*(cy-y2); 2777 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); 2778 } else { 2779 nsvg__quadBezTo(p, cx, cy, x2, y2); 2780 } 2781 2782 *cpx2 = cx; 2783 *cpy2 = cy; 2784 *cpx = x2; 2785 *cpy = y2; 2786 } 2787 2788 void nsvg__pathQuadBezShortTo (Parser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, const(float)* args, bool rel) { 2789 debug(nanosvg) { import std.stdio; writeln("nsvg__pathQuadBezShortTo: args=", args[0..2]); } 2790 2791 float x2 = args[0]; 2792 float y2 = args[1]; 2793 immutable float x1 = *cpx; 2794 immutable float y1 = *cpy; 2795 2796 if (rel) { 2797 x2 += *cpx; 2798 y2 += *cpy; 2799 } 2800 2801 immutable float cx = 2*x1-*cpx2; 2802 immutable float cy = 2*y1-*cpy2; 2803 2804 version(nanosvg_only_cubic_beziers) { 2805 // convert to cubic bezier 2806 immutable float cx1 = x1+2.0f/3.0f*(cx-x1); 2807 immutable float cy1 = y1+2.0f/3.0f*(cy-y1); 2808 immutable float cx2 = x2+2.0f/3.0f*(cx-x2); 2809 immutable float cy2 = y2+2.0f/3.0f*(cy-y2); 2810 nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); 2811 } else { 2812 nsvg__quadBezTo(p, cx, cy, x2, y2); 2813 } 2814 2815 *cpx2 = cx; 2816 *cpy2 = cy; 2817 *cpx = x2; 2818 *cpy = y2; 2819 } 2820 2821 float nsvg__sqr (in float x) pure nothrow @safe @nogc { pragma(inline, true); return x*x; } 2822 float nsvg__vmag (in float x, float y) nothrow @safe @nogc { pragma(inline, true); return sqrtf(x*x+y*y); } 2823 2824 float nsvg__vecrat (float ux, float uy, float vx, float vy) nothrow @safe @nogc { 2825 pragma(inline, true); 2826 return (ux*vx+uy*vy)/(nsvg__vmag(ux, uy)*nsvg__vmag(vx, vy)); 2827 } 2828 2829 float nsvg__vecang (float ux, float uy, float vx, float vy) nothrow @safe @nogc { 2830 float r = nsvg__vecrat(ux, uy, vx, vy); 2831 if (r < -1.0f) r = -1.0f; 2832 if (r > 1.0f) r = 1.0f; 2833 return (ux*vy < uy*vx ? -1.0f : 1.0f)*acosf(r); 2834 } 2835 2836 void nsvg__pathArcTo (Parser* p, float* cpx, float* cpy, const(float)* args, bool rel) { 2837 // ported from canvg (https://code.google.com/p/canvg/) 2838 float rx = fabsf(args[0]); // y radius 2839 float ry = fabsf(args[1]); // x radius 2840 immutable float rotx = args[2]/180.0f*NSVG_PI; // x rotation engle 2841 immutable float fa = (fabsf(args[3]) > 1e-6 ? 1 : 0); // large arc 2842 immutable float fs = (fabsf(args[4]) > 1e-6 ? 1 : 0); // sweep direction 2843 immutable float x1 = *cpx; // start point 2844 immutable float y1 = *cpy; 2845 2846 // end point 2847 float x2 = args[5]; 2848 float y2 = args[6]; 2849 2850 if (rel) { x2 += *cpx; y2 += *cpy; } 2851 2852 float dx = x1-x2; 2853 float dy = y1-y2; 2854 immutable float d0 = sqrtf(dx*dx+dy*dy); 2855 if (d0 < 1e-6f || rx < 1e-6f || ry < 1e-6f) { 2856 // the arc degenerates to a line 2857 nsvg__lineTo(p, x2, y2); 2858 *cpx = x2; 2859 *cpy = y2; 2860 return; 2861 } 2862 2863 immutable float sinrx = sinf(rotx); 2864 immutable float cosrx = cosf(rotx); 2865 2866 // convert to center point parameterization 2867 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes 2868 // 1) Compute x1', y1' 2869 immutable float x1p = cosrx*dx/2.0f+sinrx*dy/2.0f; 2870 immutable float y1p = -sinrx*dx/2.0f+cosrx*dy/2.0f; 2871 immutable float d1 = nsvg__sqr(x1p)/nsvg__sqr(rx)+nsvg__sqr(y1p)/nsvg__sqr(ry); 2872 if (d1 > 1) { 2873 immutable float d2 = sqrtf(d1); 2874 rx *= d2; 2875 ry *= d2; 2876 } 2877 // 2) Compute cx', cy' 2878 float s = 0.0f; 2879 float sa = nsvg__sqr(rx)*nsvg__sqr(ry)-nsvg__sqr(rx)*nsvg__sqr(y1p)-nsvg__sqr(ry)*nsvg__sqr(x1p); 2880 immutable float sb = nsvg__sqr(rx)*nsvg__sqr(y1p)+nsvg__sqr(ry)*nsvg__sqr(x1p); 2881 if (sa < 0.0f) sa = 0.0f; 2882 if (sb > 0.0f) s = sqrtf(sa/sb); 2883 if (fa == fs) s = -s; 2884 immutable float cxp = s*rx*y1p/ry; 2885 immutable float cyp = s*-ry*x1p/rx; 2886 2887 // 3) Compute cx,cy from cx',cy' 2888 immutable float cx = (x1+x2)/2.0f+cosrx*cxp-sinrx*cyp; 2889 immutable float cy = (y1+y2)/2.0f+sinrx*cxp+cosrx*cyp; 2890 2891 // 4) Calculate theta1, and delta theta. 2892 immutable float ux = (x1p-cxp)/rx; 2893 immutable float uy = (y1p-cyp)/ry; 2894 immutable float vx = (-x1p-cxp)/rx; 2895 immutable float vy = (-y1p-cyp)/ry; 2896 immutable float a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle 2897 float da = nsvg__vecang(ux, uy, vx, vy); // Delta angle 2898 2899 if (fs == 0 && da > 0) da -= 2*NSVG_PI; 2900 else if (fs == 1 && da < 0) da += 2*NSVG_PI; 2901 2902 float[6] t = void; 2903 // approximate the arc using cubic spline segments 2904 t.ptr[0] = cosrx; t.ptr[1] = sinrx; 2905 t.ptr[2] = -sinrx; t.ptr[3] = cosrx; 2906 t.ptr[4] = cx; t.ptr[5] = cy; 2907 2908 // split arc into max 90 degree segments 2909 // the loop assumes an iteration per end point (including start and end), this +1 2910 immutable ndivs = cast(int)(fabsf(da)/(NSVG_PI*0.5f)+1.0f); 2911 immutable float hda = (da/cast(float)ndivs)/2.0f; 2912 float kappa = fabsf(4.0f/3.0f*(1.0f-cosf(hda))/sinf(hda)); 2913 if (da < 0.0f) kappa = -kappa; 2914 2915 immutable float ndivsf = cast(float)ndivs; 2916 float px = 0, py = 0, ptanx = 0, ptany = 0; 2917 foreach (int i; 0..ndivs+1) { 2918 float x = void, y = void, tanx = void, tany = void; 2919 immutable float a = a1+da*(i/ndivsf); 2920 immutable float loopdx = cosf(a); 2921 immutable float loopdy = sinf(a); 2922 nsvg__xformPoint(&x, &y, loopdx*rx, loopdy*ry, t.ptr); // position 2923 nsvg__xformVec(&tanx, &tany, -loopdy*rx*kappa, loopdx*ry*kappa, t.ptr); // tangent 2924 if (i > 0) nsvg__cubicBezTo(p, px+ptanx, py+ptany, x-tanx, y-tany, x, y); 2925 px = x; 2926 py = y; 2927 ptanx = tanx; 2928 ptany = tany; 2929 } 2930 2931 *cpx = x2; 2932 *cpy = y2; 2933 } 2934 2935 void nsvg__parsePath (Parser* p, AttrList attr) { 2936 const(char)[] s = null; 2937 char cmd = '\0'; 2938 float[10] args = void; 2939 int nargs; 2940 int rargs = 0; 2941 float cpx = void, cpy = void, cpx2 = void, cpy2 = void; 2942 bool closedFlag = false; 2943 char[65] item = void; 2944 2945 for (usize i = 0; attr.length-i >= 2; i += 2) { 2946 if (attr[i] == "d") { 2947 s = attr[i+1]; 2948 } else { 2949 const(char)[][2] tmp; 2950 tmp[0] = attr[i]; 2951 tmp[1] = attr[i+1]; 2952 nsvg__parseAttribs(p, tmp[]); 2953 } 2954 } 2955 2956 if (s.length) { 2957 nsvg__resetPath(p); 2958 cpx = 0; 2959 cpy = 0; 2960 cpx2 = 0; 2961 cpy2 = 0; 2962 closedFlag = false; 2963 nargs = 0; 2964 2965 while (s.length) { 2966 auto skl = nsvg__getNextPathItem(s, item[]); 2967 if (skl < s.length) s = s[skl..$]; else s = s[$..$]; 2968 debug(nanosvg) { import std.stdio; writeln(":: ", item.fromAsciiz.quote, " : ", s.quote); } 2969 if (!item[0]) break; 2970 if (nsvg__isnum(item[0])) { 2971 if (nargs < 10) { 2972 args[nargs++] = nsvg__atof(item[]); 2973 } 2974 if (nargs >= rargs) { 2975 switch (cmd) { 2976 case 'm': case 'M': // move to 2977 nsvg__pathMoveTo(p, &cpx, &cpy, args.ptr, (cmd == 'm' ? 1 : 0)); 2978 // Moveto can be followed by multiple coordinate pairs, 2979 // which should be treated as linetos. 2980 cmd = (cmd == 'm' ? 'l' : 'L'); 2981 rargs = nsvg__getArgsPerElement(cmd); 2982 cpx2 = cpx; cpy2 = cpy; 2983 break; 2984 case 'l': case 'L': // line to 2985 nsvg__pathLineTo(p, &cpx, &cpy, args.ptr, (cmd == 'l' ? 1 : 0)); 2986 cpx2 = cpx; cpy2 = cpy; 2987 break; 2988 case 'H': case 'h': // horizontal line to 2989 nsvg__pathHLineTo(p, &cpx, &cpy, args.ptr, (cmd == 'h' ? 1 : 0)); 2990 cpx2 = cpx; cpy2 = cpy; 2991 break; 2992 case 'V': case 'v': // vertical line to 2993 nsvg__pathVLineTo(p, &cpx, &cpy, args.ptr, (cmd == 'v' ? 1 : 0)); 2994 cpx2 = cpx; cpy2 = cpy; 2995 break; 2996 case 'C': case 'c': // cubic bezier 2997 nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args.ptr, (cmd == 'c' ? 1 : 0)); 2998 break; 2999 case 'S': case 's': // "short" cubic bezier 3000 nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args.ptr, (cmd == 's' ? 1 : 0)); 3001 break; 3002 case 'Q': case 'q': // quadratic bezier 3003 nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args.ptr, (cmd == 'q' ? 1 : 0)); 3004 break; 3005 case 'T': case 't': // "short" quadratic bezier 3006 nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args.ptr, cmd == 't' ? 1 : 0); 3007 break; 3008 case 'A': case 'a': // arc 3009 nsvg__pathArcTo(p, &cpx, &cpy, args.ptr, cmd == 'a' ? 1 : 0); 3010 cpx2 = cpx; cpy2 = cpy; 3011 break; 3012 default: 3013 if (nargs >= 2) { 3014 cpx = args[nargs-2]; 3015 cpy = args[nargs-1]; 3016 cpx2 = cpx; 3017 cpy2 = cpy; 3018 } 3019 break; 3020 } 3021 nargs = 0; 3022 } 3023 } else { 3024 cmd = item[0]; 3025 rargs = nsvg__getArgsPerElement(cmd); 3026 if (cmd == 'M' || cmd == 'm') { 3027 // commit path 3028 if (p.nsflts > 0) nsvg__addPath(p, closedFlag); 3029 // start new subpath 3030 nsvg__resetPath(p); 3031 closedFlag = false; 3032 nargs = 0; 3033 } else if (cmd == 'Z' || cmd == 'z') { 3034 closedFlag = true; 3035 // commit path 3036 if (p.nsflts > 0) { 3037 // move current point to first point 3038 if ((cast(NSVG.Command)p.stream[0]) != NSVG.Command.MoveTo) assert(0, "NanoVega.SVG: invalid path"); 3039 cpx = p.stream[1]; 3040 cpy = p.stream[2]; 3041 cpx2 = cpx; 3042 cpy2 = cpy; 3043 nsvg__addPath(p, closedFlag); 3044 } 3045 // start new subpath 3046 nsvg__resetPath(p); 3047 nsvg__moveTo(p, cpx, cpy); 3048 closedFlag = false; 3049 nargs = 0; 3050 } 3051 } 3052 } 3053 // commit path 3054 if (p.nsflts) nsvg__addPath(p, closedFlag); 3055 } 3056 3057 nsvg__addShape(p); 3058 } 3059 3060 void nsvg__parseRect (Parser* p, AttrList attr) { 3061 float x = 0.0f; 3062 float y = 0.0f; 3063 float w = 0.0f; 3064 float h = 0.0f; 3065 float rx = -1.0f; // marks not set 3066 float ry = -1.0f; 3067 3068 for (usize i = 0; attr.length-i >= 2; i += 2) { 3069 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3070 if (attr[i] == "x") x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 3071 else if (attr[i] == "y") y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 3072 else if (attr[i] == "width") w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); 3073 else if (attr[i] == "height") h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); 3074 else if (attr[i] == "rx") rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); 3075 else if (attr[i] == "ry") ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); 3076 } 3077 } 3078 3079 if (rx < 0.0f && ry > 0.0f) rx = ry; 3080 if (ry < 0.0f && rx > 0.0f) ry = rx; 3081 if (rx < 0.0f) rx = 0.0f; 3082 if (ry < 0.0f) ry = 0.0f; 3083 if (rx > w/2.0f) rx = w/2.0f; 3084 if (ry > h/2.0f) ry = h/2.0f; 3085 3086 if (w != 0.0f && h != 0.0f) { 3087 nsvg__resetPath(p); 3088 3089 if (rx < 0.00001f || ry < 0.0001f) { 3090 nsvg__moveTo(p, x, y); 3091 nsvg__lineTo(p, x+w, y); 3092 nsvg__lineTo(p, x+w, y+h); 3093 nsvg__lineTo(p, x, y+h); 3094 } else { 3095 // Rounded rectangle 3096 nsvg__moveTo(p, x+rx, y); 3097 nsvg__lineTo(p, x+w-rx, y); 3098 nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); 3099 nsvg__lineTo(p, x+w, y+h-ry); 3100 nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); 3101 nsvg__lineTo(p, x+rx, y+h); 3102 nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); 3103 nsvg__lineTo(p, x, y+ry); 3104 nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); 3105 } 3106 3107 nsvg__addPath(p, 1); 3108 3109 nsvg__addShape(p); 3110 } 3111 } 3112 3113 void nsvg__parseCircle (Parser* p, AttrList attr) { 3114 float cx = 0.0f; 3115 float cy = 0.0f; 3116 float r = 0.0f; 3117 3118 for (usize i = 0; attr.length-i >= 2; i += 2) { 3119 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3120 if (attr[i] == "cx") cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 3121 else if (attr[i] == "cy") cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 3122 else if (attr[i] == "r") r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); 3123 } 3124 } 3125 3126 if (r > 0.0f) { 3127 nsvg__resetPath(p); 3128 3129 nsvg__moveTo(p, cx+r, cy); 3130 nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); 3131 nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); 3132 nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); 3133 nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); 3134 3135 nsvg__addPath(p, 1); 3136 3137 nsvg__addShape(p); 3138 } 3139 } 3140 3141 void nsvg__parseEllipse (Parser* p, AttrList attr) { 3142 float cx = 0.0f; 3143 float cy = 0.0f; 3144 float rx = 0.0f; 3145 float ry = 0.0f; 3146 3147 for (usize i = 0; attr.length-i >= 2; i += 2) { 3148 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3149 if (attr[i] == "cx") cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 3150 else if (attr[i] == "cy") cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 3151 else if (attr[i] == "rx") rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); 3152 else if (attr[i] == "ry") ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); 3153 } 3154 } 3155 3156 if (rx > 0.0f && ry > 0.0f) { 3157 nsvg__resetPath(p); 3158 3159 nsvg__moveTo(p, cx+rx, cy); 3160 nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); 3161 nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); 3162 nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); 3163 nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); 3164 3165 nsvg__addPath(p, 1); 3166 3167 nsvg__addShape(p); 3168 } 3169 } 3170 3171 void nsvg__parseLine (Parser* p, AttrList attr) { 3172 float x1 = 0.0; 3173 float y1 = 0.0; 3174 float x2 = 0.0; 3175 float y2 = 0.0; 3176 3177 for (usize i = 0; attr.length-i >= 2; i += 2) { 3178 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3179 if (attr[i] == "x1") x1 = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 3180 else if (attr[i] == "y1") y1 = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 3181 else if (attr[i] == "x2") x2 = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); 3182 else if (attr[i] == "y2") y2 = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); 3183 } 3184 } 3185 3186 nsvg__resetPath(p); 3187 3188 nsvg__moveTo(p, x1, y1); 3189 nsvg__lineTo(p, x2, y2); 3190 3191 nsvg__addPath(p, 0); 3192 3193 nsvg__addShape(p); 3194 } 3195 3196 void nsvg__parsePoly (Parser* p, AttrList attr, bool closeFlag) { 3197 float[2] args = void; 3198 int nargs, npts = 0; 3199 char[65] item = 0; 3200 3201 nsvg__resetPath(p); 3202 3203 for (usize i = 0; attr.length-i >= 2; i += 2) { 3204 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3205 if (attr[i] == "points") { 3206 const(char)[]s = attr[i+1]; 3207 nargs = 0; 3208 while (s.length) { 3209 auto skl = nsvg__getNextPathItem(s, item[]); 3210 if (skl < s.length) s = s[skl..$]; else s = s[$..$]; 3211 args[nargs++] = nsvg__atof(item[]); 3212 if (nargs >= 2) { 3213 if (npts == 0) nsvg__moveTo(p, args[0], args[1]); else nsvg__lineTo(p, args[0], args[1]); 3214 nargs = 0; 3215 ++npts; 3216 } 3217 } 3218 } 3219 } 3220 } 3221 3222 nsvg__addPath(p, closeFlag); 3223 3224 nsvg__addShape(p); 3225 } 3226 3227 void nsvg__parseSVG (Parser* p, AttrList attr) { 3228 for (usize i = 0; attr.length-i >= 2; i += 2) { 3229 if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3230 if (attr[i] == "width") { 3231 p.image.width = nsvg__parseCoordinate(p, attr[i+1], 0.0f, p.canvaswdt); 3232 //{ import core.stdc.stdio; printf("(%d) w=%d [%.*s]\n", p.canvaswdt, cast(int)p.image.width, cast(uint)attr[i+1].length, attr[i+1].ptr); } 3233 } else if (attr[i] == "height") { 3234 p.image.height = nsvg__parseCoordinate(p, attr[i+1], 0.0f, p.canvashgt); 3235 } else if (attr[i] == "viewBox") { 3236 xsscanf(attr[i+1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", p.viewMinx, p.viewMiny, p.viewWidth, p.viewHeight); 3237 } else if (attr[i] == "preserveAspectRatio") { 3238 if (attr[i+1].xindexOf("none") >= 0) { 3239 // No uniform scaling 3240 p.alignType = NSVG_ALIGN_NONE; 3241 } else { 3242 // Parse X align 3243 if (attr[i+1].xindexOf("xMin") >= 0) p.alignX = NSVG_ALIGN_MIN; 3244 else if (attr[i+1].xindexOf("xMid") >= 0) p.alignX = NSVG_ALIGN_MID; 3245 else if (attr[i+1].xindexOf("xMax") >= 0) p.alignX = NSVG_ALIGN_MAX; 3246 // Parse X align 3247 if (attr[i+1].xindexOf("yMin") >= 0) p.alignY = NSVG_ALIGN_MIN; 3248 else if (attr[i+1].xindexOf("yMid") >= 0) p.alignY = NSVG_ALIGN_MID; 3249 else if (attr[i+1].xindexOf("yMax") >= 0) p.alignY = NSVG_ALIGN_MAX; 3250 // Parse meet/slice 3251 p.alignType = NSVG_ALIGN_MEET; 3252 if (attr[i+1].xindexOf("slice") >= 0) p.alignType = NSVG_ALIGN_SLICE; 3253 } 3254 } 3255 } 3256 } 3257 } 3258 3259 void nsvg__parseGradient (Parser* p, AttrList attr, NSVG.PaintType type) { 3260 GradientData* grad = xalloc!GradientData; 3261 if (grad is null) return; 3262 //memset(grad, 0, GradientData.sizeof); 3263 grad.units = GradientUnits.Object; 3264 grad.type = type; 3265 if (grad.type == NSVG.PaintType.LinearGradient) { 3266 grad.linear.x1 = nsvg__coord(0.0f, Units.percent); 3267 grad.linear.y1 = nsvg__coord(0.0f, Units.percent); 3268 grad.linear.x2 = nsvg__coord(100.0f, Units.percent); 3269 grad.linear.y2 = nsvg__coord(0.0f, Units.percent); 3270 } else if (grad.type == NSVG.PaintType.RadialGradient) { 3271 grad.radial.cx = nsvg__coord(50.0f, Units.percent); 3272 grad.radial.cy = nsvg__coord(50.0f, Units.percent); 3273 grad.radial.r = nsvg__coord(50.0f, Units.percent); 3274 } 3275 3276 nsvg__xformIdentity(grad.xform.ptr); 3277 3278 for (usize i = 0; attr.length-i >= 2; i += 2) { 3279 if (attr[i] == "id") { 3280 grad.id[] = 0; 3281 const(char)[] s = attr[i+1]; 3282 if (s.length > grad.id.length-1) s = s[0..grad.id.length-1]; 3283 grad.id[0..s.length] = s[]; 3284 } else if (!nsvg__parseAttr(p, attr[i], attr[i+1])) { 3285 if (attr[i] == "gradientUnits") { if (attr[i+1] == "objectBoundingBox") grad.units = GradientUnits.Object; else grad.units = GradientUnits.User; } 3286 else if (attr[i] == "gradientTransform") { nsvg__parseTransform(grad.xform.ptr, attr[i+1]); } 3287 else if (attr[i] == "cx") { grad.radial.cx = nsvg__parseCoordinateRaw(attr[i+1]); } 3288 else if (attr[i] == "cy") { grad.radial.cy = nsvg__parseCoordinateRaw(attr[i+1]); } 3289 else if (attr[i] == "r") { grad.radial.r = nsvg__parseCoordinateRaw(attr[i+1]); } 3290 else if (attr[i] == "fx") { grad.radial.fx = nsvg__parseCoordinateRaw(attr[i+1]); } 3291 else if (attr[i] == "fy") { grad.radial.fy = nsvg__parseCoordinateRaw(attr[i+1]); } 3292 else if (attr[i] == "x1") { grad.linear.x1 = nsvg__parseCoordinateRaw(attr[i+1]); } 3293 else if (attr[i] == "y1") { grad.linear.y1 = nsvg__parseCoordinateRaw(attr[i+1]); } 3294 else if (attr[i] == "x2") { grad.linear.x2 = nsvg__parseCoordinateRaw(attr[i+1]); } 3295 else if (attr[i] == "y2") { grad.linear.y2 = nsvg__parseCoordinateRaw(attr[i+1]); } 3296 else if (attr[i] == "spreadMethod") { 3297 if (attr[i+1] == "pad") grad.spread = NSVG.SpreadType.Pad; 3298 else if (attr[i+1] == "reflect") grad.spread = NSVG.SpreadType.Reflect; 3299 else if (attr[i+1] == "repeat") grad.spread = NSVG.SpreadType.Repeat; 3300 } else if (attr[i] == "xlink:href") { 3301 grad.ref_[] = 0; 3302 const(char)[] s = attr[i+1]; 3303 if (s.length > 0 && s.ptr[0] == '#') s = s[1..$]; // remove '#' 3304 if (s.length > grad.ref_.length-1) s = s[0..grad.ref_.length-1]; 3305 grad.ref_[0..s.length] = s[]; 3306 } 3307 } 3308 } 3309 3310 grad.next = p.gradients; 3311 p.gradients = grad; 3312 } 3313 3314 void nsvg__parseGradientStop (Parser* p, AttrList attr) { 3315 import core.stdc.stdlib : realloc; 3316 3317 Attrib* curAttr = nsvg__getAttr(p); 3318 GradientData* grad; 3319 NSVG.GradientStop* stop; 3320 int idx; 3321 3322 curAttr.stopOffset = 0; 3323 curAttr.stopColor = 0; 3324 curAttr.stopOpacity = 1.0f; 3325 3326 for (usize i = 0; attr.length-i >= 2; i += 2) nsvg__parseAttr(p, attr[i], attr[i+1]); 3327 3328 // Add stop to the last gradient. 3329 grad = p.gradients; 3330 if (grad is null) return; 3331 3332 ++grad.nstops; 3333 grad.stops = cast(NSVG.GradientStop*)realloc(grad.stops, NSVG.GradientStop.sizeof*grad.nstops+256); 3334 if (grad.stops is null) assert(0, "nanosvg: out of memory"); 3335 3336 // Insert 3337 idx = grad.nstops-1; 3338 foreach (int i; 0..grad.nstops-1) { 3339 if (curAttr.stopOffset < grad.stops[i].offset) { 3340 idx = i; 3341 break; 3342 } 3343 } 3344 if (idx != grad.nstops-1) { 3345 for (int i = grad.nstops-1; i > idx; --i) grad.stops[i] = grad.stops[i-1]; 3346 } 3347 3348 stop = grad.stops+idx; 3349 stop.color = curAttr.stopColor; 3350 stop.color |= cast(uint)(curAttr.stopOpacity*255)<<24; 3351 stop.offset = curAttr.stopOffset; 3352 } 3353 3354 void nsvg__startElement (void* ud, const(char)[] el, AttrList attr) { 3355 Parser* p = cast(Parser*)ud; 3356 3357 version(nanosvg_debug_styles) { import std.stdio; writeln("tagB: ", el.quote); } 3358 version(nanosvg_crappy_stylesheet_parser) { p.inStyle = (el == "style"); } 3359 3360 if (p.defsFlag) { 3361 // Skip everything but gradients in defs 3362 if (el == "linearGradient") { 3363 nsvg__parseGradient(p, attr, NSVG.PaintType.LinearGradient); 3364 } else if (el == "radialGradient") { 3365 nsvg__parseGradient(p, attr, NSVG.PaintType.RadialGradient); 3366 } else if (el == "stop") { 3367 nsvg__parseGradientStop(p, attr); 3368 } 3369 return; 3370 } 3371 3372 if (el == "g") { 3373 nsvg__pushAttr(p); 3374 nsvg__parseAttribs(p, attr); 3375 } else if (el == "path") { 3376 if (p.pathFlag) return; // do not allow nested paths 3377 p.pathFlag = true; 3378 nsvg__pushAttr(p); 3379 nsvg__parsePath(p, attr); 3380 nsvg__popAttr(p); 3381 } else if (el == "rect") { 3382 nsvg__pushAttr(p); 3383 nsvg__parseRect(p, attr); 3384 nsvg__popAttr(p); 3385 } else if (el == "circle") { 3386 nsvg__pushAttr(p); 3387 nsvg__parseCircle(p, attr); 3388 nsvg__popAttr(p); 3389 } else if (el == "ellipse") { 3390 nsvg__pushAttr(p); 3391 nsvg__parseEllipse(p, attr); 3392 nsvg__popAttr(p); 3393 } else if (el == "line") { 3394 nsvg__pushAttr(p); 3395 nsvg__parseLine(p, attr); 3396 nsvg__popAttr(p); 3397 } else if (el == "polyline") { 3398 nsvg__pushAttr(p); 3399 nsvg__parsePoly(p, attr, 0); 3400 nsvg__popAttr(p); 3401 } else if (el == "polygon") { 3402 nsvg__pushAttr(p); 3403 nsvg__parsePoly(p, attr, 1); 3404 nsvg__popAttr(p); 3405 } else if (el == "linearGradient") { 3406 nsvg__parseGradient(p, attr, NSVG.PaintType.LinearGradient); 3407 } else if (el == "radialGradient") { 3408 nsvg__parseGradient(p, attr, NSVG.PaintType.RadialGradient); 3409 } else if (el == "stop") { 3410 nsvg__parseGradientStop(p, attr); 3411 } else if (el == "defs") { 3412 p.defsFlag = true; 3413 } else if (el == "svg") { 3414 nsvg__parseSVG(p, attr); 3415 } 3416 } 3417 3418 void nsvg__endElement (void* ud, const(char)[] el) { 3419 version(nanosvg_debug_styles) { import std.stdio; writeln("tagE: ", el.quote); } 3420 Parser* p = cast(Parser*)ud; 3421 if (el == "g") nsvg__popAttr(p); 3422 else if (el == "path") p.pathFlag = false; 3423 else if (el == "defs") p.defsFlag = false; 3424 else if (el == "style") { version(nanosvg_crappy_stylesheet_parser) p.inStyle = false; } 3425 } 3426 3427 void nsvg__content (void* ud, const(char)[] s) { 3428 version(nanosvg_crappy_stylesheet_parser) { 3429 Parser* p = cast(Parser*)ud; 3430 if (!p.inStyle) { 3431 return; 3432 } 3433 // cheap hack 3434 for (;;) { 3435 while (s.length && s.ptr[0] <= ' ') s = s[1..$]; 3436 if (!s.startsWith("<![CDATA[")) break; 3437 s = s[9..$]; 3438 } 3439 for (;;) { 3440 while (s.length && (s[$-1] <= ' ' || s[$-1] == '>')) s = s[0..$-1]; 3441 if (s.length > 1 && s[$-2..$] == "]]") s = s[0..$-2]; else break; 3442 } 3443 version(nanosvg_debug_styles) { import std.stdio; writeln("ctx: ", s.quote); } 3444 uint tokensAdded = 0; 3445 while (s.length) { 3446 if (s.length > 1 && s.ptr[0] == '/' && s.ptr[1] == '*') { 3447 // comment 3448 s = s[2..$]; 3449 while (s.length > 1 && !(s.ptr[0] == '*' && s.ptr[1] == '/')) s = s[1..$]; 3450 if (s.length <= 2) break; 3451 s = s[2..$]; 3452 continue; 3453 } else if (s.ptr[0] <= ' ') { 3454 while (s.length && s.ptr[0] <= ' ') s = s[1..$]; 3455 continue; 3456 } 3457 //version(nanosvg_debug_styles) { import std.stdio; writeln("::: ", s.quote); } 3458 if (s.ptr[0] == '{') { 3459 usize pos = 1; 3460 while (pos < s.length && s.ptr[pos] != '}') { 3461 if (s.length-pos > 1 && s.ptr[pos] == '/' && s.ptr[pos+1] == '*') { 3462 // skip comment 3463 pos += 2; 3464 while (s.length-pos > 1 && !(s.ptr[pos] == '*' && s.ptr[pos+1] == '/')) ++pos; 3465 if (s.length-pos <= 2) { pos = cast(uint)s.length; break; } 3466 pos += 2; 3467 } else { 3468 ++pos; 3469 } 3470 } 3471 version(nanosvg_debug_styles) { import std.stdio; writeln("*** style: ", s[1..pos].quote); } 3472 if (tokensAdded > 0) { 3473 foreach (immutable idx; p.styleCount-tokensAdded..p.styleCount) p.styles[idx].value = s[1..pos]; 3474 } 3475 tokensAdded = 0; 3476 if (s.length-pos < 1) break; 3477 s = s[pos+1..$]; 3478 } else { 3479 usize pos = 0; 3480 while (pos < s.length && s.ptr[pos] > ' ' && s.ptr[pos] != '{' && s.ptr[pos] != '/') ++pos; 3481 const(char)[] tk = s[0..pos]; 3482 version(nanosvg_debug_styles) { import std.stdio; writeln("token: ", tk.quote); } 3483 s = s[pos..$]; 3484 { 3485 import core.stdc.stdlib : realloc; 3486 import core.stdc.string : memset; 3487 p.styles = cast(typeof(p.styles))realloc(p.styles, p.styles[0].sizeof*(p.styleCount+1)); 3488 memset(p.styles+p.styleCount, 0, p.styles[0].sizeof); 3489 ++p.styleCount; 3490 } 3491 p.styles[p.styleCount-1].name = tk; 3492 ++tokensAdded; 3493 } 3494 } 3495 version(nanosvg_debug_styles) foreach (const ref st; p.styles[0..p.styleCount]) { import std.stdio; writeln("name: ", st.name.quote, "; value: ", st.value.quote); } 3496 } 3497 } 3498 3499 void nsvg__imageBounds (Parser* p, float* bounds) { 3500 NSVG.Shape* shape; 3501 shape = p.image.shapes; 3502 if (shape is null) { 3503 bounds[0..4] = 0.0; 3504 return; 3505 } 3506 bounds[0] = shape.bounds.ptr[0]; 3507 bounds[1] = shape.bounds.ptr[1]; 3508 bounds[2] = shape.bounds.ptr[2]; 3509 bounds[3] = shape.bounds.ptr[3]; 3510 for (shape = shape.next; shape !is null; shape = shape.next) { 3511 bounds[0] = nsvg__minf(bounds[0], shape.bounds.ptr[0]); 3512 bounds[1] = nsvg__minf(bounds[1], shape.bounds.ptr[1]); 3513 bounds[2] = nsvg__maxf(bounds[2], shape.bounds.ptr[2]); 3514 bounds[3] = nsvg__maxf(bounds[3], shape.bounds.ptr[3]); 3515 } 3516 } 3517 3518 float nsvg__viewAlign (float content, float container, int type) { 3519 if (type == NSVG_ALIGN_MIN) return 0; 3520 if (type == NSVG_ALIGN_MAX) return container-content; 3521 // mid 3522 return (container-content)*0.5f; 3523 } 3524 3525 void nsvg__scaleGradient (NSVG.Gradient* grad, float tx, float ty, float sx, float sy) { 3526 float[6] t = void; 3527 nsvg__xformSetTranslation(t.ptr, tx, ty); 3528 nsvg__xformMultiply(grad.xform.ptr, t.ptr); 3529 3530 nsvg__xformSetScale(t.ptr, sx, sy); 3531 nsvg__xformMultiply(grad.xform.ptr, t.ptr); 3532 } 3533 3534 void nsvg__scaleToViewbox (Parser* p, const(char)[] units) { 3535 NSVG.Shape* shape; 3536 NSVG.Path* path; 3537 float tx = void, ty = void, sx = void, sy = void, us = void, avgs = void; 3538 float[4] bounds = void; 3539 float[6] t = void; 3540 float* pt; 3541 3542 // Guess image size if not set completely. 3543 nsvg__imageBounds(p, bounds.ptr); 3544 3545 if (p.viewWidth == 0) { 3546 if (p.image.width > 0) { 3547 p.viewWidth = p.image.width; 3548 } else { 3549 p.viewMinx = bounds[0]; 3550 p.viewWidth = bounds[2]-bounds[0]; 3551 } 3552 } 3553 if (p.viewHeight == 0) { 3554 if (p.image.height > 0) { 3555 p.viewHeight = p.image.height; 3556 } else { 3557 p.viewMiny = bounds[1]; 3558 p.viewHeight = bounds[3]-bounds[1]; 3559 } 3560 } 3561 if (p.image.width == 0) 3562 p.image.width = p.viewWidth; 3563 if (p.image.height == 0) 3564 p.image.height = p.viewHeight; 3565 3566 tx = -p.viewMinx; 3567 ty = -p.viewMiny; 3568 sx = p.viewWidth > 0 ? p.image.width/p.viewWidth : 0; 3569 sy = p.viewHeight > 0 ? p.image.height/p.viewHeight : 0; 3570 // Unit scaling 3571 us = 1.0f/nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); 3572 3573 // Fix aspect ratio 3574 if (p.alignType == NSVG_ALIGN_MEET) { 3575 // fit whole image into viewbox 3576 sx = sy = nsvg__minf(sx, sy); 3577 tx += nsvg__viewAlign(p.viewWidth*sx, p.image.width, p.alignX)/sx; 3578 ty += nsvg__viewAlign(p.viewHeight*sy, p.image.height, p.alignY)/sy; 3579 } else if (p.alignType == NSVG_ALIGN_SLICE) { 3580 // fill whole viewbox with image 3581 sx = sy = nsvg__maxf(sx, sy); 3582 tx += nsvg__viewAlign(p.viewWidth*sx, p.image.width, p.alignX)/sx; 3583 ty += nsvg__viewAlign(p.viewHeight*sy, p.image.height, p.alignY)/sy; 3584 } 3585 3586 // Transform 3587 sx *= us; 3588 sy *= us; 3589 avgs = (sx+sy)/2.0f; 3590 for (shape = p.image.shapes; shape !is null; shape = shape.next) { 3591 shape.bounds.ptr[0] = (shape.bounds.ptr[0]+tx)*sx; 3592 shape.bounds.ptr[1] = (shape.bounds.ptr[1]+ty)*sy; 3593 shape.bounds.ptr[2] = (shape.bounds.ptr[2]+tx)*sx; 3594 shape.bounds.ptr[3] = (shape.bounds.ptr[3]+ty)*sy; 3595 for (path = shape.paths; path !is null; path = path.next) { 3596 path.bounds[0] = (path.bounds[0]+tx)*sx; 3597 path.bounds[1] = (path.bounds[1]+ty)*sy; 3598 path.bounds[2] = (path.bounds[2]+tx)*sx; 3599 path.bounds[3] = (path.bounds[3]+ty)*sy; 3600 for (int i = 0; i+3 <= path.nsflts; ) { 3601 int argc = 0; // pair of coords 3602 NSVG.Command cmd = cast(NSVG.Command)path.stream[i++]; 3603 final switch (cmd) { 3604 case NSVG.Command.MoveTo: argc = 1; break; 3605 case NSVG.Command.LineTo: argc = 1; break; 3606 case NSVG.Command.QuadTo: argc = 2; break; 3607 case NSVG.Command.BezierTo: argc = 3; break; 3608 } 3609 // scale points 3610 while (argc-- > 0) { 3611 path.stream[i+0] = (path.stream[i+0]+tx)*sx; 3612 path.stream[i+1] = (path.stream[i+1]+ty)*sy; 3613 i += 2; 3614 } 3615 } 3616 } 3617 3618 if (shape.fill.type == NSVG.PaintType.LinearGradient || shape.fill.type == NSVG.PaintType.RadialGradient) { 3619 nsvg__scaleGradient(shape.fill.gradient, tx, ty, sx, sy); 3620 //memcpy(t.ptr, shape.fill.gradient.xform.ptr, float.sizeof*6); 3621 t.ptr[0..6] = shape.fill.gradient.xform[0..6]; 3622 nsvg__xformInverse(shape.fill.gradient.xform.ptr, t.ptr); 3623 } 3624 if (shape.stroke.type == NSVG.PaintType.LinearGradient || shape.stroke.type == NSVG.PaintType.RadialGradient) { 3625 nsvg__scaleGradient(shape.stroke.gradient, tx, ty, sx, sy); 3626 //memcpy(t.ptr, shape.stroke.gradient.xform.ptr, float.sizeof*6); 3627 t.ptr[0..6] = shape.stroke.gradient.xform[0..6]; 3628 nsvg__xformInverse(shape.stroke.gradient.xform.ptr, t.ptr); 3629 } 3630 3631 shape.strokeWidth *= avgs; 3632 shape.strokeDashOffset *= avgs; 3633 foreach (immutable int i; 0..shape.strokeDashCount) shape.strokeDashArray[i] *= avgs; 3634 } 3635 } 3636 3637 /// 3638 public NSVG* nsvgParse (const(char)[] input, const(char)[] units="px", float dpi=96, int canvaswdt=-1, int canvashgt=-1) { 3639 Parser* p; 3640 NSVG* ret = null; 3641 3642 /* 3643 static if (NanoSVGHasVFS) { 3644 if (input.length > 4 && input[0..5] == "NSVG\x00" && units == "px" && dpi == 96) { 3645 return nsvgUnserialize(wrapStream(MemoryStreamRO(input))); 3646 } 3647 } 3648 */ 3649 3650 p = nsvg__createParser(); 3651 if (p is null) return null; 3652 p.dpi = dpi; 3653 p.canvaswdt = (canvaswdt < 1 ? NSVGDefaults.CanvasWidth : canvaswdt); 3654 p.canvashgt = (canvashgt < 1 ? NSVGDefaults.CanvasHeight : canvashgt); 3655 3656 nsvg__parseXML(input, &nsvg__startElement, &nsvg__endElement, &nsvg__content, p); 3657 3658 // Scale to viewBox 3659 nsvg__scaleToViewbox(p, units); 3660 3661 ret = p.image; 3662 p.image = null; 3663 3664 nsvg__deleteParser(p); 3665 3666 return ret; 3667 } 3668 3669 /// 3670 public void kill (NSVG* image) { 3671 import core.stdc.string : memset; 3672 NSVG.Shape* snext, shape; 3673 if (image is null) return; 3674 shape = image.shapes; 3675 while (shape !is null) { 3676 snext = shape.next; 3677 nsvg__deletePaths(shape.paths); 3678 nsvg__deletePaint(&shape.fill); 3679 nsvg__deletePaint(&shape.stroke); 3680 xfree(shape); 3681 shape = snext; 3682 } 3683 memset(image, 0, (*image).sizeof); 3684 xfree(image); 3685 } 3686 3687 } // nothrow @trusted @nogc 3688 3689 3690 /// 3691 public NSVG* nsvgParseFromFile (const(char)[] filename, const(char)[] units="px", float dpi=96, int canvaswdt=-1, int canvashgt=-1) nothrow { 3692 import core.stdc.stdlib : malloc, free; 3693 enum AddedBytes = 8; 3694 3695 char* data = null; 3696 scope(exit) if (data !is null) free(data); 3697 3698 if (filename.length == 0) return null; 3699 3700 try { 3701 static if (NanoSVGHasIVVFS) { 3702 auto fl = VFile(filename); 3703 auto size = fl.size; 3704 if (size > int.max/8 || size < 1) return null; 3705 data = cast(char*)malloc(cast(uint)size+AddedBytes); 3706 if (data is null) return null; 3707 data[0..cast(uint)size+AddedBytes] = 0; 3708 fl.rawReadExact(data[0..cast(uint)size]); 3709 fl.close(); 3710 } else { 3711 import core.stdc.stdio : FILE, fopen, fclose, fread, ftell, fseek; 3712 import std.internal.cstring : tempCString; 3713 auto fl = fopen(filename.tempCString, "rb"); 3714 if (fl is null) return null; 3715 scope(exit) fclose(fl); 3716 if (fseek(fl, 0, 2/*SEEK_END*/) != 0) return null; 3717 auto size = ftell(fl); 3718 if (fseek(fl, 0, 0/*SEEK_SET*/) != 0) return null; 3719 if (size < 16 || size > int.max/32) return null; 3720 data = cast(char*)malloc(cast(uint)size+AddedBytes); 3721 if (data is null) assert(0, "out of memory in NanoVega fontstash"); 3722 data[0..cast(uint)size+AddedBytes] = 0; 3723 char* dptr = data; 3724 auto left = cast(uint)size; 3725 while (left > 0) { 3726 auto rd = fread(dptr, 1, left, fl); 3727 if (rd == 0) return null; // unexpected EOF or reading error, it doesn't matter 3728 dptr += rd; 3729 left -= rd; 3730 } 3731 } 3732 return nsvgParse(data[0..cast(uint)size], units, dpi, canvaswdt, canvashgt); 3733 } catch (Exception e) { 3734 return null; 3735 } 3736 } 3737 3738 3739 static if (NanoSVGHasIVVFS) { 3740 /// 3741 public NSVG* nsvgParseFromFile(ST) (auto ref ST fi, const(char)[] units="px", float dpi=96, int canvaswdt=-1, int canvashgt=-1) nothrow 3742 if (isReadableStream!ST && isSeekableStream!ST && streamHasSize!ST) 3743 { 3744 import core.stdc.stdlib : malloc, free; 3745 3746 enum AddedBytes = 8; 3747 usize size; 3748 char* data = null; 3749 scope(exit) if (data is null) free(data); 3750 3751 try { 3752 auto sz = fi.size; 3753 auto pp = fi.tell; 3754 if (pp >= sz) return null; 3755 sz -= pp; 3756 if (sz > 0x3ff_ffff) return null; 3757 size = cast(usize)sz; 3758 data = cast(char*)malloc(size+AddedBytes); 3759 if (data is null) return null; 3760 scope(exit) free(data); 3761 data[0..size+AddedBytes] = 0; 3762 fi.rawReadExact(data[0..size]); 3763 return nsvgParse(data[0..size], units, dpi, canvaswdt, canvashgt); 3764 } catch (Exception e) { 3765 return null; 3766 } 3767 } 3768 } 3769 3770 3771 // ////////////////////////////////////////////////////////////////////////// // 3772 // rasterizer 3773 private: 3774 nothrow @trusted @nogc { 3775 3776 enum NSVG__SUBSAMPLES = 5; 3777 enum NSVG__FIXSHIFT = 10; 3778 enum NSVG__FIX = 1<<NSVG__FIXSHIFT; 3779 enum NSVG__FIXMASK = NSVG__FIX-1; 3780 enum NSVG__MEMPAGE_SIZE = 1024; 3781 3782 struct NSVGedge { 3783 float x0 = 0, y0 = 0, x1 = 0, y1 = 0; 3784 int dir = 0; 3785 NSVGedge* next; 3786 } 3787 3788 struct NSVGpoint { 3789 float x = 0, y = 0; 3790 float dx = 0, dy = 0; 3791 float len = 0; 3792 float dmx = 0, dmy = 0; 3793 ubyte flags = 0; 3794 } 3795 3796 struct NSVGactiveEdge { 3797 int x = 0, dx = 0; 3798 float ey = 0; 3799 int dir = 0; 3800 NSVGactiveEdge *next; 3801 } 3802 3803 struct NSVGmemPage { 3804 ubyte[NSVG__MEMPAGE_SIZE] mem; 3805 int size; 3806 NSVGmemPage* next; 3807 } 3808 3809 struct NSVGcachedPaint { 3810 char type; 3811 char spread; 3812 float[6] xform = 0; 3813 uint[256] colors; 3814 } 3815 3816 struct NSVGrasterizerS { 3817 float px = 0, py = 0; 3818 3819 float tessTol = 0; 3820 float distTol = 0; 3821 3822 NSVGedge* edges; 3823 int nedges; 3824 int cedges; 3825 3826 NSVGpoint* points; 3827 int npoints; 3828 int cpoints; 3829 3830 NSVGpoint* points2; 3831 int npoints2; 3832 int cpoints2; 3833 3834 NSVGactiveEdge* freelist; 3835 NSVGmemPage* pages; 3836 NSVGmemPage* curpage; 3837 3838 ubyte* scanline; 3839 int cscanline; 3840 3841 ubyte* bitmap; 3842 int width, height, stride; 3843 } 3844 3845 3846 /// 3847 public NSVGrasterizer nsvgCreateRasterizer () { 3848 NSVGrasterizer r = xalloc!NSVGrasterizerS; 3849 if (r is null) goto error; 3850 3851 r.tessTol = 0.25f; 3852 r.distTol = 0.01f; 3853 3854 return r; 3855 3856 error: 3857 r.kill(); 3858 return null; 3859 } 3860 3861 /// 3862 public void kill (NSVGrasterizer r) { 3863 NSVGmemPage* p; 3864 3865 if (r is null) return; 3866 3867 p = r.pages; 3868 while (p !is null) { 3869 NSVGmemPage* next = p.next; 3870 xfree(p); 3871 p = next; 3872 } 3873 3874 if (r.edges) xfree(r.edges); 3875 if (r.points) xfree(r.points); 3876 if (r.points2) xfree(r.points2); 3877 if (r.scanline) xfree(r.scanline); 3878 3879 xfree(r); 3880 } 3881 3882 NSVGmemPage* nsvg__nextPage (NSVGrasterizer r, NSVGmemPage* cur) { 3883 NSVGmemPage *newp; 3884 3885 // If using existing chain, return the next page in chain 3886 if (cur !is null && cur.next !is null) return cur.next; 3887 3888 // Alloc new page 3889 newp = xalloc!NSVGmemPage; 3890 if (newp is null) return null; 3891 3892 // Add to linked list 3893 if (cur !is null) 3894 cur.next = newp; 3895 else 3896 r.pages = newp; 3897 3898 return newp; 3899 } 3900 3901 void nsvg__resetPool (NSVGrasterizer r) { 3902 NSVGmemPage* p = r.pages; 3903 while (p !is null) { 3904 p.size = 0; 3905 p = p.next; 3906 } 3907 r.curpage = r.pages; 3908 } 3909 3910 ubyte* nsvg__alloc (NSVGrasterizer r, int size) { 3911 ubyte* buf; 3912 if (size > NSVG__MEMPAGE_SIZE) return null; 3913 if (r.curpage is null || r.curpage.size+size > NSVG__MEMPAGE_SIZE) { 3914 r.curpage = nsvg__nextPage(r, r.curpage); 3915 } 3916 buf = &r.curpage.mem[r.curpage.size]; 3917 r.curpage.size += size; 3918 return buf; 3919 } 3920 3921 int nsvg__ptEquals (float x1, float y1, float x2, float y2, float tol) { 3922 immutable float dx = x2-x1; 3923 immutable float dy = y2-y1; 3924 return dx*dx+dy*dy < tol*tol; 3925 } 3926 3927 void nsvg__addPathPoint (NSVGrasterizer r, float x, float y, int flags) { 3928 import core.stdc.stdlib : realloc; 3929 3930 NSVGpoint* pt; 3931 3932 if (r.npoints > 0) { 3933 pt = r.points+(r.npoints-1); 3934 if (nsvg__ptEquals(pt.x, pt.y, x, y, r.distTol)) { 3935 pt.flags |= flags; 3936 return; 3937 } 3938 } 3939 3940 if (r.npoints+1 > r.cpoints) { 3941 r.cpoints = (r.cpoints > 0 ? r.cpoints*2 : 64); 3942 r.points = cast(NSVGpoint*)realloc(r.points, NSVGpoint.sizeof*r.cpoints+256); 3943 if (r.points is null) assert(0, "nanosvg: out of memory"); 3944 } 3945 3946 pt = r.points+r.npoints; 3947 pt.x = x; 3948 pt.y = y; 3949 pt.flags = cast(ubyte)flags; 3950 ++r.npoints; 3951 } 3952 3953 void nsvg__appendPathPoint (NSVGrasterizer r, NSVGpoint pt) { 3954 import core.stdc.stdlib : realloc; 3955 if (r.npoints+1 > r.cpoints) { 3956 r.cpoints = (r.cpoints > 0 ? r.cpoints*2 : 64); 3957 r.points = cast(NSVGpoint*)realloc(r.points, NSVGpoint.sizeof*r.cpoints+256); 3958 if (r.points is null) assert(0, "nanosvg: out of memory"); 3959 } 3960 r.points[r.npoints] = pt; 3961 ++r.npoints; 3962 } 3963 3964 void nsvg__duplicatePoints (NSVGrasterizer r) { 3965 import core.stdc.stdlib : realloc; 3966 import core.stdc.string : memmove; 3967 if (r.npoints > r.cpoints2) { 3968 r.cpoints2 = r.npoints; 3969 r.points2 = cast(NSVGpoint*)realloc(r.points2, NSVGpoint.sizeof*r.cpoints2+256); 3970 if (r.points2 is null) assert(0, "nanosvg: out of memory"); 3971 } 3972 memmove(r.points2, r.points, NSVGpoint.sizeof*r.npoints); 3973 r.npoints2 = r.npoints; 3974 } 3975 3976 void nsvg__addEdge (NSVGrasterizer r, float x0, float y0, float x1, float y1) { 3977 NSVGedge* e; 3978 3979 // Skip horizontal edges 3980 if (y0 == y1) return; 3981 3982 if (r.nedges+1 > r.cedges) { 3983 import core.stdc.stdlib : realloc; 3984 r.cedges = (r.cedges > 0 ? r.cedges*2 : 64); 3985 r.edges = cast(NSVGedge*)realloc(r.edges, NSVGedge.sizeof*r.cedges+256); 3986 if (r.edges is null) assert(0, "nanosvg: out of memory"); 3987 } 3988 3989 e = &r.edges[r.nedges]; 3990 ++r.nedges; 3991 3992 if (y0 < y1) { 3993 e.x0 = x0; 3994 e.y0 = y0; 3995 e.x1 = x1; 3996 e.y1 = y1; 3997 e.dir = 1; 3998 } else { 3999 e.x0 = x1; 4000 e.y0 = y1; 4001 e.x1 = x0; 4002 e.y1 = y0; 4003 e.dir = -1; 4004 } 4005 } 4006 4007 float nsvg__normalize (float *x, float* y) { 4008 immutable float d = sqrtf((*x)*(*x)+(*y)*(*y)); 4009 if (d > 1e-6f) { 4010 float id = 1.0f/d; 4011 *x *= id; 4012 *y *= id; 4013 } 4014 return d; 4015 } 4016 4017 void nsvg__flattenCubicBez (NSVGrasterizer r, in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level, in int type) { 4018 if (level > 10) return; 4019 4020 // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case) 4021 version(none) { 4022 if (level == 0) { 4023 static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc { 4024 immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y); 4025 return (fabsf(cz*cz) <= 0.01f); 4026 } 4027 if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) { 4028 //{ import core.stdc.stdio; printf("AFD fallback!\n"); } 4029 nsvg__flattenCubicBezAFD(r, x1, y1, x2, y2, x3, y3, x4, y4, type); 4030 return; 4031 } 4032 } 4033 } 4034 4035 immutable x12 = (x1+x2)*0.5f; 4036 immutable y12 = (y1+y2)*0.5f; 4037 immutable x23 = (x2+x3)*0.5f; 4038 immutable y23 = (y2+y3)*0.5f; 4039 immutable x34 = (x3+x4)*0.5f; 4040 immutable y34 = (y3+y4)*0.5f; 4041 immutable x123 = (x12+x23)*0.5f; 4042 immutable y123 = (y12+y23)*0.5f; 4043 4044 immutable dx = x4-x1; 4045 immutable dy = y4-y1; 4046 immutable d2 = fabsf(((x2-x4)*dy-(y2-y4)*dx)); 4047 immutable d3 = fabsf(((x3-x4)*dy-(y3-y4)*dx)); 4048 4049 if ((d2+d3)*(d2+d3) < r.tessTol*(dx*dx+dy*dy)) { 4050 nsvg__addPathPoint(r, x4, y4, type); 4051 return; 4052 } 4053 4054 immutable x234 = (x23+x34)*0.5f; 4055 immutable y234 = (y23+y34)*0.5f; 4056 immutable x1234 = (x123+x234)*0.5f; 4057 immutable y1234 = (y123+y234)*0.5f; 4058 4059 // "taxicab" / "manhattan" check for flat curves 4060 if (fabsf(x1+x3-x2-x2)+fabsf(y1+y3-y2-y2)+fabsf(x2+x4-x3-x3)+fabsf(y2+y4-y3-y3) < r.tessTol/4) { 4061 nsvg__addPathPoint(r, x1234, y1234, type); 4062 return; 4063 } 4064 4065 nsvg__flattenCubicBez(r, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, 0); 4066 nsvg__flattenCubicBez(r, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, type); 4067 } 4068 4069 // Adaptive forward differencing for bezier tesselation. 4070 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt. 4071 // "Adaptive forward differencing for rendering curves and surfaces." 4072 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987. 4073 // original code by Taylor Holliday <taylor@audulus.com> 4074 void nsvg__flattenCubicBezAFD (NSVGrasterizer r, in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int type) nothrow @trusted @nogc { 4075 enum AFD_ONE = (1<<10); 4076 4077 // power basis 4078 immutable float ax = -x1+3*x2-3*x3+x4; 4079 immutable float ay = -y1+3*y2-3*y3+y4; 4080 immutable float bx = 3*x1-6*x2+3*x3; 4081 immutable float by = 3*y1-6*y2+3*y3; 4082 immutable float cx = -3*x1+3*x2; 4083 immutable float cy = -3*y1+3*y2; 4084 4085 // Transform to forward difference basis (stepsize 1) 4086 float px = x1; 4087 float py = y1; 4088 float dx = ax+bx+cx; 4089 float dy = ay+by+cy; 4090 float ddx = 6*ax+2*bx; 4091 float ddy = 6*ay+2*by; 4092 float dddx = 6*ax; 4093 float dddy = 6*ay; 4094 4095 //printf("dx: %f, dy: %f\n", dx, dy); 4096 //printf("ddx: %f, ddy: %f\n", ddx, ddy); 4097 //printf("dddx: %f, dddy: %f\n", dddx, dddy); 4098 4099 int t = 0; 4100 int dt = AFD_ONE; 4101 4102 immutable float tol = r.tessTol*4; 4103 4104 while (t < AFD_ONE) { 4105 // Flatness measure. 4106 float d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy; 4107 4108 // printf("d: %f, th: %f\n", d, th); 4109 4110 // Go to higher resolution if we're moving a lot or overshooting the end. 4111 while ((d > tol && dt > 1) || (t+dt > AFD_ONE)) { 4112 // printf("up\n"); 4113 4114 // Apply L to the curve. Increase curve resolution. 4115 dx = 0.5f*dx-(1.0f/8.0f)*ddx+(1.0f/16.0f)*dddx; 4116 dy = 0.5f*dy-(1.0f/8.0f)*ddy+(1.0f/16.0f)*dddy; 4117 ddx = (1.0f/4.0f)*ddx-(1.0f/8.0f)*dddx; 4118 ddy = (1.0f/4.0f)*ddy-(1.0f/8.0f)*dddy; 4119 dddx = (1.0f/8.0f)*dddx; 4120 dddy = (1.0f/8.0f)*dddy; 4121 4122 // Half the stepsize. 4123 dt >>= 1; 4124 4125 // Recompute d 4126 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy; 4127 } 4128 4129 // Go to lower resolution if we're really flat 4130 // and we aren't going to overshoot the end. 4131 // XXX: tol/32 is just a guess for when we are too flat. 4132 while ((d > 0 && d < tol/32.0f && dt < AFD_ONE) && (t+2*dt <= AFD_ONE)) { 4133 // printf("down\n"); 4134 4135 // Apply L^(-1) to the curve. Decrease curve resolution. 4136 dx = 2*dx+ddx; 4137 dy = 2*dy+ddy; 4138 ddx = 4*ddx+4*dddx; 4139 ddy = 4*ddy+4*dddy; 4140 dddx = 8*dddx; 4141 dddy = 8*dddy; 4142 4143 // Double the stepsize. 4144 dt <<= 1; 4145 4146 // Recompute d 4147 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy; 4148 } 4149 4150 // Forward differencing. 4151 px += dx; 4152 py += dy; 4153 dx += ddx; 4154 dy += ddy; 4155 ddx += dddx; 4156 ddy += dddy; 4157 4158 // Output a point. 4159 nsvg__addPathPoint(r, px, py, (t > 0 ? type : 0)); 4160 4161 // Advance along the curve. 4162 t += dt; 4163 4164 // Ensure we don't overshoot. 4165 assert(t <= AFD_ONE); 4166 } 4167 } 4168 4169 void nsvg__flattenShape (NSVGrasterizer r, const(NSVG.Shape)* shape, float scale) { 4170 for (const(NSVG.Path)* path = shape.paths; path !is null; path = path.next) { 4171 r.npoints = 0; 4172 if (path.empty) continue; 4173 // first point 4174 float x0, y0; 4175 path.startPoint(&x0, &y0); 4176 nsvg__addPathPoint(r, x0*scale, y0*scale, 0); 4177 // cubic beziers 4178 path.asCubics(delegate (const(float)[] cubic) { 4179 assert(cubic.length >= 8); 4180 nsvg__flattenCubicBez(r, 4181 cubic.ptr[0]*scale, cubic.ptr[1]*scale, 4182 cubic.ptr[2]*scale, cubic.ptr[3]*scale, 4183 cubic.ptr[4]*scale, cubic.ptr[5]*scale, 4184 cubic.ptr[6]*scale, cubic.ptr[7]*scale, 4185 0, 0); 4186 }); 4187 // close path 4188 nsvg__addPathPoint(r, x0*scale, y0*scale, 0); 4189 // Flatten path 4190 /+ 4191 nsvg__addPathPoint(r, path.pts[0]*scale, path.pts[1]*scale, 0); 4192 for (int i = 0; i < path.npts-1; i += 3) { 4193 const(float)* p = path.pts+(i*2); 4194 nsvg__flattenCubicBez(r, p[0]*scale, p[1]*scale, p[2]*scale, p[3]*scale, p[4]*scale, p[5]*scale, p[6]*scale, p[7]*scale, 0, 0); 4195 } 4196 // Close path 4197 nsvg__addPathPoint(r, path.pts[0]*scale, path.pts[1]*scale, 0); 4198 +/ 4199 // Build edges 4200 for (int i = 0, j = r.npoints-1; i < r.npoints; j = i++) { 4201 nsvg__addEdge(r, r.points[j].x, r.points[j].y, r.points[i].x, r.points[i].y); 4202 } 4203 } 4204 } 4205 4206 alias PtFlags = ubyte; 4207 enum : ubyte { 4208 PtFlagsCorner = 0x01, 4209 PtFlagsBevel = 0x02, 4210 PtFlagsLeft = 0x04, 4211 } 4212 4213 void nsvg__initClosed (NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p0, const(NSVGpoint)* p1, float lineWidth) { 4214 immutable float w = lineWidth*0.5f; 4215 float dx = p1.x-p0.x; 4216 float dy = p1.y-p0.y; 4217 immutable float len = nsvg__normalize(&dx, &dy); 4218 immutable float px = p0.x+dx*len*0.5f, py = p0.y+dy*len*0.5f; 4219 immutable float dlx = dy, dly = -dx; 4220 immutable float lx = px-dlx*w, ly = py-dly*w; 4221 immutable float rx = px+dlx*w, ry = py+dly*w; 4222 left.x = lx; left.y = ly; 4223 right.x = rx; right.y = ry; 4224 } 4225 4226 void nsvg__buttCap (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p, float dx, float dy, float lineWidth, int connect) { 4227 immutable float w = lineWidth*0.5f; 4228 immutable float px = p.x, py = p.y; 4229 immutable float dlx = dy, dly = -dx; 4230 immutable float lx = px-dlx*w, ly = py-dly*w; 4231 immutable float rx = px+dlx*w, ry = py+dly*w; 4232 4233 nsvg__addEdge(r, lx, ly, rx, ry); 4234 4235 if (connect) { 4236 nsvg__addEdge(r, left.x, left.y, lx, ly); 4237 nsvg__addEdge(r, rx, ry, right.x, right.y); 4238 } 4239 left.x = lx; left.y = ly; 4240 right.x = rx; right.y = ry; 4241 } 4242 4243 void nsvg__squareCap (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p, float dx, float dy, float lineWidth, int connect) { 4244 immutable float w = lineWidth*0.5f; 4245 immutable float px = p.x-dx*w, py = p.y-dy*w; 4246 immutable float dlx = dy, dly = -dx; 4247 immutable float lx = px-dlx*w, ly = py-dly*w; 4248 immutable float rx = px+dlx*w, ry = py+dly*w; 4249 4250 nsvg__addEdge(r, lx, ly, rx, ry); 4251 4252 if (connect) { 4253 nsvg__addEdge(r, left.x, left.y, lx, ly); 4254 nsvg__addEdge(r, rx, ry, right.x, right.y); 4255 } 4256 left.x = lx; left.y = ly; 4257 right.x = rx; right.y = ry; 4258 } 4259 4260 void nsvg__roundCap (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p, float dx, float dy, float lineWidth, int ncap, int connect) { 4261 immutable float w = lineWidth*0.5f; 4262 immutable float px = p.x, py = p.y; 4263 immutable float dlx = dy, dly = -dx; 4264 float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; 4265 4266 foreach (int i; 0..ncap) { 4267 immutable float a = i/cast(float)(ncap-1)*NSVG_PI; 4268 immutable float ax = cosf(a)*w, ay = sinf(a)*w; 4269 immutable float x = px-dlx*ax-dx*ay; 4270 immutable float y = py-dly*ax-dy*ay; 4271 4272 if (i > 0) nsvg__addEdge(r, prevx, prevy, x, y); 4273 4274 prevx = x; 4275 prevy = y; 4276 4277 if (i == 0) { 4278 lx = x; 4279 ly = y; 4280 } else if (i == ncap-1) { 4281 rx = x; 4282 ry = y; 4283 } 4284 } 4285 4286 if (connect) { 4287 nsvg__addEdge(r, left.x, left.y, lx, ly); 4288 nsvg__addEdge(r, rx, ry, right.x, right.y); 4289 } 4290 4291 left.x = lx; left.y = ly; 4292 right.x = rx; right.y = ry; 4293 } 4294 4295 void nsvg__bevelJoin (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p0, const(NSVGpoint)* p1, float lineWidth) { 4296 immutable float w = lineWidth*0.5f; 4297 immutable float dlx0 = p0.dy, dly0 = -p0.dx; 4298 immutable float dlx1 = p1.dy, dly1 = -p1.dx; 4299 immutable float lx0 = p1.x-(dlx0*w), ly0 = p1.y-(dly0*w); 4300 immutable float rx0 = p1.x+(dlx0*w), ry0 = p1.y+(dly0*w); 4301 immutable float lx1 = p1.x-(dlx1*w), ly1 = p1.y-(dly1*w); 4302 immutable float rx1 = p1.x+(dlx1*w), ry1 = p1.y+(dly1*w); 4303 4304 nsvg__addEdge(r, lx0, ly0, left.x, left.y); 4305 nsvg__addEdge(r, lx1, ly1, lx0, ly0); 4306 4307 nsvg__addEdge(r, right.x, right.y, rx0, ry0); 4308 nsvg__addEdge(r, rx0, ry0, rx1, ry1); 4309 4310 left.x = lx1; left.y = ly1; 4311 right.x = rx1; right.y = ry1; 4312 } 4313 4314 void nsvg__miterJoin (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p0, const(NSVGpoint)* p1, float lineWidth) { 4315 immutable float w = lineWidth*0.5f; 4316 immutable float dlx0 = p0.dy, dly0 = -p0.dx; 4317 immutable float dlx1 = p1.dy, dly1 = -p1.dx; 4318 float lx0 = void, rx0 = void, lx1 = void, rx1 = void; 4319 float ly0 = void, ry0 = void, ly1 = void, ry1 = void; 4320 4321 if (p1.flags&PtFlagsLeft) { 4322 lx0 = lx1 = p1.x-p1.dmx*w; 4323 ly0 = ly1 = p1.y-p1.dmy*w; 4324 nsvg__addEdge(r, lx1, ly1, left.x, left.y); 4325 4326 rx0 = p1.x+(dlx0*w); 4327 ry0 = p1.y+(dly0*w); 4328 rx1 = p1.x+(dlx1*w); 4329 ry1 = p1.y+(dly1*w); 4330 nsvg__addEdge(r, right.x, right.y, rx0, ry0); 4331 nsvg__addEdge(r, rx0, ry0, rx1, ry1); 4332 } else { 4333 lx0 = p1.x-(dlx0*w); 4334 ly0 = p1.y-(dly0*w); 4335 lx1 = p1.x-(dlx1*w); 4336 ly1 = p1.y-(dly1*w); 4337 nsvg__addEdge(r, lx0, ly0, left.x, left.y); 4338 nsvg__addEdge(r, lx1, ly1, lx0, ly0); 4339 4340 rx0 = rx1 = p1.x+p1.dmx*w; 4341 ry0 = ry1 = p1.y+p1.dmy*w; 4342 nsvg__addEdge(r, right.x, right.y, rx1, ry1); 4343 } 4344 4345 left.x = lx1; left.y = ly1; 4346 right.x = rx1; right.y = ry1; 4347 } 4348 4349 void nsvg__roundJoin (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p0, const(NSVGpoint)* p1, float lineWidth, int ncap) { 4350 int i, n; 4351 float w = lineWidth*0.5f; 4352 float dlx0 = p0.dy, dly0 = -p0.dx; 4353 float dlx1 = p1.dy, dly1 = -p1.dx; 4354 float a0 = atan2f(dly0, dlx0); 4355 float a1 = atan2f(dly1, dlx1); 4356 float da = a1-a0; 4357 float lx, ly, rx, ry; 4358 4359 if (da < NSVG_PI) da += NSVG_PI*2; 4360 if (da > NSVG_PI) da -= NSVG_PI*2; 4361 4362 n = cast(int)ceilf((fabsf(da)/NSVG_PI)*ncap); 4363 if (n < 2) n = 2; 4364 if (n > ncap) n = ncap; 4365 4366 lx = left.x; 4367 ly = left.y; 4368 rx = right.x; 4369 ry = right.y; 4370 4371 for (i = 0; i < n; i++) { 4372 float u = i/cast(float)(n-1); 4373 float a = a0+u*da; 4374 float ax = cosf(a)*w, ay = sinf(a)*w; 4375 float lx1 = p1.x-ax, ly1 = p1.y-ay; 4376 float rx1 = p1.x+ax, ry1 = p1.y+ay; 4377 4378 nsvg__addEdge(r, lx1, ly1, lx, ly); 4379 nsvg__addEdge(r, rx, ry, rx1, ry1); 4380 4381 lx = lx1; ly = ly1; 4382 rx = rx1; ry = ry1; 4383 } 4384 4385 left.x = lx; left.y = ly; 4386 right.x = rx; right.y = ry; 4387 } 4388 4389 void nsvg__straightJoin (NSVGrasterizer r, NSVGpoint* left, NSVGpoint* right, const(NSVGpoint)* p1, float lineWidth) { 4390 float w = lineWidth*0.5f; 4391 float lx = p1.x-(p1.dmx*w), ly = p1.y-(p1.dmy*w); 4392 float rx = p1.x+(p1.dmx*w), ry = p1.y+(p1.dmy*w); 4393 4394 nsvg__addEdge(r, lx, ly, left.x, left.y); 4395 nsvg__addEdge(r, right.x, right.y, rx, ry); 4396 4397 left.x = lx; left.y = ly; 4398 right.x = rx; right.y = ry; 4399 } 4400 4401 int nsvg__curveDivs (float r, float arc, float tol) { 4402 float da = acosf(r/(r+tol))*2.0f; 4403 int divs = cast(int)ceilf(arc/da); 4404 if (divs < 2) divs = 2; 4405 return divs; 4406 } 4407 4408 void nsvg__expandStroke (NSVGrasterizer r, const(NSVGpoint)* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) { 4409 int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r.tessTol); // Calculate divisions per half circle. 4410 //NSVGpoint left = {0, 0, 0, 0, 0, 0, 0, 0}, right = {0, 0, 0, 0, 0, 0, 0, 0}, firstLeft = {0, 0, 0, 0, 0, 0, 0, 0}, firstRight = {0, 0, 0, 0, 0, 0, 0, 0}; 4411 NSVGpoint left, right, firstLeft, firstRight; 4412 const(NSVGpoint)* p0, p1; 4413 int j, s, e; 4414 4415 // Build stroke edges 4416 if (closed) { 4417 // Looping 4418 p0 = &points[npoints-1]; 4419 p1 = &points[0]; 4420 s = 0; 4421 e = npoints; 4422 } else { 4423 // Add cap 4424 p0 = &points[0]; 4425 p1 = &points[1]; 4426 s = 1; 4427 e = npoints-1; 4428 } 4429 4430 if (closed) { 4431 nsvg__initClosed(&left, &right, p0, p1, lineWidth); 4432 firstLeft = left; 4433 firstRight = right; 4434 } else { 4435 // Add cap 4436 float dx = p1.x-p0.x; 4437 float dy = p1.y-p0.y; 4438 nsvg__normalize(&dx, &dy); 4439 if (lineCap == NSVG.LineCap.Butt) 4440 nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); 4441 else if (lineCap == NSVG.LineCap.Square) 4442 nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); 4443 else if (lineCap == NSVG.LineCap.Round) 4444 nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); 4445 } 4446 4447 for (j = s; j < e; ++j) { 4448 if (p1.flags&PtFlagsCorner) { 4449 if (lineJoin == NSVG.LineJoin.Round) 4450 nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); 4451 else if (lineJoin == NSVG.LineJoin.Bevel || (p1.flags&PtFlagsBevel)) 4452 nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); 4453 else 4454 nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); 4455 } else { 4456 nsvg__straightJoin(r, &left, &right, p1, lineWidth); 4457 } 4458 p0 = p1++; 4459 } 4460 4461 if (closed) { 4462 // Loop it 4463 nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); 4464 nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); 4465 } else { 4466 // Add cap 4467 float dx = p1.x-p0.x; 4468 float dy = p1.y-p0.y; 4469 nsvg__normalize(&dx, &dy); 4470 if (lineCap == NSVG.LineCap.Butt) 4471 nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); 4472 else if (lineCap == NSVG.LineCap.Square) 4473 nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); 4474 else if (lineCap == NSVG.LineCap.Round) 4475 nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); 4476 } 4477 } 4478 4479 void nsvg__prepareStroke (NSVGrasterizer r, float miterLimit, int lineJoin) { 4480 int i, j; 4481 NSVGpoint* p0, p1; 4482 4483 p0 = r.points+(r.npoints-1); 4484 p1 = r.points; 4485 for (i = 0; i < r.npoints; i++) { 4486 // Calculate segment direction and length 4487 p0.dx = p1.x-p0.x; 4488 p0.dy = p1.y-p0.y; 4489 p0.len = nsvg__normalize(&p0.dx, &p0.dy); 4490 // Advance 4491 p0 = p1++; 4492 } 4493 4494 // calculate joins 4495 p0 = r.points+(r.npoints-1); 4496 p1 = r.points; 4497 for (j = 0; j < r.npoints; j++) { 4498 float dlx0, dly0, dlx1, dly1, dmr2, cross; 4499 dlx0 = p0.dy; 4500 dly0 = -p0.dx; 4501 dlx1 = p1.dy; 4502 dly1 = -p1.dx; 4503 // Calculate extrusions 4504 p1.dmx = (dlx0+dlx1)*0.5f; 4505 p1.dmy = (dly0+dly1)*0.5f; 4506 dmr2 = p1.dmx*p1.dmx+p1.dmy*p1.dmy; 4507 if (dmr2 > 0.000001f) { 4508 float s2 = 1.0f/dmr2; 4509 if (s2 > 600.0f) { 4510 s2 = 600.0f; 4511 } 4512 p1.dmx *= s2; 4513 p1.dmy *= s2; 4514 } 4515 4516 // Clear flags, but keep the corner. 4517 p1.flags = (p1.flags&PtFlagsCorner) ? PtFlagsCorner : 0; 4518 4519 // Keep track of left turns. 4520 cross = p1.dx*p0.dy-p0.dx*p1.dy; 4521 if (cross > 0.0f) 4522 p1.flags |= PtFlagsLeft; 4523 4524 // Check to see if the corner needs to be beveled. 4525 if (p1.flags&PtFlagsCorner) { 4526 if ((dmr2*miterLimit*miterLimit) < 1.0f || lineJoin == NSVG.LineJoin.Bevel || lineJoin == NSVG.LineJoin.Round) { 4527 p1.flags |= PtFlagsBevel; 4528 } 4529 } 4530 4531 p0 = p1++; 4532 } 4533 } 4534 4535 void nsvg__flattenShapeStroke (NSVGrasterizer r, const(NSVG.Shape)* shape, float scale) { 4536 int i, j, closed; 4537 const(NSVG.Path)* path; 4538 const(NSVGpoint)* p0, p1; 4539 float miterLimit = shape.miterLimit; 4540 int lineJoin = shape.strokeLineJoin; 4541 int lineCap = shape.strokeLineCap; 4542 float lineWidth = shape.strokeWidth*scale; 4543 4544 for (path = shape.paths; path !is null; path = path.next) { 4545 // Flatten path 4546 r.npoints = 0; 4547 if (!path.empty) { 4548 // first point 4549 { 4550 float x0, y0; 4551 path.startPoint(&x0, &y0); 4552 nsvg__addPathPoint(r, x0*scale, y0*scale, PtFlagsCorner); 4553 } 4554 // cubic beziers 4555 path.asCubics(delegate (const(float)[] cubic) { 4556 assert(cubic.length >= 8); 4557 nsvg__flattenCubicBez(r, 4558 cubic.ptr[0]*scale, cubic.ptr[1]*scale, 4559 cubic.ptr[2]*scale, cubic.ptr[3]*scale, 4560 cubic.ptr[4]*scale, cubic.ptr[5]*scale, 4561 cubic.ptr[6]*scale, cubic.ptr[7]*scale, 4562 0, PtFlagsCorner); 4563 }); 4564 } 4565 /+ 4566 nsvg__addPathPoint(r, path.pts[0]*scale, path.pts[1]*scale, PtFlagsCorner); 4567 for (i = 0; i < path.npts-1; i += 3) { 4568 const(float)* p = &path.pts[i*2]; 4569 nsvg__flattenCubicBez(r, p[0]*scale, p[1]*scale, p[2]*scale, p[3]*scale, p[4]*scale, p[5]*scale, p[6]*scale, p[7]*scale, 0, PtFlagsCorner); 4570 } 4571 +/ 4572 if (r.npoints < 2) continue; 4573 4574 closed = path.closed; 4575 4576 // If the first and last points are the same, remove the last, mark as closed path. 4577 p0 = &r.points[r.npoints-1]; 4578 p1 = &r.points[0]; 4579 if (nsvg__ptEquals(p0.x, p0.y, p1.x, p1.y, r.distTol)) { 4580 r.npoints--; 4581 p0 = &r.points[r.npoints-1]; 4582 closed = 1; 4583 } 4584 4585 if (shape.strokeDashCount > 0) { 4586 int idash = 0, dashState = 1; 4587 float totalDist = 0, dashLen, allDashLen, dashOffset; 4588 NSVGpoint cur; 4589 4590 if (closed) nsvg__appendPathPoint(r, r.points[0]); 4591 4592 // Duplicate points . points2. 4593 nsvg__duplicatePoints(r); 4594 4595 r.npoints = 0; 4596 cur = r.points2[0]; 4597 nsvg__appendPathPoint(r, cur); 4598 4599 // Figure out dash offset. 4600 allDashLen = 0; 4601 for (j = 0; j < shape.strokeDashCount; j++) allDashLen += shape.strokeDashArray[j]; 4602 if (shape.strokeDashCount&1) allDashLen *= 2.0f; 4603 // Find location inside pattern 4604 dashOffset = fmodf(shape.strokeDashOffset, allDashLen); 4605 if (dashOffset < 0.0f) dashOffset += allDashLen; 4606 4607 while (dashOffset > shape.strokeDashArray[idash]) { 4608 dashOffset -= shape.strokeDashArray[idash]; 4609 idash = (idash+1)%shape.strokeDashCount; 4610 } 4611 dashLen = (shape.strokeDashArray[idash]-dashOffset)*scale; 4612 4613 for (j = 1; j < r.npoints2; ) { 4614 float dx = r.points2[j].x-cur.x; 4615 float dy = r.points2[j].y-cur.y; 4616 float dist = sqrtf(dx*dx+dy*dy); 4617 4618 if (totalDist+dist > dashLen) { 4619 // Calculate intermediate point 4620 float d = (dashLen-totalDist)/dist; 4621 float x = cur.x+dx*d; 4622 float y = cur.y+dy*d; 4623 nsvg__addPathPoint(r, x, y, PtFlagsCorner); 4624 4625 // Stroke 4626 if (r.npoints > 1 && dashState) { 4627 nsvg__prepareStroke(r, miterLimit, lineJoin); 4628 nsvg__expandStroke(r, r.points, r.npoints, 0, lineJoin, lineCap, lineWidth); 4629 } 4630 // Advance dash pattern 4631 dashState = !dashState; 4632 idash = (idash+1)%shape.strokeDashCount; 4633 dashLen = shape.strokeDashArray[idash]*scale; 4634 // Restart 4635 cur.x = x; 4636 cur.y = y; 4637 cur.flags = PtFlagsCorner; 4638 totalDist = 0.0f; 4639 r.npoints = 0; 4640 nsvg__appendPathPoint(r, cur); 4641 } else { 4642 totalDist += dist; 4643 cur = r.points2[j]; 4644 nsvg__appendPathPoint(r, cur); 4645 j++; 4646 } 4647 } 4648 // Stroke any leftover path 4649 if (r.npoints > 1 && dashState) nsvg__expandStroke(r, r.points, r.npoints, 0, lineJoin, lineCap, lineWidth); 4650 } else { 4651 nsvg__prepareStroke(r, miterLimit, lineJoin); 4652 nsvg__expandStroke(r, r.points, r.npoints, closed, lineJoin, lineCap, lineWidth); 4653 } 4654 } 4655 } 4656 4657 extern(C) int nsvg__cmpEdge (in void *p, in void *q) nothrow @trusted @nogc { 4658 NSVGedge* a = cast(NSVGedge*)p; 4659 NSVGedge* b = cast(NSVGedge*)q; 4660 if (a.y0 < b.y0) return -1; 4661 if (a.y0 > b.y0) return 1; 4662 return 0; 4663 } 4664 4665 4666 static NSVGactiveEdge* nsvg__addActive (NSVGrasterizer r, const(NSVGedge)* e, float startPoint) { 4667 NSVGactiveEdge* z; 4668 4669 if (r.freelist !is null) { 4670 // Restore from freelist. 4671 z = r.freelist; 4672 r.freelist = z.next; 4673 } else { 4674 // Alloc new edge. 4675 z = cast(NSVGactiveEdge*)nsvg__alloc(r, NSVGactiveEdge.sizeof); 4676 if (z is null) return null; 4677 } 4678 4679 immutable float dxdy = (e.x1-e.x0)/(e.y1-e.y0); 4680 //STBTT_assert(e.y0 <= start_point); 4681 // round dx down to avoid going too far 4682 if (dxdy < 0) 4683 z.dx = cast(int)(-floorf(NSVG__FIX*-dxdy)); 4684 else 4685 z.dx = cast(int)floorf(NSVG__FIX*dxdy); 4686 z.x = cast(int)floorf(NSVG__FIX*(e.x0+dxdy*(startPoint-e.y0))); 4687 //z.x -= off_x*FIX; 4688 z.ey = e.y1; 4689 z.next = null; 4690 z.dir = e.dir; 4691 4692 return z; 4693 } 4694 4695 void nsvg__freeActive (NSVGrasterizer r, NSVGactiveEdge* z) { 4696 z.next = r.freelist; 4697 r.freelist = z; 4698 } 4699 4700 void nsvg__fillScanline (ubyte* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) { 4701 int i = x0>>NSVG__FIXSHIFT; 4702 int j = x1>>NSVG__FIXSHIFT; 4703 if (i < *xmin) *xmin = i; 4704 if (j > *xmax) *xmax = j; 4705 if (i < len && j >= 0) { 4706 if (i == j) { 4707 // x0, x1 are the same pixel, so compute combined coverage 4708 scanline[i] += cast(ubyte)((x1-x0)*maxWeight>>NSVG__FIXSHIFT); 4709 } else { 4710 if (i >= 0) // add antialiasing for x0 4711 scanline[i] += cast(ubyte)(((NSVG__FIX-(x0&NSVG__FIXMASK))*maxWeight)>>NSVG__FIXSHIFT); 4712 else 4713 i = -1; // clip 4714 4715 if (j < len) // add antialiasing for x1 4716 scanline[j] += cast(ubyte)(((x1&NSVG__FIXMASK)*maxWeight)>>NSVG__FIXSHIFT); 4717 else 4718 j = len; // clip 4719 4720 for (++i; i < j; ++i) // fill pixels between x0 and x1 4721 scanline[i] += cast(ubyte)maxWeight; 4722 } 4723 } 4724 } 4725 4726 // note: this routine clips fills that extend off the edges... ideally this 4727 // wouldn't happen, but it could happen if the truetype glyph bounding boxes 4728 // are wrong, or if the user supplies a too-small bitmap 4729 void nsvg__fillActiveEdges (ubyte* scanline, int len, const(NSVGactiveEdge)* e, int maxWeight, int* xmin, int* xmax, char fillRule) { 4730 // non-zero winding fill 4731 int x0 = 0, w = 0; 4732 if (fillRule == NSVG.FillRule.NonZero) { 4733 // Non-zero 4734 while (e !is null) { 4735 if (w == 0) { 4736 // if we're currently at zero, we need to record the edge start point 4737 x0 = e.x; w += e.dir; 4738 } else { 4739 int x1 = e.x; w += e.dir; 4740 // if we went to zero, we need to draw 4741 if (w == 0) nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); 4742 } 4743 e = e.next; 4744 } 4745 } else if (fillRule == NSVG.FillRule.EvenOdd) { 4746 // Even-odd 4747 while (e !is null) { 4748 if (w == 0) { 4749 // if we're currently at zero, we need to record the edge start point 4750 x0 = e.x; w = 1; 4751 } else { 4752 int x1 = e.x; w = 0; 4753 nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); 4754 } 4755 e = e.next; 4756 } 4757 } 4758 } 4759 4760 float nsvg__clampf() (float a, float mn, float mx) { pragma(inline, true); return (a < mn ? mn : (a > mx ? mx : a)); } 4761 4762 uint nsvg__RGBA() (ubyte r, ubyte g, ubyte b, ubyte a) { pragma(inline, true); return (r)|(g<<8)|(b<<16)|(a<<24); } 4763 4764 uint nsvg__lerpRGBA (uint c0, uint c1, float u) { 4765 int iu = cast(int)(nsvg__clampf(u, 0.0f, 1.0f)*256.0f); 4766 int r = (((c0)&0xff)*(256-iu)+(((c1)&0xff)*iu))>>8; 4767 int g = (((c0>>8)&0xff)*(256-iu)+(((c1>>8)&0xff)*iu))>>8; 4768 int b = (((c0>>16)&0xff)*(256-iu)+(((c1>>16)&0xff)*iu))>>8; 4769 int a = (((c0>>24)&0xff)*(256-iu)+(((c1>>24)&0xff)*iu))>>8; 4770 return nsvg__RGBA(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b, cast(ubyte)a); 4771 } 4772 4773 uint nsvg__applyOpacity (uint c, float u) { 4774 int iu = cast(int)(nsvg__clampf(u, 0.0f, 1.0f)*256.0f); 4775 int r = (c)&0xff; 4776 int g = (c>>8)&0xff; 4777 int b = (c>>16)&0xff; 4778 int a = (((c>>24)&0xff)*iu)>>8; 4779 return nsvg__RGBA(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b, cast(ubyte)a); 4780 } 4781 4782 int nsvg__div255() (int x) { pragma(inline, true); return ((x+1)*257)>>16; } 4783 4784 void nsvg__scanlineSolid (ubyte* dst, int count, ubyte* cover, int x, int y, float tx, float ty, float scale, const(NSVGcachedPaint)* cache) { 4785 if (cache.type == NSVG.PaintType.Color) { 4786 int cr = cache.colors[0]&0xff; 4787 int cg = (cache.colors[0]>>8)&0xff; 4788 int cb = (cache.colors[0]>>16)&0xff; 4789 int ca = (cache.colors[0]>>24)&0xff; 4790 4791 foreach (int i; 0..count) { 4792 int r, g, b; 4793 int a = nsvg__div255(cast(int)cover[0]*ca); 4794 int ia = 255-a; 4795 // Premultiply 4796 r = nsvg__div255(cr*a); 4797 g = nsvg__div255(cg*a); 4798 b = nsvg__div255(cb*a); 4799 4800 // Blend over 4801 r += nsvg__div255(ia*cast(int)dst[0]); 4802 g += nsvg__div255(ia*cast(int)dst[1]); 4803 b += nsvg__div255(ia*cast(int)dst[2]); 4804 a += nsvg__div255(ia*cast(int)dst[3]); 4805 4806 dst[0] = cast(ubyte)r; 4807 dst[1] = cast(ubyte)g; 4808 dst[2] = cast(ubyte)b; 4809 dst[3] = cast(ubyte)a; 4810 4811 ++cover; 4812 dst += 4; 4813 } 4814 } else if (cache.type == NSVG.PaintType.LinearGradient) { 4815 // TODO: spread modes. 4816 // TODO: plenty of opportunities to optimize. 4817 const(float)* t = cache.xform.ptr; 4818 //int i, cr, cg, cb, ca; 4819 //uint c; 4820 4821 float fx = (x-tx)/scale; 4822 float fy = (y-ty)/scale; 4823 float dx = 1.0f/scale; 4824 4825 foreach (int i; 0..count) { 4826 //int r, g, b, a, ia; 4827 float gy = fx*t[1]+fy*t[3]+t[5]; 4828 uint c = cache.colors[cast(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; 4829 int cr = (c)&0xff; 4830 int cg = (c>>8)&0xff; 4831 int cb = (c>>16)&0xff; 4832 int ca = (c>>24)&0xff; 4833 4834 int a = nsvg__div255(cast(int)cover[0]*ca); 4835 int ia = 255-a; 4836 4837 // Premultiply 4838 int r = nsvg__div255(cr*a); 4839 int g = nsvg__div255(cg*a); 4840 int b = nsvg__div255(cb*a); 4841 4842 // Blend over 4843 r += nsvg__div255(ia*cast(int)dst[0]); 4844 g += nsvg__div255(ia*cast(int)dst[1]); 4845 b += nsvg__div255(ia*cast(int)dst[2]); 4846 a += nsvg__div255(ia*cast(int)dst[3]); 4847 4848 dst[0] = cast(ubyte)r; 4849 dst[1] = cast(ubyte)g; 4850 dst[2] = cast(ubyte)b; 4851 dst[3] = cast(ubyte)a; 4852 4853 ++cover; 4854 dst += 4; 4855 fx += dx; 4856 } 4857 } else if (cache.type == NSVG.PaintType.RadialGradient) { 4858 // TODO: spread modes. 4859 // TODO: plenty of opportunities to optimize. 4860 // TODO: focus (fx, fy) 4861 //float fx, fy, dx, gx, gy, gd; 4862 const(float)* t = cache.xform.ptr; 4863 //int i, cr, cg, cb, ca; 4864 //uint c; 4865 4866 float fx = (x-tx)/scale; 4867 float fy = (y-ty)/scale; 4868 float dx = 1.0f/scale; 4869 4870 foreach (int i; 0..count) { 4871 //int r, g, b, a, ia; 4872 float gx = fx*t[0]+fy*t[2]+t[4]; 4873 float gy = fx*t[1]+fy*t[3]+t[5]; 4874 float gd = sqrtf(gx*gx+gy*gy); 4875 uint c = cache.colors[cast(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; 4876 int cr = (c)&0xff; 4877 int cg = (c>>8)&0xff; 4878 int cb = (c>>16)&0xff; 4879 int ca = (c>>24)&0xff; 4880 4881 int a = nsvg__div255(cast(int)cover[0]*ca); 4882 int ia = 255-a; 4883 4884 // Premultiply 4885 int r = nsvg__div255(cr*a); 4886 int g = nsvg__div255(cg*a); 4887 int b = nsvg__div255(cb*a); 4888 4889 // Blend over 4890 r += nsvg__div255(ia*cast(int)dst[0]); 4891 g += nsvg__div255(ia*cast(int)dst[1]); 4892 b += nsvg__div255(ia*cast(int)dst[2]); 4893 a += nsvg__div255(ia*cast(int)dst[3]); 4894 4895 dst[0] = cast(ubyte)r; 4896 dst[1] = cast(ubyte)g; 4897 dst[2] = cast(ubyte)b; 4898 dst[3] = cast(ubyte)a; 4899 4900 ++cover; 4901 dst += 4; 4902 fx += dx; 4903 } 4904 } 4905 } 4906 4907 void nsvg__rasterizeSortedEdges (NSVGrasterizer r, float tx, float ty, float scale, const(NSVGcachedPaint)* cache, char fillRule) { 4908 NSVGactiveEdge* active = null; 4909 int s; 4910 int e = 0; 4911 int maxWeight = (255/NSVG__SUBSAMPLES); // weight per vertical scanline 4912 int xmin, xmax; 4913 4914 foreach (int y; 0..r.height) { 4915 import core.stdc.string : memset; 4916 memset(r.scanline, 0, r.width); 4917 xmin = r.width; 4918 xmax = 0; 4919 for (s = 0; s < NSVG__SUBSAMPLES; ++s) { 4920 // find center of pixel for this scanline 4921 float scany = y*NSVG__SUBSAMPLES+s+0.5f; 4922 NSVGactiveEdge** step = &active; 4923 4924 // update all active edges; 4925 // remove all active edges that terminate before the center of this scanline 4926 while (*step) { 4927 NSVGactiveEdge* z = *step; 4928 if (z.ey <= scany) { 4929 *step = z.next; // delete from list 4930 //NSVG__assert(z.valid); 4931 nsvg__freeActive(r, z); 4932 } else { 4933 z.x += z.dx; // advance to position for current scanline 4934 step = &((*step).next); // advance through list 4935 } 4936 } 4937 4938 // resort the list if needed 4939 for (;;) { 4940 int changed = 0; 4941 step = &active; 4942 while (*step && (*step).next) { 4943 if ((*step).x > (*step).next.x) { 4944 NSVGactiveEdge* t = *step; 4945 NSVGactiveEdge* q = t.next; 4946 t.next = q.next; 4947 q.next = t; 4948 *step = q; 4949 changed = 1; 4950 } 4951 step = &(*step).next; 4952 } 4953 if (!changed) break; 4954 } 4955 4956 // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 4957 while (e < r.nedges && r.edges[e].y0 <= scany) { 4958 if (r.edges[e].y1 > scany) { 4959 NSVGactiveEdge* z = nsvg__addActive(r, &r.edges[e], scany); 4960 if (z is null) break; 4961 // find insertion point 4962 if (active is null) { 4963 active = z; 4964 } else if (z.x < active.x) { 4965 // insert at front 4966 z.next = active; 4967 active = z; 4968 } else { 4969 // find thing to insert AFTER 4970 NSVGactiveEdge* p = active; 4971 while (p.next && p.next.x < z.x) 4972 p = p.next; 4973 // at this point, p.next.x is NOT < z.x 4974 z.next = p.next; 4975 p.next = z; 4976 } 4977 } 4978 e++; 4979 } 4980 4981 // now process all active edges in non-zero fashion 4982 if (active !is null) 4983 nsvg__fillActiveEdges(r.scanline, r.width, active, maxWeight, &xmin, &xmax, fillRule); 4984 } 4985 // Blit 4986 if (xmin < 0) xmin = 0; 4987 if (xmax > r.width-1) xmax = r.width-1; 4988 if (xmin <= xmax) { 4989 nsvg__scanlineSolid(&r.bitmap[y*r.stride]+xmin*4, xmax-xmin+1, &r.scanline[xmin], xmin, y, tx, ty, scale, cache); 4990 } 4991 } 4992 4993 } 4994 4995 void nsvg__unpremultiplyAlpha (ubyte* image, int w, int h, int stride) { 4996 // Unpremultiply 4997 foreach (int y; 0..h) { 4998 ubyte *row = &image[y*stride]; 4999 foreach (int x; 0..w) { 5000 int r = row[0], g = row[1], b = row[2], a = row[3]; 5001 if (a != 0) { 5002 row[0] = cast(ubyte)(r*255/a); 5003 row[1] = cast(ubyte)(g*255/a); 5004 row[2] = cast(ubyte)(b*255/a); 5005 } 5006 row += 4; 5007 } 5008 } 5009 5010 // Defringe 5011 foreach (int y; 0..h) { 5012 ubyte *row = &image[y*stride]; 5013 foreach (int x; 0..w) { 5014 int r = 0, g = 0, b = 0, a = row[3], n = 0; 5015 if (a == 0) { 5016 if (x-1 > 0 && row[-1] != 0) { 5017 r += row[-4]; 5018 g += row[-3]; 5019 b += row[-2]; 5020 n++; 5021 } 5022 if (x+1 < w && row[7] != 0) { 5023 r += row[4]; 5024 g += row[5]; 5025 b += row[6]; 5026 n++; 5027 } 5028 if (y-1 > 0 && row[-stride+3] != 0) { 5029 r += row[-stride]; 5030 g += row[-stride+1]; 5031 b += row[-stride+2]; 5032 n++; 5033 } 5034 if (y+1 < h && row[stride+3] != 0) { 5035 r += row[stride]; 5036 g += row[stride+1]; 5037 b += row[stride+2]; 5038 n++; 5039 } 5040 if (n > 0) { 5041 row[0] = cast(ubyte)(r/n); 5042 row[1] = cast(ubyte)(g/n); 5043 row[2] = cast(ubyte)(b/n); 5044 } 5045 } 5046 row += 4; 5047 } 5048 } 5049 } 5050 5051 5052 void nsvg__initPaint (NSVGcachedPaint* cache, const(NSVG.Paint)* paint, float opacity) { 5053 const(NSVG.Gradient)* grad; 5054 5055 cache.type = paint.type; 5056 5057 if (paint.type == NSVG.PaintType.Color) { 5058 cache.colors[0] = nsvg__applyOpacity(paint.color, opacity); 5059 return; 5060 } 5061 5062 grad = paint.gradient; 5063 5064 cache.spread = grad.spread; 5065 //memcpy(cache.xform.ptr, grad.xform.ptr, float.sizeof*6); 5066 cache.xform[0..6] = grad.xform[0..6]; 5067 5068 if (grad.nstops == 0) { 5069 //for (i = 0; i < 256; i++) cache.colors[i] = 0; 5070 cache.colors[0..256] = 0; 5071 } if (grad.nstops == 1) { 5072 foreach (int i; 0..256) cache.colors[i] = nsvg__applyOpacity(grad.stops.ptr[i].color, opacity); 5073 } else { 5074 uint cb = 0; 5075 //float ua, ub, du, u; 5076 int ia, ib, count; 5077 5078 uint ca = nsvg__applyOpacity(grad.stops.ptr[0].color, opacity); 5079 float ua = nsvg__clampf(grad.stops.ptr[0].offset, 0, 1); 5080 float ub = nsvg__clampf(grad.stops.ptr[grad.nstops-1].offset, ua, 1); 5081 ia = cast(int)(ua*255.0f); 5082 ib = cast(int)(ub*255.0f); 5083 //for (i = 0; i < ia; i++) cache.colors[i] = ca; 5084 cache.colors[0..ia] = ca; 5085 5086 foreach (int i; 0..grad.nstops-1) { 5087 ca = nsvg__applyOpacity(grad.stops.ptr[i].color, opacity); 5088 cb = nsvg__applyOpacity(grad.stops.ptr[i+1].color, opacity); 5089 ua = nsvg__clampf(grad.stops.ptr[i].offset, 0, 1); 5090 ub = nsvg__clampf(grad.stops.ptr[i+1].offset, 0, 1); 5091 ia = cast(int)(ua*255.0f); 5092 ib = cast(int)(ub*255.0f); 5093 count = ib-ia; 5094 if (count <= 0) continue; 5095 float u = 0; 5096 immutable float du = 1.0f/cast(float)count; 5097 foreach (int j; 0..count) { 5098 cache.colors[ia+j] = nsvg__lerpRGBA(ca, cb, u); 5099 u += du; 5100 } 5101 } 5102 5103 //for (i = ib; i < 256; i++) cache.colors[i] = cb; 5104 cache.colors[ib..256] = cb; 5105 } 5106 5107 } 5108 5109 extern(C) { 5110 private alias _compare_fp_t = int function (const void*, const void*) nothrow @nogc; 5111 private extern(C) void qsort (scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc; 5112 } 5113 5114 /** 5115 * Rasterizes SVG image, returns RGBA image (non-premultiplied alpha). 5116 * 5117 * Params: 5118 * r = pointer to rasterizer context 5119 * image = pointer to SVG image to rasterize 5120 * tx, ty = image offset (applied after scaling) 5121 * scale = image scale 5122 * dst = pointer to destination image data, 4 bytes per pixel (RGBA) 5123 * w = width of the image to render 5124 * h = height of the image to render 5125 * stride = number of bytes per scaleline in the destination buffer 5126 */ 5127 public void rasterize (NSVGrasterizer r, const(NSVG)* image, float tx, float ty, float scale, ubyte* dst, int w, int h, int stride=-1) { 5128 const(NSVG.Shape)* shape = null; 5129 NSVGedge* e = null; 5130 NSVGcachedPaint cache; 5131 int i; 5132 5133 if (stride <= 0) stride = w*4; 5134 r.bitmap = dst; 5135 r.width = w; 5136 r.height = h; 5137 r.stride = stride; 5138 5139 if (w > r.cscanline) { 5140 import core.stdc.stdlib : realloc; 5141 r.cscanline = w; 5142 r.scanline = cast(ubyte*)realloc(r.scanline, w+256); 5143 if (r.scanline is null) assert(0, "nanosvg: out of memory"); 5144 } 5145 5146 for (i = 0; i < h; i++) { 5147 import core.stdc.string : memset; 5148 memset(&dst[i*stride], 0, w*4); 5149 } 5150 5151 for (shape = image.shapes; shape !is null; shape = shape.next) { 5152 if (!(shape.flags&NSVG.Visible)) continue; 5153 5154 if (shape.fill.type != NSVG.PaintType.None) { 5155 //import core.stdc.stdlib : qsort; // not @nogc 5156 5157 nsvg__resetPool(r); 5158 r.freelist = null; 5159 r.nedges = 0; 5160 5161 nsvg__flattenShape(r, shape, scale); 5162 5163 // Scale and translate edges 5164 for (i = 0; i < r.nedges; i++) { 5165 e = &r.edges[i]; 5166 e.x0 = tx+e.x0; 5167 e.y0 = (ty+e.y0)*NSVG__SUBSAMPLES; 5168 e.x1 = tx+e.x1; 5169 e.y1 = (ty+e.y1)*NSVG__SUBSAMPLES; 5170 } 5171 5172 // Rasterize edges 5173 qsort(r.edges, r.nedges, NSVGedge.sizeof, &nsvg__cmpEdge); 5174 5175 // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule 5176 nsvg__initPaint(&cache, &shape.fill, shape.opacity); 5177 5178 nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape.fillRule); 5179 } 5180 if (shape.stroke.type != NSVG.PaintType.None && (shape.strokeWidth*scale) > 0.01f) { 5181 //import core.stdc.stdlib : qsort; // not @nogc 5182 5183 nsvg__resetPool(r); 5184 r.freelist = null; 5185 r.nedges = 0; 5186 5187 nsvg__flattenShapeStroke(r, shape, scale); 5188 5189 //dumpEdges(r, "edge.svg"); 5190 5191 // Scale and translate edges 5192 for (i = 0; i < r.nedges; i++) { 5193 e = &r.edges[i]; 5194 e.x0 = tx+e.x0; 5195 e.y0 = (ty+e.y0)*NSVG__SUBSAMPLES; 5196 e.x1 = tx+e.x1; 5197 e.y1 = (ty+e.y1)*NSVG__SUBSAMPLES; 5198 } 5199 5200 // Rasterize edges 5201 qsort(r.edges, r.nedges, NSVGedge.sizeof, &nsvg__cmpEdge); 5202 5203 // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule 5204 nsvg__initPaint(&cache, &shape.stroke, shape.opacity); 5205 5206 nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG.FillRule.NonZero); 5207 } 5208 } 5209 5210 nsvg__unpremultiplyAlpha(dst, w, h, stride); 5211 5212 r.bitmap = null; 5213 r.width = 0; 5214 r.height = 0; 5215 r.stride = 0; 5216 } 5217 5218 } // nothrow @trusted @nogc 5219 5220 5221 // ////////////////////////////////////////////////////////////////////////// // 5222 ptrdiff_t xindexOf (const(void)[] hay, const(void)[] need, usize stIdx=0) pure @trusted nothrow @nogc { 5223 if (hay.length <= stIdx || need.length == 0 || need.length > hay.length-stIdx) { 5224 return -1; 5225 } else { 5226 //import iv.strex : memmem; 5227 auto res = memmem(hay.ptr+stIdx, hay.length-stIdx, need.ptr, need.length); 5228 return (res !is null ? cast(ptrdiff_t)(res-hay.ptr) : -1); 5229 } 5230 } 5231 5232 ptrdiff_t xindexOf (const(void)[] hay, ubyte ch, usize stIdx=0) pure @trusted nothrow @nogc { 5233 return xindexOf(hay, (&ch)[0..1], stIdx); 5234 } 5235 5236 pure nothrow @trusted @nogc: 5237 version(linux) { 5238 extern(C) inout(void)* memmem (inout(void)* haystack, usize haystacklen, inout(void)* needle, usize needlelen); 5239 } else { 5240 inout(void)* memmem (inout(void)* haystack, usize haystacklen, inout(void)* needle, usize needlelen) { 5241 auto h = cast(const(ubyte)*)haystack; 5242 auto n = cast(const(ubyte)*)needle; 5243 // usize is unsigned 5244 if (needlelen > haystacklen) return null; 5245 foreach (immutable i; 0..haystacklen-needlelen+1) { 5246 import core.stdc.string : memcmp; 5247 if (memcmp(h+i, n, needlelen) == 0) return cast(void*)(h+i); 5248 } 5249 return null; 5250 } 5251 }