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