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