1 //
2 // Would be nice: way to take output of the canvas to an image file (raster and/or svg)
3 //
4 //
5 // Copyright (c) 2013 Mikko Mononen memon@inside.org
6 //
7 // This software is provided 'as-is', without any express or implied
8 // warranty.  In no event will the authors be held liable for any damages
9 // arising from the use of this software.
10 // Permission is granted to anyone to use this software for any purpose,
11 // including commercial applications, and to alter it and redistribute it
12 // freely, subject to the following restrictions:
13 // 1. The origin of this software must not be misrepresented; you must not
14 //    claim that you wrote the original software. If you use this software
15 //    in a product, an acknowledgment in the product documentation would be
16 //    appreciated but is not required.
17 // 2. Altered source versions must be plainly marked as such, and must not be
18 //    misrepresented as being the original software.
19 // 3. This notice may not be removed or altered from any source distribution.
20 //
21 // Fork developement, feature integration and new bugs:
22 // Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
23 // Contains code from various contributors.
24 /**
25 The NanoVega API is modeled loosely on HTML5 canvas API.
26 If you know canvas, you're up to speed with NanoVega in no time.
27 
28 $(SIDE_BY_SIDE
29 
30 	$(COLUMN
31 
32 		D code with nanovega:
33 
34 		---
35 		import arsd.simpledisplay;
36 
37 		import arsd.nanovega;
38 
39 		void main () {
40 		  NVGContext nvg; // our NanoVega context
41 
42 		  // we need at least OpenGL3 with GLSL to use NanoVega,
43 		  // so let's tell simpledisplay about that
44 		  setOpenGLContextVersion(3, 0);
45 
46 		  // now create OpenGL window
47 		  auto sdmain = new SimpleWindow(800, 600, "NanoVega Simple Sample", OpenGlOptions.yes, Resizability.allowResizing);
48 
49 		  // we need to destroy NanoVega context on window close
50 		  // stricly speaking, it is not necessary, as nothing fatal
51 		  // will happen if you'll forget it, but let's be polite.
52 		  // note that we cannot do that *after* our window was closed,
53 		  // as we need alive OpenGL context to do proper cleanup.
54 		  sdmain.onClosing = delegate () {
55 		    nvg.kill();
56 		  };
57 
58 		  // this is called just before our window will be shown for the first time.
59 		  // we must create NanoVega context here, as it needs to initialize
60 		  // internal OpenGL subsystem with valid OpenGL context.
61 		  sdmain.visibleForTheFirstTime = delegate () {
62 		    // yes, that's all
63 		    nvg = nvgCreateContext();
64 		    if (nvg is null) assert(0, "cannot initialize NanoVega");
65 		  };
66 
67 		  // this callback will be called when we will need to repaint our window
68 		  sdmain.redrawOpenGlScene = delegate () {
69 		    // fix viewport (we can do this in resize event, or here, it doesn't matter)
70 		    glViewport(0, 0, sdmain.width, sdmain.height);
71 
72 		    // clear window
73 		    glClearColor(0, 0, 0, 0);
74 		    glClear(glNVGClearFlags); // use NanoVega API to get flags for OpenGL call
75 
76 		    {
77 		      nvg.beginFrame(sdmain.width, sdmain.height); // begin rendering
78 		      scope(exit) nvg.endFrame(); // and flush render queue on exit
79 
80 		      nvg.beginPath(); // start new path
81 		      nvg.roundedRect(20.5, 30.5, sdmain.width-40, sdmain.height-60, 8); // .5 to draw at pixel center (see NanoVega documentation)
82 		      // now set filling mode for our rectangle
83 		      // you can create colors using HTML syntax, or with convenient constants
84 		      nvg.fillPaint = nvg.linearGradient(20.5, 30.5, sdmain.width-40, sdmain.height-60, NVGColor("#f70"), NVGColor.green);
85 		      // now fill our rect
86 		      nvg.fill();
87 		      // and draw a nice outline
88 		      nvg.strokeColor = NVGColor.white;
89 		      nvg.strokeWidth = 2;
90 		      nvg.stroke();
91 		      // that's all, folks!
92 		    }
93 		  };
94 
95 		  sdmain.eventLoop(0, // no pulse timer required
96 		    delegate (KeyEvent event) {
97 		      if (event == "*-Q" || event == "Escape") { sdmain.close(); return; } // quit on Q, Ctrl+Q, and so on
98 		    },
99 		  );
100 
101 		  flushGui(); // let OS do it's cleanup
102 		}
103 		---
104 	)
105 
106 	$(COLUMN
107 		Javascript code with HTML5 Canvas
108 
109 		```html
110 		<!DOCTYPE html>
111 		<html>
112 		<head>
113 			<title>NanoVega Simple Sample (HTML5 Translation)</title>
114 			<style>
115 				body { background-color: black; }
116 			</style>
117 		</head>
118 		<body>
119 			<canvas id="my-canvas" width="800" height="600"></canvas>
120 		<script>
121 			var canvas = document.getElementById("my-canvas");
122 			var context = canvas.getContext("2d");
123 
124 			context.beginPath();
125 
126 			context.rect(20.5, 30.5, canvas.width - 40, canvas.height - 60);
127 
128 			var gradient = context.createLinearGradient(20.5, 30.5, canvas.width - 40, canvas.height - 60);
129 			gradient.addColorStop(0, "#f70");
130 			gradient.addColorStop(1, "green");
131 
132 			context.fillStyle = gradient;
133 			context.fill();
134 			context.closePath();
135 			context.strokeStyle = "white";
136 			context.lineWidth = 2;
137 			context.stroke();
138 		</script>
139 		</body>
140 		</html>
141 		```
142 	)
143 )
144 
145 $(TIP
146     This library can use either inbuilt or BindBC (external dependency) provided bindings for OpenGL and FreeType.
147     Former are used by default, latter can be activated by passing the `bindbc` version specifier to the compiler.
148 )
149 
150 Creating drawing context
151 ========================
152 
153 The drawing context is created using platform specific constructor function.
154 
155   ---
156   NVGContext vg = nvgCreateContext();
157   ---
158 
159 $(WARNING You must use created context ONLY in that thread where you created it.
160           There is no way to "transfer" context between threads. Trying to do so
161           will lead to UB.)
162 
163 $(WARNING Never issue any commands outside of [beginFrame]/[endFrame]. Trying to
164           do so will lead to UB.)
165 
166 
167 Drawing shapes with NanoVega
168 ============================
169 
170 Drawing a simple shape using NanoVega consists of four steps:
171 $(LIST
172   * begin a new shape,
173   * define the path to draw,
174   * set fill or stroke,
175   * and finally fill or stroke the path.
176 )
177 
178   ---
179   vg.beginPath();
180   vg.rect(100, 100, 120, 30);
181   vg.fillColor(nvgRGBA(255, 192, 0, 255));
182   vg.fill();
183   ---
184 
185 Calling [beginPath] will clear any existing paths and start drawing from blank slate.
186 There are number of number of functions to define the path to draw, such as rectangle,
187 rounded rectangle and ellipse, or you can use the common moveTo, lineTo, bezierTo and
188 arcTo API to compose the paths step by step.
189 
190 
191 Understanding Composite Paths
192 =============================
193 
194 Because of the way the rendering backend is built in NanoVega, drawing a composite path,
195 that is path consisting from multiple paths defining holes and fills, is a bit more
196 involved. NanoVega uses non-zero filling rule and by default, and paths are wound in counter
197 clockwise order. Keep that in mind when drawing using the low level draw API. In order to
198 wind one of the predefined shapes as a hole, you should call `pathWinding(NVGSolidity.Hole)`,
199 or `pathWinding(NVGSolidity.Solid)` $(B after) defining the path.
200 
201   ---
202   vg.beginPath();
203   vg.rect(100, 100, 120, 30);
204   vg.circle(120, 120, 5);
205   vg.pathWinding(NVGSolidity.Hole); // mark circle as a hole
206   vg.fillColor(nvgRGBA(255, 192, 0, 255));
207   vg.fill();
208   ---
209 
210 
211 Rendering is wrong, what to do?
212 ===============================
213 
214 $(LIST
215   * make sure you have created NanoVega context using [nvgCreateContext] call
216   * make sure you have initialised OpenGL with $(B stencil buffer)
217   * make sure you have cleared stencil buffer
218   * make sure all rendering calls happen between [beginFrame] and [endFrame]
219   * to enable more checks for OpenGL errors, add `NVGContextFlag.Debug` flag to [nvgCreateContext]
220 )
221 
222 
223 OpenGL state touched by the backend
224 ===================================
225 
226 The OpenGL back-end touches following states:
227 
228 When textures are uploaded or updated, the following pixel store is set to defaults:
229 `GL_UNPACK_ALIGNMENT`, `GL_UNPACK_ROW_LENGTH`, `GL_UNPACK_SKIP_PIXELS`, `GL_UNPACK_SKIP_ROWS`.
230 Texture binding is also affected. Texture updates can happen when the user loads images,
231 or when new font glyphs are added. Glyphs are added as needed between calls to [beginFrame]
232 and [endFrame].
233 
234 The data for the whole frame is buffered and flushed in [endFrame].
235 The following code illustrates the OpenGL state touched by the rendering code:
236 
237   ---
238   glUseProgram(prog);
239   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
240   glEnable(GL_CULL_FACE);
241   glCullFace(GL_BACK);
242   glFrontFace(GL_CCW);
243   glEnable(GL_BLEND);
244   glDisable(GL_DEPTH_TEST);
245   glDisable(GL_SCISSOR_TEST);
246   glDisable(GL_COLOR_LOGIC_OP);
247   glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
248   glStencilMask(0xffffffff);
249   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
250   glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
251   glActiveTexture(GL_TEXTURE1);
252   glActiveTexture(GL_TEXTURE0);
253   glBindBuffer(GL_UNIFORM_BUFFER, buf);
254   glBindVertexArray(arr);
255   glBindBuffer(GL_ARRAY_BUFFER, buf);
256   glBindTexture(GL_TEXTURE_2D, tex);
257   glUniformBlockBinding(... , GLNVG_FRAG_BINDING);
258   ---
259 
260   Symbol_groups:
261 
262   context_management =
263     ## Context Management
264 
265     Functions to create and destory NanoVega context.
266 
267   frame_management =
268     ## Frame Management
269 
270     To start drawing with NanoVega context, you have to "begin frame", and then
271     "end frame" to flush your rendering commands to GPU.
272 
273   composite_operation =
274     ## Composite Operation
275 
276     The composite operations in NanoVega are modeled after HTML Canvas API, and
277     the blend func is based on OpenGL (see corresponding manuals for more info).
278     The colors in the blending state have premultiplied alpha.
279 
280   color_utils =
281     ## Color Utils
282 
283     Colors in NanoVega are stored as ARGB. Zero alpha means "transparent color".
284 
285   matrices =
286     ## Matrices and Transformations
287 
288     The paths, gradients, patterns and scissor region are transformed by an transformation
289     matrix at the time when they are passed to the API.
290     The current transformation matrix is an affine matrix:
291 
292     ----------------------
293       [sx kx tx]
294       [ky sy ty]
295       [ 0  0  1]
296     ----------------------
297 
298     Where: (sx, sy) define scaling, (kx, ky) skewing, and (tx, ty) translation.
299     The last row is assumed to be (0, 0, 1) and is not stored.
300 
301     Apart from [resetTransform], each transformation function first creates
302     specific transformation matrix and pre-multiplies the current transformation by it.
303 
304     Current coordinate system (transformation) can be saved and restored using [save] and [restore].
305 
306     The following functions can be used to make calculations on 2x3 transformation matrices.
307     A 2x3 matrix is represented as float[6].
308 
309   state_handling =
310     ## State Handling
311 
312     NanoVega contains state which represents how paths will be rendered.
313     The state contains transform, fill and stroke styles, text and font styles,
314     and scissor clipping.
315 
316   render_styles =
317     ## Render Styles
318 
319     Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.
320     Solid color is simply defined as a color value, different kinds of paints can be created
321     using [linearGradient], [boxGradient], [radialGradient] and [imagePattern].
322 
323     Current render style can be saved and restored using [save] and [restore].
324 
325     Note that if you want "almost perfect" pixel rendering, you should set aspect ratio to 1,
326     and use `integerCoord+0.5f` as pixel coordinates.
327 
328   render_transformations =
329     ## Render Transformations
330 
331     Transformation matrix management for the current rendering style. Transformations are applied in
332     backwards order. I.e. if you first translate, and then rotate, your path will be rotated around
333     it's origin, and then translated to the destination point.
334 
335   scissoring =
336     ## Scissoring
337 
338     Scissoring allows you to clip the rendering into a rectangle. This is useful for various
339     user interface cases like rendering a text edit or a timeline.
340 
341   images =
342     ## Images
343 
344     NanoVega allows you to load image files in various formats (if arsd loaders are in place) to be used for rendering.
345     In addition you can upload your own image.
346     The parameter imageFlagsList is a list of flags defined in [NVGImageFlag].
347 
348     If you will use your image as fill pattern, it will be scaled by default. To make it repeat, pass
349     [NVGImageFlag.RepeatX] and [NVGImageFlag.RepeatY] flags to image creation function respectively.
350 
351   paints =
352     ## Paints
353 
354     NanoVega supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.
355     These can be used as paints for strokes and fills.
356 
357   gpu_affine =
358     ## Render-Time Affine Transformations
359 
360     It is possible to set affine transformation matrix for GPU. That matrix will
361     be applied by the shader code. This can be used to quickly translate and rotate
362     saved paths. Call this $(B only) between [beginFrame] and [endFrame].
363 
364     Note that [beginFrame] resets this matrix to identity one.
365 
366     $(WARNING Don't use this for scaling or skewing, or your image will be heavily distorted!)
367 
368   paths =
369     ## Paths
370 
371     Drawing a new shape starts with [beginPath], it clears all the currently defined paths.
372     Then you define one or more paths and sub-paths which describe the shape. The are functions
373     to draw common shapes like rectangles and circles, and lower level step-by-step functions,
374     which allow to define a path curve by curve.
375 
376     NanoVega uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise
377     winding and holes should have counter clockwise order. To specify winding of a path you can
378     call [pathWinding]. This is useful especially for the common shapes, which are drawn CCW.
379 
380     Finally you can fill the path using current fill style by calling [fill], and stroke it
381     with current stroke style by calling [stroke].
382 
383     The curve segments and sub-paths are transformed by the current transform.
384 
385   picking_api =
386     ## Picking API
387 
388     This is picking API that works directly on paths, without rasterizing them first.
389 
390     [beginFrame] resets picking state. Then you can create paths as usual, but
391     there is a possibility to perform hit checks $(B before) rasterizing a path.
392     Call either id assigning functions ([currFillHitId]/[currStrokeHitId]), or
393     immediate hit test functions ([hitTestCurrFill]/[hitTestCurrStroke])
394     before rasterizing (i.e. calling [fill] or [stroke]) to perform hover
395     effects, for example.
396 
397     Also note that picking API is ignoring GPU affine transformation matrix.
398     You can "untransform" picking coordinates before checking with [gpuUntransformPoint].
399 
400     $(WARNING Picking API completely ignores clipping. If you want to check for
401               clip regions, you have to manually register them as fill/stroke paths,
402               and perform the necessary logic. See [hitTestForId] function.)
403 
404   clipping =
405     ## Clipping with paths
406 
407     If scissoring is not enough for you, you can clip rendering with arbitrary path,
408     or with combination of paths. Clip region is saved by [save] and restored by
409     [restore] NanoVega functions. You can combine clip paths with various logic
410     operations, see [NVGClipMode].
411 
412     Note that both [clip] and [clipStroke] are ignoring scissoring (i.e. clip mask
413     is created as if there was no scissor set). Actual rendering is affected by
414     scissors, though.
415 
416   text_api =
417     ## Text
418 
419     NanoVega allows you to load .ttf files and use the font to render text.
420     You have to load some font, and set proper font size before doing anything
421     with text, as there is no "default" font provided by NanoVega. Also, don't
422     forget to check return value of `createFont()`, 'cause NanoVega won't fail
423     if it cannot load font, it will silently try to render nothing.
424 
425     The appearance of the text can be defined by setting the current text style
426     and by specifying the fill color. Common text and font settings such as
427     font size, letter spacing and text align are supported. Font blur allows you
428     to create simple text effects such as drop shadows.
429 
430     At render time the font face can be set based on the font handles or name.
431 
432     Font measure functions return values in local space, the calculations are
433     carried in the same resolution as the final rendering. This is done because
434     the text glyph positions are snapped to the nearest pixels sharp rendering.
435 
436     The local space means that values are not rotated or scale as per the current
437     transformation. For example if you set font size to 12, which would mean that
438     line height is 16, then regardless of the current scaling and rotation, the
439     returned line height is always 16. Some measures may vary because of the scaling
440     since aforementioned pixel snapping.
441 
442     While this may sound a little odd, the setup allows you to always render the
443     same way regardless of scaling. I.e. following works regardless of scaling:
444 
445     ----------------------
446        string txt = "Text me up.";
447        vg.textBounds(x, y, txt, bounds);
448        vg.beginPath();
449        vg.roundedRect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1], 6);
450        vg.fill();
451     ----------------------
452 
453     Note: currently only solid color fill is supported for text.
454 
455   font_stash =
456     ## Low-Level Font Engine (FontStash)
457 
458     FontStash is used to load fonts, to manage font atlases, and to get various text metrics.
459     You don't need any graphics context to use FontStash, so you can do things like text
460     layouting outside of your rendering code. Loaded fonts are refcounted, so it is cheap
461     to create new FontStash, copy fonts from NanoVega context into it, and use that new
462     FontStash to do some UI layouting, for example. Also note that you can get text metrics
463     without creating glyph bitmaps (by using [FONSTextBoundsIterator], for example); this way
464     you don't need to waste CPU and memory resources to render unneeded images into font atlas,
465     and you can layout alot of text very fast.
466 
467     Note that "FontStash" is abbrevated as "FONS". So when you see some API that contains
468     word "fons" in it, this is not a typo, and it should not read "font" intead.
469 
470     TODO for Ketmar: write some nice example code here, and finish documenting FontStash API.
471  */
472 module arsd.nanovega;
473 private:
474 
475 version(aliced) {
476   import iv.meta;
477   import iv.vfs;
478 } else {
479   private alias usize = size_t;
480   // i fear phobos!
481   private template Unqual(T) {
482          static if (is(T U ==          immutable U)) alias Unqual = U;
483     else static if (is(T U == shared inout const U)) alias Unqual = U;
484     else static if (is(T U == shared inout       U)) alias Unqual = U;
485     else static if (is(T U == shared       const U)) alias Unqual = U;
486     else static if (is(T U == shared             U)) alias Unqual = U;
487     else static if (is(T U ==        inout const U)) alias Unqual = U;
488     else static if (is(T U ==        inout       U)) alias Unqual = U;
489     else static if (is(T U ==              const U)) alias Unqual = U;
490     else alias Unqual = T;
491   }
492   private template isAnyCharType(T, bool unqual=false) {
493     static if (unqual) private alias UT = Unqual!T; else private alias UT = T;
494     enum isAnyCharType = is(UT == char) || is(UT == wchar) || is(UT == dchar);
495   }
496   private template isWideCharType(T, bool unqual=false) {
497     static if (unqual) private alias UT = Unqual!T; else private alias UT = T;
498     enum isWideCharType = is(UT == wchar) || is(UT == dchar);
499   }
500 }
501 version(nanovg_disable_vfs) {
502   enum NanoVegaHasIVVFS = false;
503 } else {
504   static if (is(typeof((){import iv.vfs;}))) {
505     enum NanoVegaHasIVVFS = true;
506     import iv.vfs;
507   } else {
508     enum NanoVegaHasIVVFS = false;
509   }
510 }
511 
512 // ////////////////////////////////////////////////////////////////////////// //
513 // engine
514 // ////////////////////////////////////////////////////////////////////////// //
515 import core.stdc.stdlib : malloc, realloc, free;
516 import core.stdc.string : memset, memcpy, strlen;
517 import std.math : PI;
518 
519 //version = nanovg_force_stb_ttf;
520 
521 version(Posix) {
522   version = nanovg_use_freetype;
523 } else {
524   version = nanovg_disable_fontconfig;
525 }
526 
527 version (bindbc) {
528   version = nanovg_builtin_fontconfig_bindings;
529   version = nanovg_bindbc_opengl_bindings;
530   version = nanovg_bindbc_freetype_bindings;
531   version(BindFT_Dynamic)
532     static assert(0, "AsumFace was too lazy to write the code for the dynamic bindbc freetype bindings");
533   else {
534     version(BindFT_Static) {}
535     else
536       static assert(0, "well, duh. you got to pass the BindFT_Static version identifier to the compiler");
537   }
538 } else version(aliced) {
539   version = nanovg_default_no_font_aa;
540   version = nanovg_builtin_fontconfig_bindings;
541   version = nanovg_builtin_freetype_bindings;
542   version = nanovg_builtin_opengl_bindings; // use `arsd.simpledisplay` to get basic bindings
543 } else {
544   version = nanovg_builtin_fontconfig_bindings;
545   version = nanovg_builtin_freetype_bindings;
546   version = nanovg_builtin_opengl_bindings; // use `arsd.simpledisplay` to get basic bindings
547 }
548 
549 version(nanovg_disable_fontconfig) {
550   public enum NanoVegaHasFontConfig = false;
551 } else {
552   public enum NanoVegaHasFontConfig = true;
553   version(nanovg_builtin_fontconfig_bindings) {} else import iv.fontconfig;
554 }
555 
556 //version = nanovg_bench_flatten;
557 
558 /++
559 	Annotation to indicate the marked function is compatible with [arsd.script].
560 
561 
562 	Any function that takes a [Color] argument will be passed a string instead.
563 
564 	Scriptable Functions
565 	====================
566 
567 	$(UDA_USES)
568 
569 	$(ALWAYS_DOCUMENT)
570 +/
571 private enum scriptable = "arsd_jsvar_compatible";
572 
573 public:
574 alias NVG_PI = PI;
575 
576 enum NanoVegaHasArsdColor = (is(typeof((){ import arsd.color; })));
577 enum NanoVegaHasArsdImage = (is(typeof((){ import arsd.color; import arsd.image; })));
578 
579 static if (NanoVegaHasArsdColor) private import arsd.color;
580 static if (NanoVegaHasArsdImage) {
581   private import arsd.image;
582 } else {
583   void stbi_set_unpremultiply_on_load (int flag_true_if_should_unpremultiply) {}
584   void stbi_convert_iphone_png_to_rgb (int flag_true_if_should_convert) {}
585   ubyte* stbi_load (const(char)* filename, int* x, int* y, int* comp, int req_comp) { return null; }
586   ubyte* stbi_load_from_memory (const(void)* buffer, int len, int* x, int* y, int* comp, int req_comp) { return null; }
587   void stbi_image_free (void* retval_from_stbi_load) {}
588 }
589 
590 version(nanovg_default_no_font_aa) {
591   __gshared bool NVG_INVERT_FONT_AA = false;
592 } else {
593   __gshared bool NVG_INVERT_FONT_AA = true;
594 }
595 
596 
597 /// this is branchless for ints on x86, and even for longs on x86_64
598 public ubyte nvgClampToByte(T) (T n) pure nothrow @safe @nogc if (__traits(isIntegral, T)) {
599   static if (__VERSION__ > 2067) pragma(inline, true);
600   static if (T.sizeof == 2 || T.sizeof == 4) {
601     static if (__traits(isUnsigned, T)) {
602       return cast(ubyte)(n&0xff|(255-((-cast(int)(n < 256))>>24)));
603     } else {
604       n &= -cast(int)(n >= 0);
605       return cast(ubyte)(n|((255-cast(int)n)>>31));
606     }
607   } else static if (T.sizeof == 1) {
608     static assert(__traits(isUnsigned, T), "clampToByte: signed byte? no, really?");
609     return cast(ubyte)n;
610   } else static if (T.sizeof == 8) {
611     static if (__traits(isUnsigned, T)) {
612       return cast(ubyte)(n&0xff|(255-((-cast(long)(n < 256))>>56)));
613     } else {
614       n &= -cast(long)(n >= 0);
615       return cast(ubyte)(n|((255-cast(long)n)>>63));
616     }
617   } else {
618     static assert(false, "clampToByte: integer too big");
619   }
620 }
621 
622 
623 /// NanoVega RGBA color
624 /// Group: color_utils
625 public align(1) struct NVGColor {
626 align(1):
627 public:
628   float[4] rgba = 0; /// default color is transparent (a=1 is opaque)
629 
630 public:
631   @property string toString () const @safe { import std.string : format; return "NVGColor(%s,%s,%s,%s)".format(r, g, b, a); }
632 
633 public:
634   enum transparent = NVGColor(0.0f, 0.0f, 0.0f, 0.0f);
635   enum k8orange = NVGColor(1.0f, 0.5f, 0.0f, 1.0f);
636 
637   enum aliceblue = NVGColor(240, 248, 255);
638   enum antiquewhite = NVGColor(250, 235, 215);
639   enum aqua = NVGColor(0, 255, 255);
640   enum aquamarine = NVGColor(127, 255, 212);
641   enum azure = NVGColor(240, 255, 255);
642   enum beige = NVGColor(245, 245, 220);
643   enum bisque = NVGColor(255, 228, 196);
644   enum black = NVGColor(0, 0, 0); // basic color
645   enum blanchedalmond = NVGColor(255, 235, 205);
646   enum blue = NVGColor(0, 0, 255); // basic color
647   enum blueviolet = NVGColor(138, 43, 226);
648   enum brown = NVGColor(165, 42, 42);
649   enum burlywood = NVGColor(222, 184, 135);
650   enum cadetblue = NVGColor(95, 158, 160);
651   enum chartreuse = NVGColor(127, 255, 0);
652   enum chocolate = NVGColor(210, 105, 30);
653   enum coral = NVGColor(255, 127, 80);
654   enum cornflowerblue = NVGColor(100, 149, 237);
655   enum cornsilk = NVGColor(255, 248, 220);
656   enum crimson = NVGColor(220, 20, 60);
657   enum cyan = NVGColor(0, 255, 255); // basic color
658   enum darkblue = NVGColor(0, 0, 139);
659   enum darkcyan = NVGColor(0, 139, 139);
660   enum darkgoldenrod = NVGColor(184, 134, 11);
661   enum darkgray = NVGColor(169, 169, 169);
662   enum darkgreen = NVGColor(0, 100, 0);
663   enum darkgrey = NVGColor(169, 169, 169);
664   enum darkkhaki = NVGColor(189, 183, 107);
665   enum darkmagenta = NVGColor(139, 0, 139);
666   enum darkolivegreen = NVGColor(85, 107, 47);
667   enum darkorange = NVGColor(255, 140, 0);
668   enum darkorchid = NVGColor(153, 50, 204);
669   enum darkred = NVGColor(139, 0, 0);
670   enum darksalmon = NVGColor(233, 150, 122);
671   enum darkseagreen = NVGColor(143, 188, 143);
672   enum darkslateblue = NVGColor(72, 61, 139);
673   enum darkslategray = NVGColor(47, 79, 79);
674   enum darkslategrey = NVGColor(47, 79, 79);
675   enum darkturquoise = NVGColor(0, 206, 209);
676   enum darkviolet = NVGColor(148, 0, 211);
677   enum deeppink = NVGColor(255, 20, 147);
678   enum deepskyblue = NVGColor(0, 191, 255);
679   enum dimgray = NVGColor(105, 105, 105);
680   enum dimgrey = NVGColor(105, 105, 105);
681   enum dodgerblue = NVGColor(30, 144, 255);
682   enum firebrick = NVGColor(178, 34, 34);
683   enum floralwhite = NVGColor(255, 250, 240);
684   enum forestgreen = NVGColor(34, 139, 34);
685   enum fuchsia = NVGColor(255, 0, 255);
686   enum gainsboro = NVGColor(220, 220, 220);
687   enum ghostwhite = NVGColor(248, 248, 255);
688   enum gold = NVGColor(255, 215, 0);
689   enum goldenrod = NVGColor(218, 165, 32);
690   enum gray = NVGColor(128, 128, 128); // basic color
691   enum green = NVGColor(0, 128, 0); // basic color
692   enum greenyellow = NVGColor(173, 255, 47);
693   enum grey = NVGColor(128, 128, 128); // basic color
694   enum honeydew = NVGColor(240, 255, 240);
695   enum hotpink = NVGColor(255, 105, 180);
696   enum indianred = NVGColor(205, 92, 92);
697   enum indigo = NVGColor(75, 0, 130);
698   enum ivory = NVGColor(255, 255, 240);
699   enum khaki = NVGColor(240, 230, 140);
700   enum lavender = NVGColor(230, 230, 250);
701   enum lavenderblush = NVGColor(255, 240, 245);
702   enum lawngreen = NVGColor(124, 252, 0);
703   enum lemonchiffon = NVGColor(255, 250, 205);
704   enum lightblue = NVGColor(173, 216, 230);
705   enum lightcoral = NVGColor(240, 128, 128);
706   enum lightcyan = NVGColor(224, 255, 255);
707   enum lightgoldenrodyellow = NVGColor(250, 250, 210);
708   enum lightgray = NVGColor(211, 211, 211);
709   enum lightgreen = NVGColor(144, 238, 144);
710   enum lightgrey = NVGColor(211, 211, 211);
711   enum lightpink = NVGColor(255, 182, 193);
712   enum lightsalmon = NVGColor(255, 160, 122);
713   enum lightseagreen = NVGColor(32, 178, 170);
714   enum lightskyblue = NVGColor(135, 206, 250);
715   enum lightslategray = NVGColor(119, 136, 153);
716   enum lightslategrey = NVGColor(119, 136, 153);
717   enum lightsteelblue = NVGColor(176, 196, 222);
718   enum lightyellow = NVGColor(255, 255, 224);
719   enum lime = NVGColor(0, 255, 0);
720   enum limegreen = NVGColor(50, 205, 50);
721   enum linen = NVGColor(250, 240, 230);
722   enum magenta = NVGColor(255, 0, 255); // basic color
723   enum maroon = NVGColor(128, 0, 0);
724   enum mediumaquamarine = NVGColor(102, 205, 170);
725   enum mediumblue = NVGColor(0, 0, 205);
726   enum mediumorchid = NVGColor(186, 85, 211);
727   enum mediumpurple = NVGColor(147, 112, 219);
728   enum mediumseagreen = NVGColor(60, 179, 113);
729   enum mediumslateblue = NVGColor(123, 104, 238);
730   enum mediumspringgreen = NVGColor(0, 250, 154);
731   enum mediumturquoise = NVGColor(72, 209, 204);
732   enum mediumvioletred = NVGColor(199, 21, 133);
733   enum midnightblue = NVGColor(25, 25, 112);
734   enum mintcream = NVGColor(245, 255, 250);
735   enum mistyrose = NVGColor(255, 228, 225);
736   enum moccasin = NVGColor(255, 228, 181);
737   enum navajowhite = NVGColor(255, 222, 173);
738   enum navy = NVGColor(0, 0, 128);
739   enum oldlace = NVGColor(253, 245, 230);
740   enum olive = NVGColor(128, 128, 0);
741   enum olivedrab = NVGColor(107, 142, 35);
742   enum orange = NVGColor(255, 165, 0);
743   enum orangered = NVGColor(255, 69, 0);
744   enum orchid = NVGColor(218, 112, 214);
745   enum palegoldenrod = NVGColor(238, 232, 170);
746   enum palegreen = NVGColor(152, 251, 152);
747   enum paleturquoise = NVGColor(175, 238, 238);
748   enum palevioletred = NVGColor(219, 112, 147);
749   enum papayawhip = NVGColor(255, 239, 213);
750   enum peachpuff = NVGColor(255, 218, 185);
751   enum peru = NVGColor(205, 133, 63);
752   enum pink = NVGColor(255, 192, 203);
753   enum plum = NVGColor(221, 160, 221);
754   enum powderblue = NVGColor(176, 224, 230);
755   enum purple = NVGColor(128, 0, 128);
756   enum red = NVGColor(255, 0, 0); // basic color
757   enum rosybrown = NVGColor(188, 143, 143);
758   enum royalblue = NVGColor(65, 105, 225);
759   enum saddlebrown = NVGColor(139, 69, 19);
760   enum salmon = NVGColor(250, 128, 114);
761   enum sandybrown = NVGColor(244, 164, 96);
762   enum seagreen = NVGColor(46, 139, 87);
763   enum seashell = NVGColor(255, 245, 238);
764   enum sienna = NVGColor(160, 82, 45);
765   enum silver = NVGColor(192, 192, 192);
766   enum skyblue = NVGColor(135, 206, 235);
767   enum slateblue = NVGColor(106, 90, 205);
768   enum slategray = NVGColor(112, 128, 144);
769   enum slategrey = NVGColor(112, 128, 144);
770   enum snow = NVGColor(255, 250, 250);
771   enum springgreen = NVGColor(0, 255, 127);
772   enum steelblue = NVGColor(70, 130, 180);
773   enum tan = NVGColor(210, 180, 140);
774   enum teal = NVGColor(0, 128, 128);
775   enum thistle = NVGColor(216, 191, 216);
776   enum tomato = NVGColor(255, 99, 71);
777   enum turquoise = NVGColor(64, 224, 208);
778   enum violet = NVGColor(238, 130, 238);
779   enum wheat = NVGColor(245, 222, 179);
780   enum white = NVGColor(255, 255, 255); // basic color
781   enum whitesmoke = NVGColor(245, 245, 245);
782   enum yellow = NVGColor(255, 255, 0); // basic color
783   enum yellowgreen = NVGColor(154, 205, 50);
784 
785 nothrow @safe @nogc:
786 public:
787   ///
788   this (ubyte ar, ubyte ag, ubyte ab, ubyte aa=255) pure {
789     pragma(inline, true);
790     r = ar/255.0f;
791     g = ag/255.0f;
792     b = ab/255.0f;
793     a = aa/255.0f;
794   }
795 
796   ///
797   this (float ar, float ag, float ab, float aa=1.0f) pure {
798     pragma(inline, true);
799     r = ar;
800     g = ag;
801     b = ab;
802     a = aa;
803   }
804 
805   /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
806   this (uint c) pure {
807     pragma(inline, true);
808     r = (c&0xff)/255.0f;
809     g = ((c>>8)&0xff)/255.0f;
810     b = ((c>>16)&0xff)/255.0f;
811     a = ((c>>24)&0xff)/255.0f;
812   }
813 
814   /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
815   this (const(char)[] srgb) {
816     static int c2d (char ch) pure nothrow @safe @nogc {
817       pragma(inline, true);
818       return
819         ch >= '0' && ch <= '9' ? ch-'0' :
820         ch >= 'A' && ch <= 'F' ? ch-'A'+10 :
821         ch >= 'a' && ch <= 'f' ? ch-'a'+10 :
822         -1;
823     }
824     int[8] digs;
825     int dc = -1;
826     foreach (immutable char ch; srgb) {
827       if (ch <= ' ') continue;
828       if (ch == '#') {
829         if (dc != -1) { dc = -1; break; }
830         dc = 0;
831       } else {
832         if (dc >= digs.length) { dc = -1; break; }
833         if ((digs[dc++] = c2d(ch)) < 0) { dc = -1; break; }
834       }
835     }
836     switch (dc) {
837       case 3: // rgb
838         a = 1.0f;
839         r = digs[0]/15.0f;
840         g = digs[1]/15.0f;
841         b = digs[2]/15.0f;
842         break;
843       case 4: // argb
844         a = digs[0]/15.0f;
845         r = digs[1]/15.0f;
846         g = digs[2]/15.0f;
847         b = digs[3]/15.0f;
848         break;
849       case 6: // rrggbb
850         a = 1.0f;
851         r = (digs[0]*16+digs[1])/255.0f;
852         g = (digs[2]*16+digs[3])/255.0f;
853         b = (digs[4]*16+digs[5])/255.0f;
854         break;
855       case 8: // aarrggbb
856         a = (digs[0]*16+digs[1])/255.0f;
857         r = (digs[2]*16+digs[3])/255.0f;
858         g = (digs[4]*16+digs[5])/255.0f;
859         b = (digs[6]*16+digs[7])/255.0f;
860         break;
861       default:
862         break;
863     }
864   }
865 
866   /// Is this color completely opaque?
867   @property bool isOpaque () const pure nothrow @trusted @nogc { pragma(inline, true); return (rgba.ptr[3] >= 1.0f); }
868   /// Is this color completely transparent?
869   @property bool isTransparent () const pure nothrow @trusted @nogc { pragma(inline, true); return (rgba.ptr[3] <= 0.0f); }
870 
871   /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
872   @property uint asUint () const pure {
873     pragma(inline, true);
874     return
875       cast(uint)(r*255)|
876       (cast(uint)(g*255)<<8)|
877       (cast(uint)(b*255)<<16)|
878       (cast(uint)(a*255)<<24);
879   }
880 
881   alias asUintABGR = asUint; /// Ditto.
882 
883   /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
884   static NVGColor fromUint (uint c) pure { pragma(inline, true); return NVGColor(c); }
885 
886   alias fromUintABGR = fromUint; /// Ditto.
887 
888   /// AARRGGBB
889   @property uint asUintARGB () const pure {
890     pragma(inline, true);
891     return
892       cast(uint)(b*255)|
893       (cast(uint)(g*255)<<8)|
894       (cast(uint)(r*255)<<16)|
895       (cast(uint)(a*255)<<24);
896   }
897 
898   /// AARRGGBB
899   static NVGColor fromUintARGB (uint c) pure { pragma(inline, true); return NVGColor((c>>16)&0xff, (c>>8)&0xff, c&0xff, (c>>24)&0xff); }
900 
901   @property ref inout(float) r () inout pure @trusted { pragma(inline, true); return rgba.ptr[0]; } ///
902   @property ref inout(float) g () inout pure @trusted { pragma(inline, true); return rgba.ptr[1]; } ///
903   @property ref inout(float) b () inout pure @trusted { pragma(inline, true); return rgba.ptr[2]; } ///
904   @property ref inout(float) a () inout pure @trusted { pragma(inline, true); return rgba.ptr[3]; } ///
905 
906   ref NVGColor applyTint() (in auto ref NVGColor tint) nothrow @trusted @nogc {
907     if (tint.a == 0) return this;
908     foreach (immutable idx, ref float v; rgba[0..4]) {
909       v = nvg__clamp(v*tint.rgba.ptr[idx], 0.0f, 1.0f);
910     }
911     return this;
912   }
913 
914   NVGHSL asHSL() (bool useWeightedLightness=false) const { pragma(inline, true); return NVGHSL.fromColor(this, useWeightedLightness); } ///
915   static fromHSL() (in auto ref NVGHSL hsl) { pragma(inline, true); return hsl.asColor; } ///
916 
917   static if (NanoVegaHasArsdColor) {
918     Color toArsd () const { pragma(inline, true); return Color(cast(int)(r*255), cast(int)(g*255), cast(int)(b*255), cast(int)(a*255)); } ///
919     static NVGColor fromArsd (in Color c) { pragma(inline, true); return NVGColor(c.r, c.g, c.b, c.a); } ///
920     ///
921     this (in Color c) {
922       version(aliced) pragma(inline, true);
923       r = c.r/255.0f;
924       g = c.g/255.0f;
925       b = c.b/255.0f;
926       a = c.a/255.0f;
927     }
928   }
929 }
930 
931 
932 /// NanoVega A-HSL color
933 /// Group: color_utils
934 public align(1) struct NVGHSL {
935 align(1):
936   float h=0, s=0, l=1, a=1; ///
937 
938   string toString () const { import std.format : format; return (a != 1 ? "HSL(%s,%s,%s,%d)".format(h, s, l, a) : "HSL(%s,%s,%s)".format(h, s, l)); }
939 
940 nothrow @safe @nogc:
941 public:
942   ///
943   this (float ah, float as, float al, float aa=1) pure { pragma(inline, true); h = ah; s = as; l = al; a = aa; }
944 
945   NVGColor asColor () const { pragma(inline, true); return nvgHSLA(h, s, l, a); } ///
946 
947   // taken from Adam's arsd.color
948   /** Converts an RGB color into an HSL triplet.
949    * [useWeightedLightness] will try to get a better value for luminosity for the human eye,
950    * which is more sensitive to green than red and more to red than blue.
951    * If it is false, it just does average of the rgb. */
952   static NVGHSL fromColor() (in auto ref NVGColor c, bool useWeightedLightness=false) pure {
953     NVGHSL res;
954     res.a = c.a;
955     float r1 = c.r;
956     float g1 = c.g;
957     float b1 = c.b;
958 
959     float maxColor = r1;
960     if (g1 > maxColor) maxColor = g1;
961     if (b1 > maxColor) maxColor = b1;
962     float minColor = r1;
963     if (g1 < minColor) minColor = g1;
964     if (b1 < minColor) minColor = b1;
965 
966     res.l = (maxColor+minColor)/2;
967     if (useWeightedLightness) {
968       // the colors don't affect the eye equally
969       // this is a little more accurate than plain HSL numbers
970       res.l = 0.2126*r1+0.7152*g1+0.0722*b1;
971     }
972     if (maxColor != minColor) {
973       if (res.l < 0.5) {
974         res.s = (maxColor-minColor)/(maxColor+minColor);
975       } else {
976         res.s = (maxColor-minColor)/(2.0-maxColor-minColor);
977       }
978       if (r1 == maxColor) {
979         res.h = (g1-b1)/(maxColor-minColor);
980       } else if(g1 == maxColor) {
981         res.h = 2.0+(b1-r1)/(maxColor-minColor);
982       } else {
983         res.h = 4.0+(r1-g1)/(maxColor-minColor);
984       }
985     }
986 
987     res.h = res.h*60;
988     if (res.h < 0) res.h += 360;
989     res.h /= 360;
990 
991     return res;
992   }
993 }
994 
995 
996 //version = nanovega_debug_image_manager;
997 //version = nanovega_debug_image_manager_rc;
998 
999 /** NanoVega image handle.
1000  *
1001  * This is refcounted struct, so you don't need to do anything special to free it once it is allocated.
1002  *
1003  * Group: images
1004  */
1005 struct NVGImage {
1006 	enum isOpaqueStruct = true;
1007 private:
1008   NVGContext ctx;
1009   int id; // backend image id
1010 
1011 public:
1012   ///
1013   this() (in auto ref NVGImage src) nothrow @trusted @nogc {
1014     version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; if (src.id != 0) printf("NVGImage %p created from %p (imgid=%d)\n", &this, src, src.id); }
1015     if (src.id > 0 && src.ctx !is null) {
1016       ctx = cast(NVGContext)src.ctx;
1017       id = src.id;
1018       ctx.nvg__imageIncRef(id);
1019     }
1020   }
1021 
1022   ///
1023   ~this () nothrow @trusted @nogc { version(aliced) pragma(inline, true); clear(); }
1024 
1025   ///
1026   this (this) nothrow @trusted @nogc {
1027     version(aliced) pragma(inline, true);
1028     if (id > 0 && ctx !is null) {
1029       version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p postblit (imgid=%d)\n", &this, id); }
1030       ctx.nvg__imageIncRef(id);
1031     }
1032   }
1033 
1034   ///
1035   void opAssign() (in auto ref NVGImage src) nothrow @trusted @nogc {
1036     if (src.id <= 0 || src.ctx is null) {
1037       clear();
1038     } else {
1039       version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p (imgid=%d) assigned from %p (imgid=%d)\n", &this, id, &src, src.id); }
1040       if (src.id > 0 && src.ctx !is null) (cast(NVGContext)src.ctx).nvg__imageIncRef(src.id);
1041       if (id > 0 && ctx !is null) ctx.nvg__imageDecRef(id);
1042       ctx = cast(NVGContext)src.ctx;
1043       id = src.id;
1044     }
1045   }
1046 
1047   /// Free this image.
1048   void clear () nothrow @trusted @nogc {
1049     if (id > 0 && ctx !is null) {
1050       version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p cleared (imgid=%d)\n", &this, id); }
1051       ctx.nvg__imageDecRef(id);
1052     }
1053     id = 0;
1054     ctx = null;
1055   }
1056 
1057   /// Is this image valid?
1058   @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (id > 0 && ctx.valid); }
1059 
1060   /// Is the given image valid and comes from the same context?
1061   @property bool isSameContext (const(NVGContext) actx) const pure nothrow @safe @nogc { pragma(inline, true); return (actx !is null && ctx is actx); }
1062 
1063   /// Returns image width, or zero for invalid image.
1064   int width () const nothrow @trusted @nogc {
1065     int w = 0;
1066     if (valid) {
1067       int h = void;
1068       ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, id, &w, &h);
1069     }
1070     return w;
1071   }
1072 
1073   /// Returns image height, or zero for invalid image.
1074   int height () const nothrow @trusted @nogc {
1075     int h = 0;
1076     if (valid) {
1077       int w = void;
1078       ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, id, &w, &h);
1079     }
1080     return h;
1081   }
1082 }
1083 
1084 
1085 /// Paint parameters for various fills. Don't change anything here!
1086 /// Group: render_styles
1087 public struct NVGPaint {
1088   enum isOpaqueStruct = true;
1089 
1090   NVGMatrix xform;
1091   float[2] extent = 0.0f;
1092   float radius = 0.0f;
1093   float feather = 0.0f;
1094   NVGColor innerColor; /// this can be used to modulate images (fill/font)
1095   NVGColor middleColor;
1096   NVGColor outerColor;
1097   float midp = -1; // middle stop for 3-color gradient
1098   NVGImage image;
1099   bool simpleColor; /// if `true`, only innerColor is used, and this is solid-color paint
1100 
1101   this() (in auto ref NVGPaint p) nothrow @trusted @nogc {
1102     xform = p.xform;
1103     extent[] = p.extent[];
1104     radius = p.radius;
1105     feather = p.feather;
1106     innerColor = p.innerColor;
1107     middleColor = p.middleColor;
1108     midp = p.midp;
1109     outerColor = p.outerColor;
1110     image = p.image;
1111     simpleColor = p.simpleColor;
1112   }
1113 
1114   void opAssign() (in auto ref NVGPaint p) nothrow @trusted @nogc {
1115     xform = p.xform;
1116     extent[] = p.extent[];
1117     radius = p.radius;
1118     feather = p.feather;
1119     innerColor = p.innerColor;
1120     middleColor = p.middleColor;
1121     midp = p.midp;
1122     outerColor = p.outerColor;
1123     image = p.image;
1124     simpleColor = p.simpleColor;
1125   }
1126 
1127   void clear () nothrow @trusted @nogc {
1128     version(aliced) pragma(inline, true);
1129     import core.stdc.string : memset;
1130     image.clear();
1131     memset(&this, 0, this.sizeof);
1132     simpleColor = true;
1133   }
1134 }
1135 
1136 /// Path winding.
1137 /// Group: paths
1138 public enum NVGWinding {
1139   CCW = 1, /// Winding for solid shapes
1140   CW = 2,  /// Winding for holes
1141 }
1142 
1143 /// Path solidity.
1144 /// Group: paths
1145 public enum NVGSolidity {
1146   Solid = 1, /// Solid shape (CCW winding).
1147   Hole = 2, /// Hole (CW winding).
1148 }
1149 
1150 /// Line cap style.
1151 /// Group: render_styles
1152 public enum NVGLineCap {
1153   Butt, ///
1154   Round, ///
1155   Square, ///
1156   Bevel, ///
1157   Miter, ///
1158 }
1159 
1160 /// Text align.
1161 /// Group: text_api
1162 public align(1) struct NVGTextAlign {
1163 align(1):
1164   /// Horizontal align.
1165   enum H : ubyte {
1166     Left   = 0, /// Default, align text horizontally to left.
1167     Center = 1, /// Align text horizontally to center.
1168     Right  = 2, /// Align text horizontally to right.
1169   }
1170 
1171   /// Vertical align.
1172   enum V : ubyte {
1173     Baseline = 0, /// Default, align text vertically to baseline.
1174     Top      = 1, /// Align text vertically to top.
1175     Middle   = 2, /// Align text vertically to middle.
1176     Bottom   = 3, /// Align text vertically to bottom.
1177   }
1178 
1179 pure nothrow @safe @nogc:
1180 public:
1181   this (H h) { pragma(inline, true); value = h; } ///
1182   this (V v) { pragma(inline, true); value = cast(ubyte)(v<<4); } ///
1183   this (H h, V v) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1184   this (V v, H h) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1185   void reset () { pragma(inline, true); value = 0; } ///
1186   void reset (H h, V v) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1187   void reset (V v, H h) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1188 @property:
1189   bool left () const { pragma(inline, true); return ((value&0x0f) == H.Left); } ///
1190   void left (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Left : 0)); } ///
1191   bool center () const { pragma(inline, true); return ((value&0x0f) == H.Center); } ///
1192   void center (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Center : 0)); } ///
1193   bool right () const { pragma(inline, true); return ((value&0x0f) == H.Right); } ///
1194   void right (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Right : 0)); } ///
1195   //
1196   bool baseline () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Baseline); } ///
1197   void baseline (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Baseline<<4 : 0)); } ///
1198   bool top () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Top); } ///
1199   void top (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Top<<4 : 0)); } ///
1200   bool middle () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Middle); } ///
1201   void middle (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Middle<<4 : 0)); } ///
1202   bool bottom () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Bottom); } ///
1203   void bottom (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Bottom<<4 : 0)); } ///
1204   //
1205   H horizontal () const { pragma(inline, true); return cast(H)(value&0x0f); } ///
1206   void horizontal (H v) { pragma(inline, true); value = (value&0xf0)|v; } ///
1207   //
1208   V vertical () const { pragma(inline, true); return cast(V)((value>>4)&0x0f); } ///
1209   void vertical (V v) { pragma(inline, true); value = (value&0x0f)|cast(ubyte)(v<<4); } ///
1210   //
1211 private:
1212   ubyte value = 0; // low nibble: horizontal; high nibble: vertical
1213 }
1214 
1215 /// Blending type.
1216 /// Group: composite_operation
1217 public enum NVGBlendFactor {
1218   Zero = 1<<0, ///
1219   One = 1<<1, ///
1220   SrcColor = 1<<2, ///
1221   OneMinusSrcColor = 1<<3, ///
1222   DstColor = 1<<4, ///
1223   OneMinusDstColor = 1<<5, ///
1224   SrcAlpha = 1<<6, ///
1225   OneMinusSrcAlpha = 1<<7, ///
1226   DstAlpha = 1<<8, ///
1227   OneMinusDstAlpha = 1<<9, ///
1228   SrcAlphaSaturate = 1<<10, ///
1229 }
1230 
1231 /// Composite operation (HTML5-alike).
1232 /// Group: composite_operation
1233 public enum NVGCompositeOperation {
1234   SourceOver, ///
1235   SourceIn, ///
1236   SourceOut, ///
1237   SourceAtop, ///
1238   DestinationOver, ///
1239   DestinationIn, ///
1240   DestinationOut, ///
1241   DestinationAtop, ///
1242   Lighter, ///
1243   Copy, ///
1244   Xor, ///
1245 }
1246 
1247 /// Composite operation state.
1248 /// Group: composite_operation
1249 public struct NVGCompositeOperationState {
1250   bool simple; /// `true`: use `glBlendFunc()` instead of `glBlendFuncSeparate()`
1251   NVGBlendFactor srcRGB; ///
1252   NVGBlendFactor dstRGB; ///
1253   NVGBlendFactor srcAlpha; ///
1254   NVGBlendFactor dstAlpha; ///
1255 }
1256 
1257 /// Mask combining more
1258 /// Group: clipping
1259 public enum NVGClipMode {
1260   None, /// normal rendering (i.e. render path instead of modifying clip region)
1261   Union, /// old mask will be masked with the current one; this is the default mode for [clip]
1262   Or, /// new mask will be added to the current one (logical `OR` operation);
1263   Xor, /// new mask will be logically `XOR`ed with the current one
1264   Sub, /// "subtract" current path from mask
1265   Replace, /// replace current mask
1266   Add = Or, /// Synonym
1267 }
1268 
1269 /// Glyph position info.
1270 /// Group: text_api
1271 public struct NVGGlyphPosition {
1272   usize strpos;     /// Position of the glyph in the input string.
1273   float x;          /// The x-coordinate of the logical glyph position.
1274   float minx, maxx; /// The bounds of the glyph shape.
1275 }
1276 
1277 /// Text row storage.
1278 /// Group: text_api
1279 public struct NVGTextRow(CT) if (isAnyCharType!CT) {
1280   alias CharType = CT;
1281   const(CT)[] s;
1282   int start;        /// Index in the input text where the row starts.
1283   int end;          /// Index in the input text where the row ends (one past the last character).
1284   float width;      /// Logical width of the row.
1285   float minx, maxx; /// Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.
1286   /// Get rest of the string.
1287   @property const(CT)[] rest () const pure nothrow @trusted @nogc { pragma(inline, true); return (end <= s.length ? s[end..$] : null); }
1288   /// Get current row.
1289   @property const(CT)[] row () const pure nothrow @trusted @nogc { pragma(inline, true); return s[start..end]; }
1290   @property const(CT)[] string () const pure nothrow @trusted @nogc { pragma(inline, true); return s; }
1291   @property void string(CT) (const(CT)[] v) pure nothrow @trusted @nogc { pragma(inline, true); s = v; }
1292 }
1293 
1294 /// Image creation flags.
1295 /// Group: images
1296 public enum NVGImageFlag : uint {
1297   None            =    0, /// Nothing special.
1298   GenerateMipmaps = 1<<0, /// Generate mipmaps during creation of the image.
1299   RepeatX         = 1<<1, /// Repeat image in X direction.
1300   RepeatY         = 1<<2, /// Repeat image in Y direction.
1301   FlipY           = 1<<3, /// Flips (inverses) image in Y direction when rendered.
1302   Premultiplied   = 1<<4, /// Image data has premultiplied alpha.
1303   ClampToBorderX  = 1<<5, /// Clamp image to border (instead of clamping to edge by default)
1304   ClampToBorderY  = 1<<6, /// Clamp image to border (instead of clamping to edge by default)
1305   NoFiltering     = 1<<8, /// use GL_NEAREST instead of GL_LINEAR. Only affects upscaling if GenerateMipmaps is active.
1306   Nearest = NoFiltering,  /// compatibility with original NanoVG
1307   NoDelete        = 1<<16,/// Do not delete GL texture handle.
1308 }
1309 
1310 alias NVGImageFlags = NVGImageFlag; /// Backwards compatibility for [NVGImageFlag].
1311 
1312 
1313 // ////////////////////////////////////////////////////////////////////////// //
1314 private:
1315 
1316 static T* xdup(T) (const(T)* ptr, int count) nothrow @trusted @nogc {
1317   import core.stdc.stdlib : malloc;
1318   import core.stdc.string : memcpy;
1319   if (count == 0) return null;
1320   T* res = cast(T*)malloc(T.sizeof*count);
1321   if (res is null) assert(0, "NanoVega: out of memory");
1322   memcpy(res, ptr, T.sizeof*count);
1323   return res;
1324 }
1325 
1326 // Internal Render API
1327 enum NVGtexture {
1328   Alpha = 0x01,
1329   RGBA  = 0x02,
1330 }
1331 
1332 struct NVGscissor {
1333   NVGMatrix xform;
1334   float[2] extent = -1.0f;
1335 }
1336 
1337 /// General NanoVega vertex struct. Contains geometry coordinates, and (sometimes unused) texture coordinates.
1338 public struct NVGVertex {
1339   float x, y, u, v;
1340 }
1341 
1342 struct NVGpath {
1343   int first;
1344   int count;
1345   bool closed;
1346   int nbevel;
1347   NVGVertex* fill;
1348   int nfill;
1349   NVGVertex* stroke;
1350   int nstroke;
1351   NVGWinding mWinding;
1352   bool convex;
1353   bool cloned;
1354 
1355   @disable this (this); // no copies
1356   void opAssign() (in auto ref NVGpath a) { static assert(0, "no copies!"); }
1357 
1358   void clear () nothrow @trusted @nogc {
1359     import core.stdc.stdlib : free;
1360     import core.stdc.string : memset;
1361     if (cloned) {
1362       if (stroke !is null && stroke !is fill) free(stroke);
1363       if (fill !is null) free(fill);
1364     }
1365     memset(&this, 0, this.sizeof);
1366   }
1367 
1368   // won't clear current path
1369   void copyFrom (const NVGpath* src) nothrow @trusted @nogc {
1370     import core.stdc.string : memcpy;
1371     assert(src !is null);
1372     memcpy(&this, src, NVGpath.sizeof);
1373     this.fill = xdup(src.fill, src.nfill);
1374     if (src.stroke is src.fill) {
1375       this.stroke = this.fill;
1376     } else {
1377       this.stroke = xdup(src.stroke, src.nstroke);
1378     }
1379     this.cloned = true;
1380   }
1381 
1382   public @property const(NVGVertex)[] fillVertices () const pure nothrow @trusted @nogc {
1383     pragma(inline, true);
1384     return (nfill > 0 ? fill[0..nfill] : null);
1385   }
1386 
1387   public @property const(NVGVertex)[] strokeVertices () const pure nothrow @trusted @nogc {
1388     pragma(inline, true);
1389     return (nstroke > 0 ? stroke[0..nstroke] : null);
1390   }
1391 
1392   public @property NVGWinding winding () const pure nothrow @trusted @nogc { pragma(inline, true); return mWinding; }
1393   public @property bool complex () const pure nothrow @trusted @nogc { pragma(inline, true); return !convex; }
1394 }
1395 
1396 
1397 struct NVGparams {
1398   void* userPtr;
1399   bool edgeAntiAlias;
1400   bool fontAA;
1401   bool function (void* uptr) nothrow @trusted @nogc renderCreate;
1402   int function (void* uptr, NVGtexture type, int w, int h, int imageFlags, const(ubyte)* data) nothrow @trusted @nogc renderCreateTexture;
1403   bool function (void* uptr, int image) nothrow @trusted @nogc renderTextureIncRef;
1404   bool function (void* uptr, int image) nothrow @trusted @nogc renderDeleteTexture; // this basically does decref; also, it should be thread-safe, and postpone real deletion to next `renderViewport()` call
1405   bool function (void* uptr, int image, int x, int y, int w, int h, const(ubyte)* data) nothrow @trusted @nogc renderUpdateTexture;
1406   bool function (void* uptr, int image, int* w, int* h) nothrow @trusted @nogc renderGetTextureSize;
1407   void function (void* uptr, int width, int height) nothrow @trusted @nogc renderViewport; // called in [beginFrame]
1408   void function (void* uptr) nothrow @trusted @nogc renderCancel;
1409   void function (void* uptr) nothrow @trusted @nogc renderFlush;
1410   void function (void* uptr) nothrow @trusted @nogc renderPushClip; // backend should support stack of at least [NVG_MAX_STATES] elements
1411   void function (void* uptr) nothrow @trusted @nogc renderPopClip; // backend should support stack of at least [NVG_MAX_STATES] elements
1412   void function (void* uptr) nothrow @trusted @nogc renderResetClip; // reset current clip region to `non-clipped`
1413   void function (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, float fringe, const(float)* bounds, const(NVGpath)* paths, int npaths, bool evenOdd) nothrow @trusted @nogc renderFill;
1414   void function (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const(NVGpath)* paths, int npaths) nothrow @trusted @nogc renderStroke;
1415   void function (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, const(NVGVertex)* verts, int nverts) nothrow @trusted @nogc renderTriangles;
1416   void function (void* uptr, in ref NVGMatrix mat) nothrow @trusted @nogc renderSetAffine;
1417   void function (void* uptr) nothrow @trusted @nogc renderDelete;
1418 }
1419 
1420 // ////////////////////////////////////////////////////////////////////////// //
1421 private:
1422 
1423 enum NVG_INIT_FONTIMAGE_SIZE = 512;
1424 enum NVG_MAX_FONTIMAGE_SIZE  = 2048;
1425 enum NVG_MAX_FONTIMAGES      = 4;
1426 
1427 enum NVG_INIT_COMMANDS_SIZE = 256;
1428 enum NVG_INIT_POINTS_SIZE   = 128;
1429 enum NVG_INIT_PATHS_SIZE    = 16;
1430 enum NVG_INIT_VERTS_SIZE    = 256;
1431 enum NVG_MAX_STATES         = 32;
1432 
1433 public enum NVG_KAPPA90 = 0.5522847493f; /// Length proportional to radius of a cubic bezier handle for 90deg arcs.
1434 enum NVG_MIN_FEATHER = 0.001f; // it should be greater than zero, 'cause it is used in shader for divisions
1435 
1436 enum Command {
1437   MoveTo = 0,
1438   LineTo = 1,
1439   BezierTo = 2,
1440   Close = 3,
1441   Winding = 4,
1442 }
1443 
1444 enum PointFlag : int {
1445   Corner = 0x01,
1446   Left = 0x02,
1447   Bevel = 0x04,
1448   InnerBevelPR = 0x08,
1449 }
1450 
1451 struct NVGstate {
1452   NVGCompositeOperationState compositeOperation;
1453   bool shapeAntiAlias = true;
1454   NVGPaint fill;
1455   NVGPaint stroke;
1456   float strokeWidth = 1.0f;
1457   float miterLimit = 10.0f;
1458   NVGLineCap lineJoin = NVGLineCap.Miter;
1459   NVGLineCap lineCap = NVGLineCap.Butt;
1460   float alpha = 1.0f;
1461   NVGMatrix xform;
1462   NVGscissor scissor;
1463   float fontSize = 16.0f;
1464   float letterSpacing = 0.0f;
1465   float lineHeight = 1.0f;
1466   float fontBlur = 0.0f;
1467   NVGTextAlign textAlign;
1468   int fontId = 0;
1469   bool evenOddMode = false; // use even-odd filling rule (required for some svgs); otherwise use non-zero fill
1470   // dashing
1471   enum MaxDashes = 32; // max 16 dashes
1472   float[MaxDashes] dashes;
1473   uint dashCount = 0;
1474   uint lastFlattenDashCount = 0;
1475   float dashStart = 0;
1476   float totalDashLen;
1477   bool firstDashIsGap = false;
1478   // dasher state for flattener
1479   bool dasherActive = false;
1480 
1481   void clearPaint () nothrow @trusted @nogc {
1482     fill.clear();
1483     stroke.clear();
1484   }
1485 }
1486 
1487 struct NVGpoint {
1488   float x, y;
1489   float dx, dy;
1490   float len;
1491   float dmx, dmy;
1492   ubyte flags;
1493 }
1494 
1495 struct NVGpathCache {
1496   NVGpoint* points;
1497   int npoints;
1498   int cpoints;
1499   NVGpath* paths;
1500   int npaths;
1501   int cpaths;
1502   NVGVertex* verts;
1503   int nverts;
1504   int cverts;
1505   float[4] bounds;
1506   // this is required for saved paths
1507   bool strokeReady;
1508   bool fillReady;
1509   float strokeAlphaMul;
1510   float strokeWidth;
1511   float fringeWidth;
1512   bool evenOddMode;
1513   NVGClipMode clipmode;
1514   // non-saved path will not have this
1515   float* commands;
1516   int ncommands;
1517 
1518   @disable this (this); // no copies
1519   void opAssign() (in auto ref NVGpathCache a) { static assert(0, "no copies!"); }
1520 
1521   // won't clear current path
1522   void copyFrom (const NVGpathCache* src) nothrow @trusted @nogc {
1523     import core.stdc.stdlib : malloc;
1524     import core.stdc.string : memcpy, memset;
1525     assert(src !is null);
1526     memcpy(&this, src, NVGpathCache.sizeof);
1527     this.points = xdup(src.points, src.npoints);
1528     this.cpoints = src.npoints;
1529     this.verts = xdup(src.verts, src.nverts);
1530     this.cverts = src.nverts;
1531     this.commands = xdup(src.commands, src.ncommands);
1532     if (src.npaths > 0) {
1533       this.paths = cast(NVGpath*)malloc(src.npaths*NVGpath.sizeof);
1534       memset(this.paths, 0, npaths*NVGpath.sizeof);
1535       foreach (immutable pidx; 0..npaths) this.paths[pidx].copyFrom(&src.paths[pidx]);
1536       this.cpaths = src.npaths;
1537     } else {
1538       this.npaths = this.cpaths = 0;
1539     }
1540   }
1541 
1542   void clear () nothrow @trusted @nogc {
1543     import core.stdc.stdlib : free;
1544     import core.stdc.string : memset;
1545     if (paths !is null) {
1546       foreach (ref p; paths[0..npaths]) p.clear();
1547       free(paths);
1548     }
1549     if (points !is null) free(points);
1550     if (verts !is null) free(verts);
1551     if (commands !is null) free(commands);
1552     memset(&this, 0, this.sizeof);
1553   }
1554 }
1555 
1556 /// Pointer to opaque NanoVega context structure.
1557 /// Group: context_management
1558 public alias NVGContext = NVGcontextinternal*;
1559 
1560 /// FontStash context
1561 /// Group: font_stash
1562 public alias FONSContext = FONScontextInternal*;
1563 
1564 /// Returns FontStash context of the given NanoVega context.
1565 /// Group: font_stash
1566 public FONSContext fonsContext (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null && ctx.contextAlive ? ctx.fs : null); }
1567 
1568 /// Returns scale that should be applied to FontStash parameters due to matrix transformations on the context (or 1)
1569 /// Group: font_stash
1570 public float fonsScale (NVGContext ctx) /*pure*/ nothrow @trusted @nogc {
1571   pragma(inline, true);
1572   return (ctx !is null && ctx.contextAlive && ctx.nstates > 0 ? nvg__getFontScale(&ctx.states.ptr[ctx.nstates-1])*ctx.devicePxRatio : 1);
1573 }
1574 
1575 /// Setup FontStash from the given NanoVega context font parameters. Note that this will apply transformation scale too.
1576 /// Returns `false` if FontStash or NanoVega context is not active.
1577 /// Group: font_stash
1578 public bool setupFonsFrom (FONSContext stash, NVGContext ctx) nothrow @trusted @nogc {
1579   if (stash is null || ctx is null || !ctx.contextAlive || ctx.nstates == 0) return false;
1580   NVGstate* state = nvg__getState(ctx);
1581   immutable float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
1582   stash.size = state.fontSize*scale;
1583   stash.spacing = state.letterSpacing*scale;
1584   stash.blur = state.fontBlur*scale;
1585   stash.textAlign = state.textAlign;
1586   stash.fontId = state.fontId;
1587   return true;
1588 }
1589 
1590 /// Setup NanoVega context font parameters from the given FontStash. Note that NanoVega can apply transformation scale later.
1591 /// Returns `false` if FontStash or NanoVega context is not active.
1592 /// Group: font_stash
1593 public bool setupCtxFrom (NVGContext ctx, FONSContext stash) nothrow @trusted @nogc {
1594   if (stash is null || ctx is null || !ctx.contextAlive || ctx.nstates == 0) return false;
1595   NVGstate* state = nvg__getState(ctx);
1596   immutable float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
1597   state.fontSize = stash.size;
1598   state.letterSpacing = stash.spacing;
1599   state.fontBlur = stash.blur;
1600   state.textAlign = stash.textAlign;
1601   state.fontId = stash.fontId;
1602   return true;
1603 }
1604 
1605 /** Bezier curve rasterizer.
1606  *
1607  * De Casteljau Bezier rasterizer is faster, but currently rasterizing curves with cusps sligtly wrong.
1608  * It doesn't really matter in practice.
1609  *
1610  * AFD tesselator is somewhat slower, but does cusps better.
1611  *
1612  * McSeem rasterizer should have the best quality, bit it is the slowest method. Basically, you will
1613  * never notice any visial difference (and this code is not really debugged), so you probably should
1614  * not use it. It is there for further experiments.
1615  */
1616 public enum NVGTesselation {
1617   DeCasteljau, /// default: standard well-known tesselation algorithm
1618   AFD, /// adaptive forward differencing
1619   DeCasteljauMcSeem, /// standard well-known tesselation algorithm, with improvements from Maxim Shemanarev; slowest one, but should give best results
1620 }
1621 
1622 /// Default tesselator for Bezier curves.
1623 public __gshared NVGTesselation NVG_DEFAULT_TESSELATOR = NVGTesselation.DeCasteljau;
1624 
1625 
1626 // some public info
1627 
1628 /// valid only inside [beginFrame]/[endFrame]
1629 /// Group: context_management
1630 public int width (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.mWidth : 0); }
1631 
1632 /// valid only inside [beginFrame]/[endFrame]
1633 /// Group: context_management
1634 public int height (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.mHeight : 0); }
1635 
1636 /// valid only inside [beginFrame]/[endFrame]
1637 /// Group: context_management
1638 public float devicePixelRatio (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.devicePxRatio : float.nan); }
1639 
1640 /// Returns `true` if [beginFrame] was called, and neither [endFrame], nor [cancelFrame] were.
1641 /// Group: context_management
1642 public bool inFrame (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null && ctx.contextAlive ? ctx.nstates > 0 : false); }
1643 
1644 // path autoregistration
1645 
1646 /// [pickid] to stop autoregistration.
1647 /// Group: context_management
1648 public enum NVGNoPick = -1;
1649 
1650 /// >=0: this pickid will be assigned to all filled/stroked paths
1651 /// Group: context_management
1652 public int pickid (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.pathPickId : NVGNoPick); }
1653 
1654 /// >=0: this pickid will be assigned to all filled/stroked paths
1655 /// Group: context_management
1656 public void pickid (NVGContext ctx, int v) nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null) ctx.pathPickId = v; }
1657 
1658 /// pick autoregistration mode; see [NVGPickKind]
1659 /// Group: context_management
1660 public uint pickmode (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.pathPickRegistered&NVGPickKind.All : 0); }
1661 
1662 /// pick autoregistration mode; see [NVGPickKind]
1663 /// Group: context_management
1664 public void pickmode (NVGContext ctx, uint v) nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null) ctx.pathPickRegistered = (ctx.pathPickRegistered&0xffff_0000u)|(v&NVGPickKind.All); }
1665 
1666 // tesselator options
1667 
1668 /// Get current Bezier tesselation mode. See [NVGTesselation].
1669 /// Group: context_management
1670 public NVGTesselation tesselation (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.tesselatortype : NVGTesselation.DeCasteljau); }
1671 
1672 /// Set current Bezier tesselation mode. See [NVGTesselation].
1673 /// Group: context_management
1674 public void tesselation (NVGContext ctx, NVGTesselation v) nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null) ctx.tesselatortype = v; }
1675 
1676 
1677 private struct NVGcontextinternal {
1678 private:
1679   NVGparams params;
1680   float* commands;
1681   int ccommands;
1682   int ncommands;
1683   float commandx, commandy;
1684   NVGstate[NVG_MAX_STATES] states;
1685   int nstates;
1686   NVGpathCache* cache;
1687   public float tessTol;
1688   public float angleTol; // 0.0f -- angle tolerance for McSeem Bezier rasterizer
1689   public float cuspLimit; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
1690   float distTol;
1691   public float fringeWidth;
1692   float devicePxRatio;
1693   FONSContext fs;
1694   NVGImage[NVG_MAX_FONTIMAGES] fontImages;
1695   int fontImageIdx;
1696   int drawCallCount;
1697   int fillTriCount;
1698   int strokeTriCount;
1699   int textTriCount;
1700   NVGTesselation tesselatortype;
1701   // picking API
1702   NVGpickScene* pickScene;
1703   int pathPickId; // >=0: register all paths for picking using this id
1704   uint pathPickRegistered; // if [pathPickId] >= 0, this is used to avoid double-registration (see [NVGPickKind]); hi 16 bit is check flags, lo 16 bit is mode
1705   // path recording
1706   NVGPathSet recset;
1707   int recstart; // used to cancel recording
1708   bool recblockdraw;
1709   // internals
1710   NVGMatrix gpuAffine;
1711   int mWidth, mHeight;
1712   // image manager
1713   shared int imageCount; // number of alive images in this context
1714   bool contextAlive; // context can be dead, but still contain some images
1715 
1716   @disable this (this); // no copies
1717   void opAssign() (in auto ref NVGcontextinternal a) { static assert(0, "no copies!"); }
1718 
1719   // debug feature
1720   public @property int getImageCount () nothrow @trusted @nogc {
1721     import core.atomic;
1722     return atomicLoad(imageCount);
1723   }
1724 }
1725 
1726 /** Returns number of tesselated pathes in context.
1727  *
1728  * Tesselated pathes are either triangle strips (for strokes), or
1729  * triangle fans (for fills). Note that NanoVega can generate some
1730  * surprising pathes (like fringe stroke for antialiasing, for example).
1731  *
1732  * One render path can contain vertices both for fill, and for stroke triangles.
1733  */
1734 public int renderPathCount (NVGContext ctx) pure nothrow @trusted @nogc {
1735   pragma(inline, true);
1736   return (ctx !is null && ctx.contextAlive ? ctx.cache.npaths : 0);
1737 }
1738 
1739 /** Get vertices of "fill" triangle fan for the given render path. Can return empty slice.
1740  *
1741  * $(WARNING Returned slice can be invalidated by any other NanoVega API call
1742  *           (except the calls to render path accessors), and using it in such
1743  *           case is UB. So copy vertices to freshly allocated array if you want
1744  *           to keep them for further processing.)
1745  */
1746 public const(NVGVertex)[] renderPathFillVertices (NVGContext ctx, int pathidx) pure nothrow @trusted @nogc {
1747   pragma(inline, true);
1748   return (ctx !is null && ctx.contextAlive && pathidx >= 0 && pathidx < ctx.cache.npaths ? ctx.cache.paths[pathidx].fillVertices : null);
1749 }
1750 
1751 /** Get vertices of "stroke" triangle strip for the given render path. Can return empty slice.
1752  *
1753  * $(WARNING Returned slice can be invalidated by any other NanoVega API call
1754  *           (except the calls to render path accessors), and using it in such
1755  *           case is UB. So copy vertices to freshly allocated array if you want
1756  *           to keep them for further processing.)
1757  */
1758 public const(NVGVertex)[] renderPathStrokeVertices (NVGContext ctx, int pathidx) pure nothrow @trusted @nogc {
1759   pragma(inline, true);
1760   return (ctx !is null && ctx.contextAlive && pathidx >= 0 && pathidx < ctx.cache.npaths ? ctx.cache.paths[pathidx].strokeVertices : null);
1761 
1762 }
1763 
1764 /// Returns winding for the given render path.
1765 public NVGWinding renderPathWinding (NVGContext ctx, int pathidx) pure nothrow @trusted @nogc {
1766   pragma(inline, true);
1767   return (ctx !is null && ctx.contextAlive && pathidx >= 0 && pathidx < ctx.cache.npaths ? ctx.cache.paths[pathidx].winding : NVGWinding.CCW);
1768 
1769 }
1770 
1771 /// Returns "complex path" flag for the given render path.
1772 public bool renderPathComplex (NVGContext ctx, int pathidx) pure nothrow @trusted @nogc {
1773   pragma(inline, true);
1774   return (ctx !is null && ctx.contextAlive && pathidx >= 0 && pathidx < ctx.cache.npaths ? ctx.cache.paths[pathidx].complex : false);
1775 
1776 }
1777 
1778 void nvg__imageIncRef (NVGContext ctx, int imgid, bool increfInGL=true) nothrow @trusted @nogc {
1779   if (ctx !is null && imgid > 0) {
1780     import core.atomic : atomicOp;
1781     atomicOp!"+="(ctx.imageCount, 1);
1782     version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("image[++]ref: context %p: %d image refs (%d)\n", ctx, ctx.imageCount, imgid); }
1783     if (ctx.contextAlive && increfInGL) ctx.params.renderTextureIncRef(ctx.params.userPtr, imgid);
1784   }
1785 }
1786 
1787 void nvg__imageDecRef (NVGContext ctx, int imgid) nothrow @trusted @nogc {
1788   if (ctx !is null && imgid > 0) {
1789     import core.atomic : atomicOp;
1790     int icnt = atomicOp!"-="(ctx.imageCount, 1);
1791     if (icnt < 0) assert(0, "NanoVega: internal image refcounting error");
1792     version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("image[--]ref: context %p: %d image refs (%d)\n", ctx, ctx.imageCount, imgid); }
1793     if (ctx.contextAlive) ctx.params.renderDeleteTexture(ctx.params.userPtr, imgid);
1794     version(nanovega_debug_image_manager) if (!ctx.contextAlive) { import core.stdc.stdio; printf("image[--]ref: zombie context %p: %d image refs (%d)\n", ctx, ctx.imageCount, imgid); }
1795     if (!ctx.contextAlive && icnt == 0) {
1796       // it is finally safe to free context memory
1797       import core.stdc.stdlib : free;
1798       version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("killed zombie context %p\n", ctx); }
1799       free(ctx);
1800     }
1801   }
1802 }
1803 
1804 
1805 public import core.stdc.math :
1806   nvg__sqrtf = sqrtf,
1807   nvg__modf = fmodf,
1808   nvg__sinf = sinf,
1809   nvg__cosf = cosf,
1810   nvg__tanf = tanf,
1811   nvg__atan2f = atan2f,
1812   nvg__acosf = acosf,
1813   nvg__ceilf = ceilf;
1814 
1815 version(Windows) {
1816   public int nvg__lrintf (float f) nothrow @trusted @nogc { pragma(inline, true); return cast(int)(f+0.5); }
1817 } else {
1818   public import core.stdc.math : nvg__lrintf = lrintf;
1819 }
1820 
1821 public auto nvg__min(T) (T a, T b) { pragma(inline, true); return (a < b ? a : b); }
1822 public auto nvg__max(T) (T a, T b) { pragma(inline, true); return (a > b ? a : b); }
1823 public auto nvg__clamp(T) (T a, T mn, T mx) { pragma(inline, true); return (a < mn ? mn : (a > mx ? mx : a)); }
1824 //float nvg__absf() (float a) { pragma(inline, true); return (a >= 0.0f ? a : -a); }
1825 public auto nvg__sign(T) (T a) { pragma(inline, true); return (a >= cast(T)0 ? cast(T)1 : cast(T)(-1)); }
1826 public float nvg__cross() (float dx0, float dy0, float dx1, float dy1) { pragma(inline, true); return (dx1*dy0-dx0*dy1); }
1827 
1828 //public import core.stdc.math : nvg__absf = fabsf;
1829 public import core.math : nvg__absf = fabs;
1830 
1831 
1832 float nvg__normalize (float* x, float* y) nothrow @safe @nogc {
1833   float d = nvg__sqrtf((*x)*(*x)+(*y)*(*y));
1834   if (d > 1e-6f) {
1835     immutable float id = 1.0f/d;
1836     *x *= id;
1837     *y *= id;
1838   }
1839   return d;
1840 }
1841 
1842 void nvg__deletePathCache (ref NVGpathCache* c) nothrow @trusted @nogc {
1843   if (c !is null) {
1844     c.clear();
1845     free(c);
1846   }
1847 }
1848 
1849 NVGpathCache* nvg__allocPathCache () nothrow @trusted @nogc {
1850   NVGpathCache* c = cast(NVGpathCache*)malloc(NVGpathCache.sizeof);
1851   if (c is null) goto error;
1852   memset(c, 0, NVGpathCache.sizeof);
1853 
1854   c.points = cast(NVGpoint*)malloc(NVGpoint.sizeof*NVG_INIT_POINTS_SIZE);
1855   if (c.points is null) goto error;
1856   assert(c.npoints == 0);
1857   c.cpoints = NVG_INIT_POINTS_SIZE;
1858 
1859   c.paths = cast(NVGpath*)malloc(NVGpath.sizeof*NVG_INIT_PATHS_SIZE);
1860   if (c.paths is null) goto error;
1861   assert(c.npaths == 0);
1862   c.cpaths = NVG_INIT_PATHS_SIZE;
1863 
1864   c.verts = cast(NVGVertex*)malloc(NVGVertex.sizeof*NVG_INIT_VERTS_SIZE);
1865   if (c.verts is null) goto error;
1866   assert(c.nverts == 0);
1867   c.cverts = NVG_INIT_VERTS_SIZE;
1868 
1869   return c;
1870 
1871 error:
1872   nvg__deletePathCache(c);
1873   return null;
1874 }
1875 
1876 void nvg__setDevicePixelRatio (NVGContext ctx, float ratio) pure nothrow @safe @nogc {
1877   ctx.tessTol = 0.25f/ratio;
1878   ctx.distTol = 0.01f/ratio;
1879   ctx.fringeWidth = 1.0f/ratio;
1880   ctx.devicePxRatio = ratio;
1881 }
1882 
1883 NVGCompositeOperationState nvg__compositeOperationState (NVGCompositeOperation op) pure nothrow @safe @nogc {
1884   NVGCompositeOperationState state;
1885   NVGBlendFactor sfactor, dfactor;
1886 
1887        if (op == NVGCompositeOperation.SourceOver) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.OneMinusSrcAlpha;}
1888   else if (op == NVGCompositeOperation.SourceIn) { sfactor = NVGBlendFactor.DstAlpha; dfactor = NVGBlendFactor.Zero; }
1889   else if (op == NVGCompositeOperation.SourceOut) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.Zero; }
1890   else if (op == NVGCompositeOperation.SourceAtop) { sfactor = NVGBlendFactor.DstAlpha; dfactor = NVGBlendFactor.OneMinusSrcAlpha; }
1891   else if (op == NVGCompositeOperation.DestinationOver) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.One; }
1892   else if (op == NVGCompositeOperation.DestinationIn) { sfactor = NVGBlendFactor.Zero; dfactor = NVGBlendFactor.SrcAlpha; }
1893   else if (op == NVGCompositeOperation.DestinationOut) { sfactor = NVGBlendFactor.Zero; dfactor = NVGBlendFactor.OneMinusSrcAlpha; }
1894   else if (op == NVGCompositeOperation.DestinationAtop) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.SrcAlpha; }
1895   else if (op == NVGCompositeOperation.Lighter) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.One; }
1896   else if (op == NVGCompositeOperation.Copy) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.Zero;  }
1897   else if (op == NVGCompositeOperation.Xor) {
1898     state.simple = false;
1899     state.srcRGB = NVGBlendFactor.OneMinusDstColor;
1900     state.srcAlpha = NVGBlendFactor.OneMinusDstAlpha;
1901     state.dstRGB = NVGBlendFactor.OneMinusSrcColor;
1902     state.dstAlpha = NVGBlendFactor.OneMinusSrcAlpha;
1903     return state;
1904   }
1905   else { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.OneMinusSrcAlpha; } // default value for invalid op: SourceOver
1906 
1907   state.simple = true;
1908   state.srcAlpha = sfactor;
1909   state.dstAlpha = dfactor;
1910   return state;
1911 }
1912 
1913 NVGstate* nvg__getState (NVGContext ctx) pure nothrow @trusted @nogc {
1914   pragma(inline, true);
1915   if (ctx is null || !ctx.contextAlive || ctx.nstates == 0) assert(0, "NanoVega: cannot perform commands on inactive context");
1916   return &ctx.states.ptr[ctx.nstates-1];
1917 }
1918 
1919 // Constructor called by the render back-end.
1920 NVGContext createInternal (NVGparams* params) nothrow @trusted @nogc {
1921   FONSParams fontParams;
1922   NVGContext ctx = cast(NVGContext)malloc(NVGcontextinternal.sizeof);
1923   if (ctx is null) goto error;
1924   memset(ctx, 0, NVGcontextinternal.sizeof);
1925 
1926   ctx.angleTol = 0; // angle tolerance for McSeem Bezier rasterizer
1927   ctx.cuspLimit = 0; // cusp limit for McSeem Bezier rasterizer (0: real cusps)
1928 
1929   ctx.contextAlive = true;
1930 
1931   ctx.params = *params;
1932   //ctx.fontImages[0..NVG_MAX_FONTIMAGES] = 0;
1933 
1934   ctx.commands = cast(float*)malloc(float.sizeof*NVG_INIT_COMMANDS_SIZE);
1935   if (ctx.commands is null) goto error;
1936   ctx.ncommands = 0;
1937   ctx.ccommands = NVG_INIT_COMMANDS_SIZE;
1938 
1939   ctx.cache = nvg__allocPathCache();
1940   if (ctx.cache is null) goto error;
1941 
1942   ctx.save();
1943   ctx.reset();
1944 
1945   nvg__setDevicePixelRatio(ctx, 1.0f);
1946   ctx.mWidth = ctx.mHeight = 0;
1947 
1948   if (!ctx.params.renderCreate(ctx.params.userPtr)) goto error;
1949 
1950   // init font rendering
1951   memset(&fontParams, 0, fontParams.sizeof);
1952   fontParams.width = NVG_INIT_FONTIMAGE_SIZE;
1953   fontParams.height = NVG_INIT_FONTIMAGE_SIZE;
1954   fontParams.flags = FONSParams.Flag.ZeroTopLeft;
1955   fontParams.renderCreate = null;
1956   fontParams.renderUpdate = null;
1957   fontParams.renderDelete = null;
1958   fontParams.userPtr = null;
1959   ctx.fs = FONSContext.create(fontParams);
1960   if (ctx.fs is null) goto error;
1961 
1962   // create font texture
1963   ctx.fontImages[0].id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.Alpha, fontParams.width, fontParams.height, (ctx.params.fontAA ? 0 : NVGImageFlag.NoFiltering), null);
1964   if (ctx.fontImages[0].id == 0) goto error;
1965   ctx.fontImages[0].ctx = ctx;
1966   ctx.nvg__imageIncRef(ctx.fontImages[0].id, false); // don't increment driver refcount
1967   ctx.fontImageIdx = 0;
1968 
1969   ctx.pathPickId = -1;
1970   ctx.tesselatortype = NVG_DEFAULT_TESSELATOR;
1971 
1972   return ctx;
1973 
1974 error:
1975   ctx.deleteInternal();
1976   return null;
1977 }
1978 
1979 // Called by render backend.
1980 NVGparams* internalParams (NVGContext ctx) nothrow @trusted @nogc {
1981   return &ctx.params;
1982 }
1983 
1984 // Destructor called by the render back-end.
1985 void deleteInternal (ref NVGContext ctx) nothrow @trusted @nogc {
1986   if (ctx is null) return;
1987   if (ctx.contextAlive) {
1988     if (ctx.commands !is null) free(ctx.commands);
1989     nvg__deletePathCache(ctx.cache);
1990 
1991     if (ctx.fs) ctx.fs.kill();
1992 
1993     foreach (uint i; 0..NVG_MAX_FONTIMAGES) ctx.fontImages[i].clear();
1994 
1995     if (ctx.params.renderDelete !is null) ctx.params.renderDelete(ctx.params.userPtr);
1996 
1997     if (ctx.pickScene !is null) nvg__deletePickScene(ctx.pickScene);
1998 
1999     ctx.contextAlive = false;
2000 
2001     import core.atomic : atomicLoad;
2002     if (atomicLoad(ctx.imageCount) == 0) {
2003       version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("destroyed context %p\n", ctx); }
2004       free(ctx);
2005     } else {
2006       version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("context %p is zombie now (%d image refs)\n", ctx, ctx.imageCount); }
2007     }
2008   }
2009 }
2010 
2011 /// Delete NanoVega context.
2012 /// Group: context_management
2013 public void kill (ref NVGContext ctx) nothrow @trusted @nogc {
2014   if (ctx !is null) {
2015     ctx.deleteInternal();
2016     ctx = null;
2017   }
2018 }
2019 
2020 /// Returns `true` if the given context is not `null` and can be used for painting.
2021 /// Group: context_management
2022 public bool valid (in NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null && ctx.contextAlive); }
2023 
2024 
2025 // ////////////////////////////////////////////////////////////////////////// //
2026 // Frame Management
2027 
2028 /** Begin drawing a new frame.
2029  *
2030  * Calls to NanoVega drawing API should be wrapped in [beginFrame] and [endFrame]
2031  *
2032  * [beginFrame] defines the size of the window to render to in relation currently
2033  * set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
2034  * control the rendering on Hi-DPI devices.
2035  *
2036  * For example, GLFW returns two dimension for an opened window: window size and
2037  * frame buffer size. In that case you would set windowWidth/windowHeight to the window size,
2038  * devicePixelRatio to: `windowWidth/windowHeight`.
2039  *
2040  * Default ratio is `1`.
2041  *
2042  * Note that fractional ratio can (and will) distort your fonts and images.
2043  *
2044  * This call also resets pick marks (see picking API for non-rasterized paths),
2045  * path recording, and GPU affine transformatin matrix.
2046  *
2047  * see also [glNVGClearFlags], which returns necessary flags for [glClear].
2048  *
2049  * Group: frame_management
2050  */
2051 public void beginFrame (NVGContext ctx, int windowWidth, int windowHeight, float devicePixelRatio=1.0f) nothrow @trusted @nogc {
2052   import std.math : isNaN;
2053   /*
2054   printf("Tris: draws:%d  fill:%d  stroke:%d  text:%d  TOT:%d\n",
2055          ctx.drawCallCount, ctx.fillTriCount, ctx.strokeTriCount, ctx.textTriCount,
2056          ctx.fillTriCount+ctx.strokeTriCount+ctx.textTriCount);
2057   */
2058   if (ctx.nstates > 0) ctx.cancelFrame();
2059 
2060   if (windowWidth < 1) windowWidth = 1;
2061   if (windowHeight < 1) windowHeight = 1;
2062 
2063   if (isNaN(devicePixelRatio)) devicePixelRatio = (windowHeight > 0 ? cast(float)windowWidth/cast(float)windowHeight : 1024.0/768.0);
2064 
2065   foreach (ref NVGstate st; ctx.states[0..ctx.nstates]) st.clearPaint();
2066   ctx.nstates = 0;
2067   ctx.save();
2068   ctx.reset();
2069 
2070   nvg__setDevicePixelRatio(ctx, devicePixelRatio);
2071 
2072   ctx.params.renderViewport(ctx.params.userPtr, windowWidth, windowHeight);
2073   ctx.mWidth = windowWidth;
2074   ctx.mHeight = windowHeight;
2075 
2076   ctx.recset = null;
2077   ctx.recstart = -1;
2078 
2079   ctx.pathPickId = NVGNoPick;
2080   ctx.pathPickRegistered = 0;
2081 
2082   ctx.drawCallCount = 0;
2083   ctx.fillTriCount = 0;
2084   ctx.strokeTriCount = 0;
2085   ctx.textTriCount = 0;
2086 
2087   ctx.ncommands = 0;
2088   ctx.pathPickRegistered = 0;
2089   nvg__clearPathCache(ctx);
2090 
2091   ctx.gpuAffine = NVGMatrix.Identity;
2092 
2093   nvg__pickBeginFrame(ctx, windowWidth, windowHeight);
2094 }
2095 
2096 /// Cancels drawing the current frame. Cancels path recording.
2097 /// Group: frame_management
2098 public void cancelFrame (NVGContext ctx) nothrow @trusted @nogc {
2099   ctx.cancelRecording();
2100   //ctx.mWidth = 0;
2101   //ctx.mHeight = 0;
2102   // cancel render queue
2103   ctx.params.renderCancel(ctx.params.userPtr);
2104   // clear saved states (this may free some textures)
2105   foreach (ref NVGstate st; ctx.states[0..ctx.nstates]) st.clearPaint();
2106   ctx.nstates = 0;
2107 }
2108 
2109 /// Ends drawing the current frame (flushing remaining render state). Commits recorded paths.
2110 /// Group: frame_management
2111 public void endFrame (NVGContext ctx) nothrow @trusted @nogc {
2112   if (ctx.recset !is null) ctx.recset.takeCurrentPickScene(ctx);
2113   ctx.stopRecording();
2114   //ctx.mWidth = 0;
2115   //ctx.mHeight = 0;
2116   // flush render queue
2117   NVGstate* state = nvg__getState(ctx);
2118   ctx.params.renderFlush(ctx.params.userPtr);
2119   if (ctx.fontImageIdx != 0) {
2120     auto fontImage = ctx.fontImages[ctx.fontImageIdx];
2121     int j = 0, iw, ih;
2122     // delete images that smaller than current one
2123     if (!fontImage.valid) return;
2124     ctx.imageSize(fontImage, iw, ih);
2125     foreach (int i; 0..ctx.fontImageIdx) {
2126       if (ctx.fontImages[i].valid) {
2127         int nw, nh;
2128         ctx.imageSize(ctx.fontImages[i], nw, nh);
2129         if (nw < iw || nh < ih) {
2130           ctx.deleteImage(ctx.fontImages[i]);
2131         } else {
2132           ctx.fontImages[j++] = ctx.fontImages[i];
2133         }
2134       }
2135     }
2136     // make current font image to first
2137     ctx.fontImages[j++] = ctx.fontImages[0];
2138     ctx.fontImages[0] = fontImage;
2139     ctx.fontImageIdx = 0;
2140     // clear all images after j
2141     ctx.fontImages[j..NVG_MAX_FONTIMAGES] = NVGImage.init;
2142   }
2143   // clear saved states (this may free some textures)
2144   foreach (ref NVGstate st; ctx.states[0..ctx.nstates]) st.clearPaint();
2145   ctx.nstates = 0;
2146 }
2147 
2148 
2149 // ////////////////////////////////////////////////////////////////////////// //
2150 // Recording and Replaying Pathes
2151 
2152 // Saved path set.
2153 // Group: path_recording
2154 public alias NVGPathSet = NVGPathSetS*;
2155 
2156 
2157 //TODO: save scissor info?
2158 struct NVGPathSetS {
2159 private:
2160   // either path cache, or text item
2161   static struct Node {
2162     NVGPaint paint;
2163     NVGpathCache* path;
2164   }
2165 
2166 private:
2167   Node* nodes;
2168   int nnodes, cnodes;
2169   NVGpickScene* pickscene;
2170   //int npickscenes, cpickscenes;
2171   NVGContext svctx; // used to do some sanity checks, and to free resources
2172 
2173 private:
2174   Node* allocNode () nothrow @trusted @nogc {
2175     import core.stdc.string : memset;
2176     // grow buffer if necessary
2177     if (nnodes+1 > cnodes) {
2178       import core.stdc.stdlib : realloc;
2179       int newsz = (cnodes == 0 ? 8 : cnodes <= 1024 ? cnodes*2 : cnodes+1024);
2180       nodes = cast(Node*)realloc(nodes, newsz*Node.sizeof);
2181       if (nodes is null) assert(0, "NanoVega: out of memory");
2182       //memset(svp.caches+svp.ccaches, 0, (newsz-svp.ccaches)*NVGpathCache.sizeof);
2183       cnodes = newsz;
2184     }
2185     assert(nnodes < cnodes);
2186     memset(nodes+nnodes, 0, Node.sizeof);
2187     return &nodes[nnodes++];
2188   }
2189 
2190   Node* allocPathNode () nothrow @trusted @nogc {
2191     import core.stdc.stdlib : malloc;
2192     import core.stdc.string : memset;
2193     auto node = allocNode();
2194     // allocate path cache
2195     auto pc = cast(NVGpathCache*)malloc(NVGpathCache.sizeof);
2196     if (pc is null) assert(0, "NanoVega: out of memory");
2197     node.path = pc;
2198     return node;
2199   }
2200 
2201   void clearNode (int idx) nothrow @trusted @nogc {
2202     if (idx < 0 || idx >= nnodes) return;
2203     Node* node = &nodes[idx];
2204     if (svctx !is null && node.paint.image.valid) node.paint.image.clear();
2205     if (node.path !is null) node.path.clear();
2206   }
2207 
2208 private:
2209   void takeCurrentPickScene (NVGContext ctx) nothrow @trusted @nogc {
2210     NVGpickScene* ps = ctx.pickScene;
2211     if (ps is null) return; // nothing to do
2212     if (ps.npaths == 0) return; // pick scene is empty
2213     ctx.pickScene = null;
2214     pickscene = ps;
2215   }
2216 
2217   void replay (NVGContext ctx, in ref NVGColor fillTint, in ref NVGColor strokeTint) nothrow @trusted @nogc {
2218     NVGstate* state = nvg__getState(ctx);
2219     foreach (ref node; nodes[0..nnodes]) {
2220       if (auto cc = node.path) {
2221         if (cc.npaths <= 0) continue;
2222 
2223         if (cc.fillReady) {
2224           NVGPaint fillPaint = node.paint;
2225 
2226           // apply global alpha
2227           fillPaint.innerColor.a *= state.alpha;
2228           fillPaint.middleColor.a *= state.alpha;
2229           fillPaint.outerColor.a *= state.alpha;
2230 
2231           fillPaint.innerColor.applyTint(fillTint);
2232           fillPaint.middleColor.applyTint(fillTint);
2233           fillPaint.outerColor.applyTint(fillTint);
2234 
2235           ctx.params.renderFill(ctx.params.userPtr, state.compositeOperation, cc.clipmode, &fillPaint, &state.scissor, cc.fringeWidth, cc.bounds.ptr, cc.paths, cc.npaths, cc.evenOddMode);
2236 
2237           // count triangles
2238           foreach (int i; 0..cc.npaths) {
2239             NVGpath* path = &cc.paths[i];
2240             ctx.fillTriCount += path.nfill-2;
2241             ctx.fillTriCount += path.nstroke-2;
2242             ctx.drawCallCount += 2;
2243           }
2244         }
2245 
2246         if (cc.strokeReady) {
2247           NVGPaint strokePaint = node.paint;
2248 
2249           strokePaint.innerColor.a *= cc.strokeAlphaMul;
2250           strokePaint.middleColor.a *= cc.strokeAlphaMul;
2251           strokePaint.outerColor.a *= cc.strokeAlphaMul;
2252 
2253           // apply global alpha
2254           strokePaint.innerColor.a *= state.alpha;
2255           strokePaint.middleColor.a *= state.alpha;
2256           strokePaint.outerColor.a *= state.alpha;
2257 
2258           strokePaint.innerColor.applyTint(strokeTint);
2259           strokePaint.middleColor.applyTint(strokeTint);
2260           strokePaint.outerColor.applyTint(strokeTint);
2261 
2262           ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, cc.clipmode, &strokePaint, &state.scissor, cc.fringeWidth, cc.strokeWidth, cc.paths, cc.npaths);
2263 
2264           // count triangles
2265           foreach (int i; 0..cc.npaths) {
2266             NVGpath* path = &cc.paths[i];
2267             ctx.strokeTriCount += path.nstroke-2;
2268             ++ctx.drawCallCount;
2269           }
2270         }
2271       }
2272     }
2273   }
2274 
2275 public:
2276   @disable this (this); // no copies
2277   void opAssign() (in auto ref NVGPathSetS a) { static assert(0, "no copies!"); }
2278 
2279   // pick test
2280   // Call delegate [dg] for each path under the specified position (in no particular order).
2281   // Returns the id of the path for which delegate [dg] returned true or -1.
2282   // dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
2283   int hitTestDG(bool bestOrder=false, DG) (in float x, in float y, NVGPickKind kind, scope DG dg) if (IsGoodHitTestDG!DG || IsGoodHitTestInternalDG!DG) {
2284     if (pickscene is null) return -1;
2285 
2286     NVGpickScene* ps = pickscene;
2287     int levelwidth = 1<<(ps.nlevels-1);
2288     int cellx = nvg__clamp(cast(int)(x/ps.xdim), 0, levelwidth);
2289     int celly = nvg__clamp(cast(int)(y/ps.ydim), 0, levelwidth);
2290     int npicked = 0;
2291 
2292     for (int lvl = ps.nlevels-1; lvl >= 0; --lvl) {
2293       NVGpickPath* pp = ps.levels[lvl][celly*levelwidth+cellx];
2294       while (pp !is null) {
2295         if (nvg__pickPathTestBounds(svctx, ps, pp, x, y)) {
2296           int hit = 0;
2297           if ((kind&NVGPickKind.Stroke) && (pp.flags&NVGPathFlags.Stroke)) hit = nvg__pickPathStroke(ps, pp, x, y);
2298           if (!hit && (kind&NVGPickKind.Fill) && (pp.flags&NVGPathFlags.Fill)) hit = nvg__pickPath(ps, pp, x, y);
2299           if (hit) {
2300             static if (IsGoodHitTestDG!DG) {
2301               static if (__traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); })) {
2302                 if (dg(pp.id, cast(int)pp.order)) return pp.id;
2303               } else {
2304                 dg(pp.id, cast(int)pp.order);
2305               }
2306             } else {
2307               static if (__traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); })) {
2308                 if (dg(pp)) return pp.id;
2309               } else {
2310                 dg(pp);
2311               }
2312             }
2313           }
2314         }
2315         pp = pp.next;
2316       }
2317       cellx >>= 1;
2318       celly >>= 1;
2319       levelwidth >>= 1;
2320     }
2321 
2322     return -1;
2323   }
2324 
2325   // Fills ids with a list of the top most hit ids under the specified position.
2326   // Returns the slice of [ids].
2327   int[] hitTestAll (in float x, in float y, NVGPickKind kind, int[] ids) nothrow @trusted @nogc {
2328     if (pickscene is null || ids.length == 0) return ids[0..0];
2329 
2330     int npicked = 0;
2331     NVGpickScene* ps = pickscene;
2332 
2333     hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
2334       if (npicked == ps.cpicked) {
2335         int cpicked = ps.cpicked+ps.cpicked;
2336         NVGpickPath** picked = cast(NVGpickPath**)realloc(ps.picked, (NVGpickPath*).sizeof*ps.cpicked);
2337         if (picked is null) return true; // abort
2338         ps.cpicked = cpicked;
2339         ps.picked = picked;
2340       }
2341       ps.picked[npicked] = pp;
2342       ++npicked;
2343       return false; // go on
2344     });
2345 
2346     qsort(ps.picked, npicked, (NVGpickPath*).sizeof, &nvg__comparePaths);
2347 
2348     assert(npicked >= 0);
2349     if (npicked > ids.length) npicked = cast(int)ids.length;
2350     foreach (immutable nidx, ref int did; ids[0..npicked]) did = ps.picked[nidx].id;
2351 
2352     return ids[0..npicked];
2353   }
2354 
2355   // Returns the id of the pickable shape containing x,y or -1 if no shape was found.
2356   int hitTest (in float x, in float y, NVGPickKind kind) nothrow @trusted @nogc {
2357     if (pickscene is null) return -1;
2358 
2359     int bestOrder = -1;
2360     int bestID = -1;
2361 
2362     hitTestDG!true(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
2363       if (pp.order > bestOrder) {
2364         bestOrder = pp.order;
2365         bestID = pp.id;
2366       }
2367     });
2368 
2369     return bestID;
2370   }
2371 }
2372 
2373 // Append current path to existing path set. Is is safe to call this with `null` [svp].
2374 void appendCurrentPathToCache (NVGContext ctx, NVGPathSet svp, in ref NVGPaint paint) nothrow @trusted @nogc {
2375   if (ctx is null || svp is null) return;
2376   if (ctx !is svp.svctx) assert(0, "NanoVega: cannot save paths from different contexts");
2377   if (ctx.ncommands == 0) {
2378     assert(ctx.cache.npaths == 0);
2379     return;
2380   }
2381   if (!ctx.cache.fillReady && !ctx.cache.strokeReady) return;
2382 
2383   // tesselate current path
2384   //if (!ctx.cache.fillReady) nvg__prepareFill(ctx);
2385   //if (!ctx.cache.strokeReady) nvg__prepareStroke(ctx);
2386 
2387   auto node = svp.allocPathNode();
2388   NVGpathCache* cc = node.path;
2389   cc.copyFrom(ctx.cache);
2390   node.paint = paint;
2391   // copy path commands (we may need 'em for picking)
2392   version(all) {
2393     cc.ncommands = ctx.ncommands;
2394     if (cc.ncommands) {
2395       import core.stdc.stdlib : malloc;
2396       import core.stdc.string : memcpy;
2397       cc.commands = cast(float*)malloc(ctx.ncommands*float.sizeof);
2398       if (cc.commands is null) assert(0, "NanoVega: out of memory");
2399       memcpy(cc.commands, ctx.commands, ctx.ncommands*float.sizeof);
2400     } else {
2401       cc.commands = null;
2402     }
2403   }
2404 }
2405 
2406 // Create new empty path set.
2407 // Group: path_recording
2408 public NVGPathSet newPathSet (NVGContext ctx) nothrow @trusted @nogc {
2409   import core.stdc.stdlib : malloc;
2410   import core.stdc.string : memset;
2411   if (ctx is null) return null;
2412   NVGPathSet res = cast(NVGPathSet)malloc(NVGPathSetS.sizeof);
2413   if (res is null) assert(0, "NanoVega: out of memory");
2414   memset(res, 0, NVGPathSetS.sizeof);
2415   res.svctx = ctx;
2416   return res;
2417 }
2418 
2419 // Is the given path set empty? Empty path set can be `null`.
2420 // Group: path_recording
2421 public bool empty (NVGPathSet svp) pure nothrow @safe @nogc { pragma(inline, true); return (svp is null || svp.nnodes == 0); }
2422 
2423 // Clear path set contents. Will release $(B some) allocated memory (this function is meant to clear something that will be reused).
2424 // Group: path_recording
2425 public void clear (NVGPathSet svp) nothrow @trusted @nogc {
2426   if (svp !is null) {
2427     import core.stdc.stdlib : free;
2428     foreach (immutable idx; 0.. svp.nnodes) svp.clearNode(idx);
2429     svp.nnodes = 0;
2430   }
2431 }
2432 
2433 // Destroy path set (frees all allocated memory).
2434 // Group: path_recording
2435 public void kill (ref NVGPathSet svp) nothrow @trusted @nogc {
2436   if (svp !is null) {
2437     import core.stdc.stdlib : free;
2438     svp.clear();
2439     if (svp.nodes !is null) free(svp.nodes);
2440     free(svp);
2441     if (svp.pickscene !is null) nvg__deletePickScene(svp.pickscene);
2442     svp = null;
2443   }
2444 }
2445 
2446 // Start path recording. [svp] should be alive until recording is cancelled or stopped.
2447 // Group: path_recording
2448 public void startRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc {
2449   if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2450   ctx.stopRecording();
2451   ctx.recset = svp;
2452   ctx.recstart = (svp !is null ? svp.nnodes : -1);
2453   ctx.recblockdraw = false;
2454 }
2455 
2456 /* Start path recording. [svp] should be alive until recording is cancelled or stopped.
2457  *
2458  * This will block all rendering, so you can call your rendering functions to record paths without actual drawing.
2459  * Commiting or cancelling will re-enable rendering.
2460  * You can call this with `null` svp to block rendering without recording any paths.
2461  *
2462  * Group: path_recording
2463  */
2464 public void startBlockingRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc {
2465   if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2466   ctx.stopRecording();
2467   ctx.recset = svp;
2468   ctx.recstart = (svp !is null ? svp.nnodes : -1);
2469   ctx.recblockdraw = true;
2470 }
2471 
2472 // Commit recorded paths. It is safe to call this when recording is not started.
2473 // Group: path_recording
2474 public void stopRecording (NVGContext ctx) nothrow @trusted @nogc {
2475   if (ctx.recset !is null && ctx.recset.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2476   if (ctx.recset !is null) ctx.recset.takeCurrentPickScene(ctx);
2477   ctx.recset = null;
2478   ctx.recstart = -1;
2479   ctx.recblockdraw = false;
2480 }
2481 
2482 // Cancel path recording.
2483 // Group: path_recording
2484 public void cancelRecording (NVGContext ctx) nothrow @trusted @nogc {
2485   if (ctx.recset !is null) {
2486     if (ctx.recset.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2487     assert(ctx.recstart >= 0 && ctx.recstart <= ctx.recset.nnodes);
2488     foreach (immutable idx; ctx.recstart..ctx.recset.nnodes) ctx.recset.clearNode(idx);
2489     ctx.recset.nnodes = ctx.recstart;
2490     ctx.recset = null;
2491     ctx.recstart = -1;
2492   }
2493   ctx.recblockdraw = false;
2494 }
2495 
2496 /* Replay saved path set.
2497  *
2498  * Replaying record while you're recording another one is undefined behavior.
2499  *
2500  * Group: path_recording
2501  */
2502 public void replayRecording() (NVGContext ctx, NVGPathSet svp, in auto ref NVGColor fillTint, in auto ref NVGColor strokeTint) nothrow @trusted @nogc {
2503   if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2504   svp.replay(ctx, fillTint, strokeTint);
2505 }
2506 
2507 /// Ditto.
2508 public void replayRecording() (NVGContext ctx, NVGPathSet svp, in auto ref NVGColor fillTint) nothrow @trusted @nogc { ctx.replayRecording(svp, fillTint, NVGColor.transparent); }
2509 
2510 /// Ditto.
2511 public void replayRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc { ctx.replayRecording(svp, NVGColor.transparent, NVGColor.transparent); }
2512 
2513 
2514 // ////////////////////////////////////////////////////////////////////////// //
2515 // Composite operation
2516 
2517 /// Sets the composite operation.
2518 /// Group: composite_operation
2519 public void globalCompositeOperation (NVGContext ctx, NVGCompositeOperation op) nothrow @trusted @nogc {
2520   NVGstate* state = nvg__getState(ctx);
2521   state.compositeOperation = nvg__compositeOperationState(op);
2522 }
2523 
2524 /// Sets the composite operation with custom pixel arithmetic.
2525 /// Group: composite_operation
2526 public void globalCompositeBlendFunc (NVGContext ctx, NVGBlendFactor sfactor, NVGBlendFactor dfactor) nothrow @trusted @nogc {
2527   ctx.globalCompositeBlendFuncSeparate(sfactor, dfactor, sfactor, dfactor);
2528 }
2529 
2530 /// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately.
2531 /// Group: composite_operation
2532 public void globalCompositeBlendFuncSeparate (NVGContext ctx, NVGBlendFactor srcRGB, NVGBlendFactor dstRGB, NVGBlendFactor srcAlpha, NVGBlendFactor dstAlpha) nothrow @trusted @nogc {
2533   NVGCompositeOperationState op;
2534   op.simple = false;
2535   op.srcRGB = srcRGB;
2536   op.dstRGB = dstRGB;
2537   op.srcAlpha = srcAlpha;
2538   op.dstAlpha = dstAlpha;
2539   NVGstate* state = nvg__getState(ctx);
2540   state.compositeOperation = op;
2541 }
2542 
2543 
2544 // ////////////////////////////////////////////////////////////////////////// //
2545 // Color utils
2546 
2547 /// Returns a color value from string form.
2548 /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
2549 /// Group: color_utils
2550 public NVGColor nvgRGB (const(char)[] srgb) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(srgb); }
2551 
2552 /// Ditto.
2553 public NVGColor nvgRGBA (const(char)[] srgb) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(srgb); }
2554 
2555 /// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).
2556 /// Group: color_utils
2557 public NVGColor nvgRGB (int r, int g, int b) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(nvgClampToByte(r), nvgClampToByte(g), nvgClampToByte(b), 255); }
2558 
2559 /// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.
2560 /// Group: color_utils
2561 public NVGColor nvgRGBf (float r, float g, float b) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(r, g, b, 1.0f); }
2562 
2563 /// Returns a color value from red, green, blue and alpha values.
2564 /// Group: color_utils
2565 public NVGColor nvgRGBA (int r, int g, int b, int a=255) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(nvgClampToByte(r), nvgClampToByte(g), nvgClampToByte(b), nvgClampToByte(a)); }
2566 
2567 /// Returns a color value from red, green, blue and alpha values.
2568 /// Group: color_utils
2569 public NVGColor nvgRGBAf (float r, float g, float b, float a=1.0f) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(r, g, b, a); }
2570 
2571 /// Returns new color with transparency (alpha) set to [a].
2572 /// Group: color_utils
2573 public NVGColor nvgTransRGBA (NVGColor c, ubyte a) nothrow @trusted @nogc {
2574   pragma(inline, true);
2575   c.a = a/255.0f;
2576   return c;
2577 }
2578 
2579 /// Ditto.
2580 public NVGColor nvgTransRGBAf (NVGColor c, float a) nothrow @trusted @nogc {
2581   pragma(inline, true);
2582   c.a = a;
2583   return c;
2584 }
2585 
2586 /// Linearly interpolates from color c0 to c1, and returns resulting color value.
2587 /// Group: color_utils
2588 public NVGColor nvgLerpRGBA() (in auto ref NVGColor c0, in auto ref NVGColor c1, float u) nothrow @trusted @nogc {
2589   NVGColor cint = void;
2590   u = nvg__clamp(u, 0.0f, 1.0f);
2591   float oneminu = 1.0f-u;
2592   foreach (uint i; 0..4) cint.rgba.ptr[i] = c0.rgba.ptr[i]*oneminu+c1.rgba.ptr[i]*u;
2593   return cint;
2594 }
2595 
2596 /* see below
2597 public NVGColor nvgHSL() (float h, float s, float l) {
2598   //pragma(inline, true); // alas
2599   return nvgHSLA(h, s, l, 255);
2600 }
2601 */
2602 
2603 float nvg__hue (float h, float m1, float m2) pure nothrow @safe @nogc {
2604   if (h < 0) h += 1;
2605   if (h > 1) h -= 1;
2606   if (h < 1.0f/6.0f) return m1+(m2-m1)*h*6.0f;
2607   if (h < 3.0f/6.0f) return m2;
2608   if (h < 4.0f/6.0f) return m1+(m2-m1)*(2.0f/3.0f-h)*6.0f;
2609   return m1;
2610 }
2611 
2612 /// Returns color value specified by hue, saturation and lightness.
2613 /// HSL values are all in range [0..1], alpha will be set to 255.
2614 /// Group: color_utils
2615 public alias nvgHSL = nvgHSLA; // trick to allow inlining
2616 
2617 /// Returns color value specified by hue, saturation and lightness and alpha.
2618 /// HSL values are all in range [0..1], alpha in range [0..255].
2619 /// Group: color_utils
2620 public NVGColor nvgHSLA (float h, float s, float l, ubyte a=255) nothrow @trusted @nogc {
2621   pragma(inline, true);
2622   NVGColor col = void;
2623   h = nvg__modf(h, 1.0f);
2624   if (h < 0.0f) h += 1.0f;
2625   s = nvg__clamp(s, 0.0f, 1.0f);
2626   l = nvg__clamp(l, 0.0f, 1.0f);
2627   immutable float m2 = (l <= 0.5f ? l*(1+s) : l+s-l*s);
2628   immutable float m1 = 2*l-m2;
2629   col.r = nvg__clamp(nvg__hue(h+1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2630   col.g = nvg__clamp(nvg__hue(h, m1, m2), 0.0f, 1.0f);
2631   col.b = nvg__clamp(nvg__hue(h-1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2632   col.a = a/255.0f;
2633   return col;
2634 }
2635 
2636 /// Returns color value specified by hue, saturation and lightness and alpha.
2637 /// HSL values and alpha are all in range [0..1].
2638 /// Group: color_utils
2639 public NVGColor nvgHSLA (float h, float s, float l, float a) nothrow @trusted @nogc {
2640   // sorry for copypasta, it is for inliner
2641   static if (__VERSION__ >= 2072) pragma(inline, true);
2642   NVGColor col = void;
2643   h = nvg__modf(h, 1.0f);
2644   if (h < 0.0f) h += 1.0f;
2645   s = nvg__clamp(s, 0.0f, 1.0f);
2646   l = nvg__clamp(l, 0.0f, 1.0f);
2647   immutable m2 = (l <= 0.5f ? l*(1+s) : l+s-l*s);
2648   immutable m1 = 2*l-m2;
2649   col.r = nvg__clamp(nvg__hue(h+1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2650   col.g = nvg__clamp(nvg__hue(h, m1, m2), 0.0f, 1.0f);
2651   col.b = nvg__clamp(nvg__hue(h-1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2652   col.a = a;
2653   return col;
2654 }
2655 
2656 
2657 // ////////////////////////////////////////////////////////////////////////// //
2658 // Matrices and Transformations
2659 
2660 /** Matrix class.
2661  *
2662  * Group: matrices
2663  */
2664 public align(1) struct NVGMatrix {
2665 align(1):
2666 private:
2667   static immutable float[6] IdentityMat = [
2668     1.0f, 0.0f,
2669     0.0f, 1.0f,
2670     0.0f, 0.0f,
2671   ];
2672 
2673 public:
2674   /// Matrix values. Initial value is identity matrix.
2675   float[6] mat = [
2676     1.0f, 0.0f,
2677     0.0f, 1.0f,
2678     0.0f, 0.0f,
2679   ];
2680 
2681 public nothrow @trusted @nogc:
2682   /// Create Matrix with the given values.
2683   this (const(float)[] amat...) {
2684     pragma(inline, true);
2685     if (amat.length >= 6) {
2686       mat.ptr[0..6] = amat.ptr[0..6];
2687     } else {
2688       mat.ptr[0..6] = 0;
2689       mat.ptr[0..amat.length] = amat[];
2690     }
2691   }
2692 
2693   /// Can be used to check validity of [inverted] result
2694   @property bool valid () const { import core.stdc.math : isfinite; return (isfinite(mat.ptr[0]) != 0); }
2695 
2696   /// Returns `true` if this matrix is identity matrix.
2697   @property bool isIdentity () const { version(aliced) pragma(inline, true); return (mat[] == IdentityMat[]); }
2698 
2699   /// Returns new inverse matrix.
2700   /// If inverted matrix cannot be calculated, `res.valid` fill be `false`.
2701   NVGMatrix inverted () const {
2702     NVGMatrix res = this;
2703     res.invert;
2704     return res;
2705   }
2706 
2707   /// Inverts this matrix.
2708   /// If inverted matrix cannot be calculated, `this.valid` fill be `false`.
2709   ref NVGMatrix invert () {
2710     float[6] inv = void;
2711     immutable double det = cast(double)mat.ptr[0]*mat.ptr[3]-cast(double)mat.ptr[2]*mat.ptr[1];
2712     if (det > -1e-6 && det < 1e-6) {
2713       inv[] = float.nan;
2714     } else {
2715       immutable double invdet = 1.0/det;
2716       inv.ptr[0] = cast(float)(mat.ptr[3]*invdet);
2717       inv.ptr[2] = cast(float)(-mat.ptr[2]*invdet);
2718       inv.ptr[4] = cast(float)((cast(double)mat.ptr[2]*mat.ptr[5]-cast(double)mat.ptr[3]*mat.ptr[4])*invdet);
2719       inv.ptr[1] = cast(float)(-mat.ptr[1]*invdet);
2720       inv.ptr[3] = cast(float)(mat.ptr[0]*invdet);
2721       inv.ptr[5] = cast(float)((cast(double)mat.ptr[1]*mat.ptr[4]-cast(double)mat.ptr[0]*mat.ptr[5])*invdet);
2722     }
2723     mat.ptr[0..6] = inv.ptr[0..6];
2724     return this;
2725   }
2726 
2727   /// Sets this matrix to identity matrix.
2728   ref NVGMatrix identity () { version(aliced) pragma(inline, true); mat[] = IdentityMat[]; return this; }
2729 
2730   /// Translate this matrix.
2731   ref NVGMatrix translate (in float tx, in float ty) {
2732     version(aliced) pragma(inline, true);
2733     return this.mul(Translated(tx, ty));
2734   }
2735 
2736   /// Scale this matrix.
2737   ref NVGMatrix scale (in float sx, in float sy) {
2738     version(aliced) pragma(inline, true);
2739     return this.mul(Scaled(sx, sy));
2740   }
2741 
2742   /// Rotate this matrix.
2743   ref NVGMatrix rotate (in float a) {
2744     version(aliced) pragma(inline, true);
2745     return this.mul(Rotated(a));
2746   }
2747 
2748   /// Skew this matrix by X axis.
2749   ref NVGMatrix skewX (in float a) {
2750     version(aliced) pragma(inline, true);
2751     return this.mul(SkewedX(a));
2752   }
2753 
2754   /// Skew this matrix by Y axis.
2755   ref NVGMatrix skewY (in float a) {
2756     version(aliced) pragma(inline, true);
2757     return this.mul(SkewedY(a));
2758   }
2759 
2760   /// Skew this matrix by both axes.
2761   ref NVGMatrix skewY (in float ax, in float ay) {
2762     version(aliced) pragma(inline, true);
2763     return this.mul(SkewedXY(ax, ay));
2764   }
2765 
2766   /// Transform point with this matrix. `null` destinations are allowed.
2767   /// [sx] and [sy] is the source point. [dx] and [dy] may point to the same variables.
2768   void point (float* dx, float* dy, float sx, float sy) nothrow @trusted @nogc {
2769     version(aliced) pragma(inline, true);
2770     if (dx !is null) *dx = sx*mat.ptr[0]+sy*mat.ptr[2]+mat.ptr[4];
2771     if (dy !is null) *dy = sx*mat.ptr[1]+sy*mat.ptr[3]+mat.ptr[5];
2772   }
2773 
2774   /// Transform point with this matrix.
2775   void point (ref float x, ref float y) nothrow @trusted @nogc {
2776     version(aliced) pragma(inline, true);
2777     immutable float nx = x*mat.ptr[0]+y*mat.ptr[2]+mat.ptr[4];
2778     immutable float ny = x*mat.ptr[1]+y*mat.ptr[3]+mat.ptr[5];
2779     x = nx;
2780     y = ny;
2781   }
2782 
2783   /// Sets this matrix to the result of multiplication of `this` and [s] (this * S).
2784   ref NVGMatrix mul() (in auto ref NVGMatrix s) {
2785     immutable float t0 = mat.ptr[0]*s.mat.ptr[0]+mat.ptr[1]*s.mat.ptr[2];
2786     immutable float t2 = mat.ptr[2]*s.mat.ptr[0]+mat.ptr[3]*s.mat.ptr[2];
2787     immutable float t4 = mat.ptr[4]*s.mat.ptr[0]+mat.ptr[5]*s.mat.ptr[2]+s.mat.ptr[4];
2788     mat.ptr[1] = mat.ptr[0]*s.mat.ptr[1]+mat.ptr[1]*s.mat.ptr[3];
2789     mat.ptr[3] = mat.ptr[2]*s.mat.ptr[1]+mat.ptr[3]*s.mat.ptr[3];
2790     mat.ptr[5] = mat.ptr[4]*s.mat.ptr[1]+mat.ptr[5]*s.mat.ptr[3]+s.mat.ptr[5];
2791     mat.ptr[0] = t0;
2792     mat.ptr[2] = t2;
2793     mat.ptr[4] = t4;
2794     return this;
2795   }
2796 
2797   /// Sets this matrix to the result of multiplication of [s] and `this` (S * this).
2798   /// Sets the transform to the result of multiplication of two transforms, of A = B*A.
2799   /// Group: matrices
2800   ref NVGMatrix premul() (in auto ref NVGMatrix s) {
2801     NVGMatrix s2 = s;
2802     s2.mul(this);
2803     mat[] = s2.mat[];
2804     return this;
2805   }
2806 
2807   /// Multiply this matrix by [s], return result as new matrix.
2808   /// Performs operations in this left-to-right order.
2809   NVGMatrix opBinary(string op="*") (in auto ref NVGMatrix s) const {
2810     version(aliced) pragma(inline, true);
2811     NVGMatrix res = this;
2812     res.mul(s);
2813     return res;
2814   }
2815 
2816   /// Multiply this matrix by [s].
2817   /// Performs operations in this left-to-right order.
2818   ref NVGMatrix opOpAssign(string op="*") (in auto ref NVGMatrix s) {
2819     version(aliced) pragma(inline, true);
2820     return this.mul(s);
2821   }
2822 
2823   float scaleX () const { pragma(inline, true); return nvg__sqrtf(mat.ptr[0]*mat.ptr[0]+mat.ptr[2]*mat.ptr[2]); } /// Returns x scaling of this matrix.
2824   float scaleY () const { pragma(inline, true); return nvg__sqrtf(mat.ptr[1]*mat.ptr[1]+mat.ptr[3]*mat.ptr[3]); } /// Returns y scaling of this matrix.
2825   float rotation () const { pragma(inline, true); return nvg__atan2f(mat.ptr[1], mat.ptr[0]); } /// Returns rotation of this matrix.
2826   float tx () const { pragma(inline, true); return mat.ptr[4]; } /// Returns x translation of this matrix.
2827   float ty () const { pragma(inline, true); return mat.ptr[5]; } /// Returns y translation of this matrix.
2828 
2829   ref NVGMatrix scaleX (in float v) { pragma(inline, true); return scaleRotateTransform(v, scaleY, rotation, tx, ty); } /// Sets x scaling of this matrix.
2830   ref NVGMatrix scaleY (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, v, rotation, tx, ty); } /// Sets y scaling of this matrix.
2831   ref NVGMatrix rotation (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, scaleY, v, tx, ty); } /// Sets rotation of this matrix.
2832   ref NVGMatrix tx (in float v) { pragma(inline, true); mat.ptr[4] = v; return this; } /// Sets x translation of this matrix.
2833   ref NVGMatrix ty (in float v) { pragma(inline, true); mat.ptr[5] = v; return this; } /// Sets y translation of this matrix.
2834 
2835   /// Utility function to be used in `setXXX()`.
2836   /// This is the same as doing: `mat.identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2837   ref NVGMatrix scaleRotateTransform (in float xscale, in float yscale, in float a, in float tx, in float ty) {
2838     immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2839     mat.ptr[0] = xscale*cs; mat.ptr[1] = yscale*sn;
2840     mat.ptr[2] = xscale*-sn; mat.ptr[3] = yscale*cs;
2841     mat.ptr[4] = tx; mat.ptr[5] = ty;
2842     return this;
2843   }
2844 
2845   /// This is the same as doing: `mat.identity.rotate(a).translate(tx, ty)`, only faster
2846   ref NVGMatrix rotateTransform (in float a, in float tx, in float ty) {
2847     immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2848     mat.ptr[0] = cs; mat.ptr[1] = sn;
2849     mat.ptr[2] = -sn; mat.ptr[3] = cs;
2850     mat.ptr[4] = tx; mat.ptr[5] = ty;
2851     return this;
2852   }
2853 
2854   /// Returns new identity matrix.
2855   static NVGMatrix Identity () { pragma(inline, true); return NVGMatrix.init; }
2856 
2857   /// Returns new translation matrix.
2858   static NVGMatrix Translated (in float tx, in float ty) {
2859     version(aliced) pragma(inline, true);
2860     NVGMatrix res = void;
2861     res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
2862     res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
2863     res.mat.ptr[4] = tx; res.mat.ptr[5] = ty;
2864     return res;
2865   }
2866 
2867   /// Returns new scaling matrix.
2868   static NVGMatrix Scaled (in float sx, in float sy) {
2869     version(aliced) pragma(inline, true);
2870     NVGMatrix res = void;
2871     res.mat.ptr[0] = sx; res.mat.ptr[1] = 0.0f;
2872     res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = sy;
2873     res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2874     return res;
2875   }
2876 
2877   /// Returns new rotation matrix. Angle is specified in radians.
2878   static NVGMatrix Rotated (in float a) {
2879     version(aliced) pragma(inline, true);
2880     immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2881     NVGMatrix res = void;
2882     res.mat.ptr[0] = cs; res.mat.ptr[1] = sn;
2883     res.mat.ptr[2] = -sn; res.mat.ptr[3] = cs;
2884     res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2885     return res;
2886   }
2887 
2888   /// Returns new x-skewing matrix. Angle is specified in radians.
2889   static NVGMatrix SkewedX (in float a) {
2890     version(aliced) pragma(inline, true);
2891     NVGMatrix res = void;
2892     res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
2893     res.mat.ptr[2] = nvg__tanf(a); res.mat.ptr[3] = 1.0f;
2894     res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2895     return res;
2896   }
2897 
2898   /// Returns new y-skewing matrix. Angle is specified in radians.
2899   static NVGMatrix SkewedY (in float a) {
2900     version(aliced) pragma(inline, true);
2901     NVGMatrix res = void;
2902     res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = nvg__tanf(a);
2903     res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
2904     res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2905     return res;
2906   }
2907 
2908   /// Returns new xy-skewing matrix. Angles are specified in radians.
2909   static NVGMatrix SkewedXY (in float ax, in float ay) {
2910     version(aliced) pragma(inline, true);
2911     NVGMatrix res = void;
2912     res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = nvg__tanf(ay);
2913     res.mat.ptr[2] = nvg__tanf(ax); res.mat.ptr[3] = 1.0f;
2914     res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2915     return res;
2916   }
2917 
2918   /// Utility function to be used in `setXXX()`.
2919   /// This is the same as doing: `NVGMatrix.Identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2920   static NVGMatrix ScaledRotatedTransformed (in float xscale, in float yscale, in float a, in float tx, in float ty) {
2921     NVGMatrix res = void;
2922     res.scaleRotateTransform(xscale, yscale, a, tx, ty);
2923     return res;
2924   }
2925 
2926   /// This is the same as doing: `NVGMatrix.Identity.rotate(a).translate(tx, ty)`, only faster
2927   static NVGMatrix RotatedTransformed (in float a, in float tx, in float ty) {
2928     NVGMatrix res = void;
2929     res.rotateTransform(a, tx, ty);
2930     return res;
2931   }
2932 }
2933 
2934 
2935 /// Converts degrees to radians.
2936 /// Group: matrices
2937 public float nvgDegToRad() (in float deg) pure nothrow @safe @nogc { pragma(inline, true); return deg/180.0f*NVG_PI; }
2938 
2939 /// Converts radians to degrees.
2940 /// Group: matrices
2941 public float nvgRadToDeg() (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad/NVG_PI*180.0f; }
2942 
2943 public alias nvgDegrees = nvgDegToRad; /// Use this like `42.nvgDegrees`
2944 public float nvgRadians() (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad; } /// Use this like `0.1.nvgRadians`
2945 
2946 
2947 // ////////////////////////////////////////////////////////////////////////// //
2948 void nvg__setPaintColor() (ref NVGPaint p, in auto ref NVGColor color) nothrow @trusted @nogc {
2949   p.clear();
2950   p.xform.identity;
2951   p.radius = 0.0f;
2952   p.feather = 1.0f;
2953   p.innerColor = p.middleColor = p.outerColor = color;
2954   p.midp = -1;
2955   p.simpleColor = true;
2956 }
2957 
2958 
2959 // ////////////////////////////////////////////////////////////////////////// //
2960 // State handling
2961 
2962 version(nanovega_debug_clipping) {
2963   public void nvgClipDumpOn (NVGContext ctx) { glnvg__clipDebugDump(ctx.params.userPtr, true); }
2964   public void nvgClipDumpOff (NVGContext ctx) { glnvg__clipDebugDump(ctx.params.userPtr, false); }
2965 }
2966 
2967 /** Pushes and saves the current render state into a state stack.
2968  * A matching [restore] must be used to restore the state.
2969  * Returns `false` if state stack overflowed.
2970  *
2971  * Group: state_handling
2972  */
2973 public bool save (NVGContext ctx) nothrow @trusted @nogc {
2974   if (ctx.nstates >= NVG_MAX_STATES) return false;
2975   if (ctx.nstates > 0) {
2976     //memcpy(&ctx.states[ctx.nstates], &ctx.states[ctx.nstates-1], NVGstate.sizeof);
2977     ctx.states[ctx.nstates] = ctx.states[ctx.nstates-1];
2978     ctx.params.renderPushClip(ctx.params.userPtr);
2979   }
2980   ++ctx.nstates;
2981   return true;
2982 }
2983 
2984 /// Pops and restores current render state.
2985 /// Group: state_handling
2986 public bool restore (NVGContext ctx) nothrow @trusted @nogc {
2987   if (ctx.nstates <= 1) return false;
2988   ctx.states[ctx.nstates-1].clearPaint();
2989   ctx.params.renderPopClip(ctx.params.userPtr);
2990   --ctx.nstates;
2991   return true;
2992 }
2993 
2994 /// Resets current render state to default values. Does not affect the render state stack.
2995 /// Group: state_handling
2996 public void reset (NVGContext ctx) nothrow @trusted @nogc {
2997   NVGstate* state = nvg__getState(ctx);
2998   state.clearPaint();
2999 
3000   nvg__setPaintColor(state.fill, nvgRGBA(255, 255, 255, 255));
3001   nvg__setPaintColor(state.stroke, nvgRGBA(0, 0, 0, 255));
3002   state.compositeOperation = nvg__compositeOperationState(NVGCompositeOperation.SourceOver);
3003   state.shapeAntiAlias = true;
3004   state.strokeWidth = 1.0f;
3005   state.miterLimit = 10.0f;
3006   state.lineCap = NVGLineCap.Butt;
3007   state.lineJoin = NVGLineCap.Miter;
3008   state.alpha = 1.0f;
3009   state.xform.identity;
3010 
3011   state.scissor.extent[] = -1.0f;
3012 
3013   state.fontSize = 16.0f;
3014   state.letterSpacing = 0.0f;
3015   state.lineHeight = 1.0f;
3016   state.fontBlur = 0.0f;
3017   state.textAlign.reset;
3018   state.fontId = 0;
3019   state.evenOddMode = false;
3020   state.dashCount = 0;
3021   state.lastFlattenDashCount = 0;
3022   state.dashStart = 0;
3023   state.firstDashIsGap = false;
3024   state.dasherActive = false;
3025 
3026   ctx.params.renderResetClip(ctx.params.userPtr);
3027 }
3028 
3029 /** Returns `true` if we have any room in state stack.
3030  * It is guaranteed to have at least 32 stack slots.
3031  *
3032  * Group: state_handling
3033  */
3034 public bool canSave (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx.nstates < NVG_MAX_STATES); }
3035 
3036 /** Returns `true` if we have any saved state.
3037  *
3038  * Group: state_handling
3039  */
3040 public bool canRestore (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx.nstates > 1); }
3041 
3042 /// Returns `true` if rendering is currently blocked.
3043 /// Group: state_handling
3044 public bool renderBlocked (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null && ctx.contextAlive ? ctx.recblockdraw : false); }
3045 
3046 /// Blocks/unblocks rendering
3047 /// Group: state_handling
3048 public void renderBlocked (NVGContext ctx, bool v) pure nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null && ctx.contextAlive) ctx.recblockdraw = v; }
3049 
3050 /// Blocks/unblocks rendering; returns previous state.
3051 /// Group: state_handling
3052 public bool setRenderBlocked (NVGContext ctx, bool v) pure nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null && ctx.contextAlive) { bool res = ctx.recblockdraw; ctx.recblockdraw = v; return res; } else return false; }
3053 
3054 
3055 // ////////////////////////////////////////////////////////////////////////// //
3056 // Render styles
3057 
3058 /// Sets filling mode to "even-odd".
3059 /// Group: render_styles
3060 public void evenOddFill (NVGContext ctx) nothrow @trusted @nogc {
3061   NVGstate* state = nvg__getState(ctx);
3062   state.evenOddMode = true;
3063 }
3064 
3065 /// Sets filling mode to "non-zero" (this is default mode).
3066 /// Group: render_styles
3067 public void nonZeroFill (NVGContext ctx) nothrow @trusted @nogc {
3068   NVGstate* state = nvg__getState(ctx);
3069   state.evenOddMode = false;
3070 }
3071 
3072 /// Sets whether to draw antialias for [stroke] and [fill]. It's enabled by default.
3073 /// Group: render_styles
3074 public void shapeAntiAlias (NVGContext ctx, bool enabled) {
3075   NVGstate* state = nvg__getState(ctx);
3076   state.shapeAntiAlias = enabled;
3077 }
3078 
3079 /// Sets the stroke width of the stroke style.
3080 /// Group: render_styles
3081 @scriptable
3082 public void strokeWidth (NVGContext ctx, float width) nothrow @trusted @nogc {
3083   NVGstate* state = nvg__getState(ctx);
3084   state.strokeWidth = width;
3085 }
3086 
3087 /// Sets the miter limit of the stroke style. Miter limit controls when a sharp corner is beveled.
3088 /// Group: render_styles
3089 public void miterLimit (NVGContext ctx, float limit) nothrow @trusted @nogc {
3090   NVGstate* state = nvg__getState(ctx);
3091   state.miterLimit = limit;
3092 }
3093 
3094 /// Sets how the end of the line (cap) is drawn,
3095 /// Can be one of: NVGLineCap.Butt (default), NVGLineCap.Round, NVGLineCap.Square.
3096 /// Group: render_styles
3097 public void lineCap (NVGContext ctx, NVGLineCap cap) nothrow @trusted @nogc {
3098   NVGstate* state = nvg__getState(ctx);
3099   state.lineCap = cap;
3100 }
3101 
3102 /// Sets how sharp path corners are drawn.
3103 /// Can be one of NVGLineCap.Miter (default), NVGLineCap.Round, NVGLineCap.Bevel.
3104 /// Group: render_styles
3105 public void lineJoin (NVGContext ctx, NVGLineCap join) nothrow @trusted @nogc {
3106   NVGstate* state = nvg__getState(ctx);
3107   state.lineJoin = join;
3108 }
3109 
3110 /// Sets stroke dashing, using (dash_length, gap_length) pairs.
3111 /// Current limit is 16 pairs.
3112 /// Resets dash start to zero.
3113 /// Group: render_styles
3114 public void setLineDash (NVGContext ctx, const(float)[] dashdata) nothrow @trusted @nogc {
3115   NVGstate* state = nvg__getState(ctx);
3116   state.dashCount = 0;
3117   state.dashStart = 0;
3118   state.firstDashIsGap = false;
3119   if (dashdata.length >= 2) {
3120     bool curFIsGap = true; // trick
3121     foreach (immutable idx, float f; dashdata) {
3122       curFIsGap = !curFIsGap;
3123       if (f < 0.01f) continue; // skip it
3124       if (idx == 0) {
3125         // register first dash
3126         state.firstDashIsGap = curFIsGap;
3127         state.dashes.ptr[state.dashCount++] = f;
3128       } else {
3129         if ((idx&1) != ((state.dashCount&1)^cast(uint)state.firstDashIsGap)) {
3130           // oops, continuation
3131           state.dashes[state.dashCount-1] += f;
3132         } else {
3133           if (state.dashCount == state.dashes.length) break;
3134           state.dashes[state.dashCount++] = f;
3135         }
3136       }
3137     }
3138     if (state.dashCount&1) {
3139       if (state.dashCount == 1) {
3140         state.dashCount = 0;
3141       } else {
3142         assert(state.dashCount < state.dashes.length);
3143         state.dashes[state.dashCount++] = 0;
3144       }
3145     }
3146     // calculate total dash path length
3147     state.totalDashLen = 0;
3148     foreach (float f; state.dashes.ptr[0..state.dashCount]) state.totalDashLen += f;
3149     if (state.totalDashLen < 0.01f) {
3150       state.dashCount = 0; // nothing to do
3151     } else {
3152       if (state.lastFlattenDashCount != 0) state.lastFlattenDashCount = uint.max; // force re-flattening
3153     }
3154   }
3155 }
3156 
3157 public alias lineDash = setLineDash; /// Ditto.
3158 
3159 /// Sets stroke dashing, using (dash_length, gap_length) pairs.
3160 /// Current limit is 16 pairs.
3161 /// Group: render_styles
3162 public void setLineDashStart (NVGContext ctx, in float dashStart) nothrow @trusted @nogc {
3163   NVGstate* state = nvg__getState(ctx);
3164   if (state.lastFlattenDashCount != 0 && state.dashStart != dashStart) {
3165     state.lastFlattenDashCount = uint.max; // force re-flattening
3166   }
3167   state.dashStart = dashStart;
3168 }
3169 
3170 public alias lineDashStart = setLineDashStart; /// Ditto.
3171 
3172 /// Sets the transparency applied to all rendered shapes.
3173 /// Already transparent paths will get proportionally more transparent as well.
3174 /// Group: render_styles
3175 public void globalAlpha (NVGContext ctx, float alpha) nothrow @trusted @nogc {
3176   NVGstate* state = nvg__getState(ctx);
3177   state.alpha = alpha;
3178 }
3179 
3180 private void strokeColor() {}
3181 
3182 static if (NanoVegaHasArsdColor) {
3183 /// Sets current stroke style to a solid color.
3184 /// Group: render_styles
3185 @scriptable
3186 public void strokeColor (NVGContext ctx, Color color) nothrow @trusted @nogc {
3187   NVGstate* state = nvg__getState(ctx);
3188   nvg__setPaintColor(state.stroke, NVGColor(color));
3189 }
3190 }
3191 
3192 /// Sets current stroke style to a solid color.
3193 /// Group: render_styles
3194 public void strokeColor() (NVGContext ctx, in auto ref NVGColor color) nothrow @trusted @nogc {
3195   NVGstate* state = nvg__getState(ctx);
3196   nvg__setPaintColor(state.stroke, color);
3197 }
3198 
3199 @scriptable
3200 public void strokePaint(NVGContext ctx, in NVGPaint* paint) nothrow @trusted @nogc {
3201 	strokePaint(ctx, *paint);
3202 }
3203 
3204 /// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
3205 /// Group: render_styles
3206 @scriptable
3207 public void strokePaint() (NVGContext ctx, in auto ref NVGPaint paint) nothrow @trusted @nogc {
3208   NVGstate* state = nvg__getState(ctx);
3209   state.stroke = paint;
3210   //nvgTransformMultiply(state.stroke.xform[], state.xform[]);
3211   state.stroke.xform.mul(state.xform);
3212 }
3213 
3214 // this is a hack to work around https://issues.dlang.org/show_bug.cgi?id=16206
3215 // for scriptable reflection. it just needs to be declared first among the overloads
3216 private void fillColor (NVGContext ctx) nothrow @trusted @nogc { }
3217 
3218 static if (NanoVegaHasArsdColor) {
3219 /// Sets current fill style to a solid color.
3220 /// Group: render_styles
3221 @scriptable
3222 public void fillColor (NVGContext ctx, Color color) nothrow @trusted @nogc {
3223   NVGstate* state = nvg__getState(ctx);
3224   nvg__setPaintColor(state.fill, NVGColor(color));
3225 }
3226 }
3227 
3228 /// Sets current fill style to a solid color.
3229 /// Group: render_styles
3230 public void fillColor() (NVGContext ctx, in auto ref NVGColor color) nothrow @trusted @nogc {
3231   NVGstate* state = nvg__getState(ctx);
3232   nvg__setPaintColor(state.fill, color);
3233 
3234 }
3235 
3236 @scriptable // kinda a hack for bug 16206 but also because jsvar deals in opaque NVGPaint* instead of auto refs (which it doesn't  know how to reflect on)
3237 public void fillPaint (NVGContext ctx, in NVGPaint* paint) nothrow @trusted @nogc {
3238 	fillPaint(ctx, *paint);
3239 }
3240 
3241 /// Sets current fill style to a paint, which can be a one of the gradients or a pattern.
3242 /// Group: render_styles
3243 @scriptable
3244 public void fillPaint() (NVGContext ctx, in auto ref NVGPaint paint) nothrow @trusted @nogc {
3245   NVGstate* state = nvg__getState(ctx);
3246   state.fill = paint;
3247   //nvgTransformMultiply(state.fill.xform[], state.xform[]);
3248   state.fill.xform.mul(state.xform);
3249 }
3250 
3251 /// Sets current fill style to a multistop linear gradient.
3252 /// Group: render_styles
3253 public void fillPaint() (NVGContext ctx, in auto ref NVGLGS lgs) nothrow @trusted @nogc {
3254   if (!lgs.valid) {
3255     NVGPaint p = void;
3256     memset(&p, 0, p.sizeof);
3257     nvg__setPaintColor(p, NVGColor.red);
3258     ctx.fillPaint = p;
3259   } else if (lgs.midp >= -1) {
3260     //{ import core.stdc.stdio; printf("SIMPLE! midp=%f\n", cast(double)lgs.midp); }
3261     ctx.fillPaint = ctx.linearGradient(lgs.cx, lgs.cy, lgs.dimx, lgs.dimy, lgs.ic, lgs.midp, lgs.mc, lgs.oc);
3262   } else {
3263     ctx.fillPaint = ctx.imagePattern(lgs.cx, lgs.cy, lgs.dimx, lgs.dimy, lgs.angle, lgs.imgid);
3264   }
3265 }
3266 
3267 /// Returns current transformation matrix.
3268 /// Group: render_transformations
3269 public NVGMatrix currTransform (NVGContext ctx) pure nothrow @trusted @nogc {
3270   NVGstate* state = nvg__getState(ctx);
3271   return state.xform;
3272 }
3273 
3274 /// Sets current transformation matrix.
3275 /// Group: render_transformations
3276 public void currTransform() (NVGContext ctx, in auto ref NVGMatrix m) nothrow @trusted @nogc {
3277   NVGstate* state = nvg__getState(ctx);
3278   state.xform = m;
3279 }
3280 
3281 /// Resets current transform to an identity matrix.
3282 /// Group: render_transformations
3283 @scriptable
3284 public void resetTransform (NVGContext ctx) nothrow @trusted @nogc {
3285   NVGstate* state = nvg__getState(ctx);
3286   state.xform.identity;
3287 }
3288 
3289 /// Premultiplies current coordinate system by specified matrix.
3290 /// Group: render_transformations
3291 public void transform() (NVGContext ctx, in auto ref NVGMatrix mt) nothrow @trusted @nogc {
3292   NVGstate* state = nvg__getState(ctx);
3293   //nvgTransformPremultiply(state.xform[], t[]);
3294   state.xform *= mt;
3295 }
3296 
3297 /// Translates current coordinate system.
3298 /// Group: render_transformations
3299 @scriptable
3300 public void translate (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
3301   NVGstate* state = nvg__getState(ctx);
3302   //NVGMatrix t = void;
3303   //nvgTransformTranslate(t[], x, y);
3304   //nvgTransformPremultiply(state.xform[], t[]);
3305   state.xform.premul(NVGMatrix.Translated(x, y));
3306 }
3307 
3308 /// Rotates current coordinate system. Angle is specified in radians.
3309 /// Group: render_transformations
3310 @scriptable
3311 public void rotate (NVGContext ctx, in float angle) nothrow @trusted @nogc {
3312   NVGstate* state = nvg__getState(ctx);
3313   //NVGMatrix t = void;
3314   //nvgTransformRotate(t[], angle);
3315   //nvgTransformPremultiply(state.xform[], t[]);
3316   state.xform.premul(NVGMatrix.Rotated(angle));
3317 }
3318 
3319 /// Skews the current coordinate system along X axis. Angle is specified in radians.
3320 /// Group: render_transformations
3321 @scriptable
3322 public void skewX (NVGContext ctx, in float angle) nothrow @trusted @nogc {
3323   NVGstate* state = nvg__getState(ctx);
3324   //NVGMatrix t = void;
3325   //nvgTransformSkewX(t[], angle);
3326   //nvgTransformPremultiply(state.xform[], t[]);
3327   state.xform.premul(NVGMatrix.SkewedX(angle));
3328 }
3329 
3330 /// Skews the current coordinate system along Y axis. Angle is specified in radians.
3331 /// Group: render_transformations
3332 @scriptable
3333 public void skewY (NVGContext ctx, in float angle) nothrow @trusted @nogc {
3334   NVGstate* state = nvg__getState(ctx);
3335   //NVGMatrix t = void;
3336   //nvgTransformSkewY(t[], angle);
3337   //nvgTransformPremultiply(state.xform[], t[]);
3338   state.xform.premul(NVGMatrix.SkewedY(angle));
3339 }
3340 
3341 /// Scales the current coordinate system.
3342 /// Group: render_transformations
3343 @scriptable
3344 public void scale (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
3345   NVGstate* state = nvg__getState(ctx);
3346   //NVGMatrix t = void;
3347   //nvgTransformScale(t[], x, y);
3348   //nvgTransformPremultiply(state.xform[], t[]);
3349   state.xform.premul(NVGMatrix.Scaled(x, y));
3350 }
3351 
3352 
3353 // ////////////////////////////////////////////////////////////////////////// //
3354 // Images
3355 
3356 /// Creates image by loading it from the disk from specified file name.
3357 /// Returns handle to the image or 0 on error.
3358 /// Group: images
3359 public NVGImage createImage() (NVGContext ctx, const(char)[] filename, const(NVGImageFlag)[] imageFlagsList...) {
3360   static if (NanoVegaHasArsdImage) {
3361     import arsd.image;
3362     // do we have new arsd API to load images?
3363     static if (!is(typeof(MemoryImage.fromImageFile)) || !is(typeof(MemoryImage.clearInternal))) {
3364       static assert(0, "Sorry, your ARSD is too old. Please, update it.");
3365     }
3366     try {
3367       auto oimg = MemoryImage.fromImageFile(filename);
3368       if (auto img = cast(TrueColorImage)oimg) {
3369         scope(exit) oimg.clearInternal();
3370         return ctx.createImageRGBA(img.width, img.height, img.imageData.bytes[], imageFlagsList);
3371       } else {
3372         TrueColorImage img = oimg.getAsTrueColorImage;
3373         scope(exit) img.clearInternal();
3374         oimg.clearInternal(); // drop original image, as `getAsTrueColorImage()` MUST create a new one here
3375         oimg = null;
3376         return ctx.createImageRGBA(img.width, img.height, img.imageData.bytes[], imageFlagsList);
3377       }
3378     } catch (Exception) {}
3379     return NVGImage.init;
3380   } else {
3381     import std.internal.cstring;
3382     ubyte* img;
3383     int w, h, n;
3384     stbi_set_unpremultiply_on_load(1);
3385     stbi_convert_iphone_png_to_rgb(1);
3386     img = stbi_load(filename.tempCString, &w, &h, &n, 4);
3387     if (img is null) {
3388       //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
3389       return NVGImage.init;
3390     }
3391     auto image = ctx.createImageRGBA(w, h, img[0..w*h*4], imageFlagsList);
3392     stbi_image_free(img);
3393     return image;
3394   }
3395 }
3396 
3397 static if (NanoVegaHasArsdImage) {
3398   /// Creates image by loading it from the specified memory image.
3399   /// Returns handle to the image or 0 on error.
3400   /// Group: images
3401   public NVGImage createImageFromMemoryImage() (NVGContext ctx, MemoryImage img, const(NVGImageFlag)[] imageFlagsList...) {
3402     if (img is null) return NVGImage.init;
3403     if (auto tc = cast(TrueColorImage)img) {
3404       return ctx.createImageRGBA(tc.width, tc.height, tc.imageData.bytes[], imageFlagsList);
3405     } else {
3406       auto tc = img.getAsTrueColorImage;
3407       scope(exit) tc.clearInternal(); // here, it is guaranteed that `tc` is newly allocated image, so it is safe to kill it
3408       return ctx.createImageRGBA(tc.width, tc.height, tc.imageData.bytes[], imageFlagsList);
3409     }
3410   }
3411 } else {
3412   /// Creates image by loading it from the specified chunk of memory.
3413   /// Returns handle to the image or 0 on error.
3414   /// Group: images
3415   public NVGImage createImageMem() (NVGContext ctx, const(ubyte)* data, int ndata, const(NVGImageFlag)[] imageFlagsList...) {
3416     int w, h, n, image;
3417     ubyte* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
3418     if (img is null) {
3419       //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
3420       return NVGImage.init;
3421     }
3422     image = ctx.createImageRGBA(w, h, img[0..w*h*4], imageFlagsList);
3423     stbi_image_free(img);
3424     return image;
3425   }
3426 }
3427 
3428 /// Creates image from specified image data.
3429 /// Returns handle to the image or 0 on error.
3430 /// Group: images
3431 public NVGImage createImageRGBA (NVGContext ctx, int w, int h, const(void)[] data, const(NVGImageFlag)[] imageFlagsList...) nothrow @trusted @nogc {
3432   if (w < 1 || h < 1 || data.length < w*h*4) return NVGImage.init;
3433   uint imageFlags = 0;
3434   foreach (immutable uint flag; imageFlagsList) imageFlags |= flag;
3435   NVGImage res;
3436   res.id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.RGBA, w, h, imageFlags, cast(const(ubyte)*)data.ptr);
3437   if (res.id > 0) {
3438     version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("createImageRGBA: img=%p; imgid=%d\n", &res, res.id); }
3439     res.ctx = ctx;
3440     ctx.nvg__imageIncRef(res.id, false); // don't increment driver refcount
3441   }
3442   return res;
3443 }
3444 
3445 /// Updates image data specified by image handle.
3446 /// Group: images
3447 public void updateImage() (NVGContext ctx, auto ref NVGImage image, const(void)[] data) nothrow @trusted @nogc {
3448   if (image.valid) {
3449     int w, h;
3450     if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3451     ctx.params.renderGetTextureSize(ctx.params.userPtr, image.id, &w, &h);
3452     ctx.params.renderUpdateTexture(ctx.params.userPtr, image.id, 0, 0, w, h, cast(const(ubyte)*)data.ptr);
3453   }
3454 }
3455 
3456 /// Returns the dimensions of a created image.
3457 /// Group: images
3458 public void imageSize() (NVGContext ctx, in auto ref NVGImage image, out int w, out int h) nothrow @trusted @nogc {
3459   if (image.valid) {
3460     if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3461     ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, image.id, &w, &h);
3462   }
3463 }
3464 
3465 /// Deletes created image.
3466 /// Group: images
3467 public void deleteImage() (NVGContext ctx, ref NVGImage image) nothrow @trusted @nogc {
3468   if (ctx is null || !image.valid) return;
3469   if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3470   image.clear();
3471 }
3472 
3473 
3474 // ////////////////////////////////////////////////////////////////////////// //
3475 // Paints
3476 
3477 private void linearGradient() {} // hack for dmd bug
3478 
3479 static if (NanoVegaHasArsdColor) {
3480 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3481  * of the linear gradient, icol specifies the start color and ocol the end color.
3482  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3483  *
3484  * Group: paints
3485  */
3486 @scriptable
3487 public NVGPaint linearGradient (NVGContext ctx, in float sx, in float sy, in float ex, in float ey, in Color icol, in Color ocol) nothrow @trusted @nogc {
3488   return ctx.linearGradient(sx, sy, ex, ey, NVGColor(icol), NVGColor(ocol));
3489 }
3490 /** Creates and returns a linear gradient with middle stop. Parameters `(sx, sy) (ex, ey)` specify the start
3491  * and end coordinates of the linear gradient, icol specifies the start color, midp specifies stop point in
3492  * range `(0..1)`, and ocol the end color.
3493  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3494  *
3495  * Group: paints
3496  */
3497 public NVGPaint linearGradient (NVGContext ctx, in float sx, in float sy, in float ex, in float ey, in Color icol, in float midp, in Color mcol, in Color ocol) nothrow @trusted @nogc {
3498   return ctx.linearGradient(sx, sy, ex, ey, NVGColor(icol), midp, NVGColor(mcol), NVGColor(ocol));
3499 }
3500 }
3501 
3502 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3503  * of the linear gradient, icol specifies the start color and ocol the end color.
3504  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3505  *
3506  * Group: paints
3507  */
3508 public NVGPaint linearGradient() (NVGContext ctx, float sx, float sy, float ex, float ey, in auto ref NVGColor icol, in auto ref NVGColor ocol) nothrow @trusted @nogc {
3509   enum large = 1e5f;
3510 
3511   NVGPaint p = void;
3512   memset(&p, 0, p.sizeof);
3513   p.simpleColor = false;
3514 
3515   // Calculate transform aligned to the line
3516   float dx = ex-sx;
3517   float dy = ey-sy;
3518   immutable float d = nvg__sqrtf(dx*dx+dy*dy);
3519   if (d > 0.0001f) {
3520     dx /= d;
3521     dy /= d;
3522   } else {
3523     dx = 0;
3524     dy = 1;
3525   }
3526 
3527   p.xform.mat.ptr[0] = dy; p.xform.mat.ptr[1] = -dx;
3528   p.xform.mat.ptr[2] = dx; p.xform.mat.ptr[3] = dy;
3529   p.xform.mat.ptr[4] = sx-dx*large; p.xform.mat.ptr[5] = sy-dy*large;
3530 
3531   p.extent.ptr[0] = large;
3532   p.extent.ptr[1] = large+d*0.5f;
3533 
3534   p.radius = 0.0f;
3535 
3536   p.feather = nvg__max(NVG_MIN_FEATHER, d);
3537 
3538   p.innerColor = p.middleColor = icol;
3539   p.outerColor = ocol;
3540   p.midp = -1;
3541 
3542   return p;
3543 }
3544 
3545 /** Creates and returns a linear gradient with middle stop. Parameters `(sx, sy) (ex, ey)` specify the start
3546  * and end coordinates of the linear gradient, icol specifies the start color, midp specifies stop point in
3547  * range `(0..1)`, and ocol the end color.
3548  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3549  *
3550  * Group: paints
3551  */
3552 public NVGPaint linearGradient() (NVGContext ctx, float sx, float sy, float ex, float ey, in auto ref NVGColor icol, in float midp, in auto ref NVGColor mcol, in auto ref NVGColor ocol) nothrow @trusted @nogc {
3553   enum large = 1e5f;
3554 
3555   NVGPaint p = void;
3556   memset(&p, 0, p.sizeof);
3557   p.simpleColor = false;
3558 
3559   // Calculate transform aligned to the line
3560   float dx = ex-sx;
3561   float dy = ey-sy;
3562   immutable float d = nvg__sqrtf(dx*dx+dy*dy);
3563   if (d > 0.0001f) {
3564     dx /= d;
3565     dy /= d;
3566   } else {
3567     dx = 0;
3568     dy = 1;
3569   }
3570 
3571   p.xform.mat.ptr[0] = dy; p.xform.mat.ptr[1] = -dx;
3572   p.xform.mat.ptr[2] = dx; p.xform.mat.ptr[3] = dy;
3573   p.xform.mat.ptr[4] = sx-dx*large; p.xform.mat.ptr[5] = sy-dy*large;
3574 
3575   p.extent.ptr[0] = large;
3576   p.extent.ptr[1] = large+d*0.5f;
3577 
3578   p.radius = 0.0f;
3579 
3580   p.feather = nvg__max(NVG_MIN_FEATHER, d);
3581 
3582   if (midp <= 0) {
3583     p.innerColor = p.middleColor = mcol;
3584     p.midp = -1;
3585   } else if (midp > 1) {
3586     p.innerColor = p.middleColor = icol;
3587     p.midp = -1;
3588   } else {
3589     p.innerColor = icol;
3590     p.middleColor = mcol;
3591     p.midp = midp;
3592   }
3593   p.outerColor = ocol;
3594 
3595   return p;
3596 }
3597 
3598 static if (NanoVegaHasArsdColor) {
3599 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3600  * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3601  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3602  *
3603  * Group: paints
3604  */
3605 public NVGPaint radialGradient (NVGContext ctx, in float cx, in float cy, in float inr, in float outr, in Color icol, in Color ocol) nothrow @trusted @nogc {
3606   return ctx.radialGradient(cx, cy, inr, outr, NVGColor(icol), NVGColor(ocol));
3607 }
3608 }
3609 
3610 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3611  * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3612  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3613  *
3614  * Group: paints
3615  */
3616 public NVGPaint radialGradient() (NVGContext ctx, float cx, float cy, float inr, float outr, in auto ref NVGColor icol, in auto ref NVGColor ocol) nothrow @trusted @nogc {
3617   immutable float r = (inr+outr)*0.5f;
3618   immutable float f = (outr-inr);
3619 
3620   NVGPaint p = void;
3621   memset(&p, 0, p.sizeof);
3622   p.simpleColor = false;
3623 
3624   p.xform.identity;
3625   p.xform.mat.ptr[4] = cx;
3626   p.xform.mat.ptr[5] = cy;
3627 
3628   p.extent.ptr[0] = r;
3629   p.extent.ptr[1] = r;
3630 
3631   p.radius = r;
3632 
3633   p.feather = nvg__max(NVG_MIN_FEATHER, f);
3634 
3635   p.innerColor = p.middleColor = icol;
3636   p.outerColor = ocol;
3637   p.midp = -1;
3638 
3639   return p;
3640 }
3641 
3642 static if (NanoVegaHasArsdColor) {
3643 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3644  * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3645  * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3646  * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3647  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3648  *
3649  * Group: paints
3650  */
3651 public NVGPaint boxGradient (NVGContext ctx, in float x, in float y, in float w, in float h, in float r, in float f, in Color icol, in Color ocol) nothrow @trusted @nogc {
3652   return ctx.boxGradient(x, y, w, h, r, f, NVGColor(icol), NVGColor(ocol));
3653 }
3654 }
3655 
3656 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3657  * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3658  * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3659  * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3660  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3661  *
3662  * Group: paints
3663  */
3664 public NVGPaint boxGradient() (NVGContext ctx, float x, float y, float w, float h, float r, float f, in auto ref NVGColor icol, in auto ref NVGColor ocol) nothrow @trusted @nogc {
3665   NVGPaint p = void;
3666   memset(&p, 0, p.sizeof);
3667   p.simpleColor = false;
3668 
3669   p.xform.identity;
3670   p.xform.mat.ptr[4] = x+w*0.5f;
3671   p.xform.mat.ptr[5] = y+h*0.5f;
3672 
3673   p.extent.ptr[0] = w*0.5f;
3674   p.extent.ptr[1] = h*0.5f;
3675 
3676   p.radius = r;
3677 
3678   p.feather = nvg__max(NVG_MIN_FEATHER, f);
3679 
3680   p.innerColor = p.middleColor = icol;
3681   p.outerColor = ocol;
3682   p.midp = -1;
3683 
3684   return p;
3685 }
3686 
3687 /** Creates and returns an image pattern. Parameters `(cx, cy)` specify the left-top location of the image pattern,
3688  * `(w, h)` the size of one image, [angle] rotation around the top-left corner, [image] is handle to the image to render.
3689  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3690  *
3691  * Group: paints
3692  */
3693 public NVGPaint imagePattern() (NVGContext ctx, float cx, float cy, float w, float h, float angle, in auto ref NVGImage image, float alpha=1) nothrow @trusted @nogc {
3694   NVGPaint p = void;
3695   memset(&p, 0, p.sizeof);
3696   p.simpleColor = false;
3697 
3698   p.xform.identity.rotate(angle);
3699   p.xform.mat.ptr[4] = cx;
3700   p.xform.mat.ptr[5] = cy;
3701 
3702   p.extent.ptr[0] = w;
3703   p.extent.ptr[1] = h;
3704 
3705   p.image = image;
3706 
3707   p.innerColor = p.middleColor = p.outerColor = nvgRGBAf(1, 1, 1, alpha);
3708   p.midp = -1;
3709 
3710   return p;
3711 }
3712 
3713 /// Linear gradient with multiple stops.
3714 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3715 /// Group: paints
3716 public struct NVGLGS {
3717 private:
3718   NVGColor ic, mc, oc; // inner, middle, out
3719   float midp;
3720   NVGImage imgid;
3721   // [imagePattern] arguments
3722   float cx, cy, dimx, dimy; // dimx and dimy are ex and ey for simple gradients
3723   public float angle; ///
3724 
3725 public:
3726   @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (imgid.valid || midp >= -1); } ///
3727   void clear ()  nothrow @safe @nogc { pragma(inline, true); imgid.clear(); midp = float.nan; } ///
3728 }
3729 
3730 /** Returns [NVGPaint] for linear gradient with stops, created with [createLinearGradientWithStops].
3731  * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3732  *
3733  * $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3734  * Group: paints
3735  */
3736 public NVGPaint asPaint() (NVGContext ctx, in auto ref NVGLGS lgs) nothrow @trusted @nogc {
3737   if (!lgs.valid) {
3738     NVGPaint p = void;
3739     memset(&p, 0, p.sizeof);
3740     nvg__setPaintColor(p, NVGColor.red);
3741     return p;
3742   } else if (lgs.midp >= -1) {
3743     return ctx.linearGradient(lgs.cx, lgs.cy, lgs.dimx, lgs.dimy, lgs.ic, lgs.midp, lgs.mc, lgs.oc);
3744   } else {
3745     return ctx.imagePattern(lgs.cx, lgs.cy, lgs.dimx, lgs.dimy, lgs.angle, lgs.imgid);
3746   }
3747 }
3748 
3749 /// Gradient Stop Point.
3750 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3751 /// Group: paints
3752 public struct NVGGradientStop {
3753   float offset = 0; /// [0..1]
3754   NVGColor color; ///
3755 
3756   this() (in float aofs, in auto ref NVGColor aclr) nothrow @trusted @nogc { pragma(inline, true); offset = aofs; color = aclr; } ///
3757   static if (NanoVegaHasArsdColor) {
3758     this() (in float aofs, in Color aclr) nothrow @trusted @nogc { pragma(inline, true); offset = aofs; color = NVGColor(aclr); } ///
3759   }
3760 }
3761 
3762 /// Create linear gradient data suitable to use with `linearGradient(res)`.
3763 /// Don't forget to destroy the result when you don't need it anymore with `ctx.kill(res);`.
3764 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3765 /// Group: paints
3766 public NVGLGS createLinearGradientWithStops (NVGContext ctx, in float sx, in float sy, in float ex, in float ey, const(NVGGradientStop)[] stops...) nothrow @trusted @nogc {
3767   // based on the code by Jorge Acereda <jacereda@gmail.com>
3768   enum NVG_GRADIENT_SAMPLES = 1024;
3769   static void gradientSpan (uint* dst, const(NVGGradientStop)* s0, const(NVGGradientStop)* s1) nothrow @trusted @nogc {
3770     immutable float s0o = nvg__clamp(s0.offset, 0.0f, 1.0f);
3771     immutable float s1o = nvg__clamp(s1.offset, 0.0f, 1.0f);
3772     uint s = cast(uint)(s0o*NVG_GRADIENT_SAMPLES);
3773     uint e = cast(uint)(s1o*NVG_GRADIENT_SAMPLES);
3774     uint sc = 0xffffffffU;
3775     uint sh = 24;
3776     uint r = cast(uint)(s0.color.rgba[0]*sc);
3777     uint g = cast(uint)(s0.color.rgba[1]*sc);
3778     uint b = cast(uint)(s0.color.rgba[2]*sc);
3779     uint a = cast(uint)(s0.color.rgba[3]*sc);
3780     uint dr = cast(uint)((s1.color.rgba[0]*sc-r)/(e-s));
3781     uint dg = cast(uint)((s1.color.rgba[1]*sc-g)/(e-s));
3782     uint db = cast(uint)((s1.color.rgba[2]*sc-b)/(e-s));
3783     uint da = cast(uint)((s1.color.rgba[3]*sc-a)/(e-s));
3784     dst += s;
3785     foreach (immutable _; s..e) {
3786       version(BigEndian) {
3787         *dst++ = ((r>>sh)<<24)+((g>>sh)<<16)+((b>>sh)<<8)+((a>>sh)<<0);
3788       } else {
3789         *dst++ = ((a>>sh)<<24)+((b>>sh)<<16)+((g>>sh)<<8)+((r>>sh)<<0);
3790       }
3791       r += dr;
3792       g += dg;
3793       b += db;
3794       a += da;
3795     }
3796   }
3797 
3798   NVGLGS res;
3799   res.cx = sx;
3800   res.cy = sy;
3801 
3802   if (stops.length == 2 && stops.ptr[0].offset <= 0 && stops.ptr[1].offset >= 1) {
3803     // create simple linear gradient
3804     res.ic = res.mc = stops.ptr[0].color;
3805     res.oc = stops.ptr[1].color;
3806     res.midp = -1;
3807     res.dimx = ex;
3808     res.dimy = ey;
3809   } else if (stops.length == 3 && stops.ptr[0].offset <= 0 && stops.ptr[2].offset >= 1) {
3810     // create simple linear gradient with middle stop
3811     res.ic = stops.ptr[0].color;
3812     res.mc = stops.ptr[1].color;
3813     res.oc = stops.ptr[2].color;
3814     res.midp = stops.ptr[1].offset;
3815     res.dimx = ex;
3816     res.dimy = ey;
3817   } else {
3818     // create image gradient
3819     uint[NVG_GRADIENT_SAMPLES] data = void;
3820     immutable float w = ex-sx;
3821     immutable float h = ey-sy;
3822     res.dimx = nvg__sqrtf(w*w+h*h);
3823     res.dimy = 1; //???
3824 
3825     res.angle =
3826       (/*nvg__absf(h) < 0.0001 ? 0 :
3827        nvg__absf(w) < 0.0001 ? 90.nvgDegrees :*/
3828        nvg__atan2f(h/*ey-sy*/, w/*ex-sx*/));
3829 
3830     if (stops.length > 0) {
3831       auto s0 = NVGGradientStop(0, nvgRGBAf(0, 0, 0, 1));
3832       auto s1 = NVGGradientStop(1, nvgRGBAf(1, 1, 1, 1));
3833       if (stops.length > 64) stops = stops[0..64];
3834       if (stops.length) {
3835         s0.color = stops[0].color;
3836         s1.color = stops[$-1].color;
3837       }
3838       gradientSpan(data.ptr, &s0, (stops.length ? stops.ptr : &s1));
3839       foreach (immutable i; 0..stops.length-1) gradientSpan(data.ptr, stops.ptr+i, stops.ptr+i+1);
3840       gradientSpan(data.ptr, (stops.length ? stops.ptr+stops.length-1 : &s0), &s1);
3841       res.imgid = ctx.createImageRGBA(NVG_GRADIENT_SAMPLES, 1, data[]/*, NVGImageFlag.RepeatX, NVGImageFlag.RepeatY*/);
3842     }
3843   }
3844   return res;
3845 }
3846 
3847 
3848 // ////////////////////////////////////////////////////////////////////////// //
3849 // Scissoring
3850 
3851 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3852 /// Group: scissoring
3853 public void scissor (NVGContext ctx, in float x, in float y, float w, float h) nothrow @trusted @nogc {
3854   NVGstate* state = nvg__getState(ctx);
3855 
3856   w = nvg__max(0.0f, w);
3857   h = nvg__max(0.0f, h);
3858 
3859   state.scissor.xform.identity;
3860   state.scissor.xform.mat.ptr[4] = x+w*0.5f;
3861   state.scissor.xform.mat.ptr[5] = y+h*0.5f;
3862   //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3863   state.scissor.xform.mul(state.xform);
3864 
3865   state.scissor.extent.ptr[0] = w*0.5f;
3866   state.scissor.extent.ptr[1] = h*0.5f;
3867 }
3868 
3869 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3870 /// Arguments: [x, y, w, h]*
3871 /// Group: scissoring
3872 public void scissor (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
3873   enum ArgC = 4;
3874   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [scissor] call");
3875   if (args.length < ArgC) return;
3876   NVGstate* state = nvg__getState(ctx);
3877   const(float)* aptr = args.ptr;
3878   foreach (immutable idx; 0..args.length/ArgC) {
3879     immutable x = *aptr++;
3880     immutable y = *aptr++;
3881     immutable w = nvg__max(0.0f, *aptr++);
3882     immutable h = nvg__max(0.0f, *aptr++);
3883 
3884     state.scissor.xform.identity;
3885     state.scissor.xform.mat.ptr[4] = x+w*0.5f;
3886     state.scissor.xform.mat.ptr[5] = y+h*0.5f;
3887     //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3888     state.scissor.xform.mul(state.xform);
3889 
3890     state.scissor.extent.ptr[0] = w*0.5f;
3891     state.scissor.extent.ptr[1] = h*0.5f;
3892   }
3893 }
3894 
3895 void nvg__isectRects (float* dst, float ax, float ay, float aw, float ah, float bx, float by, float bw, float bh) nothrow @trusted @nogc {
3896   immutable float minx = nvg__max(ax, bx);
3897   immutable float miny = nvg__max(ay, by);
3898   immutable float maxx = nvg__min(ax+aw, bx+bw);
3899   immutable float maxy = nvg__min(ay+ah, by+bh);
3900   dst[0] = minx;
3901   dst[1] = miny;
3902   dst[2] = nvg__max(0.0f, maxx-minx);
3903   dst[3] = nvg__max(0.0f, maxy-miny);
3904 }
3905 
3906 /** Intersects current scissor rectangle with the specified rectangle.
3907  * The scissor rectangle is transformed by the current transform.
3908  * Note: in case the rotation of previous scissor rect differs from
3909  * the current one, the intersection will be done between the specified
3910  * rectangle and the previous scissor rectangle transformed in the current
3911  * transform space. The resulting shape is always rectangle.
3912  *
3913  * Group: scissoring
3914  */
3915 public void intersectScissor (NVGContext ctx, in float x, in float y, in float w, in float h) nothrow @trusted @nogc {
3916   NVGstate* state = nvg__getState(ctx);
3917 
3918   // If no previous scissor has been set, set the scissor as current scissor.
3919   if (state.scissor.extent.ptr[0] < 0) {
3920     ctx.scissor(x, y, w, h);
3921     return;
3922   }
3923 
3924   NVGMatrix pxform = void;
3925   NVGMatrix invxorm = void;
3926   float[4] rect = void;
3927 
3928   // Transform the current scissor rect into current transform space.
3929   // If there is difference in rotation, this will be approximation.
3930   //memcpy(pxform.mat.ptr, state.scissor.xform.ptr, float.sizeof*6);
3931   pxform = state.scissor.xform;
3932   immutable float ex = state.scissor.extent.ptr[0];
3933   immutable float ey = state.scissor.extent.ptr[1];
3934   //nvgTransformInverse(invxorm[], state.xform[]);
3935   invxorm = state.xform.inverted;
3936   //nvgTransformMultiply(pxform[], invxorm[]);
3937   pxform.mul(invxorm);
3938   immutable float tex = ex*nvg__absf(pxform.mat.ptr[0])+ey*nvg__absf(pxform.mat.ptr[2]);
3939   immutable float tey = ex*nvg__absf(pxform.mat.ptr[1])+ey*nvg__absf(pxform.mat.ptr[3]);
3940 
3941   // Intersect rects.
3942   nvg__isectRects(rect.ptr, pxform.mat.ptr[4]-tex, pxform.mat.ptr[5]-tey, tex*2, tey*2, x, y, w, h);
3943 
3944   //ctx.scissor(rect.ptr[0], rect.ptr[1], rect.ptr[2], rect.ptr[3]);
3945   ctx.scissor(rect.ptr[0..4]);
3946 }
3947 
3948 /** Intersects current scissor rectangle with the specified rectangle.
3949  * The scissor rectangle is transformed by the current transform.
3950  * Note: in case the rotation of previous scissor rect differs from
3951  * the current one, the intersection will be done between the specified
3952  * rectangle and the previous scissor rectangle transformed in the current
3953  * transform space. The resulting shape is always rectangle.
3954  *
3955  * Arguments: [x, y, w, h]*
3956  *
3957  * Group: scissoring
3958  */
3959 public void intersectScissor (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
3960   enum ArgC = 4;
3961   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [intersectScissor] call");
3962   if (args.length < ArgC) return;
3963   const(float)* aptr = args.ptr;
3964   foreach (immutable idx; 0..args.length/ArgC) {
3965     immutable x = *aptr++;
3966     immutable y = *aptr++;
3967     immutable w = *aptr++;
3968     immutable h = *aptr++;
3969     ctx.intersectScissor(x, y, w, h);
3970   }
3971 }
3972 
3973 /// Reset and disables scissoring.
3974 /// Group: scissoring
3975 public void resetScissor (NVGContext ctx) nothrow @trusted @nogc {
3976   NVGstate* state = nvg__getState(ctx);
3977   state.scissor.xform.mat[] = 0.0f;
3978   state.scissor.extent[] = -1.0f;
3979 }
3980 
3981 
3982 // ////////////////////////////////////////////////////////////////////////// //
3983 // Render-Time Affine Transformations
3984 
3985 /// Sets GPU affine transformatin matrix. Don't do scaling or skewing here.
3986 /// This matrix won't be saved/restored with context state save/restore operations, as it is not a part of that state.
3987 /// Group: gpu_affine
3988 public void affineGPU() (NVGContext ctx, in auto ref NVGMatrix mat) nothrow @trusted @nogc {
3989   ctx.gpuAffine = mat;
3990   ctx.params.renderSetAffine(ctx.params.userPtr, ctx.gpuAffine);
3991 }
3992 
3993 /// Get current GPU affine transformatin matrix.
3994 /// Group: gpu_affine
3995 public NVGMatrix affineGPU (NVGContext ctx) nothrow @safe @nogc {
3996   pragma(inline, true);
3997   return ctx.gpuAffine;
3998 }
3999 
4000 /// "Untransform" point using current GPU affine matrix.
4001 /// Group: gpu_affine
4002 public void gpuUntransformPoint (NVGContext ctx, float *dx, float *dy, in float x, in float y) nothrow @safe @nogc {
4003   if (ctx.gpuAffine.isIdentity) {
4004     if (dx !is null) *dx = x;
4005     if (dy !is null) *dy = y;
4006   } else {
4007     // inverse GPU transformation
4008     NVGMatrix igpu = ctx.gpuAffine.inverted;
4009     igpu.point(dx, dy, x, y);
4010   }
4011 }
4012 
4013 
4014 // ////////////////////////////////////////////////////////////////////////// //
4015 // rasterization (tesselation) code
4016 
4017 int nvg__ptEquals (float x1, float y1, float x2, float y2, float tol) pure nothrow @safe @nogc {
4018   //pragma(inline, true);
4019   immutable float dx = x2-x1;
4020   immutable float dy = y2-y1;
4021   return dx*dx+dy*dy < tol*tol;
4022 }
4023 
4024 float nvg__distPtSeg (float x, float y, float px, float py, float qx, float qy) pure nothrow @safe @nogc {
4025   immutable float pqx = qx-px;
4026   immutable float pqy = qy-py;
4027   float dx = x-px;
4028   float dy = y-py;
4029   immutable float d = pqx*pqx+pqy*pqy;
4030   float t = pqx*dx+pqy*dy;
4031   if (d > 0) t /= d;
4032   if (t < 0) t = 0; else if (t > 1) t = 1;
4033   dx = px+t*pqx-x;
4034   dy = py+t*pqy-y;
4035   return dx*dx+dy*dy;
4036 }
4037 
4038 void nvg__appendCommands(bool useCommand=true) (NVGContext ctx, Command acmd, const(float)[] vals...) nothrow @trusted @nogc {
4039   int nvals = cast(int)vals.length;
4040   static if (useCommand) {
4041     enum addon = 1;
4042   } else {
4043     enum addon = 0;
4044     if (nvals == 0) return; // nothing to do
4045   }
4046 
4047   NVGstate* state = nvg__getState(ctx);
4048 
4049   if (ctx.ncommands+nvals+addon > ctx.ccommands) {
4050     //int ccommands = ctx.ncommands+nvals+ctx.ccommands/2;
4051     int ccommands = ((ctx.ncommands+(nvals+addon))|0xfff)+1;
4052     float* commands = cast(float*)realloc(ctx.commands, float.sizeof*ccommands);
4053     if (commands is null) assert(0, "NanoVega: out of memory");
4054     ctx.commands = commands;
4055     ctx.ccommands = ccommands;
4056     assert(ctx.ncommands+(nvals+addon) <= ctx.ccommands);
4057   }
4058 
4059   static if (!useCommand) acmd = cast(Command)vals.ptr[0];
4060 
4061   if (acmd != Command.Close && acmd != Command.Winding) {
4062     //assert(nvals+addon >= 3);
4063     ctx.commandx = vals.ptr[nvals-2];
4064     ctx.commandy = vals.ptr[nvals-1];
4065   }
4066 
4067   // copy commands
4068   float* vp = ctx.commands+ctx.ncommands;
4069   static if (useCommand) {
4070     vp[0] = cast(float)acmd;
4071     if (nvals > 0) memcpy(vp+1, vals.ptr, nvals*float.sizeof);
4072   } else {
4073     memcpy(vp, vals.ptr, nvals*float.sizeof);
4074   }
4075   ctx.ncommands += nvals+addon;
4076 
4077   // transform commands
4078   int i = nvals+addon;
4079   while (i > 0) {
4080     int nlen = 1;
4081     final switch (cast(Command)(*vp)) {
4082       case Command.MoveTo:
4083       case Command.LineTo:
4084         assert(i >= 3);
4085         state.xform.point(vp+1, vp+2, vp[1], vp[2]);
4086         nlen = 3;
4087         break;
4088       case Command.BezierTo:
4089         assert(i >= 7);
4090         state.xform.point(vp+1, vp+2, vp[1], vp[2]);
4091         state.xform.point(vp+3, vp+4, vp[3], vp[4]);
4092         state.xform.point(vp+5, vp+6, vp[5], vp[6]);
4093         nlen = 7;
4094         break;
4095       case Command.Close:
4096         nlen = 1;
4097         break;
4098       case Command.Winding:
4099         nlen = 2;
4100         break;
4101     }
4102     assert(nlen > 0 && nlen <= i);
4103     i -= nlen;
4104     vp += nlen;
4105   }
4106 }
4107 
4108 void nvg__clearPathCache (NVGContext ctx) nothrow @trusted @nogc {
4109   // no need to clear paths, as data is not copied there
4110   //foreach (ref p; ctx.cache.paths[0..ctx.cache.npaths]) p.clear();
4111   ctx.cache.npoints = 0;
4112   ctx.cache.npaths = 0;
4113   ctx.cache.fillReady = ctx.cache.strokeReady = false;
4114   ctx.cache.clipmode = NVGClipMode.None;
4115 }
4116 
4117 NVGpath* nvg__lastPath (NVGContext ctx) nothrow @trusted @nogc {
4118   return (ctx.cache.npaths > 0 ? &ctx.cache.paths[ctx.cache.npaths-1] : null);
4119 }
4120 
4121 void nvg__addPath (NVGContext ctx) nothrow @trusted @nogc {
4122   import core.stdc.stdlib : realloc;
4123   import core.stdc.string : memset;
4124 
4125   if (ctx.cache.npaths+1 > ctx.cache.cpaths) {
4126     int cpaths = ctx.cache.npaths+1+ctx.cache.cpaths/2;
4127     NVGpath* paths = cast(NVGpath*)realloc(ctx.cache.paths, NVGpath.sizeof*cpaths);
4128     if (paths is null) assert(0, "NanoVega: out of memory");
4129     ctx.cache.paths = paths;
4130     ctx.cache.cpaths = cpaths;
4131   }
4132 
4133   NVGpath* path = &ctx.cache.paths[ctx.cache.npaths++];
4134   memset(path, 0, NVGpath.sizeof);
4135   path.first = ctx.cache.npoints;
4136   path.mWinding = NVGWinding.CCW;
4137 }
4138 
4139 NVGpoint* nvg__lastPoint (NVGContext ctx) nothrow @trusted @nogc {
4140   return (ctx.cache.npoints > 0 ? &ctx.cache.points[ctx.cache.npoints-1] : null);
4141 }
4142 
4143 void nvg__addPoint (NVGContext ctx, float x, float y, int flags) nothrow @trusted @nogc {
4144   NVGpath* path = nvg__lastPath(ctx);
4145   if (path is null) return;
4146 
4147   if (path.count > 0 && ctx.cache.npoints > 0) {
4148     NVGpoint* pt = nvg__lastPoint(ctx);
4149     if (nvg__ptEquals(pt.x, pt.y, x, y, ctx.distTol)) {
4150       pt.flags |= flags;
4151       return;
4152     }
4153   }
4154 
4155   if (ctx.cache.npoints+1 > ctx.cache.cpoints) {
4156     int cpoints = ctx.cache.npoints+1+ctx.cache.cpoints/2;
4157     NVGpoint* points = cast(NVGpoint*)realloc(ctx.cache.points, NVGpoint.sizeof*cpoints);
4158     if (points is null) return;
4159     ctx.cache.points = points;
4160     ctx.cache.cpoints = cpoints;
4161   }
4162 
4163   NVGpoint* pt = &ctx.cache.points[ctx.cache.npoints];
4164   memset(pt, 0, (*pt).sizeof);
4165   pt.x = x;
4166   pt.y = y;
4167   pt.flags = cast(ubyte)flags;
4168 
4169   ++ctx.cache.npoints;
4170   ++path.count;
4171 }
4172 
4173 void nvg__closePath (NVGContext ctx) nothrow @trusted @nogc {
4174   NVGpath* path = nvg__lastPath(ctx);
4175   if (path is null) return;
4176   path.closed = true;
4177 }
4178 
4179 void nvg__pathWinding (NVGContext ctx, NVGWinding winding) nothrow @trusted @nogc {
4180   NVGpath* path = nvg__lastPath(ctx);
4181   if (path is null) return;
4182   path.mWinding = winding;
4183 }
4184 
4185 float nvg__getAverageScale() (in auto ref NVGMatrix t) /*pure*/ nothrow @trusted @nogc {
4186   immutable float sx = nvg__sqrtf(t.mat.ptr[0]*t.mat.ptr[0]+t.mat.ptr[2]*t.mat.ptr[2]);
4187   immutable float sy = nvg__sqrtf(t.mat.ptr[1]*t.mat.ptr[1]+t.mat.ptr[3]*t.mat.ptr[3]);
4188   return (sx+sy)*0.5f;
4189 }
4190 
4191 NVGVertex* nvg__allocTempVerts (NVGContext ctx, int nverts) nothrow @trusted @nogc {
4192   if (nverts > ctx.cache.cverts) {
4193     int cverts = (nverts+0xff)&~0xff; // Round up to prevent allocations when things change just slightly.
4194     NVGVertex* verts = cast(NVGVertex*)realloc(ctx.cache.verts, NVGVertex.sizeof*cverts);
4195     if (verts is null) return null;
4196     ctx.cache.verts = verts;
4197     ctx.cache.cverts = cverts;
4198   }
4199 
4200   return ctx.cache.verts;
4201 }
4202 
4203 float nvg__triarea2 (float ax, float ay, float bx, float by, float cx, float cy) pure nothrow @safe @nogc {
4204   immutable float abx = bx-ax;
4205   immutable float aby = by-ay;
4206   immutable float acx = cx-ax;
4207   immutable float acy = cy-ay;
4208   return acx*aby-abx*acy;
4209 }
4210 
4211 float nvg__polyArea (NVGpoint* pts, int npts) nothrow @trusted @nogc {
4212   float area = 0;
4213   foreach (int i; 2..npts) {
4214     NVGpoint* a = &pts[0];
4215     NVGpoint* b = &pts[i-1];
4216     NVGpoint* c = &pts[i];
4217     area += nvg__triarea2(a.x, a.y, b.x, b.y, c.x, c.y);
4218   }
4219   return area*0.5f;
4220 }
4221 
4222 void nvg__polyReverse (NVGpoint* pts, int npts) nothrow @trusted @nogc {
4223   NVGpoint tmp = void;
4224   int i = 0, j = npts-1;
4225   while (i < j) {
4226     tmp = pts[i];
4227     pts[i] = pts[j];
4228     pts[j] = tmp;
4229     ++i;
4230     --j;
4231   }
4232 }
4233 
4234 void nvg__vset (NVGVertex* vtx, float x, float y, float u, float v) nothrow @trusted @nogc {
4235   vtx.x = x;
4236   vtx.y = y;
4237   vtx.u = u;
4238   vtx.v = v;
4239 }
4240 
4241 void nvg__tesselateBezier (NVGContext ctx, 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) nothrow @trusted @nogc {
4242   if (level > 10) return;
4243 
4244   // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case)
4245   /*
4246   if (level == 0 && ctx.tesselatortype == NVGTesselation.Combined) {
4247     static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc {
4248       immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y);
4249       return (nvg__absf(cz*cz) <= 0.01f); // arbitrary number, seems to work ok with NanoSVG output
4250     }
4251     if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) {
4252       //{ import core.stdc.stdio; printf("AFD fallback!\n"); }
4253       ctx.nvg__tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4, type);
4254       return;
4255     }
4256   }
4257   */
4258 
4259   immutable float x12 = (x1+x2)*0.5f;
4260   immutable float y12 = (y1+y2)*0.5f;
4261   immutable float x23 = (x2+x3)*0.5f;
4262   immutable float y23 = (y2+y3)*0.5f;
4263   immutable float x34 = (x3+x4)*0.5f;
4264   immutable float y34 = (y3+y4)*0.5f;
4265   immutable float x123 = (x12+x23)*0.5f;
4266   immutable float y123 = (y12+y23)*0.5f;
4267 
4268   immutable float dx = x4-x1;
4269   immutable float dy = y4-y1;
4270   immutable float d2 = nvg__absf(((x2-x4)*dy-(y2-y4)*dx));
4271   immutable float d3 = nvg__absf(((x3-x4)*dy-(y3-y4)*dx));
4272 
4273   if ((d2+d3)*(d2+d3) < ctx.tessTol*(dx*dx+dy*dy)) {
4274     nvg__addPoint(ctx, x4, y4, type);
4275     return;
4276   }
4277 
4278   immutable float x234 = (x23+x34)*0.5f;
4279   immutable float y234 = (y23+y34)*0.5f;
4280   immutable float x1234 = (x123+x234)*0.5f;
4281   immutable float y1234 = (y123+y234)*0.5f;
4282 
4283   // "taxicab" / "manhattan" check for flat curves
4284   if (nvg__absf(x1+x3-x2-x2)+nvg__absf(y1+y3-y2-y2)+nvg__absf(x2+x4-x3-x3)+nvg__absf(y2+y4-y3-y3) < ctx.tessTol/4) {
4285     nvg__addPoint(ctx, x1234, y1234, type);
4286     return;
4287   }
4288 
4289   nvg__tesselateBezier(ctx, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, 0);
4290   nvg__tesselateBezier(ctx, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, type);
4291 }
4292 
4293 // based on the ideas and code of Maxim Shemanarev. Rest in Peace, bro!
4294 // see http://www.antigrain.com/research/adaptive_bezier/index.html
4295 void nvg__tesselateBezierMcSeem (NVGContext ctx, 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) nothrow @trusted @nogc {
4296   enum CollinearEPS = 0.00000001f; // 0.00001f;
4297   enum AngleTolEPS = 0.01f;
4298 
4299   static float distSquared (in float x1, in float y1, in float x2, in float y2) pure nothrow @safe @nogc {
4300     pragma(inline, true);
4301     immutable float dx = x2-x1;
4302     immutable float dy = y2-y1;
4303     return dx*dx+dy*dy;
4304   }
4305 
4306   if (level == 0) {
4307     nvg__addPoint(ctx, x1, y1, 0);
4308     nvg__tesselateBezierMcSeem(ctx, x1, y1, x2, y2, x3, y3, x4, y4, 1, type);
4309     nvg__addPoint(ctx, x4, y4, type);
4310     return;
4311   }
4312 
4313   if (level >= 32) return; // recurse limit; practically, it should be never reached, but...
4314 
4315   // calculate all the mid-points of the line segments
4316   immutable float x12 = (x1+x2)*0.5f;
4317   immutable float y12 = (y1+y2)*0.5f;
4318   immutable float x23 = (x2+x3)*0.5f;
4319   immutable float y23 = (y2+y3)*0.5f;
4320   immutable float x34 = (x3+x4)*0.5f;
4321   immutable float y34 = (y3+y4)*0.5f;
4322   immutable float x123 = (x12+x23)*0.5f;
4323   immutable float y123 = (y12+y23)*0.5f;
4324   immutable float x234 = (x23+x34)*0.5f;
4325   immutable float y234 = (y23+y34)*0.5f;
4326   immutable float x1234 = (x123+x234)*0.5f;
4327   immutable float y1234 = (y123+y234)*0.5f;
4328 
4329   // try to approximate the full cubic curve by a single straight line
4330   immutable float dx = x4-x1;
4331   immutable float dy = y4-y1;
4332 
4333   float d2 = nvg__absf(((x2-x4)*dy-(y2-y4)*dx));
4334   float d3 = nvg__absf(((x3-x4)*dy-(y3-y4)*dx));
4335   //immutable float da1, da2, k;
4336 
4337   final switch ((cast(int)(d2 > CollinearEPS)<<1)+cast(int)(d3 > CollinearEPS)) {
4338     case 0:
4339       // all collinear or p1 == p4
4340       float k = dx*dx+dy*dy;
4341       if (k == 0) {
4342         d2 = distSquared(x1, y1, x2, y2);
4343         d3 = distSquared(x4, y4, x3, y3);
4344       } else {
4345         k = 1.0f/k;
4346         float da1 = x2-x1;
4347         float da2 = y2-y1;
4348         d2 = k*(da1*dx+da2*dy);
4349         da1 = x3-x1;
4350         da2 = y3-y1;
4351         d3 = k*(da1*dx+da2*dy);
4352         if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {
4353           // Simple collinear case, 1---2---3---4
4354           // We can leave just two endpoints
4355           return;
4356         }
4357              if (d2 <= 0) d2 = distSquared(x2, y2, x1, y1);
4358         else if (d2 >= 1) d2 = distSquared(x2, y2, x4, y4);
4359         else d2 = distSquared(x2, y2, x1+d2*dx, y1+d2*dy);
4360 
4361              if (d3 <= 0) d3 = distSquared(x3, y3, x1, y1);
4362         else if (d3 >= 1) d3 = distSquared(x3, y3, x4, y4);
4363         else d3 = distSquared(x3, y3, x1+d3*dx, y1+d3*dy);
4364       }
4365       if (d2 > d3) {
4366         if (d2 < ctx.tessTol) {
4367           nvg__addPoint(ctx, x2, y2, type);
4368           return;
4369         }
4370       } if (d3 < ctx.tessTol) {
4371         nvg__addPoint(ctx, x3, y3, type);
4372         return;
4373       }
4374       break;
4375     case 1:
4376       // p1,p2,p4 are collinear, p3 is significant
4377       if (d3*d3 <= ctx.tessTol*(dx*dx+dy*dy)) {
4378         if (ctx.angleTol < AngleTolEPS) {
4379           nvg__addPoint(ctx, x23, y23, type);
4380           return;
4381         } else {
4382           // angle condition
4383           float da1 = nvg__absf(nvg__atan2f(y4-y3, x4-x3)-nvg__atan2f(y3-y2, x3-x2));
4384           if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
4385           if (da1 < ctx.angleTol) {
4386             nvg__addPoint(ctx, x2, y2, type);
4387             nvg__addPoint(ctx, x3, y3, type);
4388             return;
4389           }
4390           if (ctx.cuspLimit != 0.0) {
4391             if (da1 > ctx.cuspLimit) {
4392               nvg__addPoint(ctx, x3, y3, type);
4393               return;
4394             }
4395           }
4396         }
4397       }
4398       break;
4399     case 2:
4400       // p1,p3,p4 are collinear, p2 is significant
4401       if (d2*d2 <= ctx.tessTol*(dx*dx+dy*dy)) {
4402         if (ctx.angleTol < AngleTolEPS) {
4403           nvg__addPoint(ctx, x23, y23, type);
4404           return;
4405         } else {
4406           // angle condition
4407           float da1 = nvg__absf(nvg__atan2f(y3-y2, x3-x2)-nvg__atan2f(y2-y1, x2-x1));
4408           if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
4409           if (da1 < ctx.angleTol) {
4410             nvg__addPoint(ctx, x2, y2, type);
4411             nvg__addPoint(ctx, x3, y3, type);
4412             return;
4413           }
4414           if (ctx.cuspLimit != 0.0) {
4415             if (da1 > ctx.cuspLimit) {
4416               nvg__addPoint(ctx, x2, y2, type);
4417               return;
4418             }
4419           }
4420         }
4421       }
4422       break;
4423     case 3:
4424       // regular case
4425       if ((d2+d3)*(d2+d3) <= ctx.tessTol*(dx*dx+dy*dy)) {
4426         // if the curvature doesn't exceed the distance tolerance value, we tend to finish subdivisions
4427         if (ctx.angleTol < AngleTolEPS) {
4428           nvg__addPoint(ctx, x23, y23, type);
4429           return;
4430         } else {
4431           // angle and cusp condition
4432           immutable float k = nvg__atan2f(y3-y2, x3-x2);
4433           float da1 = nvg__absf(k-nvg__atan2f(y2-y1, x2-x1));
4434           float da2 = nvg__absf(nvg__atan2f(y4-y3, x4-x3)-k);
4435           if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
4436           if (da2 >= NVG_PI) da2 = 2*NVG_PI-da2;
4437           if (da1+da2 < ctx.angleTol) {
4438             // finally we can stop the recursion
4439             nvg__addPoint(ctx, x23, y23, type);
4440             return;
4441           }
4442           if (ctx.cuspLimit != 0.0) {
4443             if (da1 > ctx.cuspLimit) {
4444               nvg__addPoint(ctx, x2, y2, type);
4445               return;
4446             }
4447             if (da2 > ctx.cuspLimit) {
4448               nvg__addPoint(ctx, x3, y3, type);
4449               return;
4450             }
4451           }
4452         }
4453       }
4454       break;
4455   }
4456 
4457   // continue subdivision
4458   nvg__tesselateBezierMcSeem(ctx, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, 0);
4459   nvg__tesselateBezierMcSeem(ctx, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, type);
4460 }
4461 
4462 
4463 // Adaptive forward differencing for bezier tesselation.
4464 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt.
4465 // "Adaptive forward differencing for rendering curves and surfaces."
4466 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987.
4467 // original code by Taylor Holliday <taylor@audulus.com>
4468 void nvg__tesselateBezierAFD (NVGContext ctx, 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 {
4469   enum AFD_ONE = (1<<10);
4470 
4471   // power basis
4472   immutable float ax = -x1+3*x2-3*x3+x4;
4473   immutable float ay = -y1+3*y2-3*y3+y4;
4474   immutable float bx = 3*x1-6*x2+3*x3;
4475   immutable float by = 3*y1-6*y2+3*y3;
4476   immutable float cx = -3*x1+3*x2;
4477   immutable float cy = -3*y1+3*y2;
4478 
4479   // Transform to forward difference basis (stepsize 1)
4480   float px = x1;
4481   float py = y1;
4482   float dx = ax+bx+cx;
4483   float dy = ay+by+cy;
4484   float ddx = 6*ax+2*bx;
4485   float ddy = 6*ay+2*by;
4486   float dddx = 6*ax;
4487   float dddy = 6*ay;
4488 
4489   //printf("dx: %f, dy: %f\n", dx, dy);
4490   //printf("ddx: %f, ddy: %f\n", ddx, ddy);
4491   //printf("dddx: %f, dddy: %f\n", dddx, dddy);
4492 
4493   int t = 0;
4494   int dt = AFD_ONE;
4495 
4496   immutable float tol = ctx.tessTol*4;
4497 
4498   while (t < AFD_ONE) {
4499     // Flatness measure.
4500     float d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
4501 
4502     // printf("d: %f, th: %f\n", d, th);
4503 
4504     // Go to higher resolution if we're moving a lot or overshooting the end.
4505     while ((d > tol && dt > 1) || (t+dt > AFD_ONE)) {
4506       // printf("up\n");
4507 
4508       // Apply L to the curve. Increase curve resolution.
4509       dx = 0.5f*dx-(1.0f/8.0f)*ddx+(1.0f/16.0f)*dddx;
4510       dy = 0.5f*dy-(1.0f/8.0f)*ddy+(1.0f/16.0f)*dddy;
4511       ddx = (1.0f/4.0f)*ddx-(1.0f/8.0f)*dddx;
4512       ddy = (1.0f/4.0f)*ddy-(1.0f/8.0f)*dddy;
4513       dddx = (1.0f/8.0f)*dddx;
4514       dddy = (1.0f/8.0f)*dddy;
4515 
4516       // Half the stepsize.
4517       dt >>= 1;
4518 
4519       // Recompute d
4520       d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
4521     }
4522 
4523     // Go to lower resolution if we're really flat
4524     // and we aren't going to overshoot the end.
4525     // XXX: tol/32 is just a guess for when we are too flat.
4526     while ((d > 0 && d < tol/32.0f && dt < AFD_ONE) && (t+2*dt <= AFD_ONE)) {
4527       // printf("down\n");
4528 
4529       // Apply L^(-1) to the curve. Decrease curve resolution.
4530       dx = 2*dx+ddx;
4531       dy = 2*dy+ddy;
4532       ddx = 4*ddx+4*dddx;
4533       ddy = 4*ddy+4*dddy;
4534       dddx = 8*dddx;
4535       dddy = 8*dddy;
4536 
4537       // Double the stepsize.
4538       dt <<= 1;
4539 
4540       // Recompute d
4541       d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
4542     }
4543 
4544     // Forward differencing.
4545     px += dx;
4546     py += dy;
4547     dx += ddx;
4548     dy += ddy;
4549     ddx += dddx;
4550     ddy += dddy;
4551 
4552     // Output a point.
4553     nvg__addPoint(ctx, px, py, (t > 0 ? type : 0));
4554 
4555     // Advance along the curve.
4556     t += dt;
4557 
4558     // Ensure we don't overshoot.
4559     assert(t <= AFD_ONE);
4560   }
4561 }
4562 
4563 
4564 void nvg__dashLastPath (NVGContext ctx) nothrow @trusted @nogc {
4565   import core.stdc.stdlib : realloc;
4566   import core.stdc.string : memcpy;
4567 
4568   NVGpathCache* cache = ctx.cache;
4569   if (cache.npaths == 0) return;
4570 
4571   NVGpath* path = nvg__lastPath(ctx);
4572   if (path is null) return;
4573 
4574   NVGstate* state = nvg__getState(ctx);
4575   if (!state.dasherActive) return;
4576 
4577   static NVGpoint* pts = null;
4578   static uint ptsCount = 0;
4579   static uint ptsSize = 0;
4580 
4581   if (path.count < 2) return; // just in case
4582 
4583   // copy path points (reserve one point for closed pathes)
4584   if (ptsSize < path.count+1) {
4585     ptsSize = cast(uint)(path.count+1);
4586     pts = cast(NVGpoint*)realloc(pts, ptsSize*NVGpoint.sizeof);
4587     if (pts is null) assert(0, "NanoVega: out of memory");
4588   }
4589   ptsCount = cast(uint)path.count;
4590   memcpy(pts, &cache.points[path.first], ptsCount*NVGpoint.sizeof);
4591   // add closing point for closed pathes
4592   if (path.closed && !nvg__ptEquals(pts[0].x, pts[0].y, pts[ptsCount-1].x, pts[ptsCount-1].y, ctx.distTol)) {
4593     pts[ptsCount++] = pts[0];
4594   }
4595 
4596   // remove last path (with its points)
4597   --cache.npaths;
4598   cache.npoints -= path.count;
4599 
4600   // add stroked pathes
4601   const(float)* dashes = state.dashes.ptr;
4602   immutable uint dashCount = state.dashCount;
4603   float currDashStart = 0;
4604   uint currDashIdx = 0;
4605   immutable bool firstIsGap = state.firstDashIsGap;
4606 
4607   // calculate lengthes
4608   {
4609     NVGpoint* v1 = &pts[0];
4610     NVGpoint* v2 = &pts[1];
4611     foreach (immutable _; 0..ptsCount) {
4612       float dx = v2.x-v1.x;
4613       float dy = v2.y-v1.y;
4614       v1.len = nvg__normalize(&dx, &dy);
4615       v1 = v2++;
4616     }
4617   }
4618 
4619   void calcDashStart (float ds) {
4620     if (ds < 0) {
4621       ds = ds%state.totalDashLen;
4622       while (ds < 0) ds += state.totalDashLen;
4623     }
4624     currDashIdx = 0;
4625     currDashStart = 0;
4626     while (ds > 0) {
4627       if (ds > dashes[currDashIdx]) {
4628         ds -= dashes[currDashIdx];
4629         ++currDashIdx;
4630         currDashStart = 0;
4631         if (currDashIdx >= dashCount) currDashIdx = 0;
4632       } else {
4633         currDashStart = ds;
4634         ds = 0;
4635       }
4636     }
4637   }
4638 
4639   calcDashStart(state.dashStart);
4640 
4641   uint srcPointIdx = 1;
4642   const(NVGpoint)* v1 = &pts[0];
4643   const(NVGpoint)* v2 = &pts[1];
4644   float currRest = v1.len;
4645   nvg__addPath(ctx);
4646   nvg__addPoint(ctx, v1.x, v1.y, PointFlag.Corner);
4647 
4648   void fixLastPoint () {
4649     auto lpt = nvg__lastPath(ctx);
4650     if (lpt !is null && lpt.count > 0) {
4651       // fix last point
4652       if (auto lps = nvg__lastPoint(ctx)) lps.flags = PointFlag.Corner;
4653       // fix first point
4654       NVGpathCache* cache = ctx.cache;
4655       cache.points[lpt.first].flags = PointFlag.Corner;
4656     }
4657   }
4658 
4659   for (;;) {
4660     immutable float dlen = dashes[currDashIdx];
4661     if (dlen == 0) {
4662       ++currDashIdx;
4663       if (currDashIdx >= dashCount) currDashIdx = 0;
4664       continue;
4665     }
4666     immutable float dashRest = dlen-currDashStart;
4667     if ((currDashIdx&1) != firstIsGap) {
4668       // this is "moveto" command, so create new path
4669       fixLastPoint();
4670       nvg__addPath(ctx);
4671     }
4672     if (currRest > dashRest) {
4673       currRest -= dashRest;
4674       ++currDashIdx;
4675       if (currDashIdx >= dashCount) currDashIdx = 0;
4676       currDashStart = 0;
4677       nvg__addPoint(ctx,
4678         v2.x-(v2.x-v1.x)*currRest/v1.len,
4679         v2.y-(v2.y-v1.y)*currRest/v1.len,
4680         PointFlag.Corner
4681       );
4682     } else {
4683       currDashStart += currRest;
4684       nvg__addPoint(ctx, v2.x, v2.y, v1.flags); //k8:fix flags here?
4685       ++srcPointIdx;
4686       v1 = v2;
4687       currRest = v1.len;
4688       if (srcPointIdx >= ptsCount) break;
4689       v2 = &pts[srcPointIdx];
4690     }
4691   }
4692   fixLastPoint();
4693 }
4694 
4695 
4696 version(nanovg_bench_flatten) import iv.timer : Timer;
4697 
4698 void nvg__flattenPaths(bool asStroke) (NVGContext ctx) nothrow @trusted @nogc {
4699   version(nanovg_bench_flatten) {
4700     Timer timer;
4701     char[128] tmbuf;
4702     int bzcount;
4703   }
4704   NVGpathCache* cache = ctx.cache;
4705   NVGstate* state = nvg__getState(ctx);
4706 
4707   // check if we already did flattening
4708   static if (asStroke) {
4709     if (state.dashCount >= 2) {
4710       if (cache.npaths > 0 && state.lastFlattenDashCount == state.dashCount) return; // already flattened
4711       state.dasherActive = true;
4712       state.lastFlattenDashCount = state.dashCount;
4713     } else {
4714       if (cache.npaths > 0 && state.lastFlattenDashCount == 0) return; // already flattened
4715       state.dasherActive = false;
4716       state.lastFlattenDashCount = 0;
4717     }
4718   } else {
4719     if (cache.npaths > 0 && state.lastFlattenDashCount == 0) return; // already flattened
4720     state.lastFlattenDashCount = 0; // so next stroke flattening will redo it
4721     state.dasherActive = false;
4722   }
4723 
4724   // clear path cache
4725   cache.npaths = 0;
4726   cache.npoints = 0;
4727 
4728   // flatten
4729   version(nanovg_bench_flatten) timer.restart();
4730   int i = 0;
4731   while (i < ctx.ncommands) {
4732     final switch (cast(Command)ctx.commands[i]) {
4733       case Command.MoveTo:
4734         //assert(i+3 <= ctx.ncommands);
4735         static if (asStroke) {
4736           if (cache.npaths > 0 && state.dasherActive) nvg__dashLastPath(ctx);
4737         }
4738         nvg__addPath(ctx);
4739         const p = &ctx.commands[i+1];
4740         nvg__addPoint(ctx, p[0], p[1], PointFlag.Corner);
4741         i += 3;
4742         break;
4743       case Command.LineTo:
4744         //assert(i+3 <= ctx.ncommands);
4745         const p = &ctx.commands[i+1];
4746         nvg__addPoint(ctx, p[0], p[1], PointFlag.Corner);
4747         i += 3;
4748         break;
4749       case Command.BezierTo:
4750         //assert(i+7 <= ctx.ncommands);
4751         const last = nvg__lastPoint(ctx);
4752         if (last !is null) {
4753           const cp1 = &ctx.commands[i+1];
4754           const cp2 = &ctx.commands[i+3];
4755           const p = &ctx.commands[i+5];
4756           if (ctx.tesselatortype == NVGTesselation.DeCasteljau) {
4757             nvg__tesselateBezier(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], 0, PointFlag.Corner);
4758           } else if (ctx.tesselatortype == NVGTesselation.DeCasteljauMcSeem) {
4759             nvg__tesselateBezierMcSeem(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], 0, PointFlag.Corner);
4760           } else {
4761             nvg__tesselateBezierAFD(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], PointFlag.Corner);
4762           }
4763           version(nanovg_bench_flatten) ++bzcount;
4764         }
4765         i += 7;
4766         break;
4767       case Command.Close:
4768         //assert(i+1 <= ctx.ncommands);
4769         nvg__closePath(ctx);
4770         i += 1;
4771         break;
4772       case Command.Winding:
4773         //assert(i+2 <= ctx.ncommands);
4774         nvg__pathWinding(ctx, cast(NVGWinding)ctx.commands[i+1]);
4775         i += 2;
4776         break;
4777     }
4778   }
4779   static if (asStroke) {
4780     if (cache.npaths > 0 && state.dasherActive) nvg__dashLastPath(ctx);
4781   }
4782   version(nanovg_bench_flatten) {{
4783     timer.stop();
4784     auto xb = timer.toBuffer(tmbuf[]);
4785     import core.stdc.stdio : printf;
4786     printf("flattening time: [%.*s] (%d beziers)\n", cast(uint)xb.length, xb.ptr, bzcount);
4787   }}
4788 
4789   cache.bounds.ptr[0] = cache.bounds.ptr[1] = float.max;
4790   cache.bounds.ptr[2] = cache.bounds.ptr[3] = -float.max;
4791 
4792   // calculate the direction and length of line segments
4793   version(nanovg_bench_flatten) timer.restart();
4794   foreach (int j; 0..cache.npaths) {
4795     NVGpath* path = &cache.paths[j];
4796     NVGpoint* pts = &cache.points[path.first];
4797 
4798     // if the first and last points are the same, remove the last, mark as closed path
4799     NVGpoint* p0 = &pts[path.count-1];
4800     NVGpoint* p1 = &pts[0];
4801     if (nvg__ptEquals(p0.x, p0.y, p1.x, p1.y, ctx.distTol)) {
4802       --path.count;
4803       p0 = &pts[path.count-1];
4804       path.closed = true;
4805     }
4806 
4807     // enforce winding
4808     if (path.count > 2) {
4809       immutable float area = nvg__polyArea(pts, path.count);
4810       if (path.mWinding == NVGWinding.CCW && area < 0.0f) nvg__polyReverse(pts, path.count);
4811       if (path.mWinding == NVGWinding.CW && area > 0.0f) nvg__polyReverse(pts, path.count);
4812     }
4813 
4814     foreach (immutable _; 0..path.count) {
4815       // calculate segment direction and length
4816       p0.dx = p1.x-p0.x;
4817       p0.dy = p1.y-p0.y;
4818       p0.len = nvg__normalize(&p0.dx, &p0.dy);
4819       // update bounds
4820       cache.bounds.ptr[0] = nvg__min(cache.bounds.ptr[0], p0.x);
4821       cache.bounds.ptr[1] = nvg__min(cache.bounds.ptr[1], p0.y);
4822       cache.bounds.ptr[2] = nvg__max(cache.bounds.ptr[2], p0.x);
4823       cache.bounds.ptr[3] = nvg__max(cache.bounds.ptr[3], p0.y);
4824       // advance
4825       p0 = p1++;
4826     }
4827   }
4828   version(nanovg_bench_flatten) {{
4829     timer.stop();
4830     auto xb = timer.toBuffer(tmbuf[]);
4831     import core.stdc.stdio : printf;
4832     printf("segment calculation time: [%.*s]\n", cast(uint)xb.length, xb.ptr);
4833   }}
4834 }
4835 
4836 int nvg__curveDivs (float r, float arc, float tol) nothrow @trusted @nogc {
4837   immutable float da = nvg__acosf(r/(r+tol))*2.0f;
4838   return nvg__max(2, cast(int)nvg__ceilf(arc/da));
4839 }
4840 
4841 void nvg__chooseBevel (int bevel, NVGpoint* p0, NVGpoint* p1, float w, float* x0, float* y0, float* x1, float* y1) nothrow @trusted @nogc {
4842   if (bevel) {
4843     *x0 = p1.x+p0.dy*w;
4844     *y0 = p1.y-p0.dx*w;
4845     *x1 = p1.x+p1.dy*w;
4846     *y1 = p1.y-p1.dx*w;
4847   } else {
4848     *x0 = p1.x+p1.dmx*w;
4849     *y0 = p1.y+p1.dmy*w;
4850     *x1 = p1.x+p1.dmx*w;
4851     *y1 = p1.y+p1.dmy*w;
4852   }
4853 }
4854 
4855 NVGVertex* nvg__roundJoin (NVGVertex* dst, NVGpoint* p0, NVGpoint* p1, float lw, float rw, float lu, float ru, int ncap, float fringe) nothrow @trusted @nogc {
4856   float dlx0 = p0.dy;
4857   float dly0 = -p0.dx;
4858   float dlx1 = p1.dy;
4859   float dly1 = -p1.dx;
4860   //NVG_NOTUSED(fringe);
4861 
4862   if (p1.flags&PointFlag.Left) {
4863     float lx0 = void, ly0 = void, lx1 = void, ly1 = void;
4864     nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, lw, &lx0, &ly0, &lx1, &ly1);
4865     immutable float a0 = nvg__atan2f(-dly0, -dlx0);
4866     float a1 = nvg__atan2f(-dly1, -dlx1);
4867     if (a1 > a0) a1 -= NVG_PI*2;
4868 
4869     nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4870     nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4871 
4872     int n = nvg__clamp(cast(int)nvg__ceilf(((a0-a1)/NVG_PI)*ncap), 2, ncap);
4873     for (int i = 0; i < n; ++i) {
4874       float u = i/cast(float)(n-1);
4875       float a = a0+u*(a1-a0);
4876       float rx = p1.x+nvg__cosf(a)*rw;
4877       float ry = p1.y+nvg__sinf(a)*rw;
4878       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4879       nvg__vset(dst, rx, ry, ru, 1); ++dst;
4880     }
4881 
4882     nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4883     nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4884 
4885   } else {
4886     float rx0 = void, ry0 = void, rx1 = void, ry1 = void;
4887     nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, -rw, &rx0, &ry0, &rx1, &ry1);
4888     immutable float a0 = nvg__atan2f(dly0, dlx0);
4889     float a1 = nvg__atan2f(dly1, dlx1);
4890     if (a1 < a0) a1 += NVG_PI*2;
4891 
4892     nvg__vset(dst, p1.x+dlx0*rw, p1.y+dly0*rw, lu, 1); ++dst;
4893     nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4894 
4895     int n = nvg__clamp(cast(int)nvg__ceilf(((a1-a0)/NVG_PI)*ncap), 2, ncap);
4896     for (int i = 0; i < n; i++) {
4897       float u = i/cast(float)(n-1);
4898       float a = a0+u*(a1-a0);
4899       float lx = p1.x+nvg__cosf(a)*lw;
4900       float ly = p1.y+nvg__sinf(a)*lw;
4901       nvg__vset(dst, lx, ly, lu, 1); ++dst;
4902       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4903     }
4904 
4905     nvg__vset(dst, p1.x+dlx1*rw, p1.y+dly1*rw, lu, 1); ++dst;
4906     nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4907 
4908   }
4909   return dst;
4910 }
4911 
4912 NVGVertex* nvg__bevelJoin (NVGVertex* dst, NVGpoint* p0, NVGpoint* p1, float lw, float rw, float lu, float ru, float fringe) nothrow @trusted @nogc {
4913   float rx0, ry0, rx1, ry1;
4914   float lx0, ly0, lx1, ly1;
4915   float dlx0 = p0.dy;
4916   float dly0 = -p0.dx;
4917   float dlx1 = p1.dy;
4918   float dly1 = -p1.dx;
4919   //NVG_NOTUSED(fringe);
4920 
4921   if (p1.flags&PointFlag.Left) {
4922     nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, lw, &lx0, &ly0, &lx1, &ly1);
4923 
4924     nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4925     nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4926 
4927     if (p1.flags&PointFlag.Bevel) {
4928       nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4929       nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4930 
4931       nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4932       nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4933     } else {
4934       rx0 = p1.x-p1.dmx*rw;
4935       ry0 = p1.y-p1.dmy*rw;
4936 
4937       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4938       nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4939 
4940       nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4941       nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4942 
4943       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4944       nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4945     }
4946 
4947     nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4948     nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4949 
4950   } else {
4951     nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, -rw, &rx0, &ry0, &rx1, &ry1);
4952 
4953     nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4954     nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4955 
4956     if (p1.flags&PointFlag.Bevel) {
4957       nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4958       nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4959 
4960       nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4961       nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4962     } else {
4963       lx0 = p1.x+p1.dmx*lw;
4964       ly0 = p1.y+p1.dmy*lw;
4965 
4966       nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4967       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4968 
4969       nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4970       nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4971 
4972       nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4973       nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4974     }
4975 
4976     nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4977     nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4978   }
4979 
4980   return dst;
4981 }
4982 
4983 NVGVertex* nvg__buttCapStart (NVGVertex* dst, NVGpoint* p, float dx, float dy, float w, float d, float aa) nothrow @trusted @nogc {
4984   immutable float px = p.x-dx*d;
4985   immutable float py = p.y-dy*d;
4986   immutable float dlx = dy;
4987   immutable float dly = -dx;
4988   nvg__vset(dst, px+dlx*w-dx*aa, py+dly*w-dy*aa, 0, 0); ++dst;
4989   nvg__vset(dst, px-dlx*w-dx*aa, py-dly*w-dy*aa, 1, 0); ++dst;
4990   nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
4991   nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
4992   return dst;
4993 }
4994 
4995 NVGVertex* nvg__buttCapEnd (NVGVertex* dst, NVGpoint* p, float dx, float dy, float w, float d, float aa) nothrow @trusted @nogc {
4996   immutable float px = p.x+dx*d;
4997   immutable float py = p.y+dy*d;
4998   immutable float dlx = dy;
4999   immutable float dly = -dx;
5000   nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
5001   nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
5002   nvg__vset(dst, px+dlx*w+dx*aa, py+dly*w+dy*aa, 0, 0); ++dst;
5003   nvg__vset(dst, px-dlx*w+dx*aa, py-dly*w+dy*aa, 1, 0); ++dst;
5004   return dst;
5005 }
5006 
5007 NVGVertex* nvg__roundCapStart (NVGVertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, float aa) nothrow @trusted @nogc {
5008   immutable float px = p.x;
5009   immutable float py = p.y;
5010   immutable float dlx = dy;
5011   immutable float dly = -dx;
5012   //NVG_NOTUSED(aa);
5013   immutable float ncpf = cast(float)(ncap-1);
5014   foreach (int i; 0..ncap) {
5015     float a = i/*/cast(float)(ncap-1)*//ncpf*NVG_PI;
5016     float ax = nvg__cosf(a)*w, ay = nvg__sinf(a)*w;
5017     nvg__vset(dst, px-dlx*ax-dx*ay, py-dly*ax-dy*ay, 0, 1); ++dst;
5018     nvg__vset(dst, px, py, 0.5f, 1); ++dst;
5019   }
5020   nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
5021   nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
5022   return dst;
5023 }
5024 
5025 NVGVertex* nvg__roundCapEnd (NVGVertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, float aa) nothrow @trusted @nogc {
5026   immutable float px = p.x;
5027   immutable float py = p.y;
5028   immutable float dlx = dy;
5029   immutable float dly = -dx;
5030   //NVG_NOTUSED(aa);
5031   nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
5032   nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
5033   immutable float ncpf = cast(float)(ncap-1);
5034   foreach (int i; 0..ncap) {
5035     float a = i/*cast(float)(ncap-1)*//ncpf*NVG_PI;
5036     float ax = nvg__cosf(a)*w, ay = nvg__sinf(a)*w;
5037     nvg__vset(dst, px, py, 0.5f, 1); ++dst;
5038     nvg__vset(dst, px-dlx*ax+dx*ay, py-dly*ax+dy*ay, 0, 1); ++dst;
5039   }
5040   return dst;
5041 }
5042 
5043 void nvg__calculateJoins (NVGContext ctx, float w, int lineJoin, float miterLimit) nothrow @trusted @nogc {
5044   NVGpathCache* cache = ctx.cache;
5045   float iw = 0.0f;
5046 
5047   if (w > 0.0f) iw = 1.0f/w;
5048 
5049   // Calculate which joins needs extra vertices to append, and gather vertex count.
5050   foreach (int i; 0..cache.npaths) {
5051     NVGpath* path = &cache.paths[i];
5052     NVGpoint* pts = &cache.points[path.first];
5053     NVGpoint* p0 = &pts[path.count-1];
5054     NVGpoint* p1 = &pts[0];
5055     int nleft = 0;
5056 
5057     path.nbevel = 0;
5058 
5059     foreach (int j; 0..path.count) {
5060       //float dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
5061       immutable float dlx0 = p0.dy;
5062       immutable float dly0 = -p0.dx;
5063       immutable float dlx1 = p1.dy;
5064       immutable float dly1 = -p1.dx;
5065       // Calculate extrusions
5066       p1.dmx = (dlx0+dlx1)*0.5f;
5067       p1.dmy = (dly0+dly1)*0.5f;
5068       immutable float dmr2 = p1.dmx*p1.dmx+p1.dmy*p1.dmy;
5069       if (dmr2 > 0.000001f) {
5070         float scale = 1.0f/dmr2;
5071         if (scale > 600.0f) scale = 600.0f;
5072         p1.dmx *= scale;
5073         p1.dmy *= scale;
5074       }
5075 
5076       // Clear flags, but keep the corner.
5077       p1.flags = (p1.flags&PointFlag.Corner) ? PointFlag.Corner : 0;
5078 
5079       // Keep track of left turns.
5080       immutable float cross = p1.dx*p0.dy-p0.dx*p1.dy;
5081       if (cross > 0.0f) {
5082         nleft++;
5083         p1.flags |= PointFlag.Left;
5084       }
5085 
5086       // Calculate if we should use bevel or miter for inner join.
5087       immutable float limit = nvg__max(1.01f, nvg__min(p0.len, p1.len)*iw);
5088       if ((dmr2*limit*limit) < 1.0f) p1.flags |= PointFlag.InnerBevelPR;
5089 
5090       // Check to see if the corner needs to be beveled.
5091       if (p1.flags&PointFlag.Corner) {
5092         if ((dmr2*miterLimit*miterLimit) < 1.0f || lineJoin == NVGLineCap.Bevel || lineJoin == NVGLineCap.Round) {
5093           p1.flags |= PointFlag.Bevel;
5094         }
5095       }
5096 
5097       if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) path.nbevel++;
5098 
5099       p0 = p1++;
5100     }
5101 
5102     path.convex = (nleft == path.count);
5103   }
5104 }
5105 
5106 void nvg__expandStroke (NVGContext ctx, float w, int lineCap, int lineJoin, float miterLimit) nothrow @trusted @nogc {
5107   NVGpathCache* cache = ctx.cache;
5108   immutable float aa = ctx.fringeWidth;
5109   int ncap = nvg__curveDivs(w, NVG_PI, ctx.tessTol); // Calculate divisions per half circle.
5110 
5111   nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
5112 
5113   // Calculate max vertex usage.
5114   int cverts = 0;
5115   foreach (int i; 0..cache.npaths) {
5116     NVGpath* path = &cache.paths[i];
5117     immutable bool loop = path.closed;
5118     if (lineJoin == NVGLineCap.Round) {
5119       cverts += (path.count+path.nbevel*(ncap+2)+1)*2; // plus one for loop
5120     } else {
5121       cverts += (path.count+path.nbevel*5+1)*2; // plus one for loop
5122     }
5123     if (!loop) {
5124       // space for caps
5125       if (lineCap == NVGLineCap.Round) {
5126         cverts += (ncap*2+2)*2;
5127       } else {
5128         cverts += (3+3)*2;
5129       }
5130     }
5131   }
5132 
5133   NVGVertex* verts = nvg__allocTempVerts(ctx, cverts);
5134   if (verts is null) return;
5135 
5136   foreach (int i; 0..cache.npaths) {
5137     NVGpath* path = &cache.paths[i];
5138     NVGpoint* pts = &cache.points[path.first];
5139     NVGpoint* p0;
5140     NVGpoint* p1;
5141     int s, e;
5142 
5143     path.fill = null;
5144     path.nfill = 0;
5145 
5146     // Calculate fringe or stroke
5147     immutable bool loop = path.closed;
5148     NVGVertex* dst = verts;
5149     path.stroke = dst;
5150 
5151     if (loop) {
5152       // Looping
5153       p0 = &pts[path.count-1];
5154       p1 = &pts[0];
5155       s = 0;
5156       e = path.count;
5157     } else {
5158       // Add cap
5159       p0 = &pts[0];
5160       p1 = &pts[1];
5161       s = 1;
5162       e = path.count-1;
5163     }
5164 
5165     if (!loop) {
5166       // Add cap
5167       float dx = p1.x-p0.x;
5168       float dy = p1.y-p0.y;
5169       nvg__normalize(&dx, &dy);
5170            if (lineCap == NVGLineCap.Butt) dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa);
5171       else if (lineCap == NVGLineCap.Butt || lineCap == NVGLineCap.Square) dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa);
5172       else if (lineCap == NVGLineCap.Round) dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa);
5173     }
5174 
5175     foreach (int j; s..e) {
5176       if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) {
5177         if (lineJoin == NVGLineCap.Round) {
5178           dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa);
5179         } else {
5180           dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa);
5181         }
5182       } else {
5183         nvg__vset(dst, p1.x+(p1.dmx*w), p1.y+(p1.dmy*w), 0, 1); ++dst;
5184         nvg__vset(dst, p1.x-(p1.dmx*w), p1.y-(p1.dmy*w), 1, 1); ++dst;
5185       }
5186       p0 = p1++;
5187     }
5188 
5189     if (loop) {
5190       // Loop it
5191       nvg__vset(dst, verts[0].x, verts[0].y, 0, 1); ++dst;
5192       nvg__vset(dst, verts[1].x, verts[1].y, 1, 1); ++dst;
5193     } else {
5194       // Add cap
5195       float dx = p1.x-p0.x;
5196       float dy = p1.y-p0.y;
5197       nvg__normalize(&dx, &dy);
5198            if (lineCap == NVGLineCap.Butt) dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa);
5199       else if (lineCap == NVGLineCap.Butt || lineCap == NVGLineCap.Square) dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa);
5200       else if (lineCap == NVGLineCap.Round) dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa);
5201     }
5202 
5203     path.nstroke = cast(int)(dst-verts);
5204 
5205     verts = dst;
5206   }
5207 }
5208 
5209 void nvg__expandFill (NVGContext ctx, float w, int lineJoin, float miterLimit) nothrow @trusted @nogc {
5210   NVGpathCache* cache = ctx.cache;
5211   immutable float aa = ctx.fringeWidth;
5212   bool fringe = (w > 0.0f);
5213 
5214   nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
5215 
5216   // Calculate max vertex usage.
5217   int cverts = 0;
5218   foreach (int i; 0..cache.npaths) {
5219     NVGpath* path = &cache.paths[i];
5220     cverts += path.count+path.nbevel+1;
5221     if (fringe) cverts += (path.count+path.nbevel*5+1)*2; // plus one for loop
5222   }
5223 
5224   NVGVertex* verts = nvg__allocTempVerts(ctx, cverts);
5225   if (verts is null) return;
5226 
5227   bool convex = (cache.npaths == 1 && cache.paths[0].convex);
5228 
5229   foreach (int i; 0..cache.npaths) {
5230     NVGpath* path = &cache.paths[i];
5231     NVGpoint* pts = &cache.points[path.first];
5232 
5233     // Calculate shape vertices.
5234     immutable float woff = 0.5f*aa;
5235     NVGVertex* dst = verts;
5236     path.fill = dst;
5237 
5238     if (fringe) {
5239       // Looping
5240       NVGpoint* p0 = &pts[path.count-1];
5241       NVGpoint* p1 = &pts[0];
5242       foreach (int j; 0..path.count) {
5243         if (p1.flags&PointFlag.Bevel) {
5244           immutable float dlx0 = p0.dy;
5245           immutable float dly0 = -p0.dx;
5246           immutable float dlx1 = p1.dy;
5247           immutable float dly1 = -p1.dx;
5248           if (p1.flags&PointFlag.Left) {
5249             immutable float lx = p1.x+p1.dmx*woff;
5250             immutable float ly = p1.y+p1.dmy*woff;
5251             nvg__vset(dst, lx, ly, 0.5f, 1); ++dst;
5252           } else {
5253             immutable float lx0 = p1.x+dlx0*woff;
5254             immutable float ly0 = p1.y+dly0*woff;
5255             immutable float lx1 = p1.x+dlx1*woff;
5256             immutable float ly1 = p1.y+dly1*woff;
5257             nvg__vset(dst, lx0, ly0, 0.5f, 1); ++dst;
5258             nvg__vset(dst, lx1, ly1, 0.5f, 1); ++dst;
5259           }
5260         } else {
5261           nvg__vset(dst, p1.x+(p1.dmx*woff), p1.y+(p1.dmy*woff), 0.5f, 1); ++dst;
5262         }
5263         p0 = p1++;
5264       }
5265     } else {
5266       foreach (int j; 0..path.count) {
5267         nvg__vset(dst, pts[j].x, pts[j].y, 0.5f, 1);
5268         ++dst;
5269       }
5270     }
5271 
5272     path.nfill = cast(int)(dst-verts);
5273     verts = dst;
5274 
5275     // Calculate fringe
5276     if (fringe) {
5277       float lw = w+woff;
5278       immutable float rw = w-woff;
5279       float lu = 0;
5280       immutable float ru = 1;
5281       dst = verts;
5282       path.stroke = dst;
5283 
5284       // Create only half a fringe for convex shapes so that
5285       // the shape can be rendered without stenciling.
5286       if (convex) {
5287         lw = woff; // This should generate the same vertex as fill inset above.
5288         lu = 0.5f; // Set outline fade at middle.
5289       }
5290 
5291       // Looping
5292       NVGpoint* p0 = &pts[path.count-1];
5293       NVGpoint* p1 = &pts[0];
5294 
5295       foreach (int j; 0..path.count) {
5296         if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) {
5297           dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx.fringeWidth);
5298         } else {
5299           nvg__vset(dst, p1.x+(p1.dmx*lw), p1.y+(p1.dmy*lw), lu, 1); ++dst;
5300           nvg__vset(dst, p1.x-(p1.dmx*rw), p1.y-(p1.dmy*rw), ru, 1); ++dst;
5301         }
5302         p0 = p1++;
5303       }
5304 
5305       // Loop it
5306       nvg__vset(dst, verts[0].x, verts[0].y, lu, 1); ++dst;
5307       nvg__vset(dst, verts[1].x, verts[1].y, ru, 1); ++dst;
5308 
5309       path.nstroke = cast(int)(dst-verts);
5310       verts = dst;
5311     } else {
5312       path.stroke = null;
5313       path.nstroke = 0;
5314     }
5315   }
5316 }
5317 
5318 
5319 // ////////////////////////////////////////////////////////////////////////// //
5320 // Paths
5321 
5322 /// Clears the current path and sub-paths.
5323 /// Group: paths
5324 @scriptable
5325 public void beginPath (NVGContext ctx) nothrow @trusted @nogc {
5326   ctx.ncommands = 0;
5327   ctx.pathPickRegistered &= NVGPickKind.All; // reset "registered" flags
5328   nvg__clearPathCache(ctx);
5329 }
5330 
5331 public alias newPath = beginPath; /// Ditto.
5332 
5333 /// Starts new sub-path with specified point as first point.
5334 /// Group: paths
5335 @scriptable
5336 public void moveTo (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
5337   nvg__appendCommands(ctx, Command.MoveTo, x, y);
5338 }
5339 
5340 /// Starts new sub-path with specified point as first point.
5341 /// Arguments: [x, y]*
5342 /// Group: paths
5343 public void moveTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5344   enum ArgC = 2;
5345   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [moveTo] call");
5346   if (args.length < ArgC) return;
5347   nvg__appendCommands(ctx, Command.MoveTo, args[$-2..$]);
5348 }
5349 
5350 /// Adds line segment from the last point in the path to the specified point.
5351 /// Group: paths
5352 @scriptable
5353 public void lineTo (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
5354   nvg__appendCommands(ctx, Command.LineTo, x, y);
5355 }
5356 
5357 /// Adds line segment from the last point in the path to the specified point.
5358 /// Arguments: [x, y]*
5359 /// Group: paths
5360 public void lineTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5361   enum ArgC = 2;
5362   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [lineTo] call");
5363   if (args.length < ArgC) return;
5364   foreach (immutable idx; 0..args.length/ArgC) {
5365     nvg__appendCommands(ctx, Command.LineTo, args.ptr[idx*ArgC..idx*ArgC+ArgC]);
5366   }
5367 }
5368 
5369 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
5370 /// Group: paths
5371 public void bezierTo (NVGContext ctx, in float c1x, in float c1y, in float c2x, in float c2y, in float x, in float y) nothrow @trusted @nogc {
5372   nvg__appendCommands(ctx, Command.BezierTo, c1x, c1y, c2x, c2y, x, y);
5373 }
5374 
5375 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
5376 /// Arguments: [c1x, c1y, c2x, c2y, x, y]*
5377 /// Group: paths
5378 public void bezierTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5379   enum ArgC = 6;
5380   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [bezierTo] call");
5381   if (args.length < ArgC) return;
5382   foreach (immutable idx; 0..args.length/ArgC) {
5383     nvg__appendCommands(ctx, Command.BezierTo, args.ptr[idx*ArgC..idx*ArgC+ArgC]);
5384   }
5385 }
5386 
5387 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
5388 /// Group: paths
5389 public void quadTo (NVGContext ctx, in float cx, in float cy, in float x, in float y) nothrow @trusted @nogc {
5390   immutable float x0 = ctx.commandx;
5391   immutable float y0 = ctx.commandy;
5392   nvg__appendCommands(ctx,
5393     Command.BezierTo,
5394     x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
5395     x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
5396     x, y,
5397   );
5398 }
5399 
5400 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
5401 /// Arguments: [cx, cy, x, y]*
5402 /// Group: paths
5403 public void quadTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5404   enum ArgC = 4;
5405   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [quadTo] call");
5406   if (args.length < ArgC) return;
5407   const(float)* aptr = args.ptr;
5408   foreach (immutable idx; 0..args.length/ArgC) {
5409     immutable float x0 = ctx.commandx;
5410     immutable float y0 = ctx.commandy;
5411     immutable float cx = *aptr++;
5412     immutable float cy = *aptr++;
5413     immutable float x = *aptr++;
5414     immutable float y = *aptr++;
5415     nvg__appendCommands(ctx,
5416       Command.BezierTo,
5417       x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
5418       x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
5419       x, y,
5420     );
5421   }
5422 }
5423 
5424 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
5425 /// Group: paths
5426 public void arcTo (NVGContext ctx, in float x1, in float y1, in float x2, in float y2, in float radius) nothrow @trusted @nogc {
5427   if (ctx.ncommands == 0) return;
5428 
5429   immutable float x0 = ctx.commandx;
5430   immutable float y0 = ctx.commandy;
5431 
5432   // handle degenerate cases
5433   if (nvg__ptEquals(x0, y0, x1, y1, ctx.distTol) ||
5434       nvg__ptEquals(x1, y1, x2, y2, ctx.distTol) ||
5435       nvg__distPtSeg(x1, y1, x0, y0, x2, y2) < ctx.distTol*ctx.distTol ||
5436       radius < ctx.distTol)
5437   {
5438     ctx.lineTo(x1, y1);
5439     return;
5440   }
5441 
5442   // calculate tangential circle to lines (x0, y0)-(x1, y1) and (x1, y1)-(x2, y2)
5443   float dx0 = x0-x1;
5444   float dy0 = y0-y1;
5445   float dx1 = x2-x1;
5446   float dy1 = y2-y1;
5447   nvg__normalize(&dx0, &dy0);
5448   nvg__normalize(&dx1, &dy1);
5449   immutable float a = nvg__acosf(dx0*dx1+dy0*dy1);
5450   immutable float d = radius/nvg__tanf(a/2.0f);
5451 
5452   //printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
5453 
5454   if (d > 10000.0f) {
5455     ctx.lineTo(x1, y1);
5456     return;
5457   }
5458 
5459   float cx = void, cy = void, a0 = void, a1 = void;
5460   NVGWinding dir;
5461   if (nvg__cross(dx0, dy0, dx1, dy1) > 0.0f) {
5462     cx = x1+dx0*d+dy0*radius;
5463     cy = y1+dy0*d+-dx0*radius;
5464     a0 = nvg__atan2f(dx0, -dy0);
5465     a1 = nvg__atan2f(-dx1, dy1);
5466     dir = NVGWinding.CW;
5467     //printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
5468   } else {
5469     cx = x1+dx0*d+-dy0*radius;
5470     cy = y1+dy0*d+dx0*radius;
5471     a0 = nvg__atan2f(-dx0, dy0);
5472     a1 = nvg__atan2f(dx1, -dy1);
5473     dir = NVGWinding.CCW;
5474     //printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
5475   }
5476 
5477   ctx.arc(dir, cx, cy, radius, a0, a1); // first is line
5478 }
5479 
5480 
5481 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
5482 /// Arguments: [x1, y1, x2, y2, radius]*
5483 /// Group: paths
5484 public void arcTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5485   enum ArgC = 5;
5486   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [arcTo] call");
5487   if (args.length < ArgC) return;
5488   if (ctx.ncommands == 0) return;
5489   const(float)* aptr = args.ptr;
5490   foreach (immutable idx; 0..args.length/ArgC) {
5491     immutable float x0 = ctx.commandx;
5492     immutable float y0 = ctx.commandy;
5493     immutable float x1 = *aptr++;
5494     immutable float y1 = *aptr++;
5495     immutable float x2 = *aptr++;
5496     immutable float y2 = *aptr++;
5497     immutable float radius = *aptr++;
5498     ctx.arcTo(x1, y1, x2, y2, radius);
5499   }
5500 }
5501 
5502 /// Closes current sub-path with a line segment.
5503 /// Group: paths
5504 @scriptable
5505 public void closePath (NVGContext ctx) nothrow @trusted @nogc {
5506   nvg__appendCommands(ctx, Command.Close);
5507 }
5508 
5509 /// Sets the current sub-path winding, see NVGWinding and NVGSolidity.
5510 /// Group: paths
5511 public void pathWinding (NVGContext ctx, NVGWinding dir) nothrow @trusted @nogc {
5512   nvg__appendCommands(ctx, Command.Winding, cast(float)dir);
5513 }
5514 
5515 /// Ditto.
5516 public void pathWinding (NVGContext ctx, NVGSolidity dir) nothrow @trusted @nogc {
5517   nvg__appendCommands(ctx, Command.Winding, cast(float)dir);
5518 }
5519 
5520 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
5521  * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
5522  * Angles are specified in radians.
5523  *
5524  * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
5525  *
5526  * Group: paths
5527  */
5528 public void arc(string mode="original") (NVGContext ctx, NVGWinding dir, in float cx, in float cy, in float r, in float a0, in float a1) nothrow @trusted @nogc {
5529   static assert(mode == "original" || mode == "move" || mode == "line");
5530 
5531   float[3+5*7+100] vals = void;
5532   //int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
5533   static if (mode == "original") {
5534     immutable int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
5535   } else static if (mode == "move") {
5536     enum move = Command.MoveTo;
5537   } else static if (mode == "line") {
5538     enum move = Command.LineTo;
5539   } else {
5540     static assert(0, "wtf?!");
5541   }
5542 
5543   // Clamp angles
5544   float da = a1-a0;
5545   if (dir == NVGWinding.CW) {
5546     if (nvg__absf(da) >= NVG_PI*2) {
5547       da = NVG_PI*2;
5548     } else {
5549       while (da < 0.0f) da += NVG_PI*2;
5550     }
5551   } else {
5552     if (nvg__absf(da) >= NVG_PI*2) {
5553       da = -NVG_PI*2;
5554     } else {
5555       while (da > 0.0f) da -= NVG_PI*2;
5556     }
5557   }
5558 
5559   // Split arc into max 90 degree segments.
5560   immutable int ndivs = nvg__max(1, nvg__min(cast(int)(nvg__absf(da)/(NVG_PI*0.5f)+0.5f), 5));
5561   immutable float hda = (da/cast(float)ndivs)/2.0f;
5562   float kappa = nvg__absf(4.0f/3.0f*(1.0f-nvg__cosf(hda))/nvg__sinf(hda));
5563 
5564   if (dir == NVGWinding.CCW) kappa = -kappa;
5565 
5566   int nvals = 0;
5567   float px = 0, py = 0, ptanx = 0, ptany = 0;
5568   foreach (int i; 0..ndivs+1) {
5569     immutable float a = a0+da*(i/cast(float)ndivs);
5570     immutable float dx = nvg__cosf(a);
5571     immutable float dy = nvg__sinf(a);
5572     immutable float x = cx+dx*r;
5573     immutable float y = cy+dy*r;
5574     immutable float tanx = -dy*r*kappa;
5575     immutable float tany = dx*r*kappa;
5576 
5577     if (i == 0) {
5578       if (vals.length-nvals < 3) {
5579         // flush
5580         nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
5581         nvals = 0;
5582       }
5583       vals.ptr[nvals++] = cast(float)move;
5584       vals.ptr[nvals++] = x;
5585       vals.ptr[nvals++] = y;
5586     } else {
5587       if (vals.length-nvals < 7) {
5588         // flush
5589         nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
5590         nvals = 0;
5591       }
5592       vals.ptr[nvals++] = Command.BezierTo;
5593       vals.ptr[nvals++] = px+ptanx;
5594       vals.ptr[nvals++] = py+ptany;
5595       vals.ptr[nvals++] = x-tanx;
5596       vals.ptr[nvals++] = y-tany;
5597       vals.ptr[nvals++] = x;
5598       vals.ptr[nvals++] = y;
5599     }
5600     px = x;
5601     py = y;
5602     ptanx = tanx;
5603     ptany = tany;
5604   }
5605 
5606   nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
5607 }
5608 
5609 
5610 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
5611  * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
5612  * Angles are specified in radians.
5613  *
5614  * Arguments: [cx, cy, r, a0, a1]*
5615  *
5616  * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
5617  *
5618  * Group: paths
5619  */
5620 public void arc(string mode="original") (NVGContext ctx, NVGWinding dir, in float[] args) nothrow @trusted @nogc {
5621   static assert(mode == "original" || mode == "move" || mode == "line");
5622   enum ArgC = 5;
5623   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [arc] call");
5624   if (args.length < ArgC) return;
5625   const(float)* aptr = args.ptr;
5626   foreach (immutable idx; 0..args.length/ArgC) {
5627     immutable cx = *aptr++;
5628     immutable cy = *aptr++;
5629     immutable r = *aptr++;
5630     immutable a0 = *aptr++;
5631     immutable a1 = *aptr++;
5632     ctx.arc!mode(dir, cx, cy, r, a0, a1);
5633   }
5634 }
5635 
5636 /// Creates new rectangle shaped sub-path.
5637 /// Group: paths
5638 @scriptable
5639 public void rect (NVGContext ctx, in float x, in float y, in float w, in float h) nothrow @trusted @nogc {
5640   nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5641     Command.MoveTo, x, y,
5642     Command.LineTo, x, y+h,
5643     Command.LineTo, x+w, y+h,
5644     Command.LineTo, x+w, y,
5645     Command.Close,
5646   );
5647 }
5648 
5649 /// Creates new rectangle shaped sub-path.
5650 /// Arguments: [x, y, w, h]*
5651 /// Group: paths
5652 public void rect (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5653   enum ArgC = 4;
5654   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [rect] call");
5655   if (args.length < ArgC) return;
5656   const(float)* aptr = args.ptr;
5657   foreach (immutable idx; 0..args.length/ArgC) {
5658     immutable x = *aptr++;
5659     immutable y = *aptr++;
5660     immutable w = *aptr++;
5661     immutable h = *aptr++;
5662     nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5663       Command.MoveTo, x, y,
5664       Command.LineTo, x, y+h,
5665       Command.LineTo, x+w, y+h,
5666       Command.LineTo, x+w, y,
5667       Command.Close,
5668     );
5669   }
5670 }
5671 
5672 /// Creates new rounded rectangle shaped sub-path.
5673 /// Group: paths
5674 @scriptable
5675 public void roundedRect (NVGContext ctx, in float x, in float y, in float w, in float h, in float radius) nothrow @trusted @nogc {
5676   ctx.roundedRectVarying(x, y, w, h, radius, radius, radius, radius);
5677 }
5678 
5679 /// Creates new rounded rectangle shaped sub-path.
5680 /// Arguments: [x, y, w, h, radius]*
5681 /// Group: paths
5682 public void roundedRect (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5683   enum ArgC = 5;
5684   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRect] call");
5685   if (args.length < ArgC) return;
5686   const(float)* aptr = args.ptr;
5687   foreach (immutable idx; 0..args.length/ArgC) {
5688     immutable x = *aptr++;
5689     immutable y = *aptr++;
5690     immutable w = *aptr++;
5691     immutable h = *aptr++;
5692     immutable r = *aptr++;
5693     ctx.roundedRectVarying(x, y, w, h, r, r, r, r);
5694   }
5695 }
5696 
5697 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5698 /// Group: paths
5699 @scriptable
5700 public void roundedRectEllipse (NVGContext ctx, in float x, in float y, in float w, in float h, in float rw, in float rh) nothrow @trusted @nogc {
5701   if (rw < 0.1f || rh < 0.1f) {
5702     rect(ctx, x, y, w, h);
5703   } else {
5704     nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5705       Command.MoveTo, x+rw, y,
5706       Command.LineTo, x+w-rw, y,
5707       Command.BezierTo, x+w-rw*(1-NVG_KAPPA90), y, x+w, y+rh*(1-NVG_KAPPA90), x+w, y+rh,
5708       Command.LineTo, x+w, y+h-rh,
5709       Command.BezierTo, x+w, y+h-rh*(1-NVG_KAPPA90), x+w-rw*(1-NVG_KAPPA90), y+h, x+w-rw, y+h,
5710       Command.LineTo, x+rw, y+h,
5711       Command.BezierTo, x+rw*(1-NVG_KAPPA90), y+h, x, y+h-rh*(1-NVG_KAPPA90), x, y+h-rh,
5712       Command.LineTo, x, y+rh,
5713       Command.BezierTo, x, y+rh*(1-NVG_KAPPA90), x+rw*(1-NVG_KAPPA90), y, x+rw, y,
5714       Command.Close,
5715     );
5716   }
5717 }
5718 
5719 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5720 /// Arguments: [x, y, w, h, rw, rh]*
5721 /// Group: paths
5722 public void roundedRectEllipse (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5723   enum ArgC = 6;
5724   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRectEllipse] call");
5725   if (args.length < ArgC) return;
5726   const(float)* aptr = args.ptr;
5727   foreach (immutable idx; 0..args.length/ArgC) {
5728     immutable x = *aptr++;
5729     immutable y = *aptr++;
5730     immutable w = *aptr++;
5731     immutable h = *aptr++;
5732     immutable rw = *aptr++;
5733     immutable rh = *aptr++;
5734     if (rw < 0.1f || rh < 0.1f) {
5735       rect(ctx, x, y, w, h);
5736     } else {
5737       nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5738         Command.MoveTo, x+rw, y,
5739         Command.LineTo, x+w-rw, y,
5740         Command.BezierTo, x+w-rw*(1-NVG_KAPPA90), y, x+w, y+rh*(1-NVG_KAPPA90), x+w, y+rh,
5741         Command.LineTo, x+w, y+h-rh,
5742         Command.BezierTo, x+w, y+h-rh*(1-NVG_KAPPA90), x+w-rw*(1-NVG_KAPPA90), y+h, x+w-rw, y+h,
5743         Command.LineTo, x+rw, y+h,
5744         Command.BezierTo, x+rw*(1-NVG_KAPPA90), y+h, x, y+h-rh*(1-NVG_KAPPA90), x, y+h-rh,
5745         Command.LineTo, x, y+rh,
5746         Command.BezierTo, x, y+rh*(1-NVG_KAPPA90), x+rw*(1-NVG_KAPPA90), y, x+rw, y,
5747         Command.Close,
5748       );
5749     }
5750   }
5751 }
5752 
5753 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5754 /// Group: paths
5755 public void roundedRectVarying (NVGContext ctx, in float x, in float y, in float w, in float h, in float radTopLeft, in float radTopRight, in float radBottomRight, in float radBottomLeft) nothrow @trusted @nogc {
5756   if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
5757     ctx.rect(x, y, w, h);
5758   } else {
5759     immutable float halfw = nvg__absf(w)*0.5f;
5760     immutable float halfh = nvg__absf(h)*0.5f;
5761     immutable float rxBL = nvg__min(radBottomLeft, halfw)*nvg__sign(w), ryBL = nvg__min(radBottomLeft, halfh)*nvg__sign(h);
5762     immutable float rxBR = nvg__min(radBottomRight, halfw)*nvg__sign(w), ryBR = nvg__min(radBottomRight, halfh)*nvg__sign(h);
5763     immutable float rxTR = nvg__min(radTopRight, halfw)*nvg__sign(w), ryTR = nvg__min(radTopRight, halfh)*nvg__sign(h);
5764     immutable float rxTL = nvg__min(radTopLeft, halfw)*nvg__sign(w), ryTL = nvg__min(radTopLeft, halfh)*nvg__sign(h);
5765     nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5766       Command.MoveTo, x, y+ryTL,
5767       Command.LineTo, x, y+h-ryBL,
5768       Command.BezierTo, x, y+h-ryBL*(1-NVG_KAPPA90), x+rxBL*(1-NVG_KAPPA90), y+h, x+rxBL, y+h,
5769       Command.LineTo, x+w-rxBR, y+h,
5770       Command.BezierTo, x+w-rxBR*(1-NVG_KAPPA90), y+h, x+w, y+h-ryBR*(1-NVG_KAPPA90), x+w, y+h-ryBR,
5771       Command.LineTo, x+w, y+ryTR,
5772       Command.BezierTo, x+w, y+ryTR*(1-NVG_KAPPA90), x+w-rxTR*(1-NVG_KAPPA90), y, x+w-rxTR, y,
5773       Command.LineTo, x+rxTL, y,
5774       Command.BezierTo, x+rxTL*(1-NVG_KAPPA90), y, x, y+ryTL*(1-NVG_KAPPA90), x, y+ryTL,
5775       Command.Close,
5776     );
5777   }
5778 }
5779 
5780 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5781 /// Arguments: [x, y, w, h, radTopLeft, radTopRight, radBottomRight, radBottomLeft]*
5782 /// Group: paths
5783 public void roundedRectVarying (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5784   enum ArgC = 8;
5785   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRectVarying] call");
5786   if (args.length < ArgC) return;
5787   const(float)* aptr = args.ptr;
5788   foreach (immutable idx; 0..args.length/ArgC) {
5789     immutable x = *aptr++;
5790     immutable y = *aptr++;
5791     immutable w = *aptr++;
5792     immutable h = *aptr++;
5793     immutable radTopLeft = *aptr++;
5794     immutable radTopRight = *aptr++;
5795     immutable radBottomRight = *aptr++;
5796     immutable radBottomLeft = *aptr++;
5797     if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
5798       ctx.rect(x, y, w, h);
5799     } else {
5800       immutable float halfw = nvg__absf(w)*0.5f;
5801       immutable float halfh = nvg__absf(h)*0.5f;
5802       immutable float rxBL = nvg__min(radBottomLeft, halfw)*nvg__sign(w), ryBL = nvg__min(radBottomLeft, halfh)*nvg__sign(h);
5803       immutable float rxBR = nvg__min(radBottomRight, halfw)*nvg__sign(w), ryBR = nvg__min(radBottomRight, halfh)*nvg__sign(h);
5804       immutable float rxTR = nvg__min(radTopRight, halfw)*nvg__sign(w), ryTR = nvg__min(radTopRight, halfh)*nvg__sign(h);
5805       immutable float rxTL = nvg__min(radTopLeft, halfw)*nvg__sign(w), ryTL = nvg__min(radTopLeft, halfh)*nvg__sign(h);
5806       nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5807         Command.MoveTo, x, y+ryTL,
5808         Command.LineTo, x, y+h-ryBL,
5809         Command.BezierTo, x, y+h-ryBL*(1-NVG_KAPPA90), x+rxBL*(1-NVG_KAPPA90), y+h, x+rxBL, y+h,
5810         Command.LineTo, x+w-rxBR, y+h,
5811         Command.BezierTo, x+w-rxBR*(1-NVG_KAPPA90), y+h, x+w, y+h-ryBR*(1-NVG_KAPPA90), x+w, y+h-ryBR,
5812         Command.LineTo, x+w, y+ryTR,
5813         Command.BezierTo, x+w, y+ryTR*(1-NVG_KAPPA90), x+w-rxTR*(1-NVG_KAPPA90), y, x+w-rxTR, y,
5814         Command.LineTo, x+rxTL, y,
5815         Command.BezierTo, x+rxTL*(1-NVG_KAPPA90), y, x, y+ryTL*(1-NVG_KAPPA90), x, y+ryTL,
5816         Command.Close,
5817       );
5818     }
5819   }
5820 }
5821 
5822 /// Creates new ellipse shaped sub-path.
5823 /// Group: paths
5824 public void ellipse (NVGContext ctx, in float cx, in float cy, in float rx, in float ry) nothrow @trusted @nogc {
5825   nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5826     Command.MoveTo, cx-rx, cy,
5827     Command.BezierTo, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
5828     Command.BezierTo, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
5829     Command.BezierTo, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
5830     Command.BezierTo, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
5831     Command.Close,
5832   );
5833 }
5834 
5835 /// Creates new ellipse shaped sub-path.
5836 /// Arguments: [cx, cy, rx, ry]*
5837 /// Group: paths
5838 public void ellipse (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5839   enum ArgC = 4;
5840   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [ellipse] call");
5841   if (args.length < ArgC) return;
5842   const(float)* aptr = args.ptr;
5843   foreach (immutable idx; 0..args.length/ArgC) {
5844     immutable cx = *aptr++;
5845     immutable cy = *aptr++;
5846     immutable rx = *aptr++;
5847     immutable ry = *aptr++;
5848     nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5849       Command.MoveTo, cx-rx, cy,
5850       Command.BezierTo, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
5851       Command.BezierTo, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
5852       Command.BezierTo, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
5853       Command.BezierTo, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
5854       Command.Close,
5855     );
5856   }
5857 }
5858 
5859 /// Creates new circle shaped sub-path.
5860 /// Group: paths
5861 public void circle (NVGContext ctx, in float cx, in float cy, in float r) nothrow @trusted @nogc {
5862   ctx.ellipse(cx, cy, r, r);
5863 }
5864 
5865 /// Creates new circle shaped sub-path.
5866 /// Arguments: [cx, cy, r]*
5867 /// Group: paths
5868 public void circle (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5869   enum ArgC = 3;
5870   if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [circle] call");
5871   if (args.length < ArgC) return;
5872   const(float)* aptr = args.ptr;
5873   foreach (immutable idx; 0..args.length/ArgC) {
5874     immutable cx = *aptr++;
5875     immutable cy = *aptr++;
5876     immutable r = *aptr++;
5877     ctx.ellipse(cx, cy, r, r);
5878   }
5879 }
5880 
5881 // Debug function to dump cached path data.
5882 debug public void debugDumpPathCache (NVGContext ctx) nothrow @trusted @nogc {
5883   import core.stdc.stdio : printf;
5884   const(NVGpath)* path;
5885   printf("Dumping %d cached paths\n", ctx.cache.npaths);
5886   for (int i = 0; i < ctx.cache.npaths; ++i) {
5887     path = &ctx.cache.paths[i];
5888     printf("-Path %d\n", i);
5889     if (path.nfill) {
5890       printf("-fill: %d\n", path.nfill);
5891       for (int j = 0; j < path.nfill; ++j) printf("%f\t%f\n", path.fill[j].x, path.fill[j].y);
5892     }
5893     if (path.nstroke) {
5894       printf("-stroke: %d\n", path.nstroke);
5895       for (int j = 0; j < path.nstroke; ++j) printf("%f\t%f\n", path.stroke[j].x, path.stroke[j].y);
5896     }
5897   }
5898 }
5899 
5900 // Flatten path, prepare it for fill operation.
5901 void nvg__prepareFill (NVGContext ctx) nothrow @trusted @nogc {
5902   NVGpathCache* cache = ctx.cache;
5903   NVGstate* state = nvg__getState(ctx);
5904 
5905   nvg__flattenPaths!false(ctx);
5906 
5907   if (ctx.params.edgeAntiAlias && state.shapeAntiAlias) {
5908     nvg__expandFill(ctx, ctx.fringeWidth, NVGLineCap.Miter, 2.4f);
5909   } else {
5910     nvg__expandFill(ctx, 0.0f, NVGLineCap.Miter, 2.4f);
5911   }
5912 
5913   cache.evenOddMode = state.evenOddMode;
5914   cache.fringeWidth = ctx.fringeWidth;
5915   cache.fillReady = true;
5916   cache.strokeReady = false;
5917   cache.clipmode = NVGClipMode.None;
5918 }
5919 
5920 // Flatten path, prepare it for stroke operation.
5921 void nvg__prepareStroke (NVGContext ctx) nothrow @trusted @nogc {
5922   NVGstate* state = nvg__getState(ctx);
5923   NVGpathCache* cache = ctx.cache;
5924 
5925   nvg__flattenPaths!true(ctx);
5926 
5927   immutable float scale = nvg__getAverageScale(state.xform);
5928   float strokeWidth = nvg__clamp(state.strokeWidth*scale, 0.0f, 200.0f);
5929 
5930   if (strokeWidth < ctx.fringeWidth) {
5931     // If the stroke width is less than pixel size, use alpha to emulate coverage.
5932     // Since coverage is area, scale by alpha*alpha.
5933     immutable float alpha = nvg__clamp(strokeWidth/ctx.fringeWidth, 0.0f, 1.0f);
5934     cache.strokeAlphaMul = alpha*alpha;
5935     strokeWidth = ctx.fringeWidth;
5936   } else {
5937     cache.strokeAlphaMul = 1.0f;
5938   }
5939   cache.strokeWidth = strokeWidth;
5940 
5941   if (ctx.params.edgeAntiAlias && state.shapeAntiAlias) {
5942     nvg__expandStroke(ctx, strokeWidth*0.5f+ctx.fringeWidth*0.5f, state.lineCap, state.lineJoin, state.miterLimit);
5943   } else {
5944     nvg__expandStroke(ctx, strokeWidth*0.5f, state.lineCap, state.lineJoin, state.miterLimit);
5945   }
5946 
5947   cache.fringeWidth = ctx.fringeWidth;
5948   cache.fillReady = false;
5949   cache.strokeReady = true;
5950   cache.clipmode = NVGClipMode.None;
5951 }
5952 
5953 /// Fills the current path with current fill style.
5954 /// Group: paths
5955 @scriptable
5956 public void fill (NVGContext ctx) nothrow @trusted @nogc {
5957   NVGstate* state = nvg__getState(ctx);
5958 
5959   if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Fill|(NVGPickKind.Fill<<16))) == NVGPickKind.Fill) {
5960     ctx.pathPickRegistered |= NVGPickKind.Fill<<16;
5961     ctx.currFillHitId = ctx.pathPickId;
5962   }
5963 
5964   nvg__prepareFill(ctx);
5965 
5966   // apply global alpha
5967   NVGPaint fillPaint = state.fill;
5968   fillPaint.innerColor.a *= state.alpha;
5969   fillPaint.middleColor.a *= state.alpha;
5970   fillPaint.outerColor.a *= state.alpha;
5971 
5972   ctx.appendCurrentPathToCache(ctx.recset, state.fill);
5973 
5974   if (ctx.recblockdraw) return;
5975 
5976   ctx.params.renderFill(ctx.params.userPtr, state.compositeOperation, NVGClipMode.None, &fillPaint, &state.scissor, ctx.fringeWidth, ctx.cache.bounds.ptr, ctx.cache.paths, ctx.cache.npaths, state.evenOddMode);
5977 
5978   // count triangles
5979   foreach (int i; 0..ctx.cache.npaths) {
5980     NVGpath* path = &ctx.cache.paths[i];
5981     ctx.fillTriCount += path.nfill-2;
5982     ctx.fillTriCount += path.nstroke-2;
5983     ctx.drawCallCount += 2;
5984   }
5985 }
5986 
5987 /// Fills the current path with current stroke style.
5988 /// Group: paths
5989 @scriptable
5990 public void stroke (NVGContext ctx) nothrow @trusted @nogc {
5991   NVGstate* state = nvg__getState(ctx);
5992 
5993   if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Stroke|(NVGPickKind.Stroke<<16))) == NVGPickKind.Stroke) {
5994     ctx.pathPickRegistered |= NVGPickKind.Stroke<<16;
5995     ctx.currStrokeHitId = ctx.pathPickId;
5996   }
5997 
5998   nvg__prepareStroke(ctx);
5999 
6000   NVGpathCache* cache = ctx.cache;
6001 
6002   NVGPaint strokePaint = state.stroke;
6003   strokePaint.innerColor.a *= cache.strokeAlphaMul;
6004   strokePaint.middleColor.a *= cache.strokeAlphaMul;
6005   strokePaint.outerColor.a *= cache.strokeAlphaMul;
6006 
6007   // apply global alpha
6008   strokePaint.innerColor.a *= state.alpha;
6009   strokePaint.middleColor.a *= state.alpha;
6010   strokePaint.outerColor.a *= state.alpha;
6011 
6012   ctx.appendCurrentPathToCache(ctx.recset, state.stroke);
6013 
6014   if (ctx.recblockdraw) return;
6015 
6016   ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, NVGClipMode.None, &strokePaint, &state.scissor, ctx.fringeWidth, cache.strokeWidth, ctx.cache.paths, ctx.cache.npaths);
6017 
6018   // count triangles
6019   foreach (int i; 0..ctx.cache.npaths) {
6020     NVGpath* path = &ctx.cache.paths[i];
6021     ctx.strokeTriCount += path.nstroke-2;
6022     ++ctx.drawCallCount;
6023   }
6024 }
6025 
6026 /// Sets current path as clipping region.
6027 /// Group: clipping
6028 public void clip (NVGContext ctx, NVGClipMode aclipmode=NVGClipMode.Union) nothrow @trusted @nogc {
6029   NVGstate* state = nvg__getState(ctx);
6030 
6031   if (aclipmode == NVGClipMode.None) return;
6032   if (ctx.recblockdraw) return; //???
6033 
6034   if (aclipmode == NVGClipMode.Replace) ctx.params.renderResetClip(ctx.params.userPtr);
6035 
6036   /*
6037   if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Fill|(NVGPickKind.Fill<<16))) == NVGPickKind.Fill) {
6038     ctx.pathPickRegistered |= NVGPickKind.Fill<<16;
6039     ctx.currFillHitId = ctx.pathPickId;
6040   }
6041   */
6042 
6043   nvg__prepareFill(ctx);
6044 
6045   // apply global alpha
6046   NVGPaint fillPaint = state.fill;
6047   fillPaint.innerColor.a *= state.alpha;
6048   fillPaint.middleColor.a *= state.alpha;
6049   fillPaint.outerColor.a *= state.alpha;
6050 
6051   //ctx.appendCurrentPathToCache(ctx.recset, state.fill);
6052 
6053   ctx.params.renderFill(ctx.params.userPtr, state.compositeOperation, aclipmode, &fillPaint, &state.scissor, ctx.fringeWidth, ctx.cache.bounds.ptr, ctx.cache.paths, ctx.cache.npaths, state.evenOddMode);
6054 
6055   // count triangles
6056   foreach (int i; 0..ctx.cache.npaths) {
6057     NVGpath* path = &ctx.cache.paths[i];
6058     ctx.fillTriCount += path.nfill-2;
6059     ctx.fillTriCount += path.nstroke-2;
6060     ctx.drawCallCount += 2;
6061   }
6062 }
6063 
6064 /// Sets current path as clipping region.
6065 /// Group: clipping
6066 public alias clipFill = clip;
6067 
6068 /// Sets current path' stroke as clipping region.
6069 /// Group: clipping
6070 public void clipStroke (NVGContext ctx, NVGClipMode aclipmode=NVGClipMode.Union) nothrow @trusted @nogc {
6071   NVGstate* state = nvg__getState(ctx);
6072 
6073   if (aclipmode == NVGClipMode.None) return;
6074   if (ctx.recblockdraw) return; //???
6075 
6076   if (aclipmode == NVGClipMode.Replace) ctx.params.renderResetClip(ctx.params.userPtr);
6077 
6078   /*
6079   if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Stroke|(NVGPickKind.Stroke<<16))) == NVGPickKind.Stroke) {
6080     ctx.pathPickRegistered |= NVGPickKind.Stroke<<16;
6081     ctx.currStrokeHitId = ctx.pathPickId;
6082   }
6083   */
6084 
6085   nvg__prepareStroke(ctx);
6086 
6087   NVGpathCache* cache = ctx.cache;
6088 
6089   NVGPaint strokePaint = state.stroke;
6090   strokePaint.innerColor.a *= cache.strokeAlphaMul;
6091   strokePaint.middleColor.a *= cache.strokeAlphaMul;
6092   strokePaint.outerColor.a *= cache.strokeAlphaMul;
6093 
6094   // apply global alpha
6095   strokePaint.innerColor.a *= state.alpha;
6096   strokePaint.middleColor.a *= state.alpha;
6097   strokePaint.outerColor.a *= state.alpha;
6098 
6099   //ctx.appendCurrentPathToCache(ctx.recset, state.stroke);
6100 
6101   ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, aclipmode, &strokePaint, &state.scissor, ctx.fringeWidth, cache.strokeWidth, ctx.cache.paths, ctx.cache.npaths);
6102 
6103   // count triangles
6104   foreach (int i; 0..ctx.cache.npaths) {
6105     NVGpath* path = &ctx.cache.paths[i];
6106     ctx.strokeTriCount += path.nstroke-2;
6107     ++ctx.drawCallCount;
6108   }
6109 }
6110 
6111 
6112 // ////////////////////////////////////////////////////////////////////////// //
6113 // Picking API
6114 
6115 // most of the code is by Michael Wynne <mike@mikesspace.net>
6116 // https://github.com/memononen/nanovg/pull/230
6117 // https://github.com/MikeWW/nanovg
6118 
6119 /// Pick type query. Used in [hitTest] and [hitTestAll].
6120 /// Group: picking_api
6121 public enum NVGPickKind : ubyte {
6122   Fill = 0x01, ///
6123   Stroke = 0x02, ///
6124   All = 0x03, ///
6125 }
6126 
6127 /// Marks the fill of the current path as pickable with the specified id.
6128 /// Note that you can create and mark path without rasterizing it.
6129 /// Group: picking_api
6130 public void currFillHitId (NVGContext ctx, int id) nothrow @trusted @nogc {
6131   NVGpickScene* ps = nvg__pickSceneGet(ctx);
6132   NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], id, /*forStroke:*/false);
6133   nvg__pickSceneInsert(ps, pp);
6134 }
6135 
6136 public alias currFillPickId = currFillHitId; /// Ditto.
6137 
6138 /// Marks the stroke of the current path as pickable with the specified id.
6139 /// Note that you can create and mark path without rasterizing it.
6140 /// Group: picking_api
6141 public void currStrokeHitId (NVGContext ctx, int id) nothrow @trusted @nogc {
6142   NVGpickScene* ps = nvg__pickSceneGet(ctx);
6143   NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], id, /*forStroke:*/true);
6144   nvg__pickSceneInsert(ps, pp);
6145 }
6146 
6147 public alias currStrokePickId = currStrokeHitId; /// Ditto.
6148 
6149 // Marks the saved path set (fill) as pickable with the specified id.
6150 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
6151 // Group: picking_api
6152 /+
6153 public void pathSetFillHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
6154   if (svp is null) return;
6155   if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
6156   foreach (ref cp; svp.caches[0..svp.ncaches]) {
6157     NVGpickScene* ps = nvg__pickSceneGet(ctx);
6158     NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/false);
6159     nvg__pickSceneInsert(ps, pp);
6160   }
6161 }
6162 +/
6163 
6164 // Marks the saved path set (stroke) as pickable with the specified id.
6165 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
6166 // Group: picking_api
6167 /+
6168 public void pathSetStrokeHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
6169   if (svp is null) return;
6170   if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
6171   foreach (ref cp; svp.caches[0..svp.ncaches]) {
6172     NVGpickScene* ps = nvg__pickSceneGet(ctx);
6173     NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/true);
6174     nvg__pickSceneInsert(ps, pp);
6175   }
6176 }
6177 +/
6178 
6179 private template IsGoodHitTestDG(DG) {
6180   enum IsGoodHitTestDG =
6181     __traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); }) ||
6182     __traits(compiles, (){ DG dg; dg(cast(int)42, cast(int)666); });
6183 }
6184 
6185 private template IsGoodHitTestInternalDG(DG) {
6186   enum IsGoodHitTestInternalDG =
6187     __traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); }) ||
6188     __traits(compiles, (){ DG dg; NVGpickPath* pp; dg(pp); });
6189 }
6190 
6191 /// Call delegate [dg] for each path under the specified position (in no particular order).
6192 /// Returns the id of the path for which delegate [dg] returned true or [NVGNoPick].
6193 /// dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
6194 /// Group: picking_api
6195 public int hitTestDG(bool bestOrder=false, DG) (NVGContext ctx, in float x, in float y, NVGPickKind kind, scope DG dg) if (IsGoodHitTestDG!DG || IsGoodHitTestInternalDG!DG) {
6196   if (ctx.pickScene is null || ctx.pickScene.npaths == 0 || (kind&NVGPickKind.All) == 0) return -1;
6197 
6198   NVGpickScene* ps = ctx.pickScene;
6199   int levelwidth = 1<<(ps.nlevels-1);
6200   int cellx = nvg__clamp(cast(int)(x/ps.xdim), 0, levelwidth);
6201   int celly = nvg__clamp(cast(int)(y/ps.ydim), 0, levelwidth);
6202   int npicked = 0;
6203 
6204   // if we are interested only in most-toplevel path, there is no reason to check paths with worser order.
6205   // but we cannot just get out on the first path found, 'cause we are using quad tree to speed up bounds
6206   // checking, so path walking order is not guaranteed.
6207   static if (bestOrder) {
6208     int lastBestOrder = int.min;
6209   }
6210 
6211   //{ import core.stdc.stdio; printf("npaths=%d\n", ps.npaths); }
6212   for (int lvl = ps.nlevels-1; lvl >= 0; --lvl) {
6213     for (NVGpickPath* pp = ps.levels[lvl][celly*levelwidth+cellx]; pp !is null; pp = pp.next) {
6214       //{ import core.stdc.stdio; printf("... pos=(%g,%g); bounds=(%g,%g)-(%g,%g); flags=0x%02x; kind=0x%02x; kpx=0x%02x\n", x, y, pp.bounds[0], pp.bounds[1], pp.bounds[2], pp.bounds[3], pp.flags, kind, kind&pp.flags&3); }
6215       static if (bestOrder) {
6216         // reject earlier paths
6217         if (pp.order <= lastBestOrder) continue; // not interesting
6218       }
6219       immutable uint kpx = kind&pp.flags&3;
6220       if (kpx == 0) continue; // not interesting
6221       if (!nvg__pickPathTestBounds(ctx, ps, pp, x, y)) continue; // not interesting
6222       //{ import core.stdc.stdio; printf("in bounds!\n"); }
6223       int hit = 0;
6224       if (kpx&NVGPickKind.Stroke) hit = nvg__pickPathStroke(ps, pp, x, y);
6225       if (!hit && (kpx&NVGPickKind.Fill)) hit = nvg__pickPath(ps, pp, x, y);
6226       if (!hit) continue;
6227       //{ import core.stdc.stdio; printf("  HIT!\n"); }
6228       static if (bestOrder) lastBestOrder = pp.order;
6229       static if (IsGoodHitTestDG!DG) {
6230         static if (__traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); })) {
6231           if (dg(pp.id, cast(int)pp.order)) return pp.id;
6232         } else {
6233           dg(pp.id, cast(int)pp.order);
6234         }
6235       } else {
6236         static if (__traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); })) {
6237           if (dg(pp)) return pp.id;
6238         } else {
6239           dg(pp);
6240         }
6241       }
6242     }
6243     cellx >>= 1;
6244     celly >>= 1;
6245     levelwidth >>= 1;
6246   }
6247 
6248   return -1;
6249 }
6250 
6251 /// Fills ids with a list of the top most hit ids (from bottom to top) under the specified position.
6252 /// Returns the slice of [ids].
6253 /// Group: picking_api
6254 public int[] hitTestAll (NVGContext ctx, in float x, in float y, NVGPickKind kind, int[] ids) nothrow @trusted @nogc {
6255   if (ctx.pickScene is null || ids.length == 0) return ids[0..0];
6256 
6257   int npicked = 0;
6258   NVGpickScene* ps = ctx.pickScene;
6259 
6260   ctx.hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
6261     if (npicked == ps.cpicked) {
6262       int cpicked = ps.cpicked+ps.cpicked;
6263       NVGpickPath** picked = cast(NVGpickPath**)realloc(ps.picked, (NVGpickPath*).sizeof*ps.cpicked);
6264       if (picked is null) return true; // abort
6265       ps.cpicked = cpicked;
6266       ps.picked = picked;
6267     }
6268     ps.picked[npicked] = pp;
6269     ++npicked;
6270     return false; // go on
6271   });
6272 
6273   qsort(ps.picked, npicked, (NVGpickPath*).sizeof, &nvg__comparePaths);
6274 
6275   assert(npicked >= 0);
6276   if (npicked > ids.length) npicked = cast(int)ids.length;
6277   foreach (immutable nidx, ref int did; ids[0..npicked]) did = ps.picked[nidx].id;
6278 
6279   return ids[0..npicked];
6280 }
6281 
6282 /// Returns the id of the pickable shape containing x,y or [NVGNoPick] if no shape was found.
6283 /// Group: picking_api
6284 public int hitTest (NVGContext ctx, in float x, in float y, NVGPickKind kind=NVGPickKind.All) nothrow @trusted @nogc {
6285   if (ctx.pickScene is null) return -1;
6286 
6287   int bestOrder = int.min;
6288   int bestID = -1;
6289 
6290   ctx.hitTestDG!true(x, y, kind, delegate (NVGpickPath* pp) {
6291     if (pp.order > bestOrder) {
6292       bestOrder = pp.order;
6293       bestID = pp.id;
6294     }
6295   });
6296 
6297   return bestID;
6298 }
6299 
6300 /// Returns `true` if the path with the given id contains x,y.
6301 /// Group: picking_api
6302 public bool hitTestForId (NVGContext ctx, in int id, in float x, in float y, NVGPickKind kind=NVGPickKind.All) nothrow @trusted @nogc {
6303   if (ctx.pickScene is null || id == NVGNoPick) return false;
6304 
6305   bool res = false;
6306 
6307   ctx.hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) {
6308     if (pp.id == id) {
6309       res = true;
6310       return true; // stop
6311     }
6312     return false; // continue
6313   });
6314 
6315   return res;
6316 }
6317 
6318 /// Returns `true` if the given point is within the fill of the currently defined path.
6319 /// This operation can be done before rasterizing the current path.
6320 /// Group: picking_api
6321 public bool hitTestCurrFill (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
6322   NVGpickScene* ps = nvg__pickSceneGet(ctx);
6323   int oldnpoints = ps.npoints;
6324   int oldnsegments = ps.nsegments;
6325   NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], 1, /*forStroke:*/false);
6326   if (pp is null) return false; // oops
6327   scope(exit) {
6328     nvg__freePickPath(ps, pp);
6329     ps.npoints = oldnpoints;
6330     ps.nsegments = oldnsegments;
6331   }
6332   return (nvg__pointInBounds(x, y, pp.bounds) ? nvg__pickPath(ps, pp, x, y) : false);
6333 }
6334 
6335 public alias isPointInPath = hitTestCurrFill; /// Ditto.
6336 
6337 /// Returns `true` if the given point is within the stroke of the currently defined path.
6338 /// This operation can be done before rasterizing the current path.
6339 /// Group: picking_api
6340 public bool hitTestCurrStroke (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
6341   NVGpickScene* ps = nvg__pickSceneGet(ctx);
6342   int oldnpoints = ps.npoints;
6343   int oldnsegments = ps.nsegments;
6344   NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], 1, /*forStroke:*/true);
6345   if (pp is null) return false; // oops
6346   scope(exit) {
6347     nvg__freePickPath(ps, pp);
6348     ps.npoints = oldnpoints;
6349     ps.nsegments = oldnsegments;
6350   }
6351   return (nvg__pointInBounds(x, y, pp.bounds) ? nvg__pickPathStroke(ps, pp, x, y) : false);
6352 }
6353 
6354 
6355 nothrow @trusted @nogc {
6356 extern(C) {
6357   private alias _compare_fp_t = int function (const void*, const void*) nothrow @nogc;
6358   private extern(C) void qsort (scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc;
6359 
6360   extern(C) int nvg__comparePaths (const void* a, const void* b) {
6361     return (*cast(const(NVGpickPath)**)b).order-(*cast(const(NVGpickPath)**)a).order;
6362   }
6363 }
6364 
6365 enum NVGPickEPS = 0.0001f;
6366 
6367 // Segment flags
6368 enum NVGSegmentFlags {
6369   Corner = 1,
6370   Bevel = 2,
6371   InnerBevel = 4,
6372   Cap = 8,
6373   Endcap = 16,
6374 }
6375 
6376 // Path flags
6377 enum NVGPathFlags : ushort {
6378   Fill = NVGPickKind.Fill,
6379   Stroke = NVGPickKind.Stroke,
6380   Scissor = 0x80,
6381 }
6382 
6383 struct NVGsegment {
6384   int firstPoint; // Index into NVGpickScene.points
6385   short type; // NVG_LINETO or NVG_BEZIERTO
6386   short flags; // Flags relate to the corner between the prev segment and this one.
6387   float[4] bounds;
6388   float[2] startDir; // Direction at t == 0
6389   float[2] endDir; // Direction at t == 1
6390   float[2] miterDir; // Direction of miter of corner between the prev segment and this one.
6391 }
6392 
6393 struct NVGpickSubPath {
6394   short winding; // TODO: Merge to flag field
6395   bool closed; // TODO: Merge to flag field
6396 
6397   int firstSegment; // Index into NVGpickScene.segments
6398   int nsegments;
6399 
6400   float[4] bounds;
6401 
6402   NVGpickSubPath* next;
6403 }
6404 
6405 struct NVGpickPath {
6406   int id;
6407   short flags;
6408   short order;
6409   float strokeWidth;
6410   float miterLimit;
6411   short lineCap;
6412   short lineJoin;
6413   bool evenOddMode;
6414 
6415   float[4] bounds;
6416   int scissor; // Indexes into ps->points and defines scissor rect as XVec, YVec and Center
6417 
6418   NVGpickSubPath* subPaths;
6419   NVGpickPath* next;
6420   NVGpickPath* cellnext;
6421 }
6422 
6423 struct NVGpickScene {
6424   int npaths;
6425 
6426   NVGpickPath* paths; // Linked list of paths
6427   NVGpickPath* lastPath; // The last path in the paths linked list (the first path added)
6428   NVGpickPath* freePaths; // Linked list of free paths
6429 
6430   NVGpickSubPath* freeSubPaths; // Linked list of free sub paths
6431 
6432   int width;
6433   int height;
6434 
6435   // Points for all path sub paths.
6436   float* points;
6437   int npoints;
6438   int cpoints;
6439 
6440   // Segments for all path sub paths
6441   NVGsegment* segments;
6442   int nsegments;
6443   int csegments;
6444 
6445   // Implicit quadtree
6446   float xdim; // Width / (1 << nlevels)
6447   float ydim; // Height / (1 << nlevels)
6448   int ncells; // Total number of cells in all levels
6449   int nlevels;
6450   NVGpickPath*** levels; // Index: [Level][LevelY * LevelW + LevelX] Value: Linked list of paths
6451 
6452   // Temp storage for picking
6453   int cpicked;
6454   NVGpickPath** picked;
6455 }
6456 
6457 
6458 // bounds utilities
6459 void nvg__initBounds (ref float[4] bounds) {
6460   bounds.ptr[0] = bounds.ptr[1] = float.max;
6461   bounds.ptr[2] = bounds.ptr[3] = -float.max;
6462 }
6463 
6464 void nvg__expandBounds (ref float[4] bounds, const(float)* points, int npoints) {
6465   npoints *= 2;
6466   for (int i = 0; i < npoints; i += 2) {
6467     bounds.ptr[0] = nvg__min(bounds.ptr[0], points[i]);
6468     bounds.ptr[1] = nvg__min(bounds.ptr[1], points[i+1]);
6469     bounds.ptr[2] = nvg__max(bounds.ptr[2], points[i]);
6470     bounds.ptr[3] = nvg__max(bounds.ptr[3], points[i+1]);
6471   }
6472 }
6473 
6474 void nvg__unionBounds (ref float[4] bounds, in ref float[4] boundsB) {
6475   bounds.ptr[0] = nvg__min(bounds.ptr[0], boundsB.ptr[0]);
6476   bounds.ptr[1] = nvg__min(bounds.ptr[1], boundsB.ptr[1]);
6477   bounds.ptr[2] = nvg__max(bounds.ptr[2], boundsB.ptr[2]);
6478   bounds.ptr[3] = nvg__max(bounds.ptr[3], boundsB.ptr[3]);
6479 }
6480 
6481 void nvg__intersectBounds (ref float[4] bounds, in ref float[4] boundsB) {
6482   bounds.ptr[0] = nvg__max(boundsB.ptr[0], bounds.ptr[0]);
6483   bounds.ptr[1] = nvg__max(boundsB.ptr[1], bounds.ptr[1]);
6484   bounds.ptr[2] = nvg__min(boundsB.ptr[2], bounds.ptr[2]);
6485   bounds.ptr[3] = nvg__min(boundsB.ptr[3], bounds.ptr[3]);
6486 
6487   bounds.ptr[2] = nvg__max(bounds.ptr[0], bounds.ptr[2]);
6488   bounds.ptr[3] = nvg__max(bounds.ptr[1], bounds.ptr[3]);
6489 }
6490 
6491 bool nvg__pointInBounds (in float x, in float y, in ref float[4] bounds) {
6492   pragma(inline, true);
6493   return (x >= bounds.ptr[0] && x <= bounds.ptr[2] && y >= bounds.ptr[1] && y <= bounds.ptr[3]);
6494 }
6495 
6496 // building paths & sub paths
6497 int nvg__pickSceneAddPoints (NVGpickScene* ps, const(float)* xy, int n) {
6498   import core.stdc.string : memcpy;
6499   if (ps.npoints+n > ps.cpoints) {
6500     import core.stdc.stdlib : realloc;
6501     int cpoints = ps.npoints+n+(ps.cpoints<<1);
6502     float* points = cast(float*)realloc(ps.points, float.sizeof*2*cpoints);
6503     if (points is null) assert(0, "NanoVega: out of memory");
6504     ps.points = points;
6505     ps.cpoints = cpoints;
6506   }
6507   int i = ps.npoints;
6508   if (xy !is null) memcpy(&ps.points[i*2], xy, float.sizeof*2*n);
6509   ps.npoints += n;
6510   return i;
6511 }
6512 
6513 void nvg__pickSubPathAddSegment (NVGpickScene* ps, NVGpickSubPath* psp, int firstPoint, int type, short flags) {
6514   NVGsegment* seg = null;
6515   if (ps.nsegments == ps.csegments) {
6516     int csegments = 1+ps.csegments+(ps.csegments<<1);
6517     NVGsegment* segments = cast(NVGsegment*)realloc(ps.segments, NVGsegment.sizeof*csegments);
6518     if (segments is null) assert(0, "NanoVega: out of memory");
6519     ps.segments = segments;
6520     ps.csegments = csegments;
6521   }
6522 
6523   if (psp.firstSegment == -1) psp.firstSegment = ps.nsegments;
6524 
6525   seg = &ps.segments[ps.nsegments];
6526   ++ps.nsegments;
6527   seg.firstPoint = firstPoint;
6528   seg.type = cast(short)type;
6529   seg.flags = flags;
6530   ++psp.nsegments;
6531 
6532   nvg__segmentDir(ps, psp, seg, 0, seg.startDir);
6533   nvg__segmentDir(ps, psp, seg, 1, seg.endDir);
6534 }
6535 
6536 void nvg__segmentDir (NVGpickScene* ps, NVGpickSubPath* psp, NVGsegment* seg, float t, ref float[2] d) {
6537   const(float)* points = &ps.points[seg.firstPoint*2];
6538   immutable float x0 = points[0*2+0], x1 = points[1*2+0];
6539   immutable float y0 = points[0*2+1], y1 = points[1*2+1];
6540   switch (seg.type) {
6541     case Command.LineTo:
6542       d.ptr[0] = x1-x0;
6543       d.ptr[1] = y1-y0;
6544       nvg__normalize(&d.ptr[0], &d.ptr[1]);
6545       break;
6546     case Command.BezierTo:
6547       immutable float x2 = points[2*2+0];
6548       immutable float y2 = points[2*2+1];
6549       immutable float x3 = points[3*2+0];
6550       immutable float y3 = points[3*2+1];
6551 
6552       immutable float omt = 1.0f-t;
6553       immutable float omt2 = omt*omt;
6554       immutable float t2 = t*t;
6555 
6556       d.ptr[0] =
6557         3.0f*omt2*(x1-x0)+
6558         6.0f*omt*t*(x2-x1)+
6559         3.0f*t2*(x3-x2);
6560 
6561       d.ptr[1] =
6562         3.0f*omt2*(y1-y0)+
6563         6.0f*omt*t*(y2-y1)+
6564         3.0f*t2*(y3-y2);
6565 
6566       nvg__normalize(&d.ptr[0], &d.ptr[1]);
6567       break;
6568     default:
6569       break;
6570   }
6571 }
6572 
6573 void nvg__pickSubPathAddFillSupports (NVGpickScene* ps, NVGpickSubPath* psp) {
6574   if (psp.firstSegment == -1) return;
6575   NVGsegment* segments = &ps.segments[psp.firstSegment];
6576   for (int s = 0; s < psp.nsegments; ++s) {
6577     NVGsegment* seg = &segments[s];
6578     const(float)* points = &ps.points[seg.firstPoint*2];
6579     if (seg.type == Command.LineTo) {
6580       nvg__initBounds(seg.bounds);
6581       nvg__expandBounds(seg.bounds, points, 2);
6582     } else {
6583       nvg__bezierBounds(points, seg.bounds);
6584     }
6585   }
6586 }
6587 
6588 void nvg__pickSubPathAddStrokeSupports (NVGpickScene* ps, NVGpickSubPath* psp, float strokeWidth, int lineCap, int lineJoin, float miterLimit) {
6589   if (psp.firstSegment == -1) return;
6590   immutable bool closed = psp.closed;
6591   const(float)* points = ps.points;
6592   NVGsegment* seg = null;
6593   NVGsegment* segments = &ps.segments[psp.firstSegment];
6594   int nsegments = psp.nsegments;
6595   NVGsegment* prevseg = (closed ? &segments[psp.nsegments-1] : null);
6596 
6597   int ns = 0; // nsupports
6598   float[32] supportingPoints = void;
6599   int firstPoint, lastPoint;
6600 
6601   if (!closed) {
6602     segments[0].flags |= NVGSegmentFlags.Cap;
6603     segments[nsegments-1].flags |= NVGSegmentFlags.Endcap;
6604   }
6605 
6606   for (int s = 0; s < nsegments; ++s) {
6607     seg = &segments[s];
6608     nvg__initBounds(seg.bounds);
6609 
6610     firstPoint = seg.firstPoint*2;
6611     lastPoint = firstPoint+(seg.type == Command.LineTo ? 2 : 6);
6612 
6613     ns = 0;
6614 
6615     // First two supporting points are either side of the start point
6616     supportingPoints.ptr[ns++] = points[firstPoint]-seg.startDir.ptr[1]*strokeWidth;
6617     supportingPoints.ptr[ns++] = points[firstPoint+1]+seg.startDir.ptr[0]*strokeWidth;
6618 
6619     supportingPoints.ptr[ns++] = points[firstPoint]+seg.startDir.ptr[1]*strokeWidth;
6620     supportingPoints.ptr[ns++] = points[firstPoint+1]-seg.startDir.ptr[0]*strokeWidth;
6621 
6622     // Second two supporting points are either side of the end point
6623     supportingPoints.ptr[ns++] = points[lastPoint]-seg.endDir.ptr[1]*strokeWidth;
6624     supportingPoints.ptr[ns++] = points[lastPoint+1]+seg.endDir.ptr[0]*strokeWidth;
6625 
6626     supportingPoints.ptr[ns++] = points[lastPoint]+seg.endDir.ptr[1]*strokeWidth;
6627     supportingPoints.ptr[ns++] = points[lastPoint+1]-seg.endDir.ptr[0]*strokeWidth;
6628 
6629     if ((seg.flags&NVGSegmentFlags.Corner) && prevseg !is null) {
6630       seg.miterDir.ptr[0] = 0.5f*(-prevseg.endDir.ptr[1]-seg.startDir.ptr[1]);
6631       seg.miterDir.ptr[1] = 0.5f*(prevseg.endDir.ptr[0]+seg.startDir.ptr[0]);
6632 
6633       immutable float M2 = seg.miterDir.ptr[0]*seg.miterDir.ptr[0]+seg.miterDir.ptr[1]*seg.miterDir.ptr[1];
6634 
6635       if (M2 > 0.000001f) {
6636         float scale = 1.0f/M2;
6637         if (scale > 600.0f) scale = 600.0f;
6638         seg.miterDir.ptr[0] *= scale;
6639         seg.miterDir.ptr[1] *= scale;
6640       }
6641 
6642       //NVG_PICK_DEBUG_VECTOR_SCALE(&points[firstPoint], seg.miterDir, 10);
6643 
6644       // Add an additional support at the corner on the other line
6645       supportingPoints.ptr[ns++] = points[firstPoint]-prevseg.endDir.ptr[1]*strokeWidth;
6646       supportingPoints.ptr[ns++] = points[firstPoint+1]+prevseg.endDir.ptr[0]*strokeWidth;
6647 
6648       if (lineJoin == NVGLineCap.Miter || lineJoin == NVGLineCap.Bevel) {
6649         // Set a corner as beveled if the join type is bevel or mitered and
6650         // miterLimit is hit.
6651         if (lineJoin == NVGLineCap.Bevel || (M2*miterLimit*miterLimit) < 1.0f) {
6652           seg.flags |= NVGSegmentFlags.Bevel;
6653         } else {
6654           // Corner is mitered - add miter point as a support
6655           supportingPoints.ptr[ns++] = points[firstPoint]+seg.miterDir.ptr[0]*strokeWidth;
6656           supportingPoints.ptr[ns++] = points[firstPoint+1]+seg.miterDir.ptr[1]*strokeWidth;
6657         }
6658       } else if (lineJoin == NVGLineCap.Round) {
6659         // ... and at the midpoint of the corner arc
6660         float[2] vertexN = [ -seg.startDir.ptr[0]+prevseg.endDir.ptr[0], -seg.startDir.ptr[1]+prevseg.endDir.ptr[1] ];
6661         nvg__normalize(&vertexN[0], &vertexN[1]);
6662 
6663         supportingPoints.ptr[ns++] = points[firstPoint]+vertexN[0]*strokeWidth;
6664         supportingPoints.ptr[ns++] = points[firstPoint+1]+vertexN[1]*strokeWidth;
6665       }
6666     }
6667 
6668     if (seg.flags&NVGSegmentFlags.Cap) {
6669       switch (lineCap) {
6670         case NVGLineCap.Butt:
6671           // supports for butt already added
6672           break;
6673         case NVGLineCap.Square:
6674           // square cap supports are just the original two supports moved out along the direction
6675           supportingPoints.ptr[ns++] = supportingPoints.ptr[0]-seg.startDir.ptr[0]*strokeWidth;
6676           supportingPoints.ptr[ns++] = supportingPoints.ptr[1]-seg.startDir.ptr[1]*strokeWidth;
6677           supportingPoints.ptr[ns++] = supportingPoints.ptr[2]-seg.startDir.ptr[0]*strokeWidth;
6678           supportingPoints.ptr[ns++] = supportingPoints.ptr[3]-seg.startDir.ptr[1]*strokeWidth;
6679           break;
6680         case NVGLineCap.Round:
6681           // add one additional support for the round cap along the dir
6682           supportingPoints.ptr[ns++] = points[firstPoint]-seg.startDir.ptr[0]*strokeWidth;
6683           supportingPoints.ptr[ns++] = points[firstPoint+1]-seg.startDir.ptr[1]*strokeWidth;
6684           break;
6685         default:
6686           break;
6687       }
6688     }
6689 
6690     if (seg.flags&NVGSegmentFlags.Endcap) {
6691       // end supporting points, either side of line
6692       int end = 4;
6693       switch(lineCap) {
6694         case NVGLineCap.Butt:
6695           // supports for butt already added
6696           break;
6697         case NVGLineCap.Square:
6698           // square cap supports are just the original two supports moved out along the direction
6699           supportingPoints.ptr[ns++] = supportingPoints.ptr[end+0]+seg.endDir.ptr[0]*strokeWidth;
6700           supportingPoints.ptr[ns++] = supportingPoints.ptr[end+1]+seg.endDir.ptr[1]*strokeWidth;
6701           supportingPoints.ptr[ns++] = supportingPoints.ptr[end+2]+seg.endDir.ptr[0]*strokeWidth;
6702           supportingPoints.ptr[ns++] = supportingPoints.ptr[end+3]+seg.endDir.ptr[1]*strokeWidth;
6703           break;
6704         case NVGLineCap.Round:
6705           // add one additional support for the round cap along the dir
6706           supportingPoints.ptr[ns++] = points[lastPoint]+seg.endDir.ptr[0]*strokeWidth;
6707           supportingPoints.ptr[ns++] = points[lastPoint+1]+seg.endDir.ptr[1]*strokeWidth;
6708           break;
6709         default:
6710           break;
6711       }
6712     }
6713 
6714     nvg__expandBounds(seg.bounds, supportingPoints.ptr, ns/2);
6715 
6716     prevseg = seg;
6717   }
6718 }
6719 
6720 NVGpickPath* nvg__pickPathCreate (NVGContext context, const(float)[] acommands, int id, bool forStroke) {
6721   NVGpickScene* ps = nvg__pickSceneGet(context);
6722   if (ps is null) return null;
6723 
6724   int i = 0;
6725 
6726   int ncommands = cast(int)acommands.length;
6727   const(float)* commands = acommands.ptr;
6728 
6729   NVGpickPath* pp = null;
6730   NVGpickSubPath* psp = null;
6731   float[2] start = void;
6732   int firstPoint;
6733 
6734   //bool hasHoles = false;
6735   NVGpickSubPath* prev = null;
6736 
6737   float[8] points = void;
6738   float[2] inflections = void;
6739   int ninflections = 0;
6740 
6741   NVGstate* state = nvg__getState(context);
6742   float[4] totalBounds = void;
6743   NVGsegment* segments = null;
6744   const(NVGsegment)* seg = null;
6745   NVGpickSubPath *curpsp;
6746 
6747   pp = nvg__allocPickPath(ps);
6748   if (pp is null) return null;
6749 
6750   pp.id = id;
6751 
6752   bool hasPoints = false;
6753 
6754   void closeIt () {
6755     if (psp is null || !hasPoints) return;
6756     if (ps.points[(ps.npoints-1)*2] != start.ptr[0] || ps.points[(ps.npoints-1)*2+1] != start.ptr[1]) {
6757       firstPoint = nvg__pickSceneAddPoints(ps, start.ptr, 1);
6758       nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, Command.LineTo, NVGSegmentFlags.Corner);
6759     }
6760     psp.closed = true;
6761   }
6762 
6763   while (i < ncommands) {
6764     int cmd = cast(int)commands[i++];
6765     switch (cmd) {
6766       case Command.MoveTo: // one coordinate pair
6767         const(float)* tfxy = commands+i;
6768         i += 2;
6769 
6770         // new starting point
6771         start.ptr[0..2] = tfxy[0..2];
6772 
6773         // start a new path for each sub path to handle sub paths that intersect other sub paths
6774         prev = psp;
6775         psp = nvg__allocPickSubPath(ps);
6776         if (psp is null) { psp = prev; break; }
6777         psp.firstSegment = -1;
6778         psp.winding = NVGSolidity.Solid;
6779         psp.next = prev;
6780 
6781         nvg__pickSceneAddPoints(ps, tfxy, 1);
6782         hasPoints = true;
6783         break;
6784       case Command.LineTo: // one coordinate pair
6785         const(float)* tfxy = commands+i;
6786         i += 2;
6787         firstPoint = nvg__pickSceneAddPoints(ps, tfxy, 1);
6788         nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, NVGSegmentFlags.Corner);
6789         hasPoints = true;
6790         break;
6791       case Command.BezierTo: // three coordinate pairs
6792         const(float)* tfxy = commands+i;
6793         i += 3*2;
6794 
6795         // Split the curve at it's dx==0 or dy==0 inflection points.
6796         // Thus:
6797         //    A horizontal line only ever interects the curves once.
6798         //  and
6799         //    Finding the closest point on any curve converges more reliably.
6800 
6801         // NOTE: We could just split on dy==0 here.
6802 
6803         memcpy(&points.ptr[0], &ps.points[(ps.npoints-1)*2], float.sizeof*2);
6804         memcpy(&points.ptr[2], tfxy, float.sizeof*2*3);
6805 
6806         ninflections = 0;
6807         nvg__bezierInflections(points.ptr, 1, &ninflections, inflections.ptr);
6808         nvg__bezierInflections(points.ptr, 0, &ninflections, inflections.ptr);
6809 
6810         if (ninflections) {
6811           float previnfl = 0;
6812           float[8] pointsA = void, pointsB = void;
6813 
6814           nvg__smallsort(inflections.ptr, ninflections);
6815 
6816           for (int infl = 0; infl < ninflections; ++infl) {
6817             if (nvg__absf(inflections.ptr[infl]-previnfl) < NVGPickEPS) continue;
6818 
6819             immutable float t = (inflections.ptr[infl]-previnfl)*(1.0f/(1.0f-previnfl));
6820 
6821             previnfl = inflections.ptr[infl];
6822 
6823             nvg__splitBezier(points.ptr, t, pointsA.ptr, pointsB.ptr);
6824 
6825             firstPoint = nvg__pickSceneAddPoints(ps, &pointsA.ptr[2], 3);
6826             nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, (infl == 0) ? NVGSegmentFlags.Corner : 0);
6827 
6828             memcpy(points.ptr, pointsB.ptr, float.sizeof*8);
6829           }
6830 
6831           firstPoint = nvg__pickSceneAddPoints(ps, &pointsB.ptr[2], 3);
6832           nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, 0);
6833         } else {
6834           firstPoint = nvg__pickSceneAddPoints(ps, tfxy, 3);
6835           nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, NVGSegmentFlags.Corner);
6836         }
6837         hasPoints = true;
6838         break;
6839       case Command.Close:
6840         closeIt();
6841         break;
6842       case Command.Winding:
6843         psp.winding = cast(short)cast(int)commands[i];
6844         //if (psp.winding == NVGSolidity.Hole) hasHoles = true;
6845         i += 1;
6846         break;
6847       default:
6848         break;
6849     }
6850   }
6851 
6852   // force-close filled paths
6853   if (psp !is null && !forStroke && hasPoints && !psp.closed) closeIt();
6854 
6855   pp.flags = (forStroke ? NVGPathFlags.Stroke : NVGPathFlags.Fill);
6856   pp.subPaths = psp;
6857   pp.strokeWidth = state.strokeWidth*0.5f;
6858   pp.miterLimit = state.miterLimit;
6859   pp.lineCap = cast(short)state.lineCap;
6860   pp.lineJoin = cast(short)state.lineJoin;
6861   pp.evenOddMode = nvg__getState(context).evenOddMode;
6862 
6863   nvg__initBounds(totalBounds);
6864 
6865   for (curpsp = psp; curpsp; curpsp = curpsp.next) {
6866     if (forStroke) {
6867       nvg__pickSubPathAddStrokeSupports(ps, curpsp, pp.strokeWidth, pp.lineCap, pp.lineJoin, pp.miterLimit);
6868     } else {
6869       nvg__pickSubPathAddFillSupports(ps, curpsp);
6870     }
6871 
6872     if (curpsp.firstSegment == -1) continue;
6873     segments = &ps.segments[curpsp.firstSegment];
6874     nvg__initBounds(curpsp.bounds);
6875     for (int s = 0; s < curpsp.nsegments; ++s) {
6876       seg = &segments[s];
6877       //NVG_PICK_DEBUG_BOUNDS(seg.bounds);
6878       nvg__unionBounds(curpsp.bounds, seg.bounds);
6879     }
6880 
6881     nvg__unionBounds(totalBounds, curpsp.bounds);
6882   }
6883 
6884   // Store the scissor rect if present.
6885   if (state.scissor.extent.ptr[0] != -1.0f) {
6886     // Use points storage to store the scissor data
6887     pp.scissor = nvg__pickSceneAddPoints(ps, null, 4);
6888     float* scissor = &ps.points[pp.scissor*2];
6889 
6890     //memcpy(scissor, state.scissor.xform.ptr, 6*float.sizeof);
6891     scissor[0..6] = state.scissor.xform.mat[];
6892     memcpy(scissor+6, state.scissor.extent.ptr, 2*float.sizeof);
6893 
6894     pp.flags |= NVGPathFlags.Scissor;
6895   }
6896 
6897   memcpy(pp.bounds.ptr, totalBounds.ptr, float.sizeof*4);
6898 
6899   return pp;
6900 }
6901 
6902 
6903 // Struct management
6904 NVGpickPath* nvg__allocPickPath (NVGpickScene* ps) {
6905   NVGpickPath* pp = ps.freePaths;
6906   if (pp !is null) {
6907     ps.freePaths = pp.next;
6908   } else {
6909     pp = cast(NVGpickPath*)malloc(NVGpickPath.sizeof);
6910   }
6911   memset(pp, 0, NVGpickPath.sizeof);
6912   return pp;
6913 }
6914 
6915 // Put a pick path and any sub paths (back) to the free lists.
6916 void nvg__freePickPath (NVGpickScene* ps, NVGpickPath* pp) {
6917   // Add all sub paths to the sub path free list.
6918   // Finds the end of the path sub paths, links that to the current
6919   // sub path free list head and replaces the head ptr with the
6920   // head path sub path entry.
6921   NVGpickSubPath* psp = null;
6922   for (psp = pp.subPaths; psp !is null && psp.next !is null; psp = psp.next) {}
6923 
6924   if (psp) {
6925     psp.next = ps.freeSubPaths;
6926     ps.freeSubPaths = pp.subPaths;
6927   }
6928   pp.subPaths = null;
6929 
6930   // Add the path to the path freelist
6931   pp.next = ps.freePaths;
6932   ps.freePaths = pp;
6933   if (pp.next is null) ps.lastPath = pp;
6934 }
6935 
6936 NVGpickSubPath* nvg__allocPickSubPath (NVGpickScene* ps) {
6937   NVGpickSubPath* psp = ps.freeSubPaths;
6938   if (psp !is null) {
6939     ps.freeSubPaths = psp.next;
6940   } else {
6941     psp = cast(NVGpickSubPath*)malloc(NVGpickSubPath.sizeof);
6942     if (psp is null) return null;
6943   }
6944   memset(psp, 0, NVGpickSubPath.sizeof);
6945   return psp;
6946 }
6947 
6948 void nvg__returnPickSubPath (NVGpickScene* ps, NVGpickSubPath* psp) {
6949   psp.next = ps.freeSubPaths;
6950   ps.freeSubPaths = psp;
6951 }
6952 
6953 NVGpickScene* nvg__allocPickScene () {
6954   NVGpickScene* ps = cast(NVGpickScene*)malloc(NVGpickScene.sizeof);
6955   if (ps is null) return null;
6956   memset(ps, 0, NVGpickScene.sizeof);
6957   ps.nlevels = 5;
6958   return ps;
6959 }
6960 
6961 void nvg__deletePickScene (NVGpickScene* ps) {
6962   NVGpickPath* pp;
6963   NVGpickSubPath* psp;
6964 
6965   // Add all paths (and thus sub paths) to the free list(s).
6966   while (ps.paths !is null) {
6967     pp = ps.paths.next;
6968     nvg__freePickPath(ps, ps.paths);
6969     ps.paths = pp;
6970   }
6971 
6972   // Delete all paths
6973   while (ps.freePaths !is null) {
6974     pp = ps.freePaths;
6975     ps.freePaths = pp.next;
6976     while (pp.subPaths !is null) {
6977       psp = pp.subPaths;
6978       pp.subPaths = psp.next;
6979       free(psp);
6980     }
6981     free(pp);
6982   }
6983 
6984   // Delete all sub paths
6985   while (ps.freeSubPaths !is null) {
6986     psp = ps.freeSubPaths.next;
6987     free(ps.freeSubPaths);
6988     ps.freeSubPaths = psp;
6989   }
6990 
6991   ps.npoints = 0;
6992   ps.nsegments = 0;
6993 
6994   if (ps.levels !is null) {
6995     free(ps.levels[0]);
6996     free(ps.levels);
6997   }
6998 
6999   if (ps.picked !is null) free(ps.picked);
7000   if (ps.points !is null) free(ps.points);
7001   if (ps.segments !is null) free(ps.segments);
7002 
7003   free(ps);
7004 }
7005 
7006 NVGpickScene* nvg__pickSceneGet (NVGContext ctx) {
7007   if (ctx.pickScene is null) ctx.pickScene = nvg__allocPickScene();
7008   return ctx.pickScene;
7009 }
7010 
7011 
7012 // Applies Casteljau's algorithm to a cubic bezier for a given parameter t
7013 // points is 4 points (8 floats)
7014 // lvl1 is 3 points (6 floats)
7015 // lvl2 is 2 points (4 floats)
7016 // lvl3 is 1 point (2 floats)
7017 void nvg__casteljau (const(float)* points, float t, float* lvl1, float* lvl2, float* lvl3) {
7018   enum x0 = 0*2+0; enum x1 = 1*2+0; enum x2 = 2*2+0; enum x3 = 3*2+0;
7019   enum y0 = 0*2+1; enum y1 = 1*2+1; enum y2 = 2*2+1; enum y3 = 3*2+1;
7020 
7021   // Level 1
7022   lvl1[x0] = (points[x1]-points[x0])*t+points[x0];
7023   lvl1[y0] = (points[y1]-points[y0])*t+points[y0];
7024 
7025   lvl1[x1] = (points[x2]-points[x1])*t+points[x1];
7026   lvl1[y1] = (points[y2]-points[y1])*t+points[y1];
7027 
7028   lvl1[x2] = (points[x3]-points[x2])*t+points[x2];
7029   lvl1[y2] = (points[y3]-points[y2])*t+points[y2];
7030 
7031   // Level 2
7032   lvl2[x0] = (lvl1[x1]-lvl1[x0])*t+lvl1[x0];
7033   lvl2[y0] = (lvl1[y1]-lvl1[y0])*t+lvl1[y0];
7034 
7035   lvl2[x1] = (lvl1[x2]-lvl1[x1])*t+lvl1[x1];
7036   lvl2[y1] = (lvl1[y2]-lvl1[y1])*t+lvl1[y1];
7037 
7038   // Level 3
7039   lvl3[x0] = (lvl2[x1]-lvl2[x0])*t+lvl2[x0];
7040   lvl3[y0] = (lvl2[y1]-lvl2[y0])*t+lvl2[y0];
7041 }
7042 
7043 // Calculates a point on a bezier at point t.
7044 void nvg__bezierEval (const(float)* points, float t, ref float[2] tpoint) {
7045   immutable float omt = 1-t;
7046   immutable float omt3 = omt*omt*omt;
7047   immutable float omt2 = omt*omt;
7048   immutable float t3 = t*t*t;
7049   immutable float t2 = t*t;
7050 
7051   tpoint.ptr[0] =
7052     points[0]*omt3+
7053     points[2]*3.0f*omt2*t+
7054     points[4]*3.0f*omt*t2+
7055     points[6]*t3;
7056 
7057   tpoint.ptr[1] =
7058     points[1]*omt3+
7059     points[3]*3.0f*omt2*t+
7060     points[5]*3.0f*omt*t2+
7061     points[7]*t3;
7062 }
7063 
7064 // Splits a cubic bezier curve into two parts at point t.
7065 void nvg__splitBezier (const(float)* points, float t, float* pointsA, float* pointsB) {
7066   enum x0 = 0*2+0; enum x1 = 1*2+0; enum x2 = 2*2+0; enum x3 = 3*2+0;
7067   enum y0 = 0*2+1; enum y1 = 1*2+1; enum y2 = 2*2+1; enum y3 = 3*2+1;
7068 
7069   float[6] lvl1 = void;
7070   float[4] lvl2 = void;
7071   float[2] lvl3 = void;
7072 
7073   nvg__casteljau(points, t, lvl1.ptr, lvl2.ptr, lvl3.ptr);
7074 
7075   // First half
7076   pointsA[x0] = points[x0];
7077   pointsA[y0] = points[y0];
7078 
7079   pointsA[x1] = lvl1.ptr[x0];
7080   pointsA[y1] = lvl1.ptr[y0];
7081 
7082   pointsA[x2] = lvl2.ptr[x0];
7083   pointsA[y2] = lvl2.ptr[y0];
7084 
7085   pointsA[x3] = lvl3.ptr[x0];
7086   pointsA[y3] = lvl3.ptr[y0];
7087 
7088   // Second half
7089   pointsB[x0] = lvl3.ptr[x0];
7090   pointsB[y0] = lvl3.ptr[y0];
7091 
7092   pointsB[x1] = lvl2.ptr[x1];
7093   pointsB[y1] = lvl2.ptr[y1];
7094 
7095   pointsB[x2] = lvl1.ptr[x2];
7096   pointsB[y2] = lvl1.ptr[y2];
7097 
7098   pointsB[x3] = points[x3];
7099   pointsB[y3] = points[y3];
7100 }
7101 
7102 // Calculates the inflection points in coordinate coord (X = 0, Y = 1) of a cubic bezier.
7103 // Appends any found inflection points to the array inflections and increments *ninflections.
7104 // So finds the parameters where dx/dt or dy/dt is 0
7105 void nvg__bezierInflections (const(float)* points, int coord, int* ninflections, float* inflections) {
7106   immutable float v0 = points[0*2+coord], v1 = points[1*2+coord], v2 = points[2*2+coord], v3 = points[3*2+coord];
7107   float[2] t = void;
7108   int nvalid = *ninflections;
7109 
7110   immutable float a = 3.0f*( -v0+3.0f*v1-3.0f*v2+v3 );
7111   immutable float b = 6.0f*( v0-2.0f*v1+v2 );
7112   immutable float c = 3.0f*( v1-v0 );
7113 
7114   float d = b*b-4.0f*a*c;
7115   if (nvg__absf(d-0.0f) < NVGPickEPS) {
7116     // Zero or one root
7117     t.ptr[0] = -b/2.0f*a;
7118     if (t.ptr[0] > NVGPickEPS && t.ptr[0] < (1.0f-NVGPickEPS)) {
7119       inflections[nvalid] = t.ptr[0];
7120       ++nvalid;
7121     }
7122   } else if (d > NVGPickEPS) {
7123     // zero, one or two roots
7124     d = nvg__sqrtf(d);
7125 
7126     t.ptr[0] = (-b+d)/(2.0f*a);
7127     t.ptr[1] = (-b-d)/(2.0f*a);
7128 
7129     for (int i = 0; i < 2; ++i) {
7130       if (t.ptr[i] > NVGPickEPS && t.ptr[i] < (1.0f-NVGPickEPS)) {
7131         inflections[nvalid] = t.ptr[i];
7132         ++nvalid;
7133       }
7134     }
7135   } else {
7136     // zero roots
7137   }
7138 
7139   *ninflections = nvalid;
7140 }
7141 
7142 // Sort a small number of floats in ascending order (0 < n < 6)
7143 void nvg__smallsort (float* values, int n) {
7144   bool bSwapped = true;
7145   for (int j = 0; j < n-1 && bSwapped; ++j) {
7146     bSwapped = false;
7147     for (int i = 0; i < n-1; ++i) {
7148       if (values[i] > values[i+1]) {
7149         auto tmp = values[i];
7150         values[i] = values[i+1];
7151         values[i+1] = tmp;
7152       }
7153     }
7154   }
7155 }
7156 
7157 // Calculates the bounding rect of a given cubic bezier curve.
7158 void nvg__bezierBounds (const(float)* points, ref float[4] bounds) {
7159   float[4] inflections = void;
7160   int ninflections = 0;
7161   float[2] tpoint = void;
7162 
7163   nvg__initBounds(bounds);
7164 
7165   // Include start and end points in bounds
7166   nvg__expandBounds(bounds, &points[0], 1);
7167   nvg__expandBounds(bounds, &points[6], 1);
7168 
7169   // Calculate dx==0 and dy==0 inflection points and add them to the bounds
7170 
7171   nvg__bezierInflections(points, 0, &ninflections, inflections.ptr);
7172   nvg__bezierInflections(points, 1, &ninflections, inflections.ptr);
7173 
7174   foreach (immutable int i; 0..ninflections) {
7175     nvg__bezierEval(points, inflections[i], tpoint);
7176     nvg__expandBounds(bounds, tpoint.ptr, 1);
7177   }
7178 }
7179 
7180 // Checks to see if a line originating from x,y along the +ve x axis
7181 // intersects the given line (points[0],points[1]) -> (points[2], points[3]).
7182 // Returns `true` on intersection.
7183 // Horizontal lines are never hit.
7184 bool nvg__intersectLine (const(float)* points, float x, float y) {
7185   immutable float x1 = points[0];
7186   immutable float y1 = points[1];
7187   immutable float x2 = points[2];
7188   immutable float y2 = points[3];
7189   immutable float d = y2-y1;
7190   if (d > NVGPickEPS || d < -NVGPickEPS) {
7191     immutable float s = (x2-x1)/d;
7192     immutable float lineX = x1+(y-y1)*s;
7193     return (lineX > x);
7194   } else {
7195     return false;
7196   }
7197 }
7198 
7199 // Checks to see if a line originating from x,y along the +ve x axis intersects the given bezier.
7200 // It is assumed that the line originates from within the bounding box of
7201 // the bezier and that the curve has no dy=0 inflection points.
7202 // Returns the number of intersections found (which is either 1 or 0).
7203 int nvg__intersectBezier (const(float)* points, float x, float y) {
7204   immutable float x0 = points[0*2+0], x1 = points[1*2+0], x2 = points[2*2+0], x3 = points[3*2+0];
7205   immutable float y0 = points[0*2+1], y1 = points[1*2+1], y2 = points[2*2+1], y3 = points[3*2+1];
7206 
7207   if (y0 == y1 && y1 == y2 && y2 == y3) return 0;
7208 
7209   // Initial t guess
7210   float t = void;
7211        if (y3 != y0) t = (y-y0)/(y3-y0);
7212   else if (x3 != x0) t = (x-x0)/(x3-x0);
7213   else t = 0.5f;
7214 
7215   // A few Newton iterations
7216   for (int iter = 0; iter < 6; ++iter) {
7217     immutable float omt = 1-t;
7218     immutable float omt2 = omt*omt;
7219     immutable float t2 = t*t;
7220     immutable float omt3 = omt2*omt;
7221     immutable float t3 = t2*t;
7222 
7223     immutable float ty = y0*omt3 +
7224       y1*3.0f*omt2*t +
7225       y2*3.0f*omt*t2 +
7226       y3*t3;
7227 
7228     // Newton iteration
7229     immutable float dty = 3.0f*omt2*(y1-y0) +
7230       6.0f*omt*t*(y2-y1) +
7231       3.0f*t2*(y3-y2);
7232 
7233     // dty will never == 0 since:
7234     //  Either omt, omt2 are zero OR t2 is zero
7235     //  y0 != y1 != y2 != y3 (checked above)
7236     t = t-(ty-y)/dty;
7237   }
7238 
7239   {
7240     immutable float omt = 1-t;
7241     immutable float omt2 = omt*omt;
7242     immutable float t2 = t*t;
7243     immutable float omt3 = omt2*omt;
7244     immutable float t3 = t2*t;
7245 
7246     immutable float tx =
7247       x0*omt3+
7248       x1*3.0f*omt2*t+
7249       x2*3.0f*omt*t2+
7250       x3*t3;
7251 
7252     return (tx > x ? 1 : 0);
7253   }
7254 }
7255 
7256 // Finds the closest point on a line to a given point
7257 void nvg__closestLine (const(float)* points, float x, float y, float* closest, float* ot) {
7258   immutable float x1 = points[0];
7259   immutable float y1 = points[1];
7260   immutable float x2 = points[2];
7261   immutable float y2 = points[3];
7262   immutable float pqx = x2-x1;
7263   immutable float pqz = y2-y1;
7264   immutable float dx = x-x1;
7265   immutable float dz = y-y1;
7266   immutable float d = pqx*pqx+pqz*pqz;
7267   float t = pqx*dx+pqz*dz;
7268   if (d > 0) t /= d;
7269   if (t < 0) t = 0; else if (t > 1) t = 1;
7270   closest[0] = x1+t*pqx;
7271   closest[1] = y1+t*pqz;
7272   *ot = t;
7273 }
7274 
7275 // Finds the closest point on a curve for a given point (x,y).
7276 // Assumes that the curve has no dx==0 or dy==0 inflection points.
7277 void nvg__closestBezier (const(float)* points, float x, float y, float* closest, float *ot) {
7278   immutable float x0 = points[0*2+0], x1 = points[1*2+0], x2 = points[2*2+0], x3 = points[3*2+0];
7279   immutable float y0 = points[0*2+1], y1 = points[1*2+1], y2 = points[2*2+1], y3 = points[3*2+1];
7280 
7281   // This assumes that the curve has no dy=0 inflection points.
7282 
7283   // Initial t guess
7284   float t = 0.5f;
7285 
7286   // A few Newton iterations
7287   for (int iter = 0; iter < 6; ++iter) {
7288     immutable float omt = 1-t;
7289     immutable float omt2 = omt*omt;
7290     immutable float t2 = t*t;
7291     immutable float omt3 = omt2*omt;
7292     immutable float t3 = t2*t;
7293 
7294     immutable float ty =
7295       y0*omt3+
7296       y1*3.0f*omt2*t+
7297       y2*3.0f*omt*t2+
7298       y3*t3;
7299 
7300     immutable float tx =
7301       x0*omt3+
7302       x1*3.0f*omt2*t+
7303       x2*3.0f*omt*t2+
7304       x3*t3;
7305 
7306     // Newton iteration
7307     immutable float dty =
7308       3.0f*omt2*(y1-y0)+
7309       6.0f*omt*t*(y2-y1)+
7310       3.0f*t2*(y3-y2);
7311 
7312     immutable float ddty =
7313       6.0f*omt*(y2-2.0f*y1+y0)+
7314       6.0f*t*(y3-2.0f*y2+y1);
7315 
7316     immutable float dtx =
7317       3.0f*omt2*(x1-x0)+
7318       6.0f*omt*t*(x2-x1)+
7319       3.0f*t2*(x3-x2);
7320 
7321     immutable float ddtx =
7322       6.0f*omt*(x2-2.0f*x1+x0)+
7323       6.0f*t*(x3-2.0f*x2+x1);
7324 
7325     immutable float errorx = tx-x;
7326     immutable float errory = ty-y;
7327 
7328     immutable float n = errorx*dtx+errory*dty;
7329     if (n == 0) break;
7330 
7331     immutable float d = dtx*dtx+dty*dty+errorx*ddtx+errory*ddty;
7332     if (d != 0) t = t-n/d; else break;
7333   }
7334 
7335   t = nvg__max(0, nvg__min(1.0, t));
7336   *ot = t;
7337   {
7338     immutable float omt = 1-t;
7339     immutable float omt2 = omt*omt;
7340     immutable float t2 = t*t;
7341     immutable float omt3 = omt2*omt;
7342     immutable float t3 = t2*t;
7343 
7344     immutable float ty =
7345       y0*omt3+
7346       y1*3.0f*omt2*t+
7347       y2*3.0f*omt*t2+
7348       y3*t3;
7349 
7350     immutable float tx =
7351       x0*omt3+
7352       x1*3.0f*omt2*t+
7353       x2*3.0f*omt*t2+
7354       x3*t3;
7355 
7356     closest[0] = tx;
7357     closest[1] = ty;
7358   }
7359 }
7360 
7361 // Returns:
7362 //  1  If (x,y) is contained by the stroke of the path
7363 //  0  If (x,y) is not contained by the path.
7364 int nvg__pickSubPathStroke (const NVGpickScene* ps, const NVGpickSubPath* psp, float x, float y, float strokeWidth, int lineCap, int lineJoin) {
7365   if (!nvg__pointInBounds(x, y, psp.bounds)) return 0;
7366   if (psp.firstSegment == -1) return 0;
7367 
7368   float[2] closest = void;
7369   float[2] d = void;
7370   float t = void;
7371 
7372   // trace a line from x,y out along the positive x axis and count the number of intersections
7373   int nsegments = psp.nsegments;
7374   const(NVGsegment)* seg = ps.segments+psp.firstSegment;
7375   const(NVGsegment)* prevseg = (psp.closed ? &ps.segments[psp.firstSegment+nsegments-1] : null);
7376   immutable float strokeWidthSqd = strokeWidth*strokeWidth;
7377 
7378   for (int s = 0; s < nsegments; ++s, prevseg = seg, ++seg) {
7379     if (nvg__pointInBounds(x, y, seg.bounds)) {
7380       // Line potentially hits stroke.
7381       switch (seg.type) {
7382         case Command.LineTo:
7383           nvg__closestLine(&ps.points[seg.firstPoint*2], x, y, closest.ptr, &t);
7384           break;
7385         case Command.BezierTo:
7386           nvg__closestBezier(&ps.points[seg.firstPoint*2], x, y, closest.ptr, &t);
7387           break;
7388         default:
7389           continue;
7390       }
7391 
7392       d.ptr[0] = x-closest.ptr[0];
7393       d.ptr[1] = y-closest.ptr[1];
7394 
7395       if ((t >= NVGPickEPS && t <= 1.0f-NVGPickEPS) ||
7396           (seg.flags&(NVGSegmentFlags.Corner|NVGSegmentFlags.Cap|NVGSegmentFlags.Endcap)) == 0 ||
7397           (lineJoin == NVGLineCap.Round))
7398       {
7399         // Closest point is in the middle of the line/curve, at a rounded join/cap
7400         // or at a smooth join
7401         immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
7402         if (distSqd < strokeWidthSqd) return 1;
7403       } else if ((t > 1.0f-NVGPickEPS && (seg.flags&NVGSegmentFlags.Endcap)) ||
7404                  (t < NVGPickEPS && (seg.flags&NVGSegmentFlags.Cap))) {
7405         switch (lineCap) {
7406           case NVGLineCap.Butt:
7407             immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
7408             immutable float dirD = (t < NVGPickEPS ?
7409               -(d.ptr[0]*seg.startDir.ptr[0]+d.ptr[1]*seg.startDir.ptr[1]) :
7410                 d.ptr[0]*seg.endDir.ptr[0]+d.ptr[1]*seg.endDir.ptr[1]);
7411             if (dirD < -NVGPickEPS && distSqd < strokeWidthSqd) return 1;
7412             break;
7413           case NVGLineCap.Square:
7414             if (nvg__absf(d.ptr[0]) < strokeWidth && nvg__absf(d.ptr[1]) < strokeWidth) return 1;
7415             break;
7416           case NVGLineCap.Round:
7417             immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
7418             if (distSqd < strokeWidthSqd) return 1;
7419             break;
7420           default:
7421             break;
7422         }
7423       } else if (seg.flags&NVGSegmentFlags.Corner) {
7424         // Closest point is at a corner
7425         const(NVGsegment)* seg0, seg1;
7426 
7427         if (t < NVGPickEPS) {
7428           seg0 = prevseg;
7429           seg1 = seg;
7430         } else {
7431           seg0 = seg;
7432           seg1 = (s == nsegments-1 ? &ps.segments[psp.firstSegment] : seg+1);
7433         }
7434 
7435         if (!(seg1.flags&NVGSegmentFlags.Bevel)) {
7436           immutable float prevNDist = -seg0.endDir.ptr[1]*d.ptr[0]+seg0.endDir.ptr[0]*d.ptr[1];
7437           immutable float curNDist = seg1.startDir.ptr[1]*d.ptr[0]-seg1.startDir.ptr[0]*d.ptr[1];
7438           if (nvg__absf(prevNDist) < strokeWidth && nvg__absf(curNDist) < strokeWidth) return 1;
7439         } else {
7440           d.ptr[0] -= -seg1.startDir.ptr[1]*strokeWidth;
7441           d.ptr[1] -= +seg1.startDir.ptr[0]*strokeWidth;
7442           if (seg1.miterDir.ptr[0]*d.ptr[0]+seg1.miterDir.ptr[1]*d.ptr[1] < 0) return 1;
7443         }
7444       }
7445     }
7446   }
7447 
7448   return 0;
7449 }
7450 
7451 // Returns:
7452 //   1  If (x,y) is contained by the path and the path is solid.
7453 //  -1  If (x,y) is contained by the path and the path is a hole.
7454 //   0  If (x,y) is not contained by the path.
7455 int nvg__pickSubPath (const NVGpickScene* ps, const NVGpickSubPath* psp, float x, float y, bool evenOddMode) {
7456   if (!nvg__pointInBounds(x, y, psp.bounds)) return 0;
7457   if (psp.firstSegment == -1) return 0;
7458 
7459   const(NVGsegment)* seg = &ps.segments[psp.firstSegment];
7460   int nsegments = psp.nsegments;
7461   int nintersections = 0;
7462 
7463   // trace a line from x,y out along the positive x axis and count the number of intersections
7464   for (int s = 0; s < nsegments; ++s, ++seg) {
7465     if ((seg.bounds.ptr[1]-NVGPickEPS) < y &&
7466         (seg.bounds.ptr[3]-NVGPickEPS) > y &&
7467         seg.bounds.ptr[2] > x)
7468     {
7469       // Line hits the box.
7470       switch (seg.type) {
7471         case Command.LineTo:
7472           if (seg.bounds.ptr[0] > x) {
7473             // line originates outside the box
7474             ++nintersections;
7475           } else {
7476             // line originates inside the box
7477             nintersections += nvg__intersectLine(&ps.points[seg.firstPoint*2], x, y);
7478           }
7479           break;
7480         case Command.BezierTo:
7481           if (seg.bounds.ptr[0] > x) {
7482             // line originates outside the box
7483             ++nintersections;
7484           } else {
7485             // line originates inside the box
7486             nintersections += nvg__intersectBezier(&ps.points[seg.firstPoint*2], x, y);
7487           }
7488           break;
7489         default:
7490           break;
7491       }
7492     }
7493   }
7494 
7495   if (evenOddMode) {
7496     return nintersections;
7497   } else {
7498     return (nintersections&1 ? (psp.winding == NVGSolidity.Solid ? 1 : -1) : 0);
7499   }
7500 }
7501 
7502 bool nvg__pickPath (const(NVGpickScene)* ps, const(NVGpickPath)* pp, float x, float y) {
7503   int pickCount = 0;
7504   const(NVGpickSubPath)* psp = pp.subPaths;
7505   while (psp !is null) {
7506     pickCount += nvg__pickSubPath(ps, psp, x, y, pp.evenOddMode);
7507     psp = psp.next;
7508   }
7509   return ((pp.evenOddMode ? pickCount&1 : pickCount) != 0);
7510 }
7511 
7512 bool nvg__pickPathStroke (const(NVGpickScene)* ps, const(NVGpickPath)* pp, float x, float y) {
7513   const(NVGpickSubPath)* psp = pp.subPaths;
7514   while (psp !is null) {
7515     if (nvg__pickSubPathStroke(ps, psp, x, y, pp.strokeWidth, pp.lineCap, pp.lineJoin)) return true;
7516     psp = psp.next;
7517   }
7518   return false;
7519 }
7520 
7521 bool nvg__pickPathTestBounds (NVGContext ctx, const NVGpickScene* ps, const NVGpickPath* pp, float x, float y) {
7522   if (nvg__pointInBounds(x, y, pp.bounds)) {
7523     //{ import core.stdc.stdio; printf("  (0): in bounds!\n"); }
7524     if (pp.flags&NVGPathFlags.Scissor) {
7525       const(float)* scissor = &ps.points[pp.scissor*2];
7526       // untransform scissor translation
7527       float stx = void, sty = void;
7528       ctx.gpuUntransformPoint(&stx, &sty, scissor[4], scissor[5]);
7529       immutable float rx = x-stx;
7530       immutable float ry = y-sty;
7531       //{ import core.stdc.stdio; printf("  (1): rxy=(%g,%g); scissor=[%g,%g,%g,%g,%g] [%g,%g]!\n", rx, ry, scissor[0], scissor[1], scissor[2], scissor[3], scissor[4], scissor[5], scissor[6], scissor[7]); }
7532       if (nvg__absf((scissor[0]*rx)+(scissor[1]*ry)) > scissor[6] ||
7533           nvg__absf((scissor[2]*rx)+(scissor[3]*ry)) > scissor[7])
7534       {
7535         //{ import core.stdc.stdio; printf("    (1): scissor reject!\n"); }
7536         return false;
7537       }
7538     }
7539     return true;
7540   }
7541   return false;
7542 }
7543 
7544 int nvg__countBitsUsed (uint v) pure {
7545   pragma(inline, true);
7546   import core.bitop : bsr;
7547   return (v != 0 ? bsr(v)+1 : 0);
7548 }
7549 
7550 void nvg__pickSceneInsert (NVGpickScene* ps, NVGpickPath* pp) {
7551   if (ps is null || pp is null) return;
7552 
7553   int[4] cellbounds;
7554   int base = ps.nlevels-1;
7555   int level;
7556   int levelwidth;
7557   int levelshift;
7558   int levelx;
7559   int levely;
7560   NVGpickPath** cell = null;
7561 
7562   // Bit tricks for inserting into an implicit quadtree.
7563 
7564   // Calc bounds of path in cells at the lowest level
7565   cellbounds.ptr[0] = cast(int)(pp.bounds.ptr[0]/ps.xdim);
7566   cellbounds.ptr[1] = cast(int)(pp.bounds.ptr[1]/ps.ydim);
7567   cellbounds.ptr[2] = cast(int)(pp.bounds.ptr[2]/ps.xdim);
7568   cellbounds.ptr[3] = cast(int)(pp.bounds.ptr[3]/ps.ydim);
7569 
7570   // Find which bits differ between the min/max x/y coords
7571   cellbounds.ptr[0] ^= cellbounds.ptr[2];
7572   cellbounds.ptr[1] ^= cellbounds.ptr[3];
7573 
7574   // Use the number of bits used (countBitsUsed(x) == sizeof(int) * 8 - clz(x);
7575   // to calculate the level to insert at (the level at which the bounds fit in a single cell)
7576   level = nvg__min(base-nvg__countBitsUsed(cellbounds.ptr[0]), base-nvg__countBitsUsed(cellbounds.ptr[1]));
7577   if (level < 0) level = 0;
7578   //{ import core.stdc.stdio; printf("LEVEL: %d; bounds=(%g,%g)-(%g,%g)\n", level, pp.bounds[0], pp.bounds[1], pp.bounds[2], pp.bounds[3]); }
7579   //level = 0;
7580 
7581   // Find the correct cell in the chosen level, clamping to the edges.
7582   levelwidth = 1<<level;
7583   levelshift = (ps.nlevels-level)-1;
7584   levelx = nvg__clamp(cellbounds.ptr[2]>>levelshift, 0, levelwidth-1);
7585   levely = nvg__clamp(cellbounds.ptr[3]>>levelshift, 0, levelwidth-1);
7586 
7587   // Insert the path into the linked list at that cell.
7588   cell = &ps.levels[level][levely*levelwidth+levelx];
7589 
7590   pp.cellnext = *cell;
7591   *cell = pp;
7592 
7593   if (ps.paths is null) ps.lastPath = pp;
7594   pp.next = ps.paths;
7595   ps.paths = pp;
7596 
7597   // Store the order (depth) of the path for picking ops.
7598   pp.order = cast(short)ps.npaths;
7599   ++ps.npaths;
7600 }
7601 
7602 void nvg__pickBeginFrame (NVGContext ctx, int width, int height) {
7603   NVGpickScene* ps = nvg__pickSceneGet(ctx);
7604 
7605   //NVG_PICK_DEBUG_NEWFRAME();
7606 
7607   // Return all paths & sub paths from last frame to the free list
7608   while (ps.paths !is null) {
7609     NVGpickPath* pp = ps.paths.next;
7610     nvg__freePickPath(ps, ps.paths);
7611     ps.paths = pp;
7612   }
7613 
7614   ps.paths = null;
7615   ps.npaths = 0;
7616 
7617   // Store the screen metrics for the quadtree
7618   ps.width = width;
7619   ps.height = height;
7620 
7621   immutable float lowestSubDiv = cast(float)(1<<(ps.nlevels-1));
7622   ps.xdim = cast(float)width/lowestSubDiv;
7623   ps.ydim = cast(float)height/lowestSubDiv;
7624 
7625   // Allocate the quadtree if required.
7626   if (ps.levels is null) {
7627     int ncells = 1;
7628 
7629     ps.levels = cast(NVGpickPath***)malloc((NVGpickPath**).sizeof*ps.nlevels);
7630     for (int l = 0; l < ps.nlevels; ++l) {
7631       int leveldim = 1<<l;
7632       ncells += leveldim*leveldim;
7633     }
7634 
7635     ps.levels[0] = cast(NVGpickPath**)malloc((NVGpickPath*).sizeof*ncells);
7636 
7637     int cell = 1;
7638     for (int l = 1; l < ps.nlevels; ++l) {
7639       ps.levels[l] = &ps.levels[0][cell];
7640       int leveldim = 1<<l;
7641       cell += leveldim*leveldim;
7642     }
7643 
7644     ps.ncells = ncells;
7645   }
7646   memset(ps.levels[0], 0, ps.ncells*(NVGpickPath*).sizeof);
7647 
7648   // Allocate temporary storage for nvgHitTestAll results if required.
7649   if (ps.picked is null) {
7650     ps.cpicked = 16;
7651     ps.picked = cast(NVGpickPath**)malloc((NVGpickPath*).sizeof*ps.cpicked);
7652   }
7653 
7654   ps.npoints = 0;
7655   ps.nsegments = 0;
7656 }
7657 } // nothrow @trusted @nogc
7658 
7659 
7660 /// Return outline of the current path. Returned outline is not flattened.
7661 /// Group: paths
7662 public NVGPathOutline getCurrPathOutline (NVGContext ctx) nothrow @trusted @nogc {
7663   if (ctx is null || !ctx.contextAlive || ctx.ncommands == 0) return NVGPathOutline.init;
7664 
7665   auto res = NVGPathOutline.createNew();
7666 
7667   const(float)[] acommands = ctx.commands[0..ctx.ncommands];
7668   int ncommands = cast(int)acommands.length;
7669   const(float)* commands = acommands.ptr;
7670 
7671   float cx = 0, cy = 0;
7672   float[2] start = void;
7673   float[4] totalBounds = [float.max, float.max, -float.max, -float.max];
7674   float[8] bcp = void; // bezier curve points; used to calculate bounds
7675 
7676   void addToBounds (in float x, in float y) nothrow @trusted @nogc {
7677     totalBounds.ptr[0] = nvg__min(totalBounds.ptr[0], x);
7678     totalBounds.ptr[1] = nvg__min(totalBounds.ptr[1], y);
7679     totalBounds.ptr[2] = nvg__max(totalBounds.ptr[2], x);
7680     totalBounds.ptr[3] = nvg__max(totalBounds.ptr[3], y);
7681   }
7682 
7683   bool hasPoints = false;
7684 
7685   void closeIt () nothrow @trusted @nogc {
7686     if (!hasPoints) return;
7687     if (cx != start.ptr[0] || cy != start.ptr[1]) {
7688       res.ds.putCommand(NVGPathOutline.Command.Kind.LineTo);
7689       res.ds.putArgs(start[]);
7690       cx = start.ptr[0];
7691       cy = start.ptr[1];
7692       addToBounds(cx, cy);
7693     }
7694   }
7695 
7696   int i = 0;
7697   while (i < ncommands) {
7698     int cmd = cast(int)commands[i++];
7699     switch (cmd) {
7700       case Command.MoveTo: // one coordinate pair
7701         const(float)* tfxy = commands+i;
7702         i += 2;
7703         // add command
7704         res.ds.putCommand(NVGPathOutline.Command.Kind.MoveTo);
7705         res.ds.putArgs(tfxy[0..2]);
7706         // new starting point
7707         start.ptr[0..2] = tfxy[0..2];
7708         cx = tfxy[0];
7709         cy = tfxy[0];
7710         addToBounds(cx, cy);
7711         hasPoints = true;
7712         break;
7713       case Command.LineTo: // one coordinate pair
7714         const(float)* tfxy = commands+i;
7715         i += 2;
7716         // add command
7717         res.ds.putCommand(NVGPathOutline.Command.Kind.LineTo);
7718         res.ds.putArgs(tfxy[0..2]);
7719         cx = tfxy[0];
7720         cy = tfxy[0];
7721         addToBounds(cx, cy);
7722         hasPoints = true;
7723         break;
7724       case Command.BezierTo: // three coordinate pairs
7725         const(float)* tfxy = commands+i;
7726         i += 3*2;
7727         // add command
7728         res.ds.putCommand(NVGPathOutline.Command.Kind.BezierTo);
7729         res.ds.putArgs(tfxy[0..6]);
7730         // bounds
7731         bcp.ptr[0] = cx;
7732         bcp.ptr[1] = cy;
7733         bcp.ptr[2..8] = tfxy[0..6];
7734         nvg__bezierBounds(bcp.ptr, totalBounds);
7735         cx = tfxy[4];
7736         cy = tfxy[5];
7737         hasPoints = true;
7738         break;
7739       case Command.Close:
7740         closeIt();
7741         hasPoints = false;
7742         break;
7743       case Command.Winding:
7744         //psp.winding = cast(short)cast(int)commands[i];
7745         i += 1;
7746         break;
7747       default:
7748         break;
7749     }
7750   }
7751 
7752   res.ds.bounds[] = totalBounds[];
7753   return res;
7754 }
7755 
7756 
7757 // ////////////////////////////////////////////////////////////////////////// //
7758 // Text
7759 
7760 /** Creates font by loading it from the disk from specified file name.
7761  * Returns handle to the font or FONS_INVALID (aka -1) on error.
7762  * Use "fontname:noaa" as [name] to turn off antialiasing (if font driver supports that).
7763  *
7764  * On POSIX systems it is possible to use fontconfig font names too.
7765  * `:noaa` in font path is still allowed, but it must be the last option.
7766  *
7767  * Group: text_api
7768  */
7769 public int createFont (NVGContext ctx, const(char)[] name, const(char)[] path) nothrow @trusted {
7770   return ctx.fs.addFont(name, path, ctx.params.fontAA);
7771 }
7772 
7773 /** Creates font by loading it from the specified memory chunk.
7774  * Returns handle to the font or FONS_INVALID (aka -1) on error.
7775  * Won't free data on error.
7776  *
7777  * Group: text_api
7778  */
7779 public int createFontMem (NVGContext ctx, const(char)[] name, ubyte* data, int ndata, bool freeData) nothrow @trusted @nogc {
7780   return ctx.fs.addFontMem(name, data, ndata, freeData, ctx.params.fontAA);
7781 }
7782 
7783 /// Add fonts from another context.
7784 /// This is more effective than reloading fonts, 'cause font data will be shared.
7785 /// Group: text_api
7786 public void addFontsFrom (NVGContext ctx, NVGContext source) nothrow @trusted @nogc {
7787   if (ctx is null || source is null) return;
7788   ctx.fs.addFontsFrom(source.fs);
7789 }
7790 
7791 /// Finds a loaded font of specified name, and returns handle to it, or FONS_INVALID (aka -1) if the font is not found.
7792 /// Group: text_api
7793 public int findFont (NVGContext ctx, const(char)[] name) nothrow @trusted @nogc {
7794   pragma(inline, true);
7795   return (name.length == 0 ? FONS_INVALID : ctx.fs.getFontByName(name));
7796 }
7797 
7798 /// Sets the font size of current text style.
7799 /// Group: text_api
7800 public void fontSize (NVGContext ctx, float size) nothrow @trusted @nogc {
7801   pragma(inline, true);
7802   nvg__getState(ctx).fontSize = size;
7803 }
7804 
7805 /// Gets the font size of current text style.
7806 /// Group: text_api
7807 public float fontSize (NVGContext ctx) nothrow @trusted @nogc {
7808   pragma(inline, true);
7809   return nvg__getState(ctx).fontSize;
7810 }
7811 
7812 /// Sets the blur of current text style.
7813 /// Group: text_api
7814 public void fontBlur (NVGContext ctx, float blur) nothrow @trusted @nogc {
7815   pragma(inline, true);
7816   nvg__getState(ctx).fontBlur = blur;
7817 }
7818 
7819 /// Gets the blur of current text style.
7820 /// Group: text_api
7821 public float fontBlur (NVGContext ctx) nothrow @trusted @nogc {
7822   pragma(inline, true);
7823   return nvg__getState(ctx).fontBlur;
7824 }
7825 
7826 /// Sets the letter spacing of current text style.
7827 /// Group: text_api
7828 public void textLetterSpacing (NVGContext ctx, float spacing) nothrow @trusted @nogc {
7829   pragma(inline, true);
7830   nvg__getState(ctx).letterSpacing = spacing;
7831 }
7832 
7833 /// Gets the letter spacing of current text style.
7834 /// Group: text_api
7835 public float textLetterSpacing (NVGContext ctx) nothrow @trusted @nogc {
7836   pragma(inline, true);
7837   return nvg__getState(ctx).letterSpacing;
7838 }
7839 
7840 /// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
7841 /// Group: text_api
7842 public void textLineHeight (NVGContext ctx, float lineHeight) nothrow @trusted @nogc {
7843   pragma(inline, true);
7844   nvg__getState(ctx).lineHeight = lineHeight;
7845 }
7846 
7847 /// Gets the proportional line height of current text style. The line height is specified as multiple of font size.
7848 /// Group: text_api
7849 public float textLineHeight (NVGContext ctx) nothrow @trusted @nogc {
7850   pragma(inline, true);
7851   return nvg__getState(ctx).lineHeight;
7852 }
7853 
7854 /// Sets the text align of current text style, see [NVGTextAlign] for options.
7855 /// Group: text_api
7856 public void textAlign (NVGContext ctx, NVGTextAlign talign) nothrow @trusted @nogc {
7857   pragma(inline, true);
7858   nvg__getState(ctx).textAlign = talign;
7859 }
7860 
7861 /// Ditto.
7862 public void textAlign (NVGContext ctx, NVGTextAlign.H h) nothrow @trusted @nogc {
7863   pragma(inline, true);
7864   nvg__getState(ctx).textAlign.horizontal = h;
7865 }
7866 
7867 /// Ditto.
7868 public void textAlign (NVGContext ctx, NVGTextAlign.V v) nothrow @trusted @nogc {
7869   pragma(inline, true);
7870   nvg__getState(ctx).textAlign.vertical = v;
7871 }
7872 
7873 /// Ditto.
7874 public void textAlign (NVGContext ctx, NVGTextAlign.H h, NVGTextAlign.V v) nothrow @trusted @nogc {
7875   pragma(inline, true);
7876   nvg__getState(ctx).textAlign.reset(h, v);
7877 }
7878 
7879 /// Ditto.
7880 public void textAlign (NVGContext ctx, NVGTextAlign.V v, NVGTextAlign.H h) nothrow @trusted @nogc {
7881   pragma(inline, true);
7882   nvg__getState(ctx).textAlign.reset(h, v);
7883 }
7884 
7885 /// Gets the text align of current text style, see [NVGTextAlign] for options.
7886 /// Group: text_api
7887 public NVGTextAlign textAlign (NVGContext ctx) nothrow @trusted @nogc {
7888   pragma(inline, true);
7889   return nvg__getState(ctx).textAlign;
7890 }
7891 
7892 /// Sets the font face based on specified id of current text style.
7893 /// Group: text_api
7894 public void fontFaceId (NVGContext ctx, int font) nothrow @trusted @nogc {
7895   pragma(inline, true);
7896   nvg__getState(ctx).fontId = font;
7897 }
7898 
7899 /// Gets the font face based on specified id of current text style.
7900 /// Group: text_api
7901 public int fontFaceId (NVGContext ctx) nothrow @trusted @nogc {
7902   pragma(inline, true);
7903   return nvg__getState(ctx).fontId;
7904 }
7905 
7906 /** Sets the font face based on specified name of current text style.
7907  *
7908  * The underlying implementation is using O(1) data structure to lookup
7909  * font names, so you probably should use this function instead of [fontFaceId]
7910  * to make your code more robust and less error-prone.
7911  *
7912  * Group: text_api
7913  */
7914 public void fontFace (NVGContext ctx, const(char)[] font) nothrow @trusted @nogc {
7915   pragma(inline, true);
7916   nvg__getState(ctx).fontId = ctx.fs.getFontByName(font);
7917 }
7918 
7919 static if (is(typeof(&fons__nvg__toPath))) {
7920   public enum NanoVegaHasCharToPath = true; ///
7921 } else {
7922   public enum NanoVegaHasCharToPath = false; ///
7923 }
7924 
7925 /// Adds glyph outlines to the current path. Vertical 0 is baseline.
7926 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
7927 /// Returns `false` if there is no such glyph, or current font is not scalable.
7928 /// Group: text_api
7929 public bool charToPath (NVGContext ctx, dchar dch, float[] bounds=null) nothrow @trusted @nogc {
7930   NVGstate* state = nvg__getState(ctx);
7931   ctx.fs.fontId = state.fontId;
7932   return ctx.fs.toPath(ctx, dch, bounds);
7933 }
7934 
7935 static if (is(typeof(&fons__nvg__bounds))) {
7936   public enum NanoVegaHasCharPathBounds = true; ///
7937 } else {
7938   public enum NanoVegaHasCharPathBounds = false; ///
7939 }
7940 
7941 /// Returns bounds of the glyph outlines. Vertical 0 is baseline.
7942 /// The glyph is not scaled in any way.
7943 /// Returns `false` if there is no such glyph, or current font is not scalable.
7944 /// Group: text_api
7945 public bool charPathBounds (NVGContext ctx, dchar dch, float[] bounds) nothrow @trusted @nogc {
7946   NVGstate* state = nvg__getState(ctx);
7947   ctx.fs.fontId = state.fontId;
7948   return ctx.fs.getPathBounds(dch, bounds);
7949 }
7950 
7951 /** [charOutline] will return [NVGPathOutline].
7952 
7953  some usage samples:
7954 
7955  ---
7956     float[4] bounds = void;
7957 
7958     nvg.scale(0.5, 0.5);
7959     nvg.translate(500, 800);
7960     nvg.evenOddFill;
7961 
7962     nvg.newPath();
7963     nvg.charToPath('&', bounds[]);
7964     conwriteln(bounds[]);
7965     nvg.fillPaint(nvg.linearGradient(0, 0, 600, 600, NVGColor("#f70"), NVGColor("#ff0")));
7966     nvg.strokeColor(NVGColor("#0f0"));
7967     nvg.strokeWidth = 3;
7968     nvg.fill();
7969     nvg.stroke();
7970     // glyph bounds
7971     nvg.newPath();
7972     nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7973     nvg.strokeColor(NVGColor("#00f"));
7974     nvg.stroke();
7975 
7976     nvg.newPath();
7977     nvg.charToPath('g', bounds[]);
7978     conwriteln(bounds[]);
7979     nvg.fill();
7980     nvg.strokeColor(NVGColor("#0f0"));
7981     nvg.stroke();
7982     // glyph bounds
7983     nvg.newPath();
7984     nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7985     nvg.strokeColor(NVGColor("#00f"));
7986     nvg.stroke();
7987 
7988     nvg.newPath();
7989     nvg.moveTo(0, 0);
7990     nvg.lineTo(600, 0);
7991     nvg.strokeColor(NVGColor("#0ff"));
7992     nvg.stroke();
7993 
7994     if (auto ol = nvg.charOutline('Q')) {
7995       scope(exit) ol.kill();
7996       nvg.newPath();
7997       conwriteln("==== length: ", ol.length, " ====");
7998       foreach (const ref cmd; ol.commands) {
7999         //conwriteln("  ", cmd.code, ": ", cmd.args[]);
8000         assert(cmd.valid);
8001         final switch (cmd.code) {
8002           case cmd.Kind.MoveTo: nvg.moveTo(cmd.args[0], cmd.args[1]); break;
8003           case cmd.Kind.LineTo: nvg.lineTo(cmd.args[0], cmd.args[1]); break;
8004           case cmd.Kind.QuadTo: nvg.quadTo(cmd.args[0], cmd.args[1], cmd.args[2], cmd.args[3]); break;
8005           case cmd.Kind.BezierTo: nvg.bezierTo(cmd.args[0], cmd.args[1], cmd.args[2], cmd.args[3], cmd.args[4], cmd.args[5]); break;
8006         }
8007       }
8008       nvg.strokeColor(NVGColor("#f00"));
8009       nvg.stroke();
8010     }
8011  ---
8012 
8013  Group: text_api
8014  */
8015 public struct NVGPathOutline {
8016 private nothrow @trusted @nogc:
8017   struct DataStore {
8018     uint rc; // refcount
8019     ubyte* data;
8020     uint used;
8021     uint size;
8022     uint ccount; // number of commands
8023     float[4] bounds = 0; /// outline bounds
8024   nothrow @trusted @nogc:
8025     void putBytes (const(void)[] b) {
8026       if (b.length == 0) return;
8027       if (b.length >= int.max/8) assert(0, "NanoVega: out of memory");
8028       if (int.max/8-used < b.length) assert(0, "NanoVega: out of memory");
8029       if (used+cast(uint)b.length > size) {
8030         import core.stdc.stdlib : realloc;
8031         uint newsz = size;
8032         while (newsz < used+cast(uint)b.length) newsz = (newsz == 0 ? 1024 : newsz < 32768 ? newsz*2 : newsz+8192);
8033         assert(used+cast(uint)b.length <= newsz);
8034         data = cast(ubyte*)realloc(data, newsz);
8035         if (data is null) assert(0, "NanoVega: out of memory");
8036         size = newsz;
8037       }
8038       import core.stdc.string : memcpy;
8039       memcpy(data+used, b.ptr, b.length);
8040       used += cast(uint)b.length;
8041     }
8042     void putCommand (ubyte cmd) { pragma(inline, true); ++ccount; putBytes((&cmd)[0..1]); }
8043     void putArgs (const(float)[] f...) { pragma(inline, true); putBytes(f[]); }
8044   }
8045 
8046   static void incRef (DataStore* ds) {
8047     pragma(inline, true);
8048     if (ds !is null) {
8049       ++ds.rc;
8050       //{ import core.stdc.stdio; printf("ods(%p): incref: newrc=%u\n", ds, ds.rc); }
8051     }
8052   }
8053 
8054   static void decRef (DataStore* ds) {
8055     version(aliced) pragma(inline, true);
8056     if (ds !is null) {
8057       //{ import core.stdc.stdio; printf("ods(%p): decref: newrc=%u\n", ds, ds.rc-1); }
8058       if (--ds.rc == 0) {
8059         import core.stdc.stdlib : free;
8060         import core.stdc.string : memset;
8061         if (ds.data !is null) free(ds.data);
8062         memset(ds, 0, DataStore.sizeof); // just in case
8063         free(ds);
8064         //{ import core.stdc.stdio; printf("  ods(%p): killed.\n"); }
8065       }
8066     }
8067   }
8068 
8069 private:
8070   static NVGPathOutline createNew () {
8071     import core.stdc.stdlib : malloc;
8072     import core.stdc.string : memset;
8073     auto ds = cast(DataStore*)malloc(DataStore.sizeof);
8074     if (ds is null) assert(0, "NanoVega: out of memory");
8075     memset(ds, 0, DataStore.sizeof);
8076     ds.rc = 1;
8077     NVGPathOutline res;
8078     res.dsaddr = cast(usize)ds;
8079     return res;
8080   }
8081 
8082 private:
8083   usize dsaddr; // fool GC
8084 
8085   @property inout(DataStore)* ds () inout pure { pragma(inline, true); return cast(DataStore*)dsaddr; }
8086 
8087 public:
8088   /// commands
8089   static struct Command {
8090     ///
8091     enum Kind : ubyte {
8092       MoveTo, ///
8093       LineTo, ///
8094       QuadTo, ///
8095       BezierTo, ///
8096       End, /// no more commands (this command is not `valid`!)
8097 
8098     }
8099     Kind code; ///
8100     const(float)[] args; ///
8101     @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (code >= Kind.min && code < Kind.End && args.length >= 2); } ///
8102 
8103     static uint arglen (Kind code) pure nothrow @safe @nogc {
8104       pragma(inline, true);
8105       return
8106         code == Kind.MoveTo || code == Kind.LineTo ? 2 :
8107         code == Kind.QuadTo ? 4 :
8108         code == Kind.BezierTo ? 6 :
8109         0;
8110     }
8111 
8112     /// perform NanoVega command with stored data.
8113     void perform (NVGContext ctx) const nothrow @trusted @nogc {
8114       if (ctx is null) return;
8115       final switch (code) {
8116         case Kind.MoveTo: if (args.length > 1) ctx.moveTo(args.ptr[0..2]); break;
8117         case Kind.LineTo: if (args.length > 1) ctx.lineTo(args.ptr[0..2]); break;
8118         case Kind.QuadTo: if (args.length > 3) ctx.quadTo(args.ptr[0..4]); break;
8119         case Kind.BezierTo: if (args.length > 5) ctx.bezierTo(args.ptr[0..6]); break;
8120         case Kind.End: break;
8121       }
8122     }
8123 
8124     /// perform NanoVega command with stored data, transforming points with [xform] transformation matrix.
8125     void perform() (NVGContext ctx, in auto ref NVGMatrix xform) const nothrow @trusted @nogc {
8126       if (ctx is null || !valid) return;
8127       float[6] pts = void;
8128       pts[0..args.length] = args[];
8129       foreach (immutable pidx; 0..args.length/2) xform.point(pts.ptr[pidx*2+0], pts.ptr[pidx*2+1]);
8130       final switch (code) {
8131         case Kind.MoveTo: if (args.length > 1) ctx.moveTo(pts.ptr[0..2]); break;
8132         case Kind.LineTo: if (args.length > 1) ctx.lineTo(pts.ptr[0..2]); break;
8133         case Kind.QuadTo: if (args.length > 3) ctx.quadTo(pts.ptr[0..4]); break;
8134         case Kind.BezierTo: if (args.length > 5) ctx.bezierTo(pts.ptr[0..6]); break;
8135         case Kind.End: break;
8136       }
8137     }
8138   }
8139 
8140 public:
8141   /// Create new path with quadratic bezier (first command is MoveTo, second command is QuadTo).
8142   static NVGPathOutline createNewQuad (in float x0, in float y0, in float cx, in float cy, in float x, in float y) {
8143     auto res = createNew();
8144     res.ds.putCommand(Command.Kind.MoveTo);
8145     res.ds.putArgs(x0, y0);
8146     res.ds.putCommand(Command.Kind.QuadTo);
8147     res.ds.putArgs(cx, cy, x, y);
8148     return res;
8149   }
8150 
8151   /// Create new path with cubic bezier (first command is MoveTo, second command is BezierTo).
8152   static NVGPathOutline createNewBezier (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
8153     auto res = createNew();
8154     res.ds.putCommand(Command.Kind.MoveTo);
8155     res.ds.putArgs(x1, y1);
8156     res.ds.putCommand(Command.Kind.BezierTo);
8157     res.ds.putArgs(x2, y2, x3, y3, x4, y4);
8158     return res;
8159   }
8160 
8161 public:
8162   this (this) { pragma(inline, true); incRef(cast(DataStore*)dsaddr); }
8163   ~this () { pragma(inline, true); decRef(cast(DataStore*)dsaddr); }
8164 
8165   void opAssign() (in auto ref NVGPathOutline a) {
8166     incRef(cast(DataStore*)a.dsaddr);
8167     decRef(cast(DataStore*)dsaddr);
8168     dsaddr = a.dsaddr;
8169   }
8170 
8171   /// Clear storage.
8172   void clear () {
8173     pragma(inline, true);
8174     decRef(ds);
8175     dsaddr = 0;
8176   }
8177 
8178   /// Is this outline empty?
8179   @property empty () const pure { pragma(inline, true); return (dsaddr == 0 || ds.ccount == 0); }
8180 
8181   /// Returns number of commands in outline.
8182   @property int length () const pure { pragma(inline, true); return (dsaddr ? ds.ccount : 0); }
8183 
8184   /// Returns "flattened" path. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8185   NVGPathOutline flatten () const { pragma(inline, true); return flattenInternal(null); }
8186 
8187   /// Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8188   NVGPathOutline flatten() (in auto ref NVGMatrix mt) const { pragma(inline, true); return flattenInternal(&mt); }
8189 
8190   // Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8191   private NVGPathOutline flattenInternal (scope NVGMatrix* tfm) const {
8192     import core.stdc.string : memset;
8193 
8194     NVGPathOutline res;
8195     if (dsaddr == 0 || ds.ccount == 0) { res = this; return res; } // nothing to do
8196 
8197     // check if we need to flatten the path
8198     if (tfm is null) {
8199       bool dowork = false;
8200       foreach (const ref cs; commands) {
8201         if (cs.code != Command.Kind.MoveTo && cs.code != Command.Kind.LineTo) {
8202           dowork = true;
8203           break;
8204         }
8205       }
8206       if (!dowork) { res = this; return res; } // nothing to do
8207     }
8208 
8209     NVGcontextinternal ctx;
8210     memset(&ctx, 0, ctx.sizeof);
8211     ctx.cache = nvg__allocPathCache();
8212     scope(exit) {
8213       import core.stdc.stdlib : free;
8214       nvg__deletePathCache(ctx.cache);
8215     }
8216 
8217     ctx.tessTol = 0.25f;
8218     ctx.angleTol = 0; // 0 -- angle tolerance for McSeem Bezier rasterizer
8219     ctx.cuspLimit = 0; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
8220     ctx.distTol = 0.01f;
8221     ctx.tesselatortype = NVGTesselation.DeCasteljau;
8222 
8223     nvg__addPath(&ctx); // we need this for `nvg__addPoint()`
8224 
8225     // has some curves or transformations, convert path
8226     res = createNew();
8227     float[8] args = void;
8228 
8229     res.ds.bounds = [float.max, float.max, -float.max, -float.max];
8230 
8231     float lastX = float.max, lastY = float.max;
8232     bool lastWasMove = false;
8233 
8234     void addPoint (float x, float y, Command.Kind cmd=Command.Kind.LineTo) nothrow @trusted @nogc {
8235       if (tfm !is null) tfm.point(x, y);
8236       bool isMove = (cmd == Command.Kind.MoveTo);
8237       if (isMove) {
8238         // moveto
8239         if (lastWasMove && nvg__ptEquals(lastX, lastY, x, y, ctx.distTol)) return;
8240       } else {
8241         // lineto
8242         if (nvg__ptEquals(lastX, lastY, x, y, ctx.distTol)) return;
8243       }
8244       lastWasMove = isMove;
8245       lastX = x;
8246       lastY = y;
8247       res.ds.putCommand(cmd);
8248       res.ds.putArgs(x, y);
8249       res.ds.bounds.ptr[0] = nvg__min(res.ds.bounds.ptr[0], x);
8250       res.ds.bounds.ptr[1] = nvg__min(res.ds.bounds.ptr[1], y);
8251       res.ds.bounds.ptr[2] = nvg__max(res.ds.bounds.ptr[2], x);
8252       res.ds.bounds.ptr[3] = nvg__max(res.ds.bounds.ptr[3], y);
8253     }
8254 
8255     // sorry for this pasta
8256     void flattenBezier (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) nothrow @trusted @nogc {
8257       ctx.cache.npoints = 0;
8258       if (ctx.tesselatortype == NVGTesselation.DeCasteljau) {
8259         nvg__tesselateBezier(&ctx, x1, y1, x2, y2, x3, y3, x4, y4, 0, PointFlag.Corner);
8260       } else if (ctx.tesselatortype == NVGTesselation.DeCasteljauMcSeem) {
8261         nvg__tesselateBezierMcSeem(&ctx, x1, y1, x2, y2, x3, y3, x4, y4, 0, PointFlag.Corner);
8262       } else {
8263         nvg__tesselateBezierAFD(&ctx, x1, y1, x2, y2, x3, y3, x4, y4, PointFlag.Corner);
8264       }
8265       // add generated points
8266       foreach (const ref pt; ctx.cache.points[0..ctx.cache.npoints]) addPoint(pt.x, pt.y);
8267     }
8268 
8269     void flattenQuad (in float x0, in float y0, in float cx, in float cy, in float x, in float y) {
8270       flattenBezier(
8271         x0, y0,
8272         x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
8273         x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
8274         x, y,
8275         0,
8276       );
8277     }
8278 
8279     float cx = 0, cy = 0;
8280     foreach (const ref cs; commands) {
8281       switch (cs.code) {
8282         case Command.Kind.LineTo:
8283         case Command.Kind.MoveTo:
8284           addPoint(cs.args[0], cs.args[1], cs.code);
8285           cx = cs.args[0];
8286           cy = cs.args[1];
8287           break;
8288         case Command.Kind.QuadTo:
8289           flattenQuad(cx, cy, cs.args[0], cs.args[1], cs.args[2], cs.args[3]);
8290           cx = cs.args[2];
8291           cy = cs.args[3];
8292           break;
8293         case Command.Kind.BezierTo:
8294           flattenBezier(cx, cy, cs.args[0], cs.args[1], cs.args[2], cs.args[3], cs.args[4], cs.args[5], 0);
8295           cx = cs.args[4];
8296           cy = cs.args[5];
8297           break;
8298         default:
8299           break;
8300       }
8301     }
8302 
8303     return res;
8304   }
8305 
8306   /// Returns forward range with all glyph commands.
8307   auto commands () const nothrow @trusted @nogc {
8308     static struct Range {
8309     private nothrow @trusted @nogc:
8310       usize dsaddr;
8311       uint cpos; // current position in data
8312       uint cleft; // number of commands left
8313       @property const(ubyte)* data () inout pure { pragma(inline, true); return (dsaddr ? (cast(DataStore*)dsaddr).data : null); }
8314     public:
8315       this (this) { pragma(inline, true); incRef(cast(DataStore*)dsaddr); }
8316       ~this () { pragma(inline, true); decRef(cast(DataStore*)dsaddr); }
8317       void opAssign() (in auto ref Range a) {
8318         incRef(cast(DataStore*)a.dsaddr);
8319         decRef(cast(DataStore*)dsaddr);
8320         dsaddr = a.dsaddr;
8321         cpos = a.cpos;
8322         cleft = a.cleft;
8323       }
8324       float[4] bounds () const pure { float[4] res = 0; pragma(inline, true); if (dsaddr) res[] = (cast(DataStore*)dsaddr).bounds[]; return res; } /// outline bounds
8325       @property bool empty () const pure { pragma(inline, true); return (cleft == 0); }
8326       @property int length () const pure { pragma(inline, true); return cleft; }
8327       @property Range save () const { pragma(inline, true); Range res = this; return res; }
8328       @property Command front () const {
8329         Command res = void;
8330         if (cleft > 0) {
8331           res.code = cast(Command.Kind)data[cpos];
8332           switch (res.code) {
8333             case Command.Kind.MoveTo:
8334             case Command.Kind.LineTo:
8335               res.args = (cast(const(float*))(data+cpos+1))[0..1*2];
8336               break;
8337             case Command.Kind.QuadTo:
8338               res.args = (cast(const(float*))(data+cpos+1))[0..2*2];
8339               break;
8340             case Command.Kind.BezierTo:
8341               res.args = (cast(const(float*))(data+cpos+1))[0..3*2];
8342               break;
8343             default:
8344               res.code = Command.Kind.End;
8345               res.args = null;
8346               break;
8347           }
8348         } else {
8349           res.code = Command.Kind.End;
8350           res.args = null;
8351         }
8352         return res;
8353       }
8354       void popFront () {
8355         if (cleft <= 1) { cleft = 0; return; } // don't waste time skipping last command
8356         --cleft;
8357         switch (data[cpos]) {
8358           case Command.Kind.MoveTo:
8359           case Command.Kind.LineTo:
8360             cpos += 1+1*2*cast(uint)float.sizeof;
8361             break;
8362           case Command.Kind.QuadTo:
8363             cpos += 1+2*2*cast(uint)float.sizeof;
8364             break;
8365           case Command.Kind.BezierTo:
8366             cpos += 1+3*2*cast(uint)float.sizeof;
8367             break;
8368           default:
8369             cleft = 0;
8370             break;
8371         }
8372       }
8373     }
8374     if (dsaddr) {
8375       incRef(cast(DataStore*)dsaddr); // range anchors it
8376       return Range(dsaddr, 0, ds.ccount);
8377     } else {
8378       return Range.init;
8379     }
8380   }
8381 }
8382 
8383 public alias NVGGlyphOutline = NVGPathOutline; /// For backwards compatibility.
8384 
8385 /// Destroy glyph outiline and free allocated memory.
8386 /// Group: text_api
8387 public void kill (ref NVGPathOutline ol) nothrow @trusted @nogc {
8388   pragma(inline, true);
8389   ol.clear();
8390 }
8391 
8392 static if (is(typeof(&fons__nvg__toOutline))) {
8393   public enum NanoVegaHasCharOutline = true; ///
8394 } else {
8395   public enum NanoVegaHasCharOutline = false; ///
8396 }
8397 
8398 /// Returns glyph outlines as array of commands. Vertical 0 is baseline.
8399 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
8400 /// Returns `null` if there is no such glyph, or current font is not scalable.
8401 /// Group: text_api
8402 public NVGPathOutline charOutline (NVGContext ctx, dchar dch) nothrow @trusted @nogc {
8403   import core.stdc.stdlib : malloc;
8404   import core.stdc.string : memcpy;
8405   NVGstate* state = nvg__getState(ctx);
8406   ctx.fs.fontId = state.fontId;
8407   auto oline = NVGPathOutline.createNew();
8408   if (!ctx.fs.toOutline(dch, oline.ds)) oline.clear();
8409   return oline;
8410 }
8411 
8412 
8413 float nvg__quantize (float a, float d) pure nothrow @safe @nogc {
8414   pragma(inline, true);
8415   return (cast(int)(a/d+0.5f))*d;
8416 }
8417 
8418 float nvg__getFontScale (NVGstate* state) /*pure*/ nothrow @safe @nogc {
8419   pragma(inline, true);
8420   return nvg__min(nvg__quantize(nvg__getAverageScale(state.xform), 0.01f), 4.0f);
8421 }
8422 
8423 void nvg__flushTextTexture (NVGContext ctx) nothrow @trusted @nogc {
8424   int[4] dirty = void;
8425   if (ctx.fs.validateTexture(dirty.ptr)) {
8426     auto fontImage = &ctx.fontImages[ctx.fontImageIdx];
8427     // Update texture
8428     if (fontImage.valid) {
8429       int iw, ih;
8430       const(ubyte)* data = ctx.fs.getTextureData(&iw, &ih);
8431       int x = dirty[0];
8432       int y = dirty[1];
8433       int w = dirty[2]-dirty[0];
8434       int h = dirty[3]-dirty[1];
8435       ctx.params.renderUpdateTexture(ctx.params.userPtr, fontImage.id, x, y, w, h, data);
8436     }
8437   }
8438 }
8439 
8440 bool nvg__allocTextAtlas (NVGContext ctx) nothrow @trusted @nogc {
8441   int iw, ih;
8442   nvg__flushTextTexture(ctx);
8443   if (ctx.fontImageIdx >= NVG_MAX_FONTIMAGES-1) return false;
8444   // if next fontImage already have a texture
8445   if (ctx.fontImages[ctx.fontImageIdx+1].valid) {
8446     ctx.imageSize(ctx.fontImages[ctx.fontImageIdx+1], iw, ih);
8447   } else {
8448     // calculate the new font image size and create it
8449     ctx.imageSize(ctx.fontImages[ctx.fontImageIdx], iw, ih);
8450     if (iw > ih) ih *= 2; else iw *= 2;
8451     if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) iw = ih = NVG_MAX_FONTIMAGE_SIZE;
8452     ctx.fontImages[ctx.fontImageIdx+1].id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.Alpha, iw, ih, (ctx.params.fontAA ? 0 : NVGImageFlag.NoFiltering), null);
8453     if (ctx.fontImages[ctx.fontImageIdx+1].id > 0) {
8454       ctx.fontImages[ctx.fontImageIdx+1].ctx = ctx;
8455       ctx.nvg__imageIncRef(ctx.fontImages[ctx.fontImageIdx+1].id, false); // don't increment driver refcount
8456     }
8457   }
8458   ++ctx.fontImageIdx;
8459   ctx.fs.resetAtlas(iw, ih);
8460   return true;
8461 }
8462 
8463 void nvg__renderText (NVGContext ctx, NVGVertex* verts, int nverts) nothrow @trusted @nogc {
8464   NVGstate* state = nvg__getState(ctx);
8465   NVGPaint paint = state.fill;
8466 
8467   // Render triangles.
8468   paint.image = ctx.fontImages[ctx.fontImageIdx];
8469 
8470   // Apply global alpha
8471   paint.innerColor.a *= state.alpha;
8472   paint.middleColor.a *= state.alpha;
8473   paint.outerColor.a *= state.alpha;
8474 
8475   ctx.params.renderTriangles(ctx.params.userPtr, state.compositeOperation, NVGClipMode.None, &paint, &state.scissor, verts, nverts);
8476 
8477   ++ctx.drawCallCount;
8478   ctx.textTriCount += nverts/3;
8479 }
8480 
8481 /// Draws text string at specified location. Returns next x position.
8482 /// Group: text_api
8483 public float text(T) (NVGContext ctx, float x, float y, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
8484   NVGstate* state = nvg__getState(ctx);
8485   FONSTextIter!T iter, prevIter;
8486   FONSQuad q;
8487   NVGVertex* verts;
8488   float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8489   float invscale = 1.0f/scale;
8490   int cverts = 0;
8491   int nverts = 0;
8492 
8493   if (state.fontId == FONS_INVALID) return x;
8494   if (str.length == 0) return x;
8495 
8496   ctx.fs.size = state.fontSize*scale;
8497   ctx.fs.spacing = state.letterSpacing*scale;
8498   ctx.fs.blur = state.fontBlur*scale;
8499   ctx.fs.textAlign = state.textAlign;
8500   ctx.fs.fontId = state.fontId;
8501 
8502   cverts = nvg__max(2, cast(int)(str.length))*6; // conservative estimate
8503   verts = nvg__allocTempVerts(ctx, cverts);
8504   if (verts is null) return x;
8505 
8506   if (!iter.setup(ctx.fs, x*scale, y*scale, str, FONSBitmapFlag.Required)) return x;
8507   prevIter = iter;
8508   while (iter.next(q)) {
8509     float[4*2] c = void;
8510     if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
8511       if (nverts != 0) {
8512         // TODO: add back-end bit to do this just once per frame
8513         nvg__flushTextTexture(ctx);
8514         nvg__renderText(ctx, verts, nverts);
8515         nverts = 0;
8516       }
8517       if (!nvg__allocTextAtlas(ctx)) break; // no memory :(
8518       iter = prevIter;
8519       iter.next(q); // try again
8520       if (iter.prevGlyphIndex < 0) {
8521         // still can not find glyph, try replacement
8522         iter = prevIter;
8523         if (!iter.getDummyChar(q)) break;
8524       }
8525     }
8526     prevIter = iter;
8527     // transform corners
8528     state.xform.point(&c[0], &c[1], q.x0*invscale, q.y0*invscale);
8529     state.xform.point(&c[2], &c[3], q.x1*invscale, q.y0*invscale);
8530     state.xform.point(&c[4], &c[5], q.x1*invscale, q.y1*invscale);
8531     state.xform.point(&c[6], &c[7], q.x0*invscale, q.y1*invscale);
8532     // create triangles
8533     if (nverts+6 <= cverts) {
8534       nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); ++nverts;
8535       nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); ++nverts;
8536       nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); ++nverts;
8537       nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); ++nverts;
8538       nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); ++nverts;
8539       nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); ++nverts;
8540     }
8541   }
8542 
8543   // TODO: add back-end bit to do this just once per frame
8544   if (nverts > 0) {
8545     nvg__flushTextTexture(ctx);
8546     nvg__renderText(ctx, verts, nverts);
8547   }
8548 
8549   return iter.nextx/scale;
8550 }
8551 
8552 /** Draws multi-line text string at specified location wrapped at the specified width.
8553  * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8554  * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8555  *
8556  * Group: text_api
8557  */
8558 public void textBox(T) (NVGContext ctx, float x, float y, float breakRowWidth, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
8559   NVGstate* state = nvg__getState(ctx);
8560   if (state.fontId == FONS_INVALID) return;
8561 
8562   NVGTextRow!T[2] rows;
8563   auto oldAlign = state.textAlign;
8564   scope(exit) state.textAlign = oldAlign;
8565   auto halign = state.textAlign.horizontal;
8566   float lineh = 0;
8567 
8568   ctx.textMetrics(null, null, &lineh);
8569   state.textAlign.horizontal = NVGTextAlign.H.Left;
8570   for (;;) {
8571     auto rres = ctx.textBreakLines(str, breakRowWidth, rows[]);
8572     //{ import core.stdc.stdio : printf; printf("slen=%u; rlen=%u; bw=%f\n", cast(uint)str.length, cast(uint)rres.length, cast(double)breakRowWidth); }
8573     if (rres.length == 0) break;
8574     foreach (ref row; rres) {
8575       final switch (halign) {
8576         case NVGTextAlign.H.Left: ctx.text(x, y, row.row); break;
8577         case NVGTextAlign.H.Center: ctx.text(x+breakRowWidth*0.5f-row.width*0.5f, y, row.row); break;
8578         case NVGTextAlign.H.Right: ctx.text(x+breakRowWidth-row.width, y, row.row); break;
8579       }
8580       y += lineh*state.lineHeight;
8581     }
8582     str = rres[$-1].rest;
8583   }
8584 }
8585 
8586 private template isGoodPositionDelegate(DG) {
8587   private DG dg;
8588   static if (is(typeof({ NVGGlyphPosition pos; bool res = dg(pos); })) ||
8589              is(typeof({ NVGGlyphPosition pos; dg(pos); })))
8590     enum isGoodPositionDelegate = true;
8591   else
8592     enum isGoodPositionDelegate = false;
8593 }
8594 
8595 /** Calculates the glyph x positions of the specified text.
8596  * Measured values are returned in local coordinate space.
8597  *
8598  * Group: text_api
8599  */
8600 public NVGGlyphPosition[] textGlyphPositions(T) (NVGContext ctx, float x, float y, const(T)[] str, NVGGlyphPosition[] positions) nothrow @trusted @nogc
8601 if (isAnyCharType!T)
8602 {
8603   if (str.length == 0 || positions.length == 0) return positions[0..0];
8604   usize posnum;
8605   auto len = ctx.textGlyphPositions(x, y, str, (in ref NVGGlyphPosition pos) {
8606     positions.ptr[posnum++] = pos;
8607     return (posnum < positions.length);
8608   });
8609   return positions[0..len];
8610 }
8611 
8612 /// Ditto.
8613 public int textGlyphPositions(T, DG) (NVGContext ctx, float x, float y, const(T)[] str, scope DG dg)
8614 if (isAnyCharType!T && isGoodPositionDelegate!DG)
8615 {
8616   import std.traits : ReturnType;
8617   static if (is(ReturnType!dg == void)) enum RetBool = false; else enum RetBool = true;
8618 
8619   NVGstate* state = nvg__getState(ctx);
8620   float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8621   float invscale = 1.0f/scale;
8622   FONSTextIter!T iter, prevIter;
8623   FONSQuad q;
8624   int npos = 0;
8625 
8626   if (str.length == 0) return 0;
8627 
8628   ctx.fs.size = state.fontSize*scale;
8629   ctx.fs.spacing = state.letterSpacing*scale;
8630   ctx.fs.blur = state.fontBlur*scale;
8631   ctx.fs.textAlign = state.textAlign;
8632   ctx.fs.fontId = state.fontId;
8633 
8634   if (!iter.setup(ctx.fs, x*scale, y*scale, str, FONSBitmapFlag.Optional)) return npos;
8635   prevIter = iter;
8636   while (iter.next(q)) {
8637     if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
8638       if (!nvg__allocTextAtlas(ctx)) break; // no memory
8639       iter = prevIter;
8640       iter.next(q); // try again
8641       if (iter.prevGlyphIndex < 0) {
8642         // still can not find glyph, try replacement
8643         iter = prevIter;
8644         if (!iter.getDummyChar(q)) break;
8645       }
8646     }
8647     prevIter = iter;
8648     NVGGlyphPosition position = void; //WARNING!
8649     position.strpos = cast(usize)(iter.stringp-str.ptr);
8650     position.x = iter.x*invscale;
8651     position.minx = nvg__min(iter.x, q.x0)*invscale;
8652     position.maxx = nvg__max(iter.nextx, q.x1)*invscale;
8653     ++npos;
8654     static if (RetBool) { if (!dg(position)) return npos; } else dg(position);
8655   }
8656 
8657   return npos;
8658 }
8659 
8660 private template isGoodRowDelegate(CT, DG) {
8661   private DG dg;
8662   static if (is(typeof({ NVGTextRow!CT row; bool res = dg(row); })) ||
8663              is(typeof({ NVGTextRow!CT row; dg(row); })))
8664     enum isGoodRowDelegate = true;
8665   else
8666     enum isGoodRowDelegate = false;
8667 }
8668 
8669 /** Breaks the specified text into lines.
8670  * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8671  * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8672  *
8673  * Group: text_api
8674  */
8675 public NVGTextRow!T[] textBreakLines(T) (NVGContext ctx, const(T)[] str, float breakRowWidth, NVGTextRow!T[] rows) nothrow @trusted @nogc
8676 if (isAnyCharType!T)
8677 {
8678   if (rows.length == 0) return rows;
8679   if (rows.length > int.max-1) rows = rows[0..int.max-1];
8680   int nrow = 0;
8681   auto count = ctx.textBreakLines(str, breakRowWidth, (in ref NVGTextRow!T row) {
8682     rows[nrow++] = row;
8683     return (nrow < rows.length);
8684   });
8685   return rows[0..count];
8686 }
8687 
8688 /** Breaks the specified text into lines.
8689  * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8690  * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8691  * Returns number of rows.
8692  *
8693  * Group: text_api
8694  */
8695 public int textBreakLines(T, DG) (NVGContext ctx, const(T)[] str, float breakRowWidth, scope DG dg)
8696 if (isAnyCharType!T && isGoodRowDelegate!(T, DG))
8697 {
8698   import std.traits : ReturnType;
8699   static if (is(ReturnType!dg == void)) enum RetBool = false; else enum RetBool = true;
8700 
8701   enum NVGcodepointType : int {
8702     Space,
8703     NewLine,
8704     Char,
8705   }
8706 
8707   NVGstate* state = nvg__getState(ctx);
8708   float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8709   float invscale = 1.0f/scale;
8710   FONSTextIter!T iter, prevIter;
8711   FONSQuad q;
8712   int nrows = 0;
8713   float rowStartX = 0;
8714   float rowWidth = 0;
8715   float rowMinX = 0;
8716   float rowMaxX = 0;
8717   int rowStart = 0;
8718   int rowEnd = 0;
8719   int wordStart = 0;
8720   float wordStartX = 0;
8721   float wordMinX = 0;
8722   int breakEnd = 0;
8723   float breakWidth = 0;
8724   float breakMaxX = 0;
8725   int type = NVGcodepointType.Space, ptype = NVGcodepointType.Space;
8726   uint pcodepoint = 0;
8727 
8728   if (state.fontId == FONS_INVALID) return 0;
8729   if (str.length == 0 || dg is null) return 0;
8730 
8731   ctx.fs.size = state.fontSize*scale;
8732   ctx.fs.spacing = state.letterSpacing*scale;
8733   ctx.fs.blur = state.fontBlur*scale;
8734   ctx.fs.textAlign = state.textAlign;
8735   ctx.fs.fontId = state.fontId;
8736 
8737   breakRowWidth *= scale;
8738 
8739   enum Phase {
8740     Normal, // searching for breaking point
8741     SkipBlanks, // skip leading blanks
8742   }
8743   Phase phase = Phase.SkipBlanks; // don't skip blanks on first line
8744 
8745   if (!iter.setup(ctx.fs, 0, 0, str, FONSBitmapFlag.Optional)) return 0;
8746   prevIter = iter;
8747   while (iter.next(q)) {
8748     if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
8749       if (!nvg__allocTextAtlas(ctx)) break; // no memory
8750       iter = prevIter;
8751       iter.next(q); // try again
8752       if (iter.prevGlyphIndex < 0) {
8753         // still can not find glyph, try replacement
8754         iter = prevIter;
8755         if (!iter.getDummyChar(q)) break;
8756       }
8757     }
8758     prevIter = iter;
8759     switch (iter.codepoint) {
8760       case 9: // \t
8761       case 11: // \v
8762       case 12: // \f
8763       case 32: // space
8764       case 0x00a0: // NBSP
8765         type = NVGcodepointType.Space;
8766         break;
8767       case 10: // \n
8768         type = (pcodepoint == 13 ? NVGcodepointType.Space : NVGcodepointType.NewLine);
8769         break;
8770       case 13: // \r
8771         type = (pcodepoint == 10 ? NVGcodepointType.Space : NVGcodepointType.NewLine);
8772         break;
8773       case 0x0085: // NEL
8774       case 0x2028: // Line Separator
8775       case 0x2029: // Paragraph Separator
8776         type = NVGcodepointType.NewLine;
8777         break;
8778       default:
8779         type = NVGcodepointType.Char;
8780         break;
8781     }
8782     if (phase == Phase.SkipBlanks) {
8783       // fix row start
8784       rowStart = cast(int)(iter.stringp-str.ptr);
8785       rowEnd = rowStart;
8786       rowStartX = iter.x;
8787       rowWidth = iter.nextx-rowStartX; // q.x1-rowStartX;
8788       rowMinX = q.x0-rowStartX;
8789       rowMaxX = q.x1-rowStartX;
8790       wordStart = rowStart;
8791       wordStartX = iter.x;
8792       wordMinX = q.x0-rowStartX;
8793       breakEnd = rowStart;
8794       breakWidth = 0.0;
8795       breakMaxX = 0.0;
8796       if (type == NVGcodepointType.Space) continue;
8797       phase = Phase.Normal;
8798     }
8799 
8800     if (type == NVGcodepointType.NewLine) {
8801       // always handle new lines
8802       NVGTextRow!T row;
8803       row.string = str;
8804       row.start = rowStart;
8805       row.end = rowEnd;
8806       row.width = rowWidth*invscale;
8807       row.minx = rowMinX*invscale;
8808       row.maxx = rowMaxX*invscale;
8809       ++nrows;
8810       static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
8811       phase = Phase.SkipBlanks;
8812     } else {
8813       float nextWidth = iter.nextx-rowStartX;
8814       // track last non-white space character
8815       if (type == NVGcodepointType.Char) {
8816         rowEnd = cast(int)(iter.nextp-str.ptr);
8817         rowWidth = iter.nextx-rowStartX;
8818         rowMaxX = q.x1-rowStartX;
8819       }
8820       // track last end of a word
8821       if (ptype == NVGcodepointType.Char && type == NVGcodepointType.Space) {
8822         breakEnd = cast(int)(iter.stringp-str.ptr);
8823         breakWidth = rowWidth;
8824         breakMaxX = rowMaxX;
8825       }
8826       // track last beginning of a word
8827       if (ptype == NVGcodepointType.Space && type == NVGcodepointType.Char) {
8828         wordStart = cast(int)(iter.stringp-str.ptr);
8829         wordStartX = iter.x;
8830         wordMinX = q.x0-rowStartX;
8831       }
8832       // break to new line when a character is beyond break width
8833       if (type == NVGcodepointType.Char && nextWidth > breakRowWidth) {
8834         // the run length is too long, need to break to new line
8835         NVGTextRow!T row;
8836         row.string = str;
8837         if (breakEnd == rowStart) {
8838           // the current word is longer than the row length, just break it from here
8839           row.start = rowStart;
8840           row.end = cast(int)(iter.stringp-str.ptr);
8841           row.width = rowWidth*invscale;
8842           row.minx = rowMinX*invscale;
8843           row.maxx = rowMaxX*invscale;
8844           ++nrows;
8845           static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
8846           rowStartX = iter.x;
8847           rowStart = cast(int)(iter.stringp-str.ptr);
8848           rowEnd = cast(int)(iter.nextp-str.ptr);
8849           rowWidth = iter.nextx-rowStartX;
8850           rowMinX = q.x0-rowStartX;
8851           rowMaxX = q.x1-rowStartX;
8852           wordStart = rowStart;
8853           wordStartX = iter.x;
8854           wordMinX = q.x0-rowStartX;
8855         } else {
8856           // break the line from the end of the last word, and start new line from the beginning of the new
8857           //{ import core.stdc.stdio : printf; printf("rowStart=%u; rowEnd=%u; breakEnd=%u; len=%u\n", rowStart, rowEnd, breakEnd, cast(uint)str.length); }
8858           row.start = rowStart;
8859           row.end = breakEnd;
8860           row.width = breakWidth*invscale;
8861           row.minx = rowMinX*invscale;
8862           row.maxx = breakMaxX*invscale;
8863           ++nrows;
8864           static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
8865           rowStartX = wordStartX;
8866           rowStart = wordStart;
8867           rowEnd = cast(int)(iter.nextp-str.ptr);
8868           rowWidth = iter.nextx-rowStartX;
8869           rowMinX = wordMinX;
8870           rowMaxX = q.x1-rowStartX;
8871           // no change to the word start
8872         }
8873         // set null break point
8874         breakEnd = rowStart;
8875         breakWidth = 0.0;
8876         breakMaxX = 0.0;
8877       }
8878     }
8879 
8880     pcodepoint = iter.codepoint;
8881     ptype = type;
8882   }
8883 
8884   // break the line from the end of the last word, and start new line from the beginning of the new
8885   if (phase != Phase.SkipBlanks && rowStart < str.length) {
8886     //{ import core.stdc.stdio : printf; printf("  rowStart=%u; len=%u\n", rowStart, cast(uint)str.length); }
8887     NVGTextRow!T row;
8888     row.string = str;
8889     row.start = rowStart;
8890     row.end = cast(int)str.length;
8891     row.width = rowWidth*invscale;
8892     row.minx = rowMinX*invscale;
8893     row.maxx = rowMaxX*invscale;
8894     ++nrows;
8895     static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
8896   }
8897 
8898   return nrows;
8899 }
8900 
8901 /** Returns iterator which you can use to calculate text bounds and advancement.
8902  * This is usable when you need to do some text layouting with wrapping, to avoid
8903  * guesswork ("will advancement for this space stay the same?"), and Schlemiel's
8904  * algorithm. Note that you can copy the returned struct to save iterator state.
8905  *
8906  * You can check if iterator is valid with [valid] property, put new chars with
8907  * [put] method, get current advance with [advance] property, and current
8908  * bounds with `getBounds(ref float[4] bounds)` method.
8909  *
8910  * $(WARNING Don't change font parameters while iterating! Or use [restoreFont] method.)
8911  *
8912  * Group: text_api
8913  */
8914 public struct TextBoundsIterator {
8915 private:
8916   NVGContext ctx;
8917   FONSTextBoundsIterator fsiter; // fontstash iterator
8918   float scale, invscale, xscaled, yscaled;
8919   // font settings
8920   float fsSize, fsSpacing, fsBlur;
8921   int fsFontId;
8922   NVGTextAlign fsAlign;
8923 
8924 public:
8925   /// Setups iteration. Takes current font parameters from the given NanoVega context.
8926   this (NVGContext actx, float ax=0, float ay=0) nothrow @trusted @nogc { reset(actx, ax, ay); }
8927 
8928   /// Resets iteration. Takes current font parameters from the given NanoVega context.
8929   void reset (NVGContext actx, float ax=0, float ay=0) nothrow @trusted @nogc {
8930     fsiter = fsiter.init;
8931     this = this.init;
8932     if (actx is null) return;
8933     NVGstate* state = nvg__getState(actx);
8934     if (state is null) return;
8935     if (state.fontId == FONS_INVALID) { ctx = null; return; }
8936 
8937     ctx = actx;
8938     scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8939     invscale = 1.0f/scale;
8940 
8941     fsSize = state.fontSize*scale;
8942     fsSpacing = state.letterSpacing*scale;
8943     fsBlur = state.fontBlur*scale;
8944     fsAlign = state.textAlign;
8945     fsFontId = state.fontId;
8946     restoreFont();
8947 
8948     xscaled = ax*scale;
8949     yscaled = ay*scale;
8950     fsiter.reset(ctx.fs, xscaled, yscaled);
8951   }
8952 
8953   /// Restart iteration. Will not restore font.
8954   void restart () nothrow @trusted @nogc {
8955     if (ctx !is null) fsiter.reset(ctx.fs, xscaled, yscaled);
8956   }
8957 
8958   /// Restore font settings for the context.
8959   void restoreFont () nothrow @trusted @nogc {
8960     if (ctx !is null) {
8961       ctx.fs.size = fsSize;
8962       ctx.fs.spacing = fsSpacing;
8963       ctx.fs.blur = fsBlur;
8964       ctx.fs.textAlign = fsAlign;
8965       ctx.fs.fontId = fsFontId;
8966     }
8967   }
8968 
8969   /// Is this iterator valid?
8970   @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (ctx !is null); }
8971 
8972   /// Add chars.
8973   void put(T) (const(T)[] str...) nothrow @trusted @nogc if (isAnyCharType!T) { pragma(inline, true); if (ctx !is null) fsiter.put(str[]); }
8974 
8975   /// Returns current advance
8976   @property float advance () const pure nothrow @safe @nogc { pragma(inline, true); return (ctx !is null ? fsiter.advance*invscale : 0); }
8977 
8978   /// Returns current text bounds.
8979   void getBounds (ref float[4] bounds) nothrow @trusted @nogc {
8980     if (ctx !is null) {
8981       fsiter.getBounds(bounds);
8982       ctx.fs.getLineBounds(yscaled, &bounds[1], &bounds[3]);
8983       bounds[0] *= invscale;
8984       bounds[1] *= invscale;
8985       bounds[2] *= invscale;
8986       bounds[3] *= invscale;
8987     } else {
8988       bounds[] = 0;
8989     }
8990   }
8991 
8992   /// Returns current horizontal text bounds.
8993   void getHBounds (out float xmin, out float xmax) nothrow @trusted @nogc {
8994     if (ctx !is null) {
8995       fsiter.getHBounds(xmin, xmax);
8996       xmin *= invscale;
8997       xmax *= invscale;
8998     }
8999   }
9000 
9001   /// Returns current vertical text bounds.
9002   void getVBounds (out float ymin, out float ymax) nothrow @trusted @nogc {
9003     if (ctx !is null) {
9004       //fsiter.getVBounds(ymin, ymax);
9005       ctx.fs.getLineBounds(yscaled, &ymin, &ymax);
9006       ymin *= invscale;
9007       ymax *= invscale;
9008     }
9009   }
9010 }
9011 
9012 /// Returns font line height (without line spacing), measured in local coordinate space.
9013 /// Group: text_api
9014 public float textFontHeight (NVGContext ctx) nothrow @trusted @nogc {
9015   float res = void;
9016   ctx.textMetrics(null, null, &res);
9017   return res;
9018 }
9019 
9020 /// Returns font ascender (positive), measured in local coordinate space.
9021 /// Group: text_api
9022 public float textFontAscender (NVGContext ctx) nothrow @trusted @nogc {
9023   float res = void;
9024   ctx.textMetrics(&res, null, null);
9025   return res;
9026 }
9027 
9028 /// Returns font descender (negative), measured in local coordinate space.
9029 /// Group: text_api
9030 public float textFontDescender (NVGContext ctx) nothrow @trusted @nogc {
9031   float res = void;
9032   ctx.textMetrics(null, &res, null);
9033   return res;
9034 }
9035 
9036 /** Measures the specified text string. Returns horizontal and vertical sizes of the measured text.
9037  * Measured values are returned in local coordinate space.
9038  *
9039  * Group: text_api
9040  */
9041 public void textExtents(T) (NVGContext ctx, const(T)[] str, float *w, float *h) nothrow @trusted @nogc if (isAnyCharType!T) {
9042   float[4] bnd = void;
9043   ctx.textBounds(0, 0, str, bnd[]);
9044   if (!ctx.fs.getFontAA(nvg__getState(ctx).fontId)) {
9045     if (w !is null) *w = nvg__lrintf(bnd.ptr[2]-bnd.ptr[0]);
9046     if (h !is null) *h = nvg__lrintf(bnd.ptr[3]-bnd.ptr[1]);
9047   } else {
9048     if (w !is null) *w = bnd.ptr[2]-bnd.ptr[0];
9049     if (h !is null) *h = bnd.ptr[3]-bnd.ptr[1];
9050   }
9051 }
9052 
9053 /** Measures the specified text string. Returns horizontal size of the measured text.
9054  * Measured values are returned in local coordinate space.
9055  *
9056  * Group: text_api
9057  */
9058 public float textWidth(T) (NVGContext ctx, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
9059   float w = void;
9060   ctx.textExtents(str, &w, null);
9061   return w;
9062 }
9063 
9064 /** Measures the specified text string. Parameter bounds should be a float[4],
9065  * if the bounding box of the text should be returned. The bounds value are [xmin, ymin, xmax, ymax]
9066  * Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
9067  * Measured values are returned in local coordinate space.
9068  *
9069  * Group: text_api
9070  */
9071 public float textBounds(T) (NVGContext ctx, float x, float y, const(T)[] str, float[] bounds) nothrow @trusted @nogc
9072 if (isAnyCharType!T)
9073 {
9074   NVGstate* state = nvg__getState(ctx);
9075 
9076   if (state.fontId == FONS_INVALID) {
9077     bounds[] = 0;
9078     return 0;
9079   }
9080 
9081   immutable float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
9082   ctx.fs.size = state.fontSize*scale;
9083   ctx.fs.spacing = state.letterSpacing*scale;
9084   ctx.fs.blur = state.fontBlur*scale;
9085   ctx.fs.textAlign = state.textAlign;
9086   ctx.fs.fontId = state.fontId;
9087 
9088   float[4] b = void;
9089   immutable float width = ctx.fs.getTextBounds(x*scale, y*scale, str, b[]);
9090   immutable float invscale = 1.0f/scale;
9091   if (bounds.length) {
9092     // use line bounds for height
9093     ctx.fs.getLineBounds(y*scale, b.ptr+1, b.ptr+3);
9094     if (bounds.length > 0) bounds.ptr[0] = b.ptr[0]*invscale;
9095     if (bounds.length > 1) bounds.ptr[1] = b.ptr[1]*invscale;
9096     if (bounds.length > 2) bounds.ptr[2] = b.ptr[2]*invscale;
9097     if (bounds.length > 3) bounds.ptr[3] = b.ptr[3]*invscale;
9098   }
9099   return width*invscale;
9100 }
9101 
9102 /// Ditto.
9103 public void textBoxBounds(T) (NVGContext ctx, float x, float y, float breakRowWidth, const(T)[] str, float[] bounds) if (isAnyCharType!T) {
9104   NVGstate* state = nvg__getState(ctx);
9105   NVGTextRow!T[2] rows;
9106   float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
9107   float invscale = 1.0f/scale;
9108   float lineh = 0, rminy = 0, rmaxy = 0;
9109   float minx, miny, maxx, maxy;
9110 
9111   if (state.fontId == FONS_INVALID) {
9112     bounds[] = 0;
9113     return;
9114   }
9115 
9116   auto oldAlign = state.textAlign;
9117   scope(exit) state.textAlign = oldAlign;
9118   auto halign = state.textAlign.horizontal;
9119 
9120   ctx.textMetrics(null, null, &lineh);
9121   state.textAlign.horizontal = NVGTextAlign.H.Left;
9122 
9123   minx = maxx = x;
9124   miny = maxy = y;
9125 
9126   ctx.fs.size = state.fontSize*scale;
9127   ctx.fs.spacing = state.letterSpacing*scale;
9128   ctx.fs.blur = state.fontBlur*scale;
9129   ctx.fs.textAlign = state.textAlign;
9130   ctx.fs.fontId = state.fontId;
9131   ctx.fs.getLineBounds(0, &rminy, &rmaxy);
9132   rminy *= invscale;
9133   rmaxy *= invscale;
9134 
9135   for (;;) {
9136     auto rres = ctx.textBreakLines(str, breakRowWidth, rows[]);
9137     if (rres.length == 0) break;
9138     foreach (ref row; rres) {
9139       float rminx, rmaxx, dx = 0;
9140       // horizontal bounds
9141       final switch (halign) {
9142         case NVGTextAlign.H.Left: dx = 0; break;
9143         case NVGTextAlign.H.Center: dx = breakRowWidth*0.5f-row.width*0.5f; break;
9144         case NVGTextAlign.H.Right: dx = breakRowWidth-row.width; break;
9145       }
9146       rminx = x+row.minx+dx;
9147       rmaxx = x+row.maxx+dx;
9148       minx = nvg__min(minx, rminx);
9149       maxx = nvg__max(maxx, rmaxx);
9150       // vertical bounds
9151       miny = nvg__min(miny, y+rminy);
9152       maxy = nvg__max(maxy, y+rmaxy);
9153       y += lineh*state.lineHeight;
9154     }
9155     str = rres[$-1].rest;
9156   }
9157 
9158   if (bounds.length) {
9159     if (bounds.length > 0) bounds.ptr[0] = minx;
9160     if (bounds.length > 1) bounds.ptr[1] = miny;
9161     if (bounds.length > 2) bounds.ptr[2] = maxx;
9162     if (bounds.length > 3) bounds.ptr[3] = maxy;
9163   }
9164 }
9165 
9166 /// Returns the vertical metrics based on the current text style. Measured values are returned in local coordinate space.
9167 /// Group: text_api
9168 public void textMetrics (NVGContext ctx, float* ascender, float* descender, float* lineh) nothrow @trusted @nogc {
9169   NVGstate* state = nvg__getState(ctx);
9170 
9171   if (state.fontId == FONS_INVALID) {
9172     if (ascender !is null) *ascender *= 0;
9173     if (descender !is null) *descender *= 0;
9174     if (lineh !is null) *lineh *= 0;
9175     return;
9176   }
9177 
9178   immutable float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
9179   immutable float invscale = 1.0f/scale;
9180 
9181   ctx.fs.size = state.fontSize*scale;
9182   ctx.fs.spacing = state.letterSpacing*scale;
9183   ctx.fs.blur = state.fontBlur*scale;
9184   ctx.fs.textAlign = state.textAlign;
9185   ctx.fs.fontId = state.fontId;
9186 
9187   ctx.fs.getVertMetrics(ascender, descender, lineh);
9188   if (ascender !is null) *ascender *= invscale;
9189   if (descender !is null) *descender *= invscale;
9190   if (lineh !is null) *lineh *= invscale;
9191 }
9192 
9193 
9194 // ////////////////////////////////////////////////////////////////////////// //
9195 // fontstash
9196 // ////////////////////////////////////////////////////////////////////////// //
9197 import core.stdc.stdlib : malloc, realloc, free;
9198 import core.stdc.string : memset, memcpy, strncpy, strcmp, strlen;
9199 import core.stdc.stdio : FILE, fopen, fclose, fseek, ftell, fread, SEEK_END, SEEK_SET;
9200 
9201 public:
9202 // welcome to version hell!
9203 version(nanovg_force_stb_ttf) {
9204 } else {
9205   version(nanovg_force_detect) {} else version(nanovg_use_freetype) { version = nanovg_use_freetype_ii; }
9206 }
9207 version(nanovg_ignore_iv_stb_ttf) enum nanovg_ignore_iv_stb_ttf = true; else enum nanovg_ignore_iv_stb_ttf = false;
9208 //version(nanovg_ignore_mono);
9209 
9210 version(nanovg_force_stb_ttf) {
9211   private enum NanoVegaForceFreeType = false;
9212 } else {
9213   version (nanovg_builtin_freetype_bindings) {
9214     version(Posix) {
9215       private enum NanoVegaForceFreeType = true;
9216     } else {
9217       private enum NanoVegaForceFreeType = false;
9218     }
9219   } else {
9220     version(Posix) {
9221       private enum NanoVegaForceFreeType = true;
9222     } else {
9223       private enum NanoVegaForceFreeType = false;
9224     }
9225   }
9226 }
9227 
9228 version(nanovg_use_freetype_ii) {
9229   enum NanoVegaIsUsingSTBTTF = false;
9230   //pragma(msg, "iv.freetype: forced");
9231 } else {
9232   static if (NanoVegaForceFreeType) {
9233     enum NanoVegaIsUsingSTBTTF = false;
9234   } else {
9235     static if (!nanovg_ignore_iv_stb_ttf && __traits(compiles, { import iv.stb.ttf; })) {
9236       import iv.stb.ttf;
9237       enum NanoVegaIsUsingSTBTTF = true;
9238       version(nanovg_report_stb_ttf) pragma(msg, "iv.stb.ttf");
9239     } else static if (__traits(compiles, { import arsd.ttf; })) {
9240       import arsd.ttf;
9241       enum NanoVegaIsUsingSTBTTF = true;
9242       version(nanovg_report_stb_ttf) pragma(msg, "arsd.ttf");
9243     } else static if (__traits(compiles, { import stb_truetype; })) {
9244       import stb_truetype;
9245       enum NanoVegaIsUsingSTBTTF = true;
9246       version(nanovg_report_stb_ttf) pragma(msg, "stb_truetype");
9247     } else static if (__traits(compiles, { import iv.freetype; })) {
9248       version (nanovg_builtin_freetype_bindings) {
9249         enum NanoVegaIsUsingSTBTTF = false;
9250         version = nanovg_builtin_freetype_bindings;
9251       } else {
9252         import iv.freetype;
9253         enum NanoVegaIsUsingSTBTTF = false;
9254       }
9255       version(nanovg_report_stb_ttf) pragma(msg, "freetype");
9256     } else {
9257       static assert(0, "no stb_ttf/iv.freetype found!");
9258     }
9259   }
9260 }
9261 
9262 
9263 // ////////////////////////////////////////////////////////////////////////// //
9264 //version = nanovg_ft_mono;
9265 
9266 /// Invald font id.
9267 /// Group: font_stash
9268 public enum FONS_INVALID = -1;
9269 
9270 public enum FONSBitmapFlag : uint {
9271   Required = 0,
9272   Optional = 1,
9273 }
9274 
9275 public enum FONSError : int {
9276   NoError = 0,
9277   AtlasFull = 1, // Font atlas is full.
9278   ScratchFull = 2, // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
9279   StatesOverflow = 3, // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
9280   StatesUnderflow = 4, // Trying to pop too many states fonsPopState().
9281 }
9282 
9283 /// Initial parameters for new FontStash.
9284 /// Group: font_stash
9285 public struct FONSParams {
9286   enum Flag : uint {
9287     ZeroTopLeft    = 0U, // default
9288     ZeroBottomLeft = 1U,
9289   }
9290   int width, height;
9291   Flag flags = Flag.ZeroTopLeft;
9292   void* userPtr;
9293   bool function (void* uptr, int width, int height) nothrow @trusted @nogc renderCreate;
9294   int function (void* uptr, int width, int height) nothrow @trusted @nogc renderResize;
9295   void function (void* uptr, int* rect, const(ubyte)* data) nothrow @trusted @nogc renderUpdate;
9296   void function (void* uptr) nothrow @trusted @nogc renderDelete;
9297   @property bool isZeroTopLeft () const pure nothrow @trusted @nogc { pragma(inline, true); return ((flags&Flag.ZeroBottomLeft) == 0); }
9298 }
9299 
9300 //TODO: document this
9301 public struct FONSQuad {
9302   float x0=0, y0=0, s0=0, t0=0;
9303   float x1=0, y1=0, s1=0, t1=0;
9304 }
9305 
9306 //TODO: document this
9307 public struct FONSTextIter(CT) if (isAnyCharType!CT) {
9308   alias CharType = CT;
9309   float x=0, y=0, nextx=0, nexty=0, scale=0, spacing=0;
9310   uint codepoint;
9311   short isize, iblur;
9312   FONSContext stash;
9313   FONSfont* font;
9314   int prevGlyphIndex;
9315   const(CT)* s; // string
9316   const(CT)* n; // next
9317   const(CT)* e; // end
9318   FONSBitmapFlag bitmapOption;
9319   static if (is(CT == char)) {
9320     uint utf8state;
9321   }
9322 
9323   this (FONSContext astash, float ax, float ay, const(CharType)[] astr, FONSBitmapFlag abitmapOption) nothrow @trusted @nogc { setup(astash, ax, ay, astr, abitmapOption); }
9324   ~this () nothrow @trusted @nogc { pragma(inline, true); static if (is(CT == char)) utf8state = 0; s = n = e = null; }
9325 
9326   @property const(CT)* stringp () const pure nothrow @trusted @nogc { pragma(inline, true); return s; }
9327   @property const(CT)* nextp () const pure nothrow @trusted @nogc { pragma(inline, true); return n; }
9328   @property const(CT)* endp () const pure nothrow @trusted @nogc { pragma(inline, true); return e; }
9329 
9330   bool setup (FONSContext astash, float ax, float ay, const(CharType)[] astr, FONSBitmapFlag abitmapOption) nothrow @trusted @nogc {
9331     import core.stdc.string : memset;
9332 
9333     memset(&this, 0, this.sizeof);
9334     if (astash is null) return false;
9335 
9336     FONSstate* state = astash.getState;
9337 
9338     if (state.font < 0 || state.font >= astash.nfonts) return false;
9339     font = astash.fonts[state.font];
9340     if (font is null || font.fdata is null) return false;
9341 
9342     isize = cast(short)(state.size*10.0f);
9343     iblur = cast(short)state.blur;
9344     scale = fons__tt_getPixelHeightScale(&font.font, cast(float)isize/10.0f);
9345 
9346     // align horizontally
9347     if (state.talign.left) {
9348       // empty
9349     } else if (state.talign.right) {
9350       immutable float width = astash.getTextBounds(ax, ay, astr, null);
9351       ax -= width;
9352     } else if (state.talign.center) {
9353       immutable float width = astash.getTextBounds(ax, ay, astr, null);
9354       ax -= width*0.5f;
9355     }
9356 
9357     // align vertically
9358     ay += astash.getVertAlign(font, state.talign, isize);
9359 
9360     x = nextx = ax;
9361     y = nexty = ay;
9362     spacing = state.spacing;
9363 
9364     if (astr.ptr is null) {
9365            static if (is(CharType == char)) astr = "";
9366       else static if (is(CharType == wchar)) astr = ""w;
9367       else static if (is(CharType == dchar)) astr = ""d;
9368       else static assert(0, "wtf?!");
9369     }
9370     s = astr.ptr;
9371     n = astr.ptr;
9372     e = astr.ptr+astr.length;
9373 
9374     codepoint = 0;
9375     prevGlyphIndex = -1;
9376     bitmapOption = abitmapOption;
9377     stash = astash;
9378 
9379     return true;
9380   }
9381 
9382   bool getDummyChar (ref FONSQuad quad) nothrow @trusted @nogc {
9383     if (stash is null || font is null) return false;
9384     // get glyph and quad
9385     x = nextx;
9386     y = nexty;
9387     FONSglyph* glyph = stash.getGlyph(font, 0xFFFD, isize, iblur, bitmapOption);
9388     if (glyph !is null) {
9389       stash.getQuad(font, prevGlyphIndex, glyph, isize/10.0f, scale, spacing, &nextx, &nexty, &quad);
9390       prevGlyphIndex = glyph.index;
9391       return true;
9392     } else {
9393       prevGlyphIndex = -1;
9394       return false;
9395     }
9396   }
9397 
9398   bool next (ref FONSQuad quad) nothrow @trusted @nogc {
9399     if (stash is null || font is null) return false;
9400     FONSglyph* glyph = null;
9401     static if (is(CharType == char)) {
9402       const(char)* str = this.n;
9403       this.s = this.n;
9404       if (str is this.e) return false;
9405       const(char)* e = this.e;
9406       for (; str !is e; ++str) {
9407         /*if (fons__decutf8(&utf8state, &codepoint, *cast(const(ubyte)*)str)) continue;*/
9408         mixin(DecUtfMixin!("this.utf8state", "this.codepoint", "*cast(const(ubyte)*)str"));
9409         if (utf8state) continue;
9410         ++str; // 'cause we'll break anyway
9411         // get glyph and quad
9412         x = nextx;
9413         y = nexty;
9414         glyph = stash.getGlyph(font, codepoint, isize, iblur, bitmapOption);
9415         if (glyph !is null) {
9416           stash.getQuad(font, prevGlyphIndex, glyph, isize/10.0f, scale, spacing, &nextx, &nexty, &quad);
9417           prevGlyphIndex = glyph.index;
9418         } else {
9419           prevGlyphIndex = -1;
9420         }
9421         break;
9422       }
9423       this.n = str;
9424     } else {
9425       const(CharType)* str = this.n;
9426       this.s = this.n;
9427       if (str is this.e) return false;
9428       codepoint = cast(uint)(*str++);
9429       if (codepoint > dchar.max) codepoint = 0xFFFD;
9430       // get glyph and quad
9431       x = nextx;
9432       y = nexty;
9433       glyph = stash.getGlyph(font, codepoint, isize, iblur, bitmapOption);
9434       if (glyph !is null) {
9435         stash.getQuad(font, prevGlyphIndex, glyph, isize/10.0f, scale, spacing, &nextx, &nexty, &quad);
9436         prevGlyphIndex = glyph.index;
9437       } else {
9438         prevGlyphIndex = -1;
9439       }
9440       this.n = str;
9441     }
9442     return true;
9443   }
9444 }
9445 
9446 
9447 // ////////////////////////////////////////////////////////////////////////// //
9448 //static if (!HasAST) version = nanovg_use_freetype_ii_x;
9449 
9450 /*version(nanovg_use_freetype_ii_x)*/ static if (!NanoVegaIsUsingSTBTTF) {
9451 version(nanovg_builtin_freetype_bindings) {
9452 pragma(lib, "freetype");
9453 private extern(C) nothrow @trusted @nogc {
9454 private import core.stdc.config : c_long, c_ulong;
9455 alias FT_Pos = c_long;
9456 // config/ftconfig.h
9457 alias FT_Int16 = short;
9458 alias FT_UInt16 = ushort;
9459 alias FT_Int32 = int;
9460 alias FT_UInt32 = uint;
9461 alias FT_Fast = int;
9462 alias FT_UFast = uint;
9463 alias FT_Int64 = long;
9464 alias FT_Uint64 = ulong;
9465 // fttypes.h
9466 alias FT_Bool = ubyte;
9467 alias FT_FWord = short;
9468 alias FT_UFWord = ushort;
9469 alias FT_Char = char;
9470 alias FT_Byte = ubyte;
9471 alias FT_Bytes = FT_Byte*;
9472 alias FT_Tag = FT_UInt32;
9473 alias FT_String = char;
9474 alias FT_Short = short;
9475 alias FT_UShort = ushort;
9476 alias FT_Int = int;
9477 alias FT_UInt = uint;
9478 alias FT_Long = c_long;
9479 alias FT_ULong = c_ulong;
9480 alias FT_F2Dot14 = short;
9481 alias FT_F26Dot6 = c_long;
9482 alias FT_Fixed = c_long;
9483 alias FT_Error = int;
9484 alias FT_Pointer = void*;
9485 alias FT_Offset = usize;
9486 alias FT_PtrDist = ptrdiff_t;
9487 
9488 struct FT_UnitVector {
9489   FT_F2Dot14 x;
9490   FT_F2Dot14 y;
9491 }
9492 
9493 struct FT_Matrix {
9494   FT_Fixed xx, xy;
9495   FT_Fixed yx, yy;
9496 }
9497 
9498 struct FT_Data {
9499   const(FT_Byte)* pointer;
9500   FT_Int length;
9501 }
9502 alias FT_Face = FT_FaceRec*;
9503 struct FT_FaceRec {
9504   FT_Long num_faces;
9505   FT_Long face_index;
9506   FT_Long face_flags;
9507   FT_Long style_flags;
9508   FT_Long num_glyphs;
9509   FT_String* family_name;
9510   FT_String* style_name;
9511   FT_Int num_fixed_sizes;
9512   FT_Bitmap_Size* available_sizes;
9513   FT_Int num_charmaps;
9514   FT_CharMap* charmaps;
9515   FT_Generic generic;
9516   FT_BBox bbox;
9517   FT_UShort units_per_EM;
9518   FT_Short ascender;
9519   FT_Short descender;
9520   FT_Short height;
9521   FT_Short max_advance_width;
9522   FT_Short max_advance_height;
9523   FT_Short underline_position;
9524   FT_Short underline_thickness;
9525   FT_GlyphSlot glyph;
9526   FT_Size size;
9527   FT_CharMap charmap;
9528   FT_Driver driver;
9529   FT_Memory memory;
9530   FT_Stream stream;
9531   FT_ListRec sizes_list;
9532   FT_Generic autohint;
9533   void* extensions;
9534   FT_Face_Internal internal;
9535 }
9536 struct FT_Bitmap_Size {
9537   FT_Short height;
9538   FT_Short width;
9539   FT_Pos size;
9540   FT_Pos x_ppem;
9541   FT_Pos y_ppem;
9542 }
9543 alias FT_CharMap = FT_CharMapRec*;
9544 struct FT_CharMapRec {
9545   FT_Face face;
9546   FT_Encoding encoding;
9547   FT_UShort platform_id;
9548   FT_UShort encoding_id;
9549 }
9550 extern(C) nothrow @nogc { alias FT_Generic_Finalizer = void function (void* object); }
9551 struct FT_Generic {
9552   void* data;
9553   FT_Generic_Finalizer finalizer;
9554 }
9555 struct FT_Vector {
9556   FT_Pos x;
9557   FT_Pos y;
9558 }
9559 struct FT_BBox {
9560   FT_Pos xMin, yMin;
9561   FT_Pos xMax, yMax;
9562 }
9563 alias FT_Pixel_Mode = int;
9564 enum {
9565   FT_PIXEL_MODE_NONE = 0,
9566   FT_PIXEL_MODE_MONO,
9567   FT_PIXEL_MODE_GRAY,
9568   FT_PIXEL_MODE_GRAY2,
9569   FT_PIXEL_MODE_GRAY4,
9570   FT_PIXEL_MODE_LCD,
9571   FT_PIXEL_MODE_LCD_V,
9572   FT_PIXEL_MODE_MAX
9573 }
9574 struct FT_Bitmap {
9575   uint rows;
9576   uint width;
9577   int pitch;
9578   ubyte* buffer;
9579   ushort num_grays;
9580   ubyte pixel_mode;
9581   ubyte palette_mode;
9582   void* palette;
9583 }
9584 struct FT_Outline {
9585   short n_contours;
9586   short n_points;
9587   FT_Vector* points;
9588   byte* tags;
9589   short* contours;
9590   int flags;
9591 }
9592 alias FT_GlyphSlot = FT_GlyphSlotRec*;
9593 struct FT_GlyphSlotRec {
9594   FT_Library library;
9595   FT_Face face;
9596   FT_GlyphSlot next;
9597   FT_UInt reserved;
9598   FT_Generic generic;
9599   FT_Glyph_Metrics metrics;
9600   FT_Fixed linearHoriAdvance;
9601   FT_Fixed linearVertAdvance;
9602   FT_Vector advance;
9603   FT_Glyph_Format format;
9604   FT_Bitmap bitmap;
9605   FT_Int bitmap_left;
9606   FT_Int bitmap_top;
9607   FT_Outline outline;
9608   FT_UInt num_subglyphs;
9609   FT_SubGlyph subglyphs;
9610   void* control_data;
9611   c_long control_len;
9612   FT_Pos lsb_delta;
9613   FT_Pos rsb_delta;
9614   void* other;
9615   FT_Slot_Internal internal;
9616 }
9617 alias FT_Size = FT_SizeRec*;
9618 struct FT_SizeRec {
9619   FT_Face face;
9620   FT_Generic generic;
9621   FT_Size_Metrics metrics;
9622   FT_Size_Internal internal;
9623 }
9624 alias FT_Encoding = FT_Tag;
9625 alias FT_Face_Internal = void*;
9626 alias FT_Driver = void*;
9627 alias FT_Memory = void*;
9628 alias FT_Stream = void*;
9629 alias FT_Library = void*;
9630 alias FT_SubGlyph = void*;
9631 alias FT_Slot_Internal = void*;
9632 alias FT_Size_Internal = void*;
9633 alias FT_ListNode = FT_ListNodeRec*;
9634 alias FT_List = FT_ListRec*;
9635 struct FT_ListNodeRec {
9636   FT_ListNode prev;
9637   FT_ListNode next;
9638   void* data;
9639 }
9640 struct FT_ListRec {
9641   FT_ListNode head;
9642   FT_ListNode tail;
9643 }
9644 struct FT_Glyph_Metrics {
9645   FT_Pos width;
9646   FT_Pos height;
9647   FT_Pos horiBearingX;
9648   FT_Pos horiBearingY;
9649   FT_Pos horiAdvance;
9650   FT_Pos vertBearingX;
9651   FT_Pos vertBearingY;
9652   FT_Pos vertAdvance;
9653 }
9654 alias FT_Glyph_Format = FT_Tag;
9655 FT_Tag FT_MAKE_TAG (char x1, char x2, char x3, char x4) pure nothrow @safe @nogc {
9656   pragma(inline, true);
9657   return cast(FT_UInt32)((x1<<24)|(x2<<16)|(x3<<8)|x4);
9658 }
9659 enum : FT_Tag {
9660   FT_GLYPH_FORMAT_NONE = 0,
9661   FT_GLYPH_FORMAT_COMPOSITE = FT_MAKE_TAG('c','o','m','p'),
9662   FT_GLYPH_FORMAT_BITMAP = FT_MAKE_TAG('b','i','t','s'),
9663   FT_GLYPH_FORMAT_OUTLINE = FT_MAKE_TAG('o','u','t','l'),
9664   FT_GLYPH_FORMAT_PLOTTER = FT_MAKE_TAG('p','l','o','t'),
9665 }
9666 struct FT_Size_Metrics {
9667   FT_UShort x_ppem;
9668   FT_UShort y_ppem;
9669 
9670   FT_Fixed x_scale;
9671   FT_Fixed y_scale;
9672 
9673   FT_Pos ascender;
9674   FT_Pos descender;
9675   FT_Pos height;
9676   FT_Pos max_advance;
9677 }
9678 enum FT_LOAD_DEFAULT = 0x0U;
9679 enum FT_LOAD_NO_SCALE = 1U<<0;
9680 enum FT_LOAD_NO_HINTING = 1U<<1;
9681 enum FT_LOAD_RENDER = 1U<<2;
9682 enum FT_LOAD_NO_BITMAP = 1U<<3;
9683 enum FT_LOAD_VERTICAL_LAYOUT = 1U<<4;
9684 enum FT_LOAD_FORCE_AUTOHINT = 1U<<5;
9685 enum FT_LOAD_CROP_BITMAP = 1U<<6;
9686 enum FT_LOAD_PEDANTIC = 1U<<7;
9687 enum FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1U<<9;
9688 enum FT_LOAD_NO_RECURSE = 1U<<10;
9689 enum FT_LOAD_IGNORE_TRANSFORM = 1U<<11;
9690 enum FT_LOAD_MONOCHROME = 1U<<12;
9691 enum FT_LOAD_LINEAR_DESIGN = 1U<<13;
9692 enum FT_LOAD_NO_AUTOHINT = 1U<<15;
9693 enum FT_LOAD_COLOR = 1U<<20;
9694 enum FT_LOAD_COMPUTE_METRICS = 1U<<21;
9695 enum FT_FACE_FLAG_KERNING = 1U<<6;
9696 alias FT_Kerning_Mode = int;
9697 enum /*FT_Kerning_Mode*/ {
9698   FT_KERNING_DEFAULT = 0,
9699   FT_KERNING_UNFITTED,
9700   FT_KERNING_UNSCALED
9701 }
9702 extern(C) nothrow @nogc {
9703   alias FT_Outline_MoveToFunc = int function (const(FT_Vector)*, void*);
9704   alias FT_Outline_LineToFunc = int function (const(FT_Vector)*, void*);
9705   alias FT_Outline_ConicToFunc = int function (const(FT_Vector)*, const(FT_Vector)*, void*);
9706   alias FT_Outline_CubicToFunc = int function (const(FT_Vector)*, const(FT_Vector)*, const(FT_Vector)*, void*);
9707 }
9708 struct FT_Outline_Funcs {
9709   FT_Outline_MoveToFunc move_to;
9710   FT_Outline_LineToFunc line_to;
9711   FT_Outline_ConicToFunc conic_to;
9712   FT_Outline_CubicToFunc cubic_to;
9713   int shift;
9714   FT_Pos delta;
9715 }
9716 
9717 FT_Error FT_Init_FreeType (FT_Library*);
9718 FT_Error FT_New_Memory_Face (FT_Library, const(FT_Byte)*, FT_Long, FT_Long, FT_Face*);
9719 FT_UInt FT_Get_Char_Index (FT_Face, FT_ULong);
9720 FT_Error FT_Set_Pixel_Sizes (FT_Face, FT_UInt, FT_UInt);
9721 FT_Error FT_Load_Glyph (FT_Face, FT_UInt, FT_Int32);
9722 FT_Error FT_Get_Advance (FT_Face, FT_UInt, FT_Int32, FT_Fixed*);
9723 FT_Error FT_Get_Kerning (FT_Face, FT_UInt, FT_UInt, FT_UInt, FT_Vector*);
9724 void FT_Outline_Get_CBox (const(FT_Outline)*, FT_BBox*);
9725 FT_Error FT_Outline_Decompose (FT_Outline*, const(FT_Outline_Funcs)*, void*);
9726 }
9727 } else version(bindbc) {
9728   import bindbc.freetype;
9729   alias FT_KERNING_DEFAULT = FT_Kerning_Mode.FT_KERNING_DEFAULT;
9730   alias FT_KERNING_UNFITTED = FT_Kerning_Mode.FT_KERNING_UNFITTED;
9731   alias FT_KERNING_UNSCALED = FT_Kerning_Mode.FT_KERNING_UNSCALED;
9732 } else {
9733   import iv.freetype;
9734 }
9735 
9736 struct FONSttFontImpl {
9737   FT_Face font;
9738   bool mono; // no aa?
9739 }
9740 
9741 __gshared FT_Library ftLibrary;
9742 
9743 int fons__tt_init (FONSContext context) nothrow @trusted @nogc {
9744   FT_Error ftError;
9745   //FONS_NOTUSED(context);
9746   ftError = FT_Init_FreeType(&ftLibrary);
9747   return (ftError == 0);
9748 }
9749 
9750 void fons__tt_setMono (FONSContext context, FONSttFontImpl* font, bool v) nothrow @trusted @nogc {
9751   font.mono = v;
9752 }
9753 
9754 bool fons__tt_getMono (FONSContext context, FONSttFontImpl* font) nothrow @trusted @nogc {
9755   return font.mono;
9756 }
9757 
9758 int fons__tt_loadFont (FONSContext context, FONSttFontImpl* font, ubyte* data, int dataSize) nothrow @trusted @nogc {
9759   FT_Error ftError;
9760   //font.font.userdata = stash;
9761   ftError = FT_New_Memory_Face(ftLibrary, cast(const(FT_Byte)*)data, dataSize, 0, &font.font);
9762   return ftError == 0;
9763 }
9764 
9765 void fons__tt_getFontVMetrics (FONSttFontImpl* font, int* ascent, int* descent, int* lineGap) nothrow @trusted @nogc {
9766   *ascent = font.font.ascender;
9767   *descent = font.font.descender;
9768   *lineGap = font.font.height-(*ascent - *descent);
9769 }
9770 
9771 float fons__tt_getPixelHeightScale (FONSttFontImpl* font, float size) nothrow @trusted @nogc {
9772   return size/(font.font.ascender-font.font.descender);
9773 }
9774 
9775 int fons__tt_getGlyphIndex (FONSttFontImpl* font, int codepoint) nothrow @trusted @nogc {
9776   return FT_Get_Char_Index(font.font, codepoint);
9777 }
9778 
9779 int fons__tt_buildGlyphBitmap (FONSttFontImpl* font, int glyph, float size, float scale, int* advance, int* lsb, int* x0, int* y0, int* x1, int* y1) nothrow @trusted @nogc {
9780   FT_Error ftError;
9781   FT_GlyphSlot ftGlyph;
9782   //version(nanovg_ignore_mono) enum exflags = 0;
9783   //else version(nanovg_ft_mono) enum exflags = FT_LOAD_MONOCHROME; else enum exflags = 0;
9784   uint exflags = (font.mono ? FT_LOAD_MONOCHROME : 0);
9785   ftError = FT_Set_Pixel_Sizes(font.font, 0, cast(FT_UInt)(size*cast(float)font.font.units_per_EM/cast(float)(font.font.ascender-font.font.descender)));
9786   if (ftError) return 0;
9787   ftError = FT_Load_Glyph(font.font, glyph, FT_LOAD_RENDER|/*FT_LOAD_NO_AUTOHINT|*/exflags);
9788   if (ftError) return 0;
9789   ftError = FT_Get_Advance(font.font, glyph, FT_LOAD_NO_SCALE|/*FT_LOAD_NO_AUTOHINT|*/exflags, cast(FT_Fixed*)advance);
9790   if (ftError) return 0;
9791   ftGlyph = font.font.glyph;
9792   *lsb = cast(int)ftGlyph.metrics.horiBearingX;
9793   *x0 = ftGlyph.bitmap_left;
9794   *x1 = *x0+ftGlyph.bitmap.width;
9795   *y0 = -ftGlyph.bitmap_top;
9796   *y1 = *y0+ftGlyph.bitmap.rows;
9797   return 1;
9798 }
9799 
9800 void fons__tt_renderGlyphBitmap (FONSttFontImpl* font, ubyte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) nothrow @trusted @nogc {
9801   FT_GlyphSlot ftGlyph = font.font.glyph;
9802   //FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap
9803   //version(nanovg_ignore_mono) enum RenderAA = true;
9804   //else version(nanovg_ft_mono) enum RenderAA = false;
9805   //else enum RenderAA = true;
9806   if (font.mono) {
9807     auto src = ftGlyph.bitmap.buffer;
9808     auto dst = output;
9809     auto spt = ftGlyph.bitmap.pitch;
9810     if (spt < 0) spt = -spt;
9811     foreach (int y; 0..ftGlyph.bitmap.rows) {
9812       ubyte count = 0, b = 0;
9813       auto s = src;
9814       auto d = dst;
9815       foreach (int x; 0..ftGlyph.bitmap.width) {
9816         if (count-- == 0) { count = 7; b = *s++; } else b <<= 1;
9817         *d++ = (b&0x80 ? 255 : 0);
9818       }
9819       src += spt;
9820       dst += outStride;
9821     }
9822   } else {
9823     auto src = ftGlyph.bitmap.buffer;
9824     auto dst = output;
9825     auto spt = ftGlyph.bitmap.pitch;
9826     if (spt < 0) spt = -spt;
9827     foreach (int y; 0..ftGlyph.bitmap.rows) {
9828       import core.stdc.string : memcpy;
9829       //dst[0..ftGlyph.bitmap.width] = src[0..ftGlyph.bitmap.width];
9830       memcpy(dst, src, ftGlyph.bitmap.width);
9831       src += spt;
9832       dst += outStride;
9833     }
9834   }
9835 }
9836 
9837 float fons__tt_getGlyphKernAdvance (FONSttFontImpl* font, float size, int glyph1, int glyph2) nothrow @trusted @nogc {
9838   FT_Vector ftKerning;
9839   version(none) {
9840     // fitted kerning
9841     FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
9842     //{ import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d\n", glyph1, glyph2, ftKerning.x, ftKerning.y); }
9843     return cast(int)ftKerning.x; // round up and convert to integer
9844   } else {
9845     // unfitted kerning
9846     //FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_UNFITTED, &ftKerning);
9847     if (glyph1 <= 0 || glyph2 <= 0 || (font.font.face_flags&FT_FACE_FLAG_KERNING) == 0) return 0;
9848     if (FT_Set_Pixel_Sizes(font.font, 0, cast(FT_UInt)(size*cast(float)font.font.units_per_EM/cast(float)(font.font.ascender-font.font.descender)))) return 0;
9849     if (FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning)) return 0;
9850     version(none) {
9851       if (ftKerning.x) {
9852         //{ import core.stdc.stdio : printf; printf("has kerning: %u\n", cast(uint)(font.font.face_flags&FT_FACE_FLAG_KERNING)); }
9853         { import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d (size=%g)\n", glyph1, glyph2, ftKerning.x, ftKerning.y, cast(double)size); }
9854       }
9855     }
9856     version(none) {
9857       FT_Vector kk;
9858       if (FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_UNSCALED, &kk)) assert(0, "wtf?!");
9859       auto kadvfrac = FT_MulFix(kk.x, font.font.size.metrics.x_scale); // 1/64 of pixel
9860       //return cast(int)((kadvfrac/*+(kadvfrac < 0 ? -32 : 32)*/)>>6);
9861       //assert(ftKerning.x == kadvfrac);
9862       if (ftKerning.x || kadvfrac) {
9863         { import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d (%d) (size=%g)\n", glyph1, glyph2, ftKerning.x, cast(int)kadvfrac, cast(int)(kadvfrac+(kadvfrac < 0 ? -31 : 32)>>6), cast(double)size); }
9864       }
9865       //return cast(int)(kadvfrac+(kadvfrac < 0 ? -31 : 32)>>6); // round up and convert to integer
9866       return kadvfrac/64.0f;
9867     }
9868     //return cast(int)(ftKerning.x+(ftKerning.x < 0 ? -31 : 32)>>6); // round up and convert to integer
9869     return ftKerning.x/64.0f;
9870   }
9871 }
9872 
9873 extern(C) nothrow @trusted @nogc {
9874   static struct OutlinerData {
9875     @disable this (this);
9876     void opAssign() (in auto ref OutlinerData a) { static assert(0, "no copies!"); }
9877     NVGContext vg;
9878     NVGPathOutline.DataStore* ol;
9879     FT_BBox outlineBBox;
9880   nothrow @trusted @nogc:
9881     static float transx(T) (T v) pure { pragma(inline, true); return cast(float)v; }
9882     static float transy(T) (T v) pure { pragma(inline, true); return -cast(float)v; }
9883   }
9884 
9885   int fons__nvg__moveto_cb (const(FT_Vector)* to, void* user) {
9886     auto odata = cast(OutlinerData*)user;
9887     if (odata.vg !is null) odata.vg.moveTo(odata.transx(to.x), odata.transy(to.y));
9888     if (odata.ol !is null) {
9889       odata.ol.putCommand(NVGPathOutline.Command.Kind.MoveTo);
9890       odata.ol.putArgs(odata.transx(to.x));
9891       odata.ol.putArgs(odata.transy(to.y));
9892     }
9893     return 0;
9894   }
9895 
9896   int fons__nvg__lineto_cb (const(FT_Vector)* to, void* user) {
9897     auto odata = cast(OutlinerData*)user;
9898     if (odata.vg !is null) odata.vg.lineTo(odata.transx(to.x), odata.transy(to.y));
9899     if (odata.ol !is null) {
9900       odata.ol.putCommand(NVGPathOutline.Command.Kind.LineTo);
9901       odata.ol.putArgs(odata.transx(to.x));
9902       odata.ol.putArgs(odata.transy(to.y));
9903     }
9904     return 0;
9905   }
9906 
9907   int fons__nvg__quadto_cb (const(FT_Vector)* c1, const(FT_Vector)* to, void* user) {
9908     auto odata = cast(OutlinerData*)user;
9909     if (odata.vg !is null) odata.vg.quadTo(odata.transx(c1.x), odata.transy(c1.y), odata.transx(to.x), odata.transy(to.y));
9910     if (odata.ol !is null) {
9911       odata.ol.putCommand(NVGPathOutline.Command.Kind.QuadTo);
9912       odata.ol.putArgs(odata.transx(c1.x));
9913       odata.ol.putArgs(odata.transy(c1.y));
9914       odata.ol.putArgs(odata.transx(to.x));
9915       odata.ol.putArgs(odata.transy(to.y));
9916     }
9917     return 0;
9918   }
9919 
9920   int fons__nvg__cubicto_cb (const(FT_Vector)* c1, const(FT_Vector)* c2, const(FT_Vector)* to, void* user) {
9921     auto odata = cast(OutlinerData*)user;
9922     if (odata.vg !is null) odata.vg.bezierTo(odata.transx(c1.x), odata.transy(c1.y), odata.transx(c2.x), odata.transy(c2.y), odata.transx(to.x), odata.transy(to.y));
9923     if (odata.ol !is null) {
9924       odata.ol.putCommand(NVGPathOutline.Command.Kind.BezierTo);
9925       odata.ol.putArgs(odata.transx(c1.x));
9926       odata.ol.putArgs(odata.transy(c1.y));
9927       odata.ol.putArgs(odata.transx(c2.x));
9928       odata.ol.putArgs(odata.transy(c2.y));
9929       odata.ol.putArgs(odata.transx(to.x));
9930       odata.ol.putArgs(odata.transy(to.y));
9931     }
9932     return 0;
9933   }
9934 }
9935 
9936 bool fons__nvg__toPath (NVGContext vg, FONSttFontImpl* font, uint glyphidx, float[] bounds=null) nothrow @trusted @nogc {
9937   if (bounds.length > 4) bounds = bounds.ptr[0..4];
9938 
9939   FT_Outline_Funcs funcs;
9940   funcs.move_to = &fons__nvg__moveto_cb;
9941   funcs.line_to = &fons__nvg__lineto_cb;
9942   funcs.conic_to = &fons__nvg__quadto_cb;
9943   funcs.cubic_to = &fons__nvg__cubicto_cb;
9944 
9945   auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
9946   if (err) { bounds[] = 0; return false; }
9947   if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) { bounds[] = 0; return false; }
9948 
9949   FT_Outline outline = font.font.glyph.outline;
9950 
9951   OutlinerData odata;
9952   odata.vg = vg;
9953   FT_Outline_Get_CBox(&outline, &odata.outlineBBox);
9954 
9955   err = FT_Outline_Decompose(&outline, &funcs, &odata);
9956   if (err) { bounds[] = 0; return false; }
9957   if (bounds.length > 0) bounds.ptr[0] = odata.outlineBBox.xMin;
9958   if (bounds.length > 1) bounds.ptr[1] = -odata.outlineBBox.yMax;
9959   if (bounds.length > 2) bounds.ptr[2] = odata.outlineBBox.xMax;
9960   if (bounds.length > 3) bounds.ptr[3] = -odata.outlineBBox.yMin;
9961   return true;
9962 }
9963 
9964 bool fons__nvg__toOutline (FONSttFontImpl* font, uint glyphidx, NVGPathOutline.DataStore* ol) nothrow @trusted @nogc {
9965   FT_Outline_Funcs funcs;
9966   funcs.move_to = &fons__nvg__moveto_cb;
9967   funcs.line_to = &fons__nvg__lineto_cb;
9968   funcs.conic_to = &fons__nvg__quadto_cb;
9969   funcs.cubic_to = &fons__nvg__cubicto_cb;
9970 
9971   auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
9972   if (err) return false;
9973   if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) return false;
9974 
9975   FT_Outline outline = font.font.glyph.outline;
9976 
9977   OutlinerData odata;
9978   odata.ol = ol;
9979   FT_Outline_Get_CBox(&outline, &odata.outlineBBox);
9980 
9981   err = FT_Outline_Decompose(&outline, &funcs, &odata);
9982   if (err) return false;
9983   ol.bounds.ptr[0] = odata.outlineBBox.xMin;
9984   ol.bounds.ptr[1] = -odata.outlineBBox.yMax;
9985   ol.bounds.ptr[2] = odata.outlineBBox.xMax;
9986   ol.bounds.ptr[3] = -odata.outlineBBox.yMin;
9987   return true;
9988 }
9989 
9990 bool fons__nvg__bounds (FONSttFontImpl* font, uint glyphidx, float[] bounds) nothrow @trusted @nogc {
9991   if (bounds.length > 4) bounds = bounds.ptr[0..4];
9992 
9993   auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
9994   if (err) return false;
9995   if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) { bounds[] = 0; return false; }
9996 
9997   FT_Outline outline = font.font.glyph.outline;
9998   FT_BBox outlineBBox;
9999   FT_Outline_Get_CBox(&outline, &outlineBBox);
10000   if (bounds.length > 0) bounds.ptr[0] = outlineBBox.xMin;
10001   if (bounds.length > 1) bounds.ptr[1] = -outlineBBox.yMax;
10002   if (bounds.length > 2) bounds.ptr[2] = outlineBBox.xMax;
10003   if (bounds.length > 3) bounds.ptr[3] = -outlineBBox.yMin;
10004   return true;
10005 }
10006 
10007 
10008 } else {
10009 // ////////////////////////////////////////////////////////////////////////// //
10010 // sorry
10011 import std.traits : isFunctionPointer, isDelegate;
10012 private auto assumeNoThrowNoGC(T) (scope T t) if (isFunctionPointer!T || isDelegate!T) {
10013   import std.traits;
10014   enum attrs = functionAttributes!T|FunctionAttribute.nogc|FunctionAttribute.nothrow_;
10015   return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
10016 }
10017 
10018 private auto forceNoThrowNoGC(T) (scope T t) if (isFunctionPointer!T || isDelegate!T) {
10019   try {
10020     return assumeNoThrowNoGC(t)();
10021   } catch (Exception e) {
10022     assert(0, "OOPS!");
10023   }
10024 }
10025 
10026 struct FONSttFontImpl {
10027   stbtt_fontinfo font;
10028   bool mono; // no aa?
10029 }
10030 
10031 int fons__tt_init (FONSContext context) nothrow @trusted @nogc {
10032   return 1;
10033 }
10034 
10035 void fons__tt_setMono (FONSContext context, FONSttFontImpl* font, bool v) nothrow @trusted @nogc {
10036   font.mono = v;
10037 }
10038 
10039 bool fons__tt_getMono (FONSContext context, FONSttFontImpl* font) nothrow @trusted @nogc {
10040   return font.mono;
10041 }
10042 
10043 int fons__tt_loadFont (FONSContext context, FONSttFontImpl* font, ubyte* data, int dataSize) nothrow @trusted @nogc {
10044   int stbError;
10045   font.font.userdata = context;
10046   forceNoThrowNoGC({ stbError = stbtt_InitFont(&font.font, data, 0); });
10047   return stbError;
10048 }
10049 
10050 void fons__tt_getFontVMetrics (FONSttFontImpl* font, int* ascent, int* descent, int* lineGap) nothrow @trusted @nogc {
10051   forceNoThrowNoGC({ stbtt_GetFontVMetrics(&font.font, ascent, descent, lineGap); });
10052 }
10053 
10054 float fons__tt_getPixelHeightScale (FONSttFontImpl* font, float size) nothrow @trusted @nogc {
10055   float res = void;
10056   forceNoThrowNoGC({ res = stbtt_ScaleForPixelHeight(&font.font, size); });
10057   return res;
10058 }
10059 
10060 int fons__tt_getGlyphIndex (FONSttFontImpl* font, int codepoint) nothrow @trusted @nogc {
10061   int res;
10062   forceNoThrowNoGC({ res = stbtt_FindGlyphIndex(&font.font, codepoint); });
10063   return res;
10064 }
10065 
10066 int fons__tt_buildGlyphBitmap (FONSttFontImpl* font, int glyph, float size, float scale, int* advance, int* lsb, int* x0, int* y0, int* x1, int* y1) nothrow @trusted @nogc {
10067   forceNoThrowNoGC({ stbtt_GetGlyphHMetrics(&font.font, glyph, advance, lsb); });
10068   forceNoThrowNoGC({ stbtt_GetGlyphBitmapBox(&font.font, glyph, scale, scale, x0, y0, x1, y1); });
10069   return 1;
10070 }
10071 
10072 void fons__tt_renderGlyphBitmap (FONSttFontImpl* font, ubyte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) nothrow @trusted @nogc {
10073   forceNoThrowNoGC({ stbtt_MakeGlyphBitmap(&font.font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); });
10074 }
10075 
10076 float fons__tt_getGlyphKernAdvance (FONSttFontImpl* font, float size, int glyph1, int glyph2) nothrow @trusted @nogc {
10077   // FUnits -> pixels: pointSize * resolution / (72 points per inch * units_per_em)
10078   // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#converting
10079   float res = void;
10080   forceNoThrowNoGC({
10081     res = stbtt_GetGlyphKernAdvance(&font.font, glyph1, glyph2);
10082     res *= stbtt_ScaleForPixelHeight(&font.font, size);
10083   });
10084   /*
10085   if (res != 0) {
10086     { import core.stdc.stdio; printf("fres=%g; size=%g; %g (%g); rv=%g\n", res, size, res*stbtt_ScaleForMappingEmToPixels(&font.font, size), stbtt_ScaleForPixelHeight(&font.font, size*100), res*stbtt_ScaleForPixelHeight(&font.font, size*100)); }
10087   }
10088   */
10089   //k8: dunno if this is right; i guess it isn't but...
10090   return res;
10091 }
10092 
10093 // old arsd.ttf sux! ;-)
10094 static if (is(typeof(STBTT_vcubic))) {
10095 
10096 static struct OutlinerData {
10097   @disable this (this);
10098   void opAssign() (in auto ref OutlinerData a) { static assert(0, "no copies!"); }
10099   NVGPathOutline.DataStore* ol;
10100 nothrow @trusted @nogc:
10101   static float transx(T) (T v) pure { pragma(inline, true); return cast(float)v; }
10102   static float transy(T) (T v) pure { pragma(inline, true); return -cast(float)v; }
10103 }
10104 
10105 
10106 bool fons__nvg__toPath (NVGContext vg, FONSttFontImpl* font, uint glyphidx, float[] bounds=null) nothrow @trusted @nogc {
10107   if (bounds.length > 4) bounds = bounds.ptr[0..4];
10108 
10109   bool okflag = false;
10110 
10111   forceNoThrowNoGC({
10112     int x0, y0, x1, y1;
10113     if (!stbtt_GetGlyphBox(&font.font, glyphidx, &x0, &y0, &x1, &y1)) {
10114       bounds[] = 0;
10115       return;
10116     }
10117 
10118     if (bounds.length > 0) bounds.ptr[0] = x0;
10119     if (bounds.length > 1) bounds.ptr[1] = -y1;
10120     if (bounds.length > 2) bounds.ptr[2] = x1;
10121     if (bounds.length > 3) bounds.ptr[3] = -y0;
10122 
10123     static float transy(T) (T v) pure { pragma(inline, true); return -cast(float)v; }
10124 
10125     stbtt_vertex* verts = null;
10126     scope(exit) { import core.stdc.stdlib : free; if (verts !is null) free(verts); }
10127     int vcount = stbtt_GetGlyphShape(&font.font, glyphidx, &verts);
10128     if (vcount < 1) return;
10129 
10130     foreach (const ref vt; verts[0..vcount]) {
10131       switch (vt.type) {
10132         case STBTT_vmove: vg.moveTo(vt.x, transy(vt.y)); break;
10133         case STBTT_vline: vg.lineTo(vt.x, transy(vt.y)); break;
10134         case STBTT_vcurve: vg.quadTo(vt.x, transy(vt.y), vt.cx, transy(vt.cy)); break;
10135         case STBTT_vcubic: vg.bezierTo(vt.x, transy(vt.y), vt.cx, transy(vt.cy), vt.cx1, transy(vt.cy1)); break;
10136         default:
10137       }
10138     }
10139 
10140     okflag = true;
10141   });
10142 
10143   return okflag;
10144 }
10145 
10146 bool fons__nvg__toOutline (FONSttFontImpl* font, uint glyphidx, NVGPathOutline.DataStore* ol) nothrow @trusted @nogc {
10147   bool okflag = false;
10148 
10149   forceNoThrowNoGC({
10150     int x0, y0, x1, y1;
10151 
10152     if (!stbtt_GetGlyphBox(&font.font, glyphidx, &x0, &y0, &x1, &y1)) {
10153       ol.bounds[] = 0;
10154       return;
10155     }
10156 
10157     ol.bounds.ptr[0] = x0;
10158     ol.bounds.ptr[1] = -y1;
10159     ol.bounds.ptr[2] = x1;
10160     ol.bounds.ptr[3] = -y0;
10161 
10162     stbtt_vertex* verts = null;
10163     scope(exit) { import core.stdc.stdlib : free; if (verts !is null) free(verts); }
10164     int vcount = stbtt_GetGlyphShape(&font.font, glyphidx, &verts);
10165     if (vcount < 1) return;
10166 
10167     OutlinerData odata;
10168     odata.ol = ol;
10169 
10170     foreach (const ref vt; verts[0..vcount]) {
10171       switch (vt.type) {
10172         case STBTT_vmove:
10173           odata.ol.putCommand(NVGPathOutline.Command.Kind.MoveTo);
10174           odata.ol.putArgs(odata.transx(vt.x));
10175           odata.ol.putArgs(odata.transy(vt.y));
10176           break;
10177         case STBTT_vline:
10178           odata.ol.putCommand(NVGPathOutline.Command.Kind.LineTo);
10179           odata.ol.putArgs(odata.transx(vt.x));
10180           odata.ol.putArgs(odata.transy(vt.y));
10181           break;
10182         case STBTT_vcurve:
10183           odata.ol.putCommand(NVGPathOutline.Command.Kind.QuadTo);
10184           odata.ol.putArgs(odata.transx(vt.x));
10185           odata.ol.putArgs(odata.transy(vt.y));
10186           odata.ol.putArgs(odata.transx(vt.cx));
10187           odata.ol.putArgs(odata.transy(vt.cy));
10188           break;
10189         case STBTT_vcubic:
10190           odata.ol.putCommand(NVGPathOutline.Command.Kind.BezierTo);
10191           odata.ol.putArgs(odata.transx(vt.x));
10192           odata.ol.putArgs(odata.transy(vt.y));
10193           odata.ol.putArgs(odata.transx(vt.cx));
10194           odata.ol.putArgs(odata.transy(vt.cy));
10195           odata.ol.putArgs(odata.transx(vt.cx1));
10196           odata.ol.putArgs(odata.transy(vt.cy1));
10197           break;
10198         default:
10199       }
10200     }
10201 
10202     okflag = true;
10203   });
10204 
10205   return okflag;
10206 }
10207 
10208 bool fons__nvg__bounds (FONSttFontImpl* font, uint glyphidx, float[] bounds) nothrow @trusted @nogc {
10209   if (bounds.length > 4) bounds = bounds.ptr[0..4];
10210 
10211   bool okflag = false;
10212 
10213   forceNoThrowNoGC({
10214     int x0, y0, x1, y1;
10215     if (stbtt_GetGlyphBox(&font.font, glyphidx, &x0, &y0, &x1, &y1)) {
10216       if (bounds.length > 0) bounds.ptr[0] = x0;
10217       if (bounds.length > 1) bounds.ptr[1] = -y1;
10218       if (bounds.length > 2) bounds.ptr[2] = x1;
10219       if (bounds.length > 3) bounds.ptr[3] = -y0;
10220       okflag = true;
10221     } else {
10222       bounds[] = 0;
10223     }
10224   });
10225 
10226   return okflag;
10227 }
10228 
10229 } // check for old stb_ttf
10230 
10231 
10232 } // version
10233 
10234 
10235 // ////////////////////////////////////////////////////////////////////////// //
10236 private:
10237 enum FONS_SCRATCH_BUF_SIZE = 64000;
10238 enum FONS_HASH_LUT_SIZE = 256;
10239 enum FONS_INIT_FONTS = 4;
10240 enum FONS_INIT_GLYPHS = 256;
10241 enum FONS_INIT_ATLAS_NODES = 256;
10242 enum FONS_VERTEX_COUNT = 1024;
10243 enum FONS_MAX_STATES = 20;
10244 enum FONS_MAX_FALLBACKS = 20;
10245 
10246 
10247 struct FONSglyph {
10248   uint codepoint;
10249   int index;
10250   int next;
10251   short size, blur;
10252   short x0, y0, x1, y1;
10253   short xadv, xoff, yoff;
10254 }
10255 
10256 // refcounted
10257 struct FONSfontData {
10258   ubyte* data;
10259   int dataSize;
10260   bool freeData;
10261   int rc;
10262 
10263   @disable this (this); // no copies
10264   void opAssign() (in auto ref FONSfontData a) { static assert(0, "no copies!"); }
10265 }
10266 
10267 // won't set rc to 1
10268 FONSfontData* fons__createFontData (ubyte* adata, int asize, bool afree) nothrow @trusted @nogc {
10269   import core.stdc.stdlib : malloc;
10270   assert(adata !is null);
10271   assert(asize > 0);
10272   auto res = cast(FONSfontData*)malloc(FONSfontData.sizeof);
10273   if (res is null) assert(0, "FONS: out of memory");
10274   res.data = adata;
10275   res.dataSize = asize;
10276   res.freeData = afree;
10277   res.rc = 0;
10278   return res;
10279 }
10280 
10281 void incref (FONSfontData* fd) pure nothrow @trusted @nogc {
10282   pragma(inline, true);
10283   if (fd !is null) ++fd.rc;
10284 }
10285 
10286 void decref (ref FONSfontData* fd) nothrow @trusted @nogc {
10287   if (fd !is null) {
10288     if (--fd.rc == 0) {
10289       import core.stdc.stdlib : free;
10290       if (fd.freeData && fd.data !is null) {
10291         free(fd.data);
10292         fd.data = null;
10293       }
10294       free(fd);
10295       fd = null;
10296     }
10297   }
10298 }
10299 
10300 // as creating and destroying fonts is a rare operation, malloc some data
10301 struct FONSfont {
10302   FONSttFontImpl font;
10303   char* name; // malloced, strz, always lowercase
10304   uint namelen;
10305   uint namehash;
10306   char* path; // malloced, strz
10307   FONSfontData* fdata;
10308   float ascender;
10309   float descender;
10310   float lineh;
10311   FONSglyph* glyphs;
10312   int cglyphs;
10313   int nglyphs;
10314   int[FONS_HASH_LUT_SIZE] lut;
10315   int[FONS_MAX_FALLBACKS] fallbacks;
10316   int nfallbacks;
10317 
10318   @disable this (this);
10319   void opAssign() (in auto ref FONSfont a) { static assert(0, "no copies"); }
10320 
10321   static uint djbhash (const(void)[] s) pure nothrow @safe @nogc {
10322     uint hash = 5381;
10323     foreach (ubyte b; cast(const(ubyte)[])s) {
10324       if (b >= 'A' && b <= 'Z') b += 32; // poor man's tolower
10325       hash = ((hash<<5)+hash)+b;
10326     }
10327     return hash;
10328   }
10329 
10330   // except glyphs
10331   void freeMemory () nothrow @trusted @nogc {
10332     import core.stdc.stdlib : free;
10333     if (name !is null) { free(name); name = null; }
10334     namelen = namehash = 0;
10335     if (path !is null) { free(path); path = null; }
10336     fdata.decref();
10337   }
10338 
10339   // this also calcs name hash
10340   void setName (const(char)[] aname) nothrow @trusted @nogc {
10341     //{ import core.stdc.stdio; printf("setname: [%.*s]\n", cast(uint)aname.length, aname.ptr); }
10342     import core.stdc.stdlib : realloc;
10343     if (aname.length > int.max/32) assert(0, "FONS: invalid font name");
10344     namelen = cast(uint)aname.length;
10345     name = cast(char*)realloc(name, namelen+1);
10346     if (name is null) assert(0, "FONS: out of memory");
10347     if (aname.length) name[0..aname.length] = aname[];
10348     name[namelen] = 0;
10349     // lowercase it
10350     foreach (ref char ch; name[0..namelen]) if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
10351     namehash = djbhash(name[0..namelen]);
10352     //{ import core.stdc.stdio; printf("  [%s] [%.*s] [0x%08x]\n", name, namelen, name, namehash); }
10353   }
10354 
10355   void setPath (const(char)[] apath) nothrow @trusted @nogc {
10356     import core.stdc.stdlib : realloc;
10357     if (apath.length > int.max/32) assert(0, "FONS: invalid font path");
10358     path = cast(char*)realloc(path, apath.length+1);
10359     if (path is null) assert(0, "FONS: out of memory");
10360     if (apath.length) path[0..apath.length] = apath[];
10361     path[apath.length] = 0;
10362   }
10363 
10364   // this won't check hash
10365   bool nameEqu (const(char)[] aname) const pure nothrow @trusted @nogc {
10366     //{ import core.stdc.stdio; printf("nameEqu: aname=[%.*s]; namelen=%u; aslen=%u\n", cast(uint)aname.length, aname.ptr, namelen, cast(uint)aname.length); }
10367     if (namelen != aname.length) return false;
10368     const(char)* ns = name;
10369     // name part
10370     foreach (char ch; aname) {
10371       if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
10372       if (ch != *ns++) return false;
10373     }
10374     // done (length was checked earlier)
10375     return true;
10376   }
10377 
10378   void clear () nothrow @trusted @nogc {
10379     import core.stdc.stdlib : free;
10380     import core.stdc.string : memset;
10381     if (glyphs !is null) free(glyphs);
10382     freeMemory();
10383     memset(&this, 0, this.sizeof);
10384   }
10385 
10386   FONSglyph* allocGlyph () nothrow @trusted @nogc {
10387     if (nglyphs+1 > cglyphs) {
10388       import core.stdc.stdlib : realloc;
10389       cglyphs = (cglyphs == 0 ? 8 : cglyphs*2);
10390       glyphs = cast(FONSglyph*)realloc(glyphs, FONSglyph.sizeof*cglyphs);
10391       if (glyphs is null) assert(0, "FontStash: out of memory");
10392     }
10393     ++nglyphs;
10394     return &glyphs[nglyphs-1];
10395   }
10396 }
10397 
10398 void kill (ref FONSfont* font) nothrow @trusted @nogc {
10399   if (font !is null) {
10400     import core.stdc.stdlib : free;
10401     font.clear();
10402     free(font);
10403     font = null;
10404   }
10405 }
10406 
10407 
10408 // ////////////////////////////////////////////////////////////////////////// //
10409 struct FONSstate {
10410   int font;
10411   NVGTextAlign talign;
10412   float size = 0;
10413   float blur = 0;
10414   float spacing = 0;
10415 }
10416 
10417 
10418 // ////////////////////////////////////////////////////////////////////////// //
10419 // atlas based on Skyline Bin Packer by Jukka Jylänki
10420 alias FONSAtlas = FONSatlasInternal*;
10421 
10422 struct FONSatlasInternal {
10423   static struct Node {
10424     short x, y, width;
10425   }
10426 
10427   int width, height;
10428   Node* nodes;
10429   int nnodes;
10430   int cnodes;
10431 
10432   @disable this (this);
10433   void opAssign() (in auto ref FONSatlasInternal a) { static assert(0, "no copies"); }
10434 
10435 nothrow @trusted @nogc:
10436   static FONSAtlas create (int w, int h, int nnodes) {
10437     import core.stdc.stdlib : malloc;
10438     import core.stdc.string : memset;
10439 
10440     FONSAtlas atlas = cast(FONSAtlas)malloc(FONSatlasInternal.sizeof);
10441     if (atlas is null) assert(0, "FontStash: out of memory");
10442     memset(atlas, 0, FONSatlasInternal.sizeof);
10443 
10444     atlas.width = w;
10445     atlas.height = h;
10446 
10447     // allocate space for skyline nodes
10448     atlas.nodes = cast(Node*)malloc(Node.sizeof*nnodes);
10449     if (atlas.nodes is null) assert(0, "FontStash: out of memory");
10450     memset(atlas.nodes, 0, Node.sizeof*nnodes);
10451     atlas.nnodes = 0;
10452     atlas.cnodes = nnodes;
10453 
10454     // init root node
10455     atlas.nodes[0].x = 0;
10456     atlas.nodes[0].y = 0;
10457     atlas.nodes[0].width = cast(short)w;
10458     ++atlas.nnodes;
10459 
10460     return atlas;
10461   }
10462 
10463   void clear () {
10464     import core.stdc.stdlib : free;
10465     import core.stdc.string : memset;
10466 
10467     if (nodes !is null) free(nodes);
10468     memset(&this, 0, this.sizeof);
10469   }
10470 
10471   void insertNode (int idx, int x, int y, int w) {
10472     if (nnodes+1 > cnodes) {
10473       import core.stdc.stdlib : realloc;
10474       cnodes = (cnodes == 0 ? 8 : cnodes*2);
10475       nodes = cast(Node*)realloc(nodes, Node.sizeof*cnodes);
10476       if (nodes is null) assert(0, "FontStash: out of memory");
10477     }
10478     for (int i = nnodes; i > idx; --i) nodes[i] = nodes[i-1];
10479     nodes[idx].x = cast(short)x;
10480     nodes[idx].y = cast(short)y;
10481     nodes[idx].width = cast(short)w;
10482     ++nnodes;
10483   }
10484 
10485   void removeNode (int idx) {
10486     if (nnodes == 0) return;
10487     foreach (immutable int i; idx+1..nnodes) nodes[i-1] = nodes[i];
10488     --nnodes;
10489   }
10490 
10491   // insert node for empty space
10492   void expand (int w, int h) {
10493     if (w > width) insertNode(nnodes, width, 0, w-width);
10494     width = w;
10495     height = h;
10496   }
10497 
10498   void reset (int w, int h) {
10499     width = w;
10500     height = h;
10501     nnodes = 0;
10502     // init root node
10503     nodes[0].x = 0;
10504     nodes[0].y = 0;
10505     nodes[0].width = cast(short)w;
10506     ++nnodes;
10507   }
10508 
10509   void addSkylineLevel (int idx, int x, int y, int w, int h) {
10510     insertNode(idx, x, y+h, w);
10511 
10512     // delete skyline segments that fall under the shadow of the new segment
10513     for (int i = idx+1; i < nnodes; ++i) {
10514       if (nodes[i].x < nodes[i-1].x+nodes[i-1].width) {
10515         int shrink = nodes[i-1].x+nodes[i-1].width-nodes[i].x;
10516         nodes[i].x += cast(short)shrink;
10517         nodes[i].width -= cast(short)shrink;
10518         if (nodes[i].width <= 0) {
10519           removeNode(i);
10520           --i;
10521         } else {
10522           break;
10523         }
10524       } else {
10525         break;
10526       }
10527     }
10528 
10529     // Merge same height skyline segments that are next to each other
10530     for (int i = 0; i < nnodes-1; ++i) {
10531       if (nodes[i].y == nodes[i+1].y) {
10532         nodes[i].width += nodes[i+1].width;
10533         removeNode(i+1);
10534         --i;
10535       }
10536     }
10537   }
10538 
10539   // checks if there is enough space at the location of skyline span 'i',
10540   // and return the max height of all skyline spans under that at that location,
10541   // (think tetris block being dropped at that position); or -1 if no space found
10542   int rectFits (int i, int w, int h) {
10543     int x = nodes[i].x;
10544     int y = nodes[i].y;
10545     if (x+w > width) return -1;
10546     int spaceLeft = w;
10547     while (spaceLeft > 0) {
10548       if (i == nnodes) return -1;
10549       y = nvg__max(y, nodes[i].y);
10550       if (y+h > height) return -1;
10551       spaceLeft -= nodes[i].width;
10552       ++i;
10553     }
10554     return y;
10555   }
10556 
10557   bool addRect (int rw, int rh, int* rx, int* ry) {
10558     int besth = height, bestw = width, besti = -1;
10559     int bestx = -1, besty = -1;
10560 
10561     // Bottom left fit heuristic.
10562     for (int i = 0; i < nnodes; ++i) {
10563       int y = rectFits(i, rw, rh);
10564       if (y != -1) {
10565         if (y+rh < besth || (y+rh == besth && nodes[i].width < bestw)) {
10566           besti = i;
10567           bestw = nodes[i].width;
10568           besth = y+rh;
10569           bestx = nodes[i].x;
10570           besty = y;
10571         }
10572       }
10573     }
10574 
10575     if (besti == -1) return false;
10576 
10577     // perform the actual packing
10578     addSkylineLevel(besti, bestx, besty, rw, rh);
10579 
10580     *rx = bestx;
10581     *ry = besty;
10582 
10583     return true;
10584   }
10585 }
10586 
10587 void kill (ref FONSAtlas atlas) nothrow @trusted @nogc {
10588   if (atlas !is null) {
10589     import core.stdc.stdlib : free;
10590     atlas.clear();
10591     free(atlas);
10592     atlas = null;
10593   }
10594 }
10595 
10596 
10597 // ////////////////////////////////////////////////////////////////////////// //
10598 /// FontStash context (internal definition). Don't use it derectly, it was made public only to generate documentation.
10599 /// Group: font_stash
10600 public struct FONScontextInternal {
10601 private:
10602   FONSParams params;
10603   float itw, ith;
10604   ubyte* texData;
10605   int[4] dirtyRect;
10606   FONSfont** fonts; // actually, a simple hash table; can't grow yet
10607   int cfonts; // allocated
10608   int nfonts; // used (so we can track hash table stats)
10609   int* hashidx; // [hsize] items; holds indicies in [fonts] array
10610   int hused, hsize;// used items and total items in [hashidx]
10611   FONSAtlas atlas;
10612   ubyte* scratch;
10613   int nscratch;
10614   FONSstate[FONS_MAX_STATES] states;
10615   int nstates;
10616 
10617   void delegate (FONSError error, int val) nothrow @trusted @nogc handleError;
10618 
10619   @disable this (this);
10620   void opAssign() (in auto ref FONScontextInternal ctx) { static assert(0, "FONS copying is not allowed"); }
10621 
10622 private:
10623   static bool strequci (const(char)[] s0, const(char)[] s1) pure nothrow @trusted @nogc {
10624     if (s0.length != s1.length) return false;
10625     const(char)* sp0 = s0.ptr;
10626     const(char)* sp1 = s1.ptr;
10627     foreach (immutable _; 0..s0.length) {
10628       char c0 = *sp0++;
10629       char c1 = *sp1++;
10630       if (c0 != c1) {
10631         if (c0 >= 'A' && c0 <= 'Z') c0 += 32; // poor man tolower
10632         if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man tolower
10633         if (c0 != c1) return false;
10634       }
10635     }
10636     return true;
10637   }
10638 
10639   inout(FONSstate)* getState () inout pure nothrow @trusted @nogc {
10640     pragma(inline, true);
10641     return cast(inout)(&states[(nstates > 0 ? nstates-1 : 0)]);
10642   }
10643 
10644   // simple linear probing; returns [FONS_INVALID] if not found
10645   int findNameInHash (const(char)[] name) const pure nothrow @trusted @nogc {
10646     if (nfonts == 0) return FONS_INVALID;
10647     auto nhash = FONSfont.djbhash(name);
10648     //{ import core.stdc.stdio; printf("findinhash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10649     auto res = nhash%hsize;
10650     // hash will never be 100% full, so this loop is safe
10651     for (;;) {
10652       int idx = hashidx[res];
10653       if (idx == -1) break;
10654       auto font = fonts[idx];
10655       if (font is null) assert(0, "FONS internal error");
10656       if (font.namehash == nhash && font.nameEqu(name)) return idx;
10657       //{ import core.stdc.stdio; printf("findinhash chained: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10658       res = (res+1)%hsize;
10659     }
10660     return FONS_INVALID;
10661   }
10662 
10663   // should be called $(B before) freeing `fonts[fidx]`
10664   void removeIndexFromHash (int fidx) nothrow @trusted @nogc {
10665     if (fidx < 0 || fidx >= nfonts) assert(0, "FONS internal error");
10666     if (fonts[fidx] is null) assert(0, "FONS internal error");
10667     if (hused != nfonts) assert(0, "FONS internal error");
10668     auto nhash = fonts[fidx].namehash;
10669     auto res = nhash%hsize;
10670     // hash will never be 100% full, so this loop is safe
10671     for (;;) {
10672       int idx = hashidx[res];
10673       if (idx == -1) assert(0, "FONS INTERNAL ERROR");
10674       if (idx == fidx) {
10675         // i found her! copy rest here
10676         int nidx = (res+1)%hsize;
10677         for (;;) {
10678           if ((hashidx[res] = hashidx[nidx]) == -1) break; // so it will copy `-1` too
10679           res = nidx;
10680           nidx = (nidx+1)%hsize;
10681         }
10682         return;
10683       }
10684       res = (res+1)%hsize;
10685     }
10686   }
10687 
10688   // add font with the given index to hash
10689   // prerequisite: font should not exists in hash
10690   void addIndexToHash (int idx) nothrow @trusted @nogc {
10691     if (idx < 0 || idx >= nfonts) assert(0, "FONS internal error");
10692     if (fonts[idx] is null) assert(0, "FONS internal error");
10693     import core.stdc.stdlib : realloc;
10694     auto nhash = fonts[idx].namehash;
10695     //{ import core.stdc.stdio; printf("addtohash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10696     // allocate new hash table if there was none
10697     if (hsize == 0) {
10698       enum InitSize = 256;
10699       auto newlist = cast(int*)realloc(null, InitSize*hashidx[0].sizeof);
10700       if (newlist is null) assert(0, "FONS: out of memory");
10701       newlist[0..InitSize] = -1;
10702       hsize = InitSize;
10703       hused = 0;
10704       hashidx = newlist;
10705     }
10706     int res = cast(int)(nhash%hsize);
10707     // need to rehash? we want our hash table 50% full at max
10708     if (hashidx[res] != -1 && hused >= hsize/2) {
10709       uint nsz = hsize*2;
10710       if (nsz > 1024*1024) assert(0, "FONS: out of memory for fonts");
10711       auto newlist = cast(int*)realloc(fonts, nsz*hashidx[0].sizeof);
10712       if (newlist is null) assert(0, "FONS: out of memory");
10713       newlist[0..nsz] = -1;
10714       hused = 0;
10715       // rehash
10716       foreach (immutable fidx, FONSfont* ff; fonts[0..nfonts]) {
10717         if (ff is null) continue;
10718         // find slot for this font (guaranteed to have one)
10719         uint newslot = ff.namehash%nsz;
10720         while (newlist[newslot] != -1) newslot = (newslot+1)%nsz;
10721         newlist[newslot] = cast(int)fidx;
10722         ++hused;
10723       }
10724       hsize = nsz;
10725       hashidx = newlist;
10726       // we added everything, including [idx], so nothing more to do here
10727     } else {
10728       // find slot (guaranteed to have one)
10729       while (hashidx[res] != -1) res = (res+1)%hsize;
10730       // i found her!
10731       hashidx[res] = idx;
10732       ++hused;
10733     }
10734   }
10735 
10736   void addWhiteRect (int w, int h) nothrow @trusted @nogc {
10737     int gx, gy;
10738     ubyte* dst;
10739 
10740     if (!atlas.addRect(w, h, &gx, &gy)) return;
10741 
10742     // Rasterize
10743     dst = &texData[gx+gy*params.width];
10744     foreach (int y; 0..h) {
10745       foreach (int x; 0..w) {
10746         dst[x] = 0xff;
10747       }
10748       dst += params.width;
10749     }
10750 
10751     dirtyRect.ptr[0] = nvg__min(dirtyRect.ptr[0], gx);
10752     dirtyRect.ptr[1] = nvg__min(dirtyRect.ptr[1], gy);
10753     dirtyRect.ptr[2] = nvg__max(dirtyRect.ptr[2], gx+w);
10754     dirtyRect.ptr[3] = nvg__max(dirtyRect.ptr[3], gy+h);
10755   }
10756 
10757   // returns fid, not hash slot
10758   int allocFontAt (int atidx) nothrow @trusted @nogc {
10759     if (atidx >= 0 && atidx >= nfonts) assert(0, "internal NanoVega fontstash error");
10760 
10761     if (atidx < 0) {
10762       if (nfonts >= cfonts) {
10763         import core.stdc.stdlib : realloc;
10764         import core.stdc.string : memset;
10765         assert(nfonts == cfonts);
10766         int newsz = cfonts+64;
10767         if (newsz > 65535) assert(0, "FONS: too many fonts");
10768         auto newlist = cast(FONSfont**)realloc(fonts, newsz*(FONSfont*).sizeof);
10769         if (newlist is null) assert(0, "FONS: out of memory");
10770         memset(newlist+cfonts, 0, (newsz-cfonts)*(FONSfont*).sizeof);
10771         fonts = newlist;
10772         cfonts = newsz;
10773       }
10774       assert(nfonts < cfonts);
10775     }
10776 
10777     FONSfont* font = cast(FONSfont*)malloc(FONSfont.sizeof);
10778     if (font is null) assert(0, "FONS: out of memory");
10779     memset(font, 0, FONSfont.sizeof);
10780 
10781     font.glyphs = cast(FONSglyph*)malloc(FONSglyph.sizeof*FONS_INIT_GLYPHS);
10782     if (font.glyphs is null) assert(0, "FONS: out of memory");
10783     font.cglyphs = FONS_INIT_GLYPHS;
10784     font.nglyphs = 0;
10785 
10786     if (atidx < 0) {
10787       fonts[nfonts] = font;
10788       return nfonts++;
10789     } else {
10790       fonts[atidx] = font;
10791       return atidx;
10792     }
10793   }
10794 
10795   // 0: ooops
10796   int findGlyphForCP (FONSfont *font, dchar dch, FONSfont** renderfont) nothrow @trusted @nogc {
10797     if (renderfont !is null) *renderfont = font;
10798     if (font is null || font.fdata is null) return 0;
10799     auto g = fons__tt_getGlyphIndex(&font.font, cast(uint)dch);
10800     // try to find the glyph in fallback fonts
10801     if (g == 0) {
10802       foreach (immutable i; 0..font.nfallbacks) {
10803         FONSfont* fallbackFont = fonts[font.fallbacks.ptr[i]];
10804         if (fallbackFont !is null) {
10805           int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont.font, cast(uint)dch);
10806           if (fallbackIndex != 0) {
10807             if (renderfont !is null) *renderfont = fallbackFont;
10808             return g;
10809           }
10810         }
10811       }
10812       // no char, try to find replacement one
10813       if (dch != 0xFFFD) {
10814         g = fons__tt_getGlyphIndex(&font.font, 0xFFFD);
10815         if (g == 0) {
10816           foreach (immutable i; 0..font.nfallbacks) {
10817             FONSfont* fallbackFont = fonts[font.fallbacks.ptr[i]];
10818             if (fallbackFont !is null) {
10819               int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont.font, 0xFFFD);
10820               if (fallbackIndex != 0) {
10821                 if (renderfont !is null) *renderfont = fallbackFont;
10822                 return g;
10823               }
10824             }
10825           }
10826         }
10827       }
10828     }
10829     return g;
10830   }
10831 
10832   void clear () nothrow @trusted @nogc {
10833     import core.stdc.stdlib : free;
10834 
10835     if (params.renderDelete !is null) params.renderDelete(params.userPtr);
10836     foreach (immutable int i; 0..nfonts) fonts[i].kill();
10837 
10838     if (atlas !is null) atlas.kill();
10839     if (fonts !is null) free(fonts);
10840     if (texData !is null) free(texData);
10841     if (scratch !is null) free(scratch);
10842     if (hashidx !is null) free(hashidx);
10843   }
10844 
10845   // add font from another fontstash
10846   int addCookedFont (FONSfont* font) nothrow @trusted @nogc {
10847     if (font is null || font.fdata is null) return FONS_INVALID;
10848     font.fdata.incref();
10849     auto res = addFontWithData(font.name[0..font.namelen], font.fdata, !font.font.mono);
10850     if (res == FONS_INVALID) font.fdata.decref(); // oops
10851     return res;
10852   }
10853 
10854   // fdata refcount must be already increased; it won't be changed
10855   int addFontWithData (const(char)[] name, FONSfontData* fdata, bool defAA) nothrow @trusted @nogc {
10856     int i, ascent, descent, fh, lineGap;
10857 
10858     if (name.length == 0 || strequci(name, NoAlias)) return FONS_INVALID;
10859     if (name.length > 32767) return FONS_INVALID;
10860     if (fdata is null) return FONS_INVALID;
10861 
10862     // find a font with the given name
10863     int newidx;
10864     FONSfont* oldfont = null;
10865     int oldidx = findNameInHash(name);
10866     if (oldidx != FONS_INVALID) {
10867       // replacement font
10868       oldfont = fonts[oldidx];
10869       newidx = oldidx;
10870     } else {
10871       // new font, allocate new bucket
10872       newidx = -1;
10873     }
10874 
10875     newidx = allocFontAt(newidx);
10876     FONSfont* font = fonts[newidx];
10877     font.setName(name);
10878     font.lut.ptr[0..FONS_HASH_LUT_SIZE] = -1; // init hash lookup
10879     font.fdata = fdata; // set the font data (don't change reference count)
10880     fons__tt_setMono(&this, &font.font, !defAA);
10881 
10882     // init font
10883     nscratch = 0;
10884     if (!fons__tt_loadFont(&this, &font.font, fdata.data, fdata.dataSize)) {
10885       // we promised to not free data on error, so just clear the data store (it will be freed by the caller)
10886       font.fdata = null;
10887       font.kill();
10888       if (oldidx != FONS_INVALID) {
10889         assert(oldidx == newidx);
10890         fonts[oldidx] = oldfont;
10891       } else {
10892         assert(newidx == nfonts-1);
10893         fonts[newidx] = null;
10894         --nfonts;
10895       }
10896       return FONS_INVALID;
10897     } else {
10898       // free old font data, if any
10899       if (oldfont !is null) oldfont.kill();
10900     }
10901 
10902     // add font to name hash
10903     if (oldidx == FONS_INVALID) addIndexToHash(newidx);
10904 
10905     // store normalized line height
10906     // the real line height is got by multiplying the lineh by font size
10907     fons__tt_getFontVMetrics(&font.font, &ascent, &descent, &lineGap);
10908     fh = ascent-descent;
10909     font.ascender = cast(float)ascent/cast(float)fh;
10910     font.descender = cast(float)descent/cast(float)fh;
10911     font.lineh = cast(float)(fh+lineGap)/cast(float)fh;
10912 
10913     //{ import core.stdc.stdio; printf("created font [%.*s] (idx=%d)...\n", cast(uint)name.length, name.ptr, idx); }
10914     return newidx;
10915   }
10916 
10917   // isize: size*10
10918   float getVertAlign (FONSfont* font, NVGTextAlign talign, short isize) pure nothrow @trusted @nogc {
10919     if (params.isZeroTopLeft) {
10920       final switch (talign.vertical) {
10921         case NVGTextAlign.V.Top: return font.ascender*cast(float)isize/10.0f;
10922         case NVGTextAlign.V.Middle: return (font.ascender+font.descender)/2.0f*cast(float)isize/10.0f;
10923         case NVGTextAlign.V.Baseline: return 0.0f;
10924         case NVGTextAlign.V.Bottom: return font.descender*cast(float)isize/10.0f;
10925       }
10926     } else {
10927       final switch (talign.vertical) {
10928         case NVGTextAlign.V.Top: return -font.ascender*cast(float)isize/10.0f;
10929         case NVGTextAlign.V.Middle: return -(font.ascender+font.descender)/2.0f*cast(float)isize/10.0f;
10930         case NVGTextAlign.V.Baseline: return 0.0f;
10931         case NVGTextAlign.V.Bottom: return -font.descender*cast(float)isize/10.0f;
10932       }
10933     }
10934     assert(0);
10935   }
10936 
10937 public:
10938   /** Create new FontStash context. It can be destroyed with `fs.kill()` later.
10939    *
10940    * Note that if you don't plan to rasterize glyphs (i.e. you will use created
10941    * FontStash only to measure text), you can simply pass `FONSParams.init`).
10942    */
10943   static FONSContext create() (in auto ref FONSParams params) nothrow @trusted @nogc {
10944     import core.stdc.string : memcpy;
10945 
10946     FONSContext stash = null;
10947 
10948     // allocate memory for the font stash
10949     stash = cast(FONSContext)malloc(FONScontextInternal.sizeof);
10950     if (stash is null) goto error;
10951     memset(stash, 0, FONScontextInternal.sizeof);
10952 
10953     memcpy(&stash.params, &params, params.sizeof);
10954     if (stash.params.width < 1) stash.params.width = 32;
10955     if (stash.params.height < 1) stash.params.height = 32;
10956 
10957     // allocate scratch buffer
10958     stash.scratch = cast(ubyte*)malloc(FONS_SCRATCH_BUF_SIZE);
10959     if (stash.scratch is null) goto error;
10960 
10961     // initialize implementation library
10962     if (!fons__tt_init(stash)) goto error;
10963 
10964     if (stash.params.renderCreate !is null) {
10965       if (!stash.params.renderCreate(stash.params.userPtr, stash.params.width, stash.params.height)) goto error;
10966     }
10967 
10968     stash.atlas = FONSAtlas.create(stash.params.width, stash.params.height, FONS_INIT_ATLAS_NODES);
10969     if (stash.atlas is null) goto error;
10970 
10971     // don't allocate space for fonts: hash manager will do that for us later
10972     //stash.cfonts = 0;
10973     //stash.nfonts = 0;
10974 
10975     // create texture for the cache
10976     stash.itw = 1.0f/stash.params.width;
10977     stash.ith = 1.0f/stash.params.height;
10978     stash.texData = cast(ubyte*)malloc(stash.params.width*stash.params.height);
10979     if (stash.texData is null) goto error;
10980     memset(stash.texData, 0, stash.params.width*stash.params.height);
10981 
10982     stash.dirtyRect.ptr[0] = stash.params.width;
10983     stash.dirtyRect.ptr[1] = stash.params.height;
10984     stash.dirtyRect.ptr[2] = 0;
10985     stash.dirtyRect.ptr[3] = 0;
10986 
10987     // add white rect at 0, 0 for debug drawing
10988     stash.addWhiteRect(2, 2);
10989 
10990     stash.pushState();
10991     stash.clearState();
10992 
10993     return stash;
10994 
10995   error:
10996     stash.kill();
10997     return null;
10998   }
10999 
11000 public:
11001   /// Add fallback font (FontStash will try to find missing glyph in all fallback fonts before giving up).
11002   bool addFallbackFont (int base, int fallback) nothrow @trusted @nogc {
11003     FONSfont* baseFont = fonts[base];
11004     if (baseFont !is null && baseFont.nfallbacks < FONS_MAX_FALLBACKS) {
11005       baseFont.fallbacks.ptr[baseFont.nfallbacks++] = fallback;
11006       return true;
11007     }
11008     return false;
11009   }
11010 
11011   @property void size (float size) nothrow @trusted @nogc { pragma(inline, true); getState.size = size; } /// Set current font size.
11012   @property float size () const pure nothrow @trusted @nogc { pragma(inline, true); return getState.size; } /// Get current font size.
11013 
11014   @property void spacing (float spacing) nothrow @trusted @nogc { pragma(inline, true); getState.spacing = spacing; } /// Set current letter spacing.
11015   @property float spacing () const pure nothrow @trusted @nogc { pragma(inline, true); return getState.spacing; } /// Get current letter spacing.
11016 
11017   @property void blur (float blur) nothrow @trusted @nogc { pragma(inline, true); getState.blur = blur; } /// Set current letter blur.
11018   @property float blur () const pure nothrow @trusted @nogc { pragma(inline, true); return getState.blur; } /// Get current letter blur.
11019 
11020   @property void textAlign (NVGTextAlign talign) nothrow @trusted @nogc { pragma(inline, true); getState.talign = talign; } /// Set current text align.
11021   @property NVGTextAlign textAlign () const pure nothrow @trusted @nogc { pragma(inline, true); return getState.talign; } /// Get current text align.
11022 
11023   @property void fontId (int font) nothrow @trusted @nogc { pragma(inline, true); getState.font = font; } /// Set current font id.
11024   @property int fontId () const pure nothrow @trusted @nogc { pragma(inline, true); return getState.font; } /// Get current font id.
11025 
11026   @property void fontId (const(char)[] name) nothrow @trusted @nogc { pragma(inline, true); getState.font = getFontByName(name); } /// Set current font using its name.
11027 
11028   /// Check if FontStash has a font with the given name loaded.
11029   bool hasFont (const(char)[] name) const pure nothrow @trusted @nogc { pragma(inline, true); return (getFontByName(name) >= 0); }
11030 
11031   /// Get AA for the current font, or for the specified font.
11032   bool getFontAA (int font=-1) nothrow @trusted @nogc {
11033     FONSstate* state = getState;
11034     if (font < 0) font = state.font;
11035     if (font < 0 || font >= nfonts) return false;
11036     FONSfont* f = fonts[font];
11037     return (f !is null ? !f.font.mono : false);
11038   }
11039 
11040   /// Push current state. Returns `false` if state stack overflowed.
11041   bool pushState () nothrow @trusted @nogc {
11042     if (nstates >= FONS_MAX_STATES) {
11043       if (handleError !is null) handleError(FONSError.StatesOverflow, 0);
11044       return false;
11045     }
11046     if (nstates > 0) {
11047       import core.stdc.string : memcpy;
11048       memcpy(&states[nstates], &states[nstates-1], FONSstate.sizeof);
11049     }
11050     ++nstates;
11051     return true;
11052   }
11053 
11054   /// Pop current state. Returns `false` if state stack underflowed.
11055   bool popState () nothrow @trusted @nogc {
11056     if (nstates <= 1) {
11057       if (handleError !is null) handleError(FONSError.StatesUnderflow, 0);
11058       return false;
11059     }
11060     --nstates;
11061     return true;
11062   }
11063 
11064   /// Clear current state (i.e. set it to some sane defaults).
11065   void clearState () nothrow @trusted @nogc {
11066     FONSstate* state = getState;
11067     state.size = 12.0f;
11068     state.font = 0;
11069     state.blur = 0;
11070     state.spacing = 0;
11071     state.talign.reset;
11072   }
11073 
11074   private enum NoAlias = ":noaa";
11075 
11076   /** Add font to FontStash.
11077    *
11078    * Load scalable font from disk, and add it to FontStash. If you will try to load a font
11079    * with same name and path several times, FontStash will load it only once. Also, you can
11080    * load new disk font for any existing logical font.
11081    *
11082    * Params:
11083    *   name = logical font name, that will be used to select this font later.
11084    *   path = path to disk file with your font.
11085    *   defAA = should FontStash use antialiased font rasterizer?
11086    *
11087    * Returns:
11088    *   font id or [FONS_INVALID].
11089    */
11090   int addFont (const(char)[] name, const(char)[] path, bool defAA=false) nothrow @trusted {
11091     if (path.length == 0 || name.length == 0 || strequci(name, NoAlias)) return FONS_INVALID;
11092     if (path.length > 32768) return FONS_INVALID; // arbitrary limit
11093 
11094     // if font path ends with ":noaa", turn off antialiasing
11095     if (path.length >= NoAlias.length && strequci(path[$-NoAlias.length..$], NoAlias)) {
11096       path = path[0..$-NoAlias.length];
11097       if (path.length == 0) return FONS_INVALID;
11098       defAA = false;
11099     }
11100 
11101     // if font name ends with ":noaa", turn off antialiasing
11102     if (name.length > NoAlias.length && strequci(name[$-NoAlias.length..$], NoAlias)) {
11103       name = name[0..$-NoAlias.length];
11104       defAA = false;
11105     }
11106 
11107     // find a font with the given name
11108     int fidx = findNameInHash(name);
11109     //{ import core.stdc.stdio; printf("loading font '%.*s' [%s] (fidx=%d)...\n", cast(uint)path.length, path.ptr, fontnamebuf.ptr, fidx); }
11110 
11111     int loadFontFile (const(char)[] path) {
11112       // check if existing font (if any) has the same path
11113       if (fidx >= 0) {
11114         import core.stdc.string : strlen;
11115         auto plen = (fonts[fidx].path !is null ? strlen(fonts[fidx].path) : 0);
11116         version(Posix) {
11117           //{ import core.stdc.stdio; printf("+++ font [%.*s] was loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)fonts[fidx].path.length, fonts[fidx].path.ptr); }
11118           if (plen == path.length && fonts[fidx].path[0..plen] == path) {
11119             //{ import core.stdc.stdio; printf("*** font [%.*s] already loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)plen, path.ptr); }
11120             // i found her!
11121             return fidx;
11122           }
11123         } else {
11124           if (plen == path.length && strequci(fonts[fidx].path[0..plen], path)) {
11125             // i found her!
11126             return fidx;
11127           }
11128         }
11129       }
11130       version(Windows) {
11131         // special shitdows check: this will reject fontconfig font names (but still allow things like "c:myfont")
11132         foreach (immutable char ch; path[(path.length >= 2 && path[1] == ':' ? 2 : 0)..$]) if (ch == ':') return FONS_INVALID;
11133       }
11134       // either no such font, or different path
11135       //{ import core.stdc.stdio; printf("trying font [%.*s] from file [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)path.length, path.ptr); }
11136       int xres = FONS_INVALID;
11137       try {
11138         import core.stdc.stdlib : free, malloc;
11139         static if (NanoVegaHasIVVFS) {
11140           auto fl = VFile(path);
11141           auto dataSize = fl.size;
11142           if (dataSize < 16 || dataSize > int.max/32) return FONS_INVALID;
11143           ubyte* data = cast(ubyte*)malloc(cast(uint)dataSize);
11144           if (data is null) assert(0, "out of memory in NanoVega fontstash");
11145           scope(failure) free(data); // oops
11146           fl.rawReadExact(data[0..cast(uint)dataSize]);
11147           fl.close();
11148         } else {
11149           import core.stdc.stdio : FILE, fopen, fclose, fread, ftell, fseek;
11150           import std.internal.cstring : tempCString;
11151           auto fl = fopen(path.tempCString, "rb");
11152           if (fl is null) return FONS_INVALID;
11153           scope(exit) fclose(fl);
11154           if (fseek(fl, 0, 2/*SEEK_END*/) != 0) return FONS_INVALID;
11155           auto dataSize = ftell(fl);
11156           if (fseek(fl, 0, 0/*SEEK_SET*/) != 0) return FONS_INVALID;
11157           if (dataSize < 16 || dataSize > int.max/32) return FONS_INVALID;
11158           ubyte* data = cast(ubyte*)malloc(cast(uint)dataSize);
11159           if (data is null) assert(0, "out of memory in NanoVega fontstash");
11160           scope(failure) free(data); // oops
11161           ubyte* dptr = data;
11162           auto left = cast(uint)dataSize;
11163           while (left > 0) {
11164             auto rd = fread(dptr, 1, left, fl);
11165             if (rd == 0) { free(data); return FONS_INVALID; } // unexpected EOF or reading error, it doesn't matter
11166             dptr += rd;
11167             left -= rd;
11168           }
11169         }
11170         scope(failure) free(data); // oops
11171         // create font data
11172         FONSfontData* fdata = fons__createFontData(data, cast(int)dataSize, true); // free data
11173         fdata.incref();
11174         xres = addFontWithData(name, fdata, defAA);
11175         if (xres == FONS_INVALID) {
11176           fdata.decref(); // this will free [data] and [fdata]
11177         } else {
11178           // remember path
11179           fonts[xres].setPath(path);
11180         }
11181       } catch (Exception e) {
11182         // oops; sorry
11183       }
11184       return xres;
11185     }
11186 
11187     // first try direct path
11188     auto res = loadFontFile(path);
11189     // if loading failed, try fontconfig (if fontconfig is available)
11190     static if (NanoVegaHasFontConfig) {
11191       if (res == FONS_INVALID && fontconfigAvailable) {
11192         // idiotic fontconfig NEVER fails; let's skip it if `path` looks like a path
11193         bool ok = true;
11194         if (path.length > 4 && (path[$-4..$] == ".ttf" || path[$-4..$] == ".ttc")) ok = false;
11195         if (ok) { foreach (immutable char ch; path) if (ch == '/') { ok = false; break; } }
11196         if (ok) {
11197           import std.internal.cstring : tempCString;
11198           FcPattern* pat = FcNameParse(path.tempCString);
11199           if (pat !is null) {
11200             scope(exit) FcPatternDestroy(pat);
11201             if (FcConfigSubstitute(null, pat, FcMatchPattern)) {
11202               FcDefaultSubstitute(pat);
11203               // find the font
11204               FcResult result;
11205               FcPattern* font = FcFontMatch(null, pat, &result);
11206               if (font !is null) {
11207                 scope(exit) FcPatternDestroy(font);
11208                 char* file = null;
11209                 if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
11210                   if (file !is null && file[0]) {
11211                     import core.stdc.string : strlen;
11212                     res = loadFontFile(file[0..strlen(file)]);
11213                   }
11214                 }
11215               }
11216             }
11217           }
11218         }
11219       }
11220     }
11221     return res;
11222   }
11223 
11224   /** Add font to FontStash, using data from memory.
11225    *
11226    * And already loaded font to FontStash. You can replace existing logical fonts.
11227    * But note that you can't remove logical font by passing "empty" data.
11228    *
11229    * $(WARNING If [FONS_INVALID] returned, `data` won't be freed even if `freeData` is `true`.)
11230    *
11231    * Params:
11232    *   name = logical font name, that will be used to select this font later.
11233    *   data = font data.
11234    *   dataSize = font data size.
11235    *   freeData = should FontStash take ownership of the font data?
11236    *   defAA = should FontStash use antialiased font rasterizer?
11237    *
11238    * Returns:
11239    *   font id or [FONS_INVALID].
11240    */
11241   int addFontMem (const(char)[] name, ubyte* data, int dataSize, bool freeData, bool defAA=false) nothrow @trusted @nogc {
11242     if (data is null || dataSize < 16) return FONS_INVALID;
11243     FONSfontData* fdata = fons__createFontData(data, dataSize, freeData);
11244     fdata.incref();
11245     auto res = addFontWithData(name, fdata, defAA);
11246     if (res == FONS_INVALID) {
11247       // we promised to not free data on error
11248       fdata.freeData = false;
11249       fdata.decref(); // this will free [fdata]
11250     }
11251     return res;
11252   }
11253 
11254   /** Add fonts from another FontStash.
11255    *
11256    * This is more effective (and faster) than reloading fonts, because internally font data
11257    * is reference counted.
11258    */
11259   void addFontsFrom (FONSContext source) nothrow @trusted @nogc {
11260     if (source is null) return;
11261     foreach (FONSfont* font; source.fonts[0..source.nfonts]) {
11262       if (font !is null) {
11263         auto newidx = addCookedFont(font);
11264         FONSfont* newfont = fonts[newidx];
11265         assert(newfont !is null);
11266         assert(newfont.path is null);
11267         // copy path
11268         if (font.path !is null && font.path[0]) {
11269           import core.stdc.stdlib : malloc;
11270           import core.stdc.string : strcpy, strlen;
11271           newfont.path = cast(char*)malloc(strlen(font.path)+1);
11272           if (newfont.path is null) assert(0, "FONS: out of memory");
11273           strcpy(newfont.path, font.path);
11274         }
11275       }
11276     }
11277   }
11278 
11279   /// Returns logical font name corresponding to the given font id, or `null`.
11280   /// $(WARNING Copy returned name, as name buffer can be invalidated by next FontStash API call!)
11281   const(char)[] getNameById (int idx) const pure nothrow @trusted @nogc {
11282     if (idx < 0 || idx >= nfonts || fonts[idx] is null) return null;
11283     return fonts[idx].name[0..fonts[idx].namelen];
11284   }
11285 
11286   /// Returns font id corresponding to the given logical font name, or [FONS_INVALID].
11287   int getFontByName (const(char)[] name) const pure nothrow @trusted @nogc {
11288     //{ import core.stdc.stdio; printf("fonsGetFontByName: [%.*s]\n", cast(uint)name.length, name.ptr); }
11289     // remove ":noaa" suffix
11290     if (name.length >= NoAlias.length && strequci(name[$-NoAlias.length..$], NoAlias)) {
11291       name = name[0..$-NoAlias.length];
11292     }
11293     if (name.length == 0) return FONS_INVALID;
11294     return findNameInHash(name);
11295   }
11296 
11297   /** Measures the specified text string. Parameter bounds should be a float[4],
11298    * if the bounding box of the text should be returned. The bounds value are [xmin, ymin, xmax, ymax]
11299    * Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
11300    */
11301   float getTextBounds(T) (float x, float y, const(T)[] str, float[] bounds) nothrow @trusted @nogc if (isAnyCharType!T) {
11302     FONSstate* state = getState;
11303     uint codepoint;
11304     uint utf8state = 0;
11305     FONSQuad q;
11306     FONSglyph* glyph = null;
11307     int prevGlyphIndex = -1;
11308     short isize = cast(short)(state.size*10.0f);
11309     short iblur = cast(short)state.blur;
11310     FONSfont* font;
11311 
11312     if (state.font < 0 || state.font >= nfonts) return 0;
11313     font = fonts[state.font];
11314     if (font is null || font.fdata is null) return 0;
11315 
11316     float scale = fons__tt_getPixelHeightScale(&font.font, cast(float)isize/10.0f);
11317 
11318     // Align vertically.
11319     y += getVertAlign(font, state.talign, isize);
11320 
11321     float minx = x, maxx = x;
11322     float miny = y, maxy = y;
11323     float startx = x;
11324 
11325     foreach (T ch; str) {
11326       static if (T.sizeof == 1) {
11327         //if (fons__decutf8(&utf8state, &codepoint, *cast(const(ubyte)*)str)) continue;
11328         mixin(DecUtfMixin!("utf8state", "codepoint", "(cast(ubyte)ch)"));
11329         if (utf8state) continue;
11330       } else {
11331         static if (T.sizeof == 4) {
11332           if (ch > dchar.max) ch = 0xFFFD;
11333         }
11334         codepoint = cast(uint)ch;
11335       }
11336       glyph = getGlyph(font, codepoint, isize, iblur, FONSBitmapFlag.Optional);
11337       if (glyph !is null) {
11338         getQuad(font, prevGlyphIndex, glyph, isize/10.0f, scale, state.spacing, &x, &y, &q);
11339         if (q.x0 < minx) minx = q.x0;
11340         if (q.x1 > maxx) maxx = q.x1;
11341         if (params.isZeroTopLeft) {
11342           if (q.y0 < miny) miny = q.y0;
11343           if (q.y1 > maxy) maxy = q.y1;
11344         } else {
11345           if (q.y1 < miny) miny = q.y1;
11346           if (q.y0 > maxy) maxy = q.y0;
11347         }
11348         prevGlyphIndex = glyph.index;
11349       } else {
11350         //{ import core.stdc.stdio; printf("NO GLYPH FOR 0x%04x\n", cast(uint)codepoint); }
11351         prevGlyphIndex = -1;
11352       }
11353     }
11354 
11355     float advance = x-startx;
11356     //{ import core.stdc.stdio; printf("***: x=%g; startx=%g; advance=%g\n", cast(double)x, cast(double)startx, cast(double)advance); }
11357 
11358     // Align horizontally
11359     if (state.talign.left) {
11360       // empty
11361     } else if (state.talign.right) {
11362       minx -= advance;
11363       maxx -= advance;
11364     } else if (state.talign.center) {
11365       minx -= advance*0.5f;
11366       maxx -= advance*0.5f;
11367     }
11368 
11369     if (bounds.length) {
11370       if (bounds.length > 0) bounds.ptr[0] = minx;
11371       if (bounds.length > 1) bounds.ptr[1] = miny;
11372       if (bounds.length > 2) bounds.ptr[2] = maxx;
11373       if (bounds.length > 3) bounds.ptr[3] = maxy;
11374     }
11375 
11376     return advance;
11377   }
11378 
11379   /// Returns various font metrics. Any argument can be `null` if you aren't interested in its value.
11380   void getVertMetrics (float* ascender, float* descender, float* lineh) nothrow @trusted @nogc {
11381     FONSstate* state = getState;
11382     if (state.font < 0 || state.font >= nfonts) {
11383       if (ascender !is null) *ascender = 0;
11384       if (descender !is null) *descender = 0;
11385       if (lineh !is null) *lineh = 0;
11386     } else {
11387       FONSfont* font = fonts[state.font];
11388       if (font is null || font.fdata is null) {
11389         if (ascender !is null) *ascender = 0;
11390         if (descender !is null) *descender = 0;
11391         if (lineh !is null) *lineh = 0;
11392       } else {
11393         short isize = cast(short)(state.size*10.0f);
11394         if (ascender !is null) *ascender = font.ascender*isize/10.0f;
11395         if (descender !is null) *descender = font.descender*isize/10.0f;
11396         if (lineh !is null) *lineh = font.lineh*isize/10.0f;
11397       }
11398     }
11399   }
11400 
11401   /// Returns line bounds. Any argument can be `null` if you aren't interested in its value.
11402   void getLineBounds (float y, float* minyp, float* maxyp) nothrow @trusted @nogc {
11403     FONSfont* font;
11404     FONSstate* state = getState;
11405     short isize;
11406 
11407     if (minyp !is null) *minyp = 0;
11408     if (maxyp !is null) *maxyp = 0;
11409 
11410     if (state.font < 0 || state.font >= nfonts) return;
11411     font = fonts[state.font];
11412     isize = cast(short)(state.size*10.0f);
11413     if (font is null || font.fdata is null) return;
11414 
11415     y += getVertAlign(font, state.talign, isize);
11416 
11417     if (params.isZeroTopLeft) {
11418       immutable float miny = y-font.ascender*cast(float)isize/10.0f;
11419       immutable float maxy = miny+font.lineh*isize/10.0f;
11420       if (minyp !is null) *minyp = miny;
11421       if (maxyp !is null) *maxyp = maxy;
11422     } else {
11423       immutable float maxy = y+font.descender*cast(float)isize/10.0f;
11424       immutable float miny = maxy-font.lineh*isize/10.0f;
11425       if (minyp !is null) *minyp = miny;
11426       if (maxyp !is null) *maxyp = maxy;
11427     }
11428   }
11429 
11430   /// Returns font line height.
11431   float fontHeight () nothrow @trusted @nogc {
11432     float res = void;
11433     getVertMetrics(null, null, &res);
11434     return res;
11435   }
11436 
11437   /// Returns font ascender (positive).
11438   float fontAscender () nothrow @trusted @nogc {
11439     float res = void;
11440     getVertMetrics(&res, null, null);
11441     return res;
11442   }
11443 
11444   /// Returns font descender (negative).
11445   float fontDescender () nothrow @trusted @nogc {
11446     float res = void;
11447     getVertMetrics(null, &res, null);
11448     return res;
11449   }
11450 
11451   //TODO: document this
11452   const(ubyte)* getTextureData (int* width, int* height) nothrow @trusted @nogc {
11453     if (width !is null) *width = params.width;
11454     if (height !is null) *height = params.height;
11455     return texData;
11456   }
11457 
11458   //TODO: document this
11459   bool validateTexture (int* dirty) nothrow @trusted @nogc {
11460     if (dirtyRect.ptr[0] < dirtyRect.ptr[2] && dirtyRect.ptr[1] < dirtyRect.ptr[3]) {
11461       dirty[0] = dirtyRect.ptr[0];
11462       dirty[1] = dirtyRect.ptr[1];
11463       dirty[2] = dirtyRect.ptr[2];
11464       dirty[3] = dirtyRect.ptr[3];
11465       // reset dirty rect
11466       dirtyRect.ptr[0] = params.width;
11467       dirtyRect.ptr[1] = params.height;
11468       dirtyRect.ptr[2] = 0;
11469       dirtyRect.ptr[3] = 0;
11470       return true;
11471     }
11472     return false;
11473   }
11474 
11475   //TODO: document this
11476   void errorCallback (void delegate (FONSError error, int val) nothrow @trusted @nogc callback) nothrow @trusted @nogc {
11477     handleError = callback;
11478   }
11479 
11480   //TODO: document this
11481   void getAtlasSize (int* width, int* height) const pure nothrow @trusted @nogc {
11482     if (width !is null) *width = params.width;
11483     if (height !is null) *height = params.height;
11484   }
11485 
11486   //TODO: document this
11487   bool expandAtlas (int width, int height) nothrow @trusted @nogc {
11488     import core.stdc.stdlib : free;
11489     import core.stdc.string : memcpy, memset;
11490 
11491     int maxy = 0;
11492     ubyte* data = null;
11493 
11494     width = nvg__max(width, params.width);
11495     height = nvg__max(height, params.height);
11496 
11497     if (width == params.width && height == params.height) return true;
11498 
11499     // Flush pending glyphs.
11500     flush();
11501 
11502     // Create new texture
11503     if (params.renderResize !is null) {
11504       if (params.renderResize(params.userPtr, width, height) == 0) return false;
11505     }
11506     // Copy old texture data over.
11507     data = cast(ubyte*)malloc(width*height);
11508     if (data is null) return 0;
11509     foreach (immutable int i; 0..params.height) {
11510       ubyte* dst = &data[i*width];
11511       ubyte* src = &texData[i*params.width];
11512       memcpy(dst, src, params.width);
11513       if (width > params.width) memset(dst+params.width, 0, width-params.width);
11514     }
11515     if (height > params.height) memset(&data[params.height*width], 0, (height-params.height)*width);
11516 
11517     free(texData);
11518     texData = data;
11519 
11520     // Increase atlas size
11521     atlas.expand(width, height);
11522 
11523     // Add existing data as dirty.
11524     foreach (immutable int i; 0..atlas.nnodes) maxy = nvg__max(maxy, atlas.nodes[i].y);
11525     dirtyRect.ptr[0] = 0;
11526     dirtyRect.ptr[1] = 0;
11527     dirtyRect.ptr[2] = params.width;
11528     dirtyRect.ptr[3] = maxy;
11529 
11530     params.width = width;
11531     params.height = height;
11532     itw = 1.0f/params.width;
11533     ith = 1.0f/params.height;
11534 
11535     return true;
11536   }
11537 
11538   //TODO: document this
11539   bool resetAtlas (int width, int height) nothrow @trusted @nogc {
11540     import core.stdc.stdlib : realloc;
11541     import core.stdc.string : memcpy, memset;
11542 
11543     // flush pending glyphs
11544     flush();
11545 
11546     // create new texture
11547     if (params.renderResize !is null) {
11548       if (params.renderResize(params.userPtr, width, height) == 0) return false;
11549     }
11550 
11551     // reset atlas
11552     atlas.reset(width, height);
11553 
11554     // clear texture data
11555     texData = cast(ubyte*)realloc(texData, width*height);
11556     if (texData is null) assert(0, "FONS: out of memory");
11557     memset(texData, 0, width*height);
11558 
11559     // reset dirty rect
11560     dirtyRect.ptr[0] = width;
11561     dirtyRect.ptr[1] = height;
11562     dirtyRect.ptr[2] = 0;
11563     dirtyRect.ptr[3] = 0;
11564 
11565     // Reset cached glyphs
11566     foreach (FONSfont* font; fonts[0..nfonts]) {
11567       if (font !is null) {
11568         font.nglyphs = 0;
11569         font.lut.ptr[0..FONS_HASH_LUT_SIZE] = -1;
11570       }
11571     }
11572 
11573     params.width = width;
11574     params.height = height;
11575     itw = 1.0f/params.width;
11576     ith = 1.0f/params.height;
11577 
11578     // Add white rect at 0, 0 for debug drawing.
11579     addWhiteRect(2, 2);
11580 
11581     return true;
11582   }
11583 
11584   //TODO: document this
11585   bool getPathBounds (dchar dch, float[] bounds) nothrow @trusted @nogc {
11586     if (bounds.length > 4) bounds = bounds.ptr[0..4];
11587     static if (is(typeof(&fons__nvg__bounds))) {
11588       FONSstate* state = getState;
11589       if (state.font < 0 || state.font >= nfonts) { bounds[] = 0; return false; }
11590       FONSfont* font;
11591       auto g = findGlyphForCP(fonts[state.font], dch, &font);
11592       if (g == 0) { bounds[] = 0; return false; }
11593       assert(font !is null);
11594       return fons__nvg__bounds(&font.font, g, bounds);
11595     } else {
11596       bounds[] = 0;
11597       return false;
11598     }
11599   }
11600 
11601   //TODO: document this
11602   bool toPath() (NVGContext vg, dchar dch, float[] bounds=null) nothrow @trusted @nogc {
11603     if (bounds.length > 4) bounds = bounds.ptr[0..4];
11604     static if (is(typeof(&fons__nvg__toPath))) {
11605       if (vg is null) { bounds[] = 0; return false; }
11606       FONSstate* state = getState;
11607       if (state.font < 0 || state.font >= nfonts) { bounds[] = 0; return false; }
11608       FONSfont* font;
11609       auto g = findGlyphForCP(fonts[state.font], dch, &font);
11610       if (g == 0) { bounds[] = 0; return false; }
11611       assert(font !is null);
11612       return fons__nvg__toPath(vg, &font.font, g, bounds);
11613     } else {
11614       bounds[] = 0;
11615       return false;
11616     }
11617   }
11618 
11619   //TODO: document this
11620   bool toOutline (dchar dch, NVGPathOutline.DataStore* ol) nothrow @trusted @nogc {
11621     if (ol is null) return false;
11622     static if (is(typeof(&fons__nvg__toOutline))) {
11623       FONSstate* state = getState;
11624       if (state.font < 0 || state.font >= nfonts) return false;
11625       FONSfont* font;
11626       auto g = findGlyphForCP(fonts[state.font], dch, &font);
11627       if (g == 0) return false;
11628       assert(font !is null);
11629       return fons__nvg__toOutline(&font.font, g, ol);
11630     } else {
11631       return false;
11632     }
11633   }
11634 
11635   //TODO: document this
11636   FONSglyph* getGlyph (FONSfont* font, uint codepoint, short isize, short iblur, FONSBitmapFlag bitmapOption) nothrow @trusted @nogc {
11637     static uint fons__hashint() (uint a) pure nothrow @safe @nogc {
11638       pragma(inline, true);
11639       a += ~(a<<15);
11640       a ^=  (a>>10);
11641       a +=  (a<<3);
11642       a ^=  (a>>6);
11643       a += ~(a<<11);
11644       a ^=  (a>>16);
11645       return a;
11646     }
11647 
11648     // based on Exponential blur, Jani Huhtanen, 2006
11649     enum APREC = 16;
11650     enum ZPREC = 7;
11651 
11652     static void fons__blurCols (ubyte* dst, int w, int h, int dstStride, int alpha) nothrow @trusted @nogc {
11653       foreach (immutable int y; 0..h) {
11654         int z = 0; // force zero border
11655         foreach (int x; 1..w) {
11656           z += (alpha*((cast(int)(dst[x])<<ZPREC)-z))>>APREC;
11657           dst[x] = cast(ubyte)(z>>ZPREC);
11658         }
11659         dst[w-1] = 0; // force zero border
11660         z = 0;
11661         for (int x = w-2; x >= 0; --x) {
11662           z += (alpha*((cast(int)(dst[x])<<ZPREC)-z))>>APREC;
11663           dst[x] = cast(ubyte)(z>>ZPREC);
11664         }
11665         dst[0] = 0; // force zero border
11666         dst += dstStride;
11667       }
11668     }
11669 
11670     static void fons__blurRows (ubyte* dst, int w, int h, int dstStride, int alpha) nothrow @trusted @nogc {
11671       foreach (immutable int x; 0..w) {
11672         int z = 0; // force zero border
11673         for (int y = dstStride; y < h*dstStride; y += dstStride) {
11674           z += (alpha*((cast(int)(dst[y])<<ZPREC)-z))>>APREC;
11675           dst[y] = cast(ubyte)(z>>ZPREC);
11676         }
11677         dst[(h-1)*dstStride] = 0; // force zero border
11678         z = 0;
11679         for (int y = (h-2)*dstStride; y >= 0; y -= dstStride) {
11680           z += (alpha*((cast(int)(dst[y])<<ZPREC)-z))>>APREC;
11681           dst[y] = cast(ubyte)(z>>ZPREC);
11682         }
11683         dst[0] = 0; // force zero border
11684         ++dst;
11685       }
11686     }
11687 
11688     static void fons__blur (ubyte* dst, int w, int h, int dstStride, int blur) nothrow @trusted @nogc {
11689       import std.math : expf = exp;
11690       if (blur < 1) return;
11691       // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
11692       immutable float sigma = cast(float)blur*0.57735f; // 1/sqrt(3)
11693       int alpha = cast(int)((1<<APREC)*(1.0f-expf(-2.3f/(sigma+1.0f))));
11694       fons__blurRows(dst, w, h, dstStride, alpha);
11695       fons__blurCols(dst, w, h, dstStride, alpha);
11696       fons__blurRows(dst, w, h, dstStride, alpha);
11697       fons__blurCols(dst, w, h, dstStride, alpha);
11698       //fons__blurrows(dst, w, h, dstStride, alpha);
11699       //fons__blurcols(dst, w, h, dstStride, alpha);
11700     }
11701 
11702     int advance, lsb, x0, y0, x1, y1, gx, gy;
11703     FONSglyph* glyph = null;
11704     float size = isize/10.0f;
11705     FONSfont* renderFont = font;
11706 
11707     if (isize < 2) return null;
11708     if (iblur > 20) iblur = 20;
11709     int pad = iblur+2;
11710 
11711     // Reset allocator.
11712     nscratch = 0;
11713 
11714     // Find code point and size.
11715     uint h = fons__hashint(codepoint)&(FONS_HASH_LUT_SIZE-1);
11716     int i = font.lut.ptr[h];
11717     while (i != -1) {
11718       //if (font.glyphs[i].codepoint == codepoint && font.glyphs[i].size == isize && font.glyphs[i].blur == iblur) return &font.glyphs[i];
11719       if (font.glyphs[i].codepoint == codepoint && font.glyphs[i].size == isize && font.glyphs[i].blur == iblur) {
11720         glyph = &font.glyphs[i];
11721         // Negative coordinate indicates there is no bitmap data created.
11722         if (bitmapOption == FONSBitmapFlag.Optional || (glyph.x0 >= 0 && glyph.y0 >= 0)) return glyph;
11723         // At this point, glyph exists but the bitmap data is not yet created.
11724         break;
11725       }
11726       i = font.glyphs[i].next;
11727     }
11728 
11729     // Create a new glyph or rasterize bitmap data for a cached glyph.
11730     //scale = fons__tt_getPixelHeightScale(&font.font, size);
11731     int g = findGlyphForCP(font, cast(dchar)codepoint, &renderFont);
11732     // It is possible that we did not find a fallback glyph.
11733     // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
11734 
11735     float scale = fons__tt_getPixelHeightScale(&renderFont.font, size);
11736     fons__tt_buildGlyphBitmap(&renderFont.font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
11737     int gw = x1-x0+pad*2;
11738     int gh = y1-y0+pad*2;
11739 
11740     // Determines the spot to draw glyph in the atlas.
11741     if (bitmapOption == FONSBitmapFlag.Required) {
11742       // Find free spot for the rect in the atlas.
11743       bool added = atlas.addRect(gw, gh, &gx, &gy);
11744       if (!added && handleError !is null) {
11745         // Atlas is full, let the user to resize the atlas (or not), and try again.
11746         handleError(FONSError.AtlasFull, 0);
11747         added = atlas.addRect(gw, gh, &gx, &gy);
11748       }
11749       if (!added) return null;
11750     } else {
11751       // Negative coordinate indicates there is no bitmap data created.
11752       gx = -1;
11753       gy = -1;
11754     }
11755 
11756     // Init glyph.
11757     if (glyph is null) {
11758       glyph = font.allocGlyph();
11759       glyph.codepoint = codepoint;
11760       glyph.size = isize;
11761       glyph.blur = iblur;
11762       glyph.next = 0;
11763 
11764       // Insert char to hash lookup.
11765       glyph.next = font.lut.ptr[h];
11766       font.lut.ptr[h] = font.nglyphs-1;
11767     }
11768     glyph.index = g;
11769     glyph.x0 = cast(short)gx;
11770     glyph.y0 = cast(short)gy;
11771     glyph.x1 = cast(short)(glyph.x0+gw);
11772     glyph.y1 = cast(short)(glyph.y0+gh);
11773     glyph.xadv = cast(short)(scale*advance*10.0f);
11774     glyph.xoff = cast(short)(x0-pad);
11775     glyph.yoff = cast(short)(y0-pad);
11776 
11777     if (bitmapOption == FONSBitmapFlag.Optional) return glyph;
11778 
11779     // Rasterize
11780     ubyte* dst = &texData[(glyph.x0+pad)+(glyph.y0+pad)*params.width];
11781     fons__tt_renderGlyphBitmap(&font.font, dst, gw-pad*2, gh-pad*2, params.width, scale, scale, g);
11782 
11783     // Make sure there is one pixel empty border.
11784     dst = &texData[glyph.x0+glyph.y0*params.width];
11785     foreach (immutable int y; 0..gh) {
11786       dst[y*params.width] = 0;
11787       dst[gw-1+y*params.width] = 0;
11788     }
11789     foreach (immutable int x; 0..gw) {
11790       dst[x] = 0;
11791       dst[x+(gh-1)*params.width] = 0;
11792     }
11793 
11794     // Debug code to color the glyph background
11795     version(none) {
11796       foreach (immutable yy; 0..gh) {
11797         foreach (immutable xx; 0..gw) {
11798           int a = cast(int)dst[xx+yy*params.width]+42;
11799           if (a > 255) a = 255;
11800           dst[xx+yy*params.width] = cast(ubyte)a;
11801         }
11802       }
11803     }
11804 
11805     // Blur
11806     if (iblur > 0) {
11807       nscratch = 0;
11808       ubyte* bdst = &texData[glyph.x0+glyph.y0*params.width];
11809       fons__blur(bdst, gw, gh, params.width, iblur);
11810     }
11811 
11812     dirtyRect.ptr[0] = nvg__min(dirtyRect.ptr[0], glyph.x0);
11813     dirtyRect.ptr[1] = nvg__min(dirtyRect.ptr[1], glyph.y0);
11814     dirtyRect.ptr[2] = nvg__max(dirtyRect.ptr[2], glyph.x1);
11815     dirtyRect.ptr[3] = nvg__max(dirtyRect.ptr[3], glyph.y1);
11816 
11817     return glyph;
11818   }
11819 
11820   //TODO: document this
11821   void getQuad (FONSfont* font, int prevGlyphIndex, FONSglyph* glyph, float size, float scale, float spacing, float* x, float* y, FONSQuad* q) nothrow @trusted @nogc {
11822     if (prevGlyphIndex >= 0) {
11823       immutable float adv = fons__tt_getGlyphKernAdvance(&font.font, size, prevGlyphIndex, glyph.index)/**scale*/; //k8: do we really need scale here?
11824       //if (adv != 0) { import core.stdc.stdio; printf("adv=%g (scale=%g; spacing=%g)\n", cast(double)adv, cast(double)scale, cast(double)spacing); }
11825       *x += cast(int)(adv+spacing /*+0.5f*/); //k8: for me, it looks better this way (with non-aa fonts)
11826     }
11827 
11828     // Each glyph has 2px border to allow good interpolation,
11829     // one pixel to prevent leaking, and one to allow good interpolation for rendering.
11830     // Inset the texture region by one pixel for correct interpolation.
11831     immutable float xoff = cast(short)(glyph.xoff+1);
11832     immutable float yoff = cast(short)(glyph.yoff+1);
11833     immutable float x0 = cast(float)(glyph.x0+1);
11834     immutable float y0 = cast(float)(glyph.y0+1);
11835     immutable float x1 = cast(float)(glyph.x1-1);
11836     immutable float y1 = cast(float)(glyph.y1-1);
11837 
11838     if (params.isZeroTopLeft) {
11839       immutable float rx = cast(float)cast(int)(*x+xoff);
11840       immutable float ry = cast(float)cast(int)(*y+yoff);
11841 
11842       q.x0 = rx;
11843       q.y0 = ry;
11844       q.x1 = rx+x1-x0;
11845       q.y1 = ry+y1-y0;
11846 
11847       q.s0 = x0*itw;
11848       q.t0 = y0*ith;
11849       q.s1 = x1*itw;
11850       q.t1 = y1*ith;
11851     } else {
11852       immutable float rx = cast(float)cast(int)(*x+xoff);
11853       immutable float ry = cast(float)cast(int)(*y-yoff);
11854 
11855       q.x0 = rx;
11856       q.y0 = ry;
11857       q.x1 = rx+x1-x0;
11858       q.y1 = ry-y1+y0;
11859 
11860       q.s0 = x0*itw;
11861       q.t0 = y0*ith;
11862       q.s1 = x1*itw;
11863       q.t1 = y1*ith;
11864     }
11865 
11866     *x += cast(int)(glyph.xadv/10.0f+0.5f);
11867   }
11868 
11869   void flush () nothrow @trusted @nogc {
11870     // flush texture
11871     if (dirtyRect.ptr[0] < dirtyRect.ptr[2] && dirtyRect.ptr[1] < dirtyRect.ptr[3]) {
11872       if (params.renderUpdate !is null) params.renderUpdate(params.userPtr, dirtyRect.ptr, texData);
11873       // reset dirty rect
11874       dirtyRect.ptr[0] = params.width;
11875       dirtyRect.ptr[1] = params.height;
11876       dirtyRect.ptr[2] = 0;
11877       dirtyRect.ptr[3] = 0;
11878     }
11879   }
11880 }
11881 
11882 /// Free all resources used by the `stash`, and `stash` itself.
11883 /// Group: font_stash
11884 public void kill (ref FONSContext stash) nothrow @trusted @nogc {
11885   import core.stdc.stdlib : free;
11886   if (stash is null) return;
11887   stash.clear();
11888   free(stash);
11889   stash = null;
11890 }
11891 
11892 
11893 // ////////////////////////////////////////////////////////////////////////// //
11894 void* fons__tmpalloc (usize size, void* up) nothrow @trusted @nogc {
11895   ubyte* ptr;
11896   FONSContext stash = cast(FONSContext)up;
11897   // 16-byte align the returned pointer
11898   size = (size+0xf)&~0xf;
11899   if (stash.nscratch+cast(int)size > FONS_SCRATCH_BUF_SIZE) {
11900     if (stash.handleError !is null) stash.handleError(FONSError.ScratchFull, stash.nscratch+cast(int)size);
11901     return null;
11902   }
11903   ptr = stash.scratch+stash.nscratch;
11904   stash.nscratch += cast(int)size;
11905   return ptr;
11906 }
11907 
11908 void fons__tmpfree (void* ptr, void* up) nothrow @trusted @nogc {
11909   // empty
11910 }
11911 
11912 
11913 // ////////////////////////////////////////////////////////////////////////// //
11914 // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
11915 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
11916 
11917 enum FONS_UTF8_ACCEPT = 0;
11918 enum FONS_UTF8_REJECT = 12;
11919 
11920 static immutable ubyte[364] utf8d = [
11921   // The first part of the table maps bytes to character classes that
11922   // to reduce the size of the transition table and create bitmasks.
11923   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11924   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11925   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11926   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11927   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
11928   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
11929   8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
11930   10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
11931 
11932   // The second part is a transition table that maps a combination
11933   // of a state of the automaton and a character class to a state.
11934   0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
11935   12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12,
11936   12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12,
11937   12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12,
11938   12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
11939 ];
11940 
11941 private enum DecUtfMixin(string state, string codep, string byte_) =
11942 `{
11943   uint type_ = utf8d.ptr[`~byte_~`];
11944   `~codep~` = (`~state~` != FONS_UTF8_ACCEPT ? (`~byte_~`&0x3fu)|(`~codep~`<<6) : (0xff>>type_)&`~byte_~`);
11945   if ((`~state~` = utf8d.ptr[256+`~state~`+type_]) == FONS_UTF8_REJECT) {
11946     `~state~` = FONS_UTF8_ACCEPT;
11947     `~codep~` = 0xFFFD;
11948   }
11949  }`;
11950 
11951 /*
11952 uint fons__decutf8 (uint* state, uint* codep, uint byte_) {
11953   pragma(inline, true);
11954   uint type = utf8d.ptr[byte_];
11955   *codep = (*state != FONS_UTF8_ACCEPT ? (byte_&0x3fu)|(*codep<<6) : (0xff>>type)&byte_);
11956   *state = utf8d.ptr[256 + *state+type];
11957   return *state;
11958 }
11959 */
11960 
11961 
11962 // ////////////////////////////////////////////////////////////////////////// //
11963 /// This iterator can be used to do text measurement.
11964 /// $(WARNING Don't add new fonts to stash while you are iterating, or you WILL get segfault!)
11965 /// Group: font_stash
11966 public struct FONSTextBoundsIterator {
11967 private:
11968   FONSContext stash;
11969   FONSstate state;
11970   uint codepoint = 0xFFFD;
11971   uint utf8state = 0;
11972   int prevGlyphIndex = -1;
11973   short isize, iblur;
11974   float scale = 0;
11975   FONSfont* font;
11976   float startx = 0, x = 0, y = 0;
11977   float minx = 0, miny = 0, maxx = 0, maxy = 0;
11978 
11979 private:
11980   void clear () nothrow @trusted @nogc {
11981     import core.stdc.string : memset;
11982     memset(&this, 0, this.sizeof);
11983     this.prevGlyphIndex = -1;
11984     this.codepoint = 0xFFFD;
11985   }
11986 
11987 public:
11988   /// Initialize iterator with the current FontStash state. FontStash state can be changed after initialization without affecting the iterator.
11989   this (FONSContext astash, float ax=0, float ay=0) nothrow @trusted @nogc { reset(astash, ax, ay); }
11990 
11991   /// (Re)initialize iterator with the current FontStash state. FontStash state can be changed after initialization without affecting the iterator.
11992   void reset (FONSContext astash, float ax=0, float ay=0) nothrow @trusted @nogc {
11993     clear();
11994 
11995     if (astash is null || astash.nstates == 0) return;
11996 
11997     stash = astash;
11998     state = *stash.getState;
11999 
12000     if (state.font < 0 || state.font >= stash.nfonts) { clear(); return; }
12001     font = stash.fonts[state.font];
12002     if (font is null || font.fdata is null) { clear(); return; }
12003 
12004     x = ax;
12005     y = ay;
12006     isize = cast(short)(state.size*10.0f);
12007     iblur = cast(short)state.blur;
12008     scale = fons__tt_getPixelHeightScale(&font.font, cast(float)isize/10.0f);
12009 
12010     // align vertically
12011     y += astash.getVertAlign(font, state.talign, isize);
12012 
12013     minx = maxx = x;
12014     miny = maxy = y;
12015     startx = x;
12016   }
12017 
12018   /// Can this iterator be used?
12019   @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (stash !is null); }
12020 
12021   /// Put some text into iterator, calculate new values.
12022   void put(T) (const(T)[] str...) nothrow @trusted @nogc if (isAnyCharType!T) {
12023     enum DoCodePointMixin = q{
12024       glyph = stash.getGlyph(font, codepoint, isize, iblur, FONSBitmapFlag.Optional);
12025       if (glyph !is null) {
12026         stash.getQuad(font, prevGlyphIndex, glyph, isize/10.0f, scale, state.spacing, &x, &y, &q);
12027         if (q.x0 < minx) minx = q.x0;
12028         if (q.x1 > maxx) maxx = q.x1;
12029         if (stash.params.isZeroTopLeft) {
12030           if (q.y0 < miny) miny = q.y0;
12031           if (q.y1 > maxy) maxy = q.y1;
12032         } else {
12033           if (q.y1 < miny) miny = q.y1;
12034           if (q.y0 > maxy) maxy = q.y0;
12035         }
12036         prevGlyphIndex = glyph.index;
12037       } else {
12038         prevGlyphIndex = -1;
12039       }
12040     };
12041 
12042     if (stash is null || str.length == 0) return; // alas
12043 
12044     FONSQuad q;
12045     FONSglyph* glyph;
12046 
12047     static if (is(T == char)) {
12048       foreach (char ch; str) {
12049         mixin(DecUtfMixin!("utf8state", "codepoint", "cast(ubyte)ch"));
12050         if (utf8state) continue; // full char is not collected yet
12051         mixin(DoCodePointMixin);
12052       }
12053     } else {
12054       if (utf8state) {
12055         utf8state = 0;
12056         codepoint = 0xFFFD;
12057         mixin(DoCodePointMixin);
12058       }
12059       foreach (T dch; str) {
12060         static if (is(T == dchar)) {
12061           if (dch > dchar.max) dch = 0xFFFD;
12062         }
12063         codepoint = cast(uint)dch;
12064         mixin(DoCodePointMixin);
12065       }
12066     }
12067   }
12068 
12069   /// Returns current advance.
12070   @property float advance () const pure nothrow @safe @nogc { pragma(inline, true); return (stash !is null ? x-startx : 0); }
12071 
12072   /// Returns current text bounds.
12073   void getBounds (ref float[4] bounds) const pure nothrow @safe @nogc {
12074     if (stash is null) { bounds[] = 0; return; }
12075     float lminx = minx, lmaxx = maxx;
12076     // align horizontally
12077     if (state.talign.left) {
12078       // empty
12079     } else if (state.talign.right) {
12080       float ca = advance;
12081       lminx -= ca;
12082       lmaxx -= ca;
12083     } else if (state.talign.center) {
12084       float ca = advance*0.5f;
12085       lminx -= ca;
12086       lmaxx -= ca;
12087     }
12088     bounds[0] = lminx;
12089     bounds[1] = miny;
12090     bounds[2] = lmaxx;
12091     bounds[3] = maxy;
12092   }
12093 
12094   /// Returns current horizontal text bounds.
12095   void getHBounds (out float xmin, out float xmax) nothrow @trusted @nogc {
12096     if (stash !is null) {
12097       float lminx = minx, lmaxx = maxx;
12098       // align horizontally
12099       if (state.talign.left) {
12100         // empty
12101       } else if (state.talign.right) {
12102         float ca = advance;
12103         lminx -= ca;
12104         lmaxx -= ca;
12105       } else if (state.talign.center) {
12106         float ca = advance*0.5f;
12107         lminx -= ca;
12108         lmaxx -= ca;
12109       }
12110       xmin = lminx;
12111       xmax = lmaxx;
12112     } else {
12113       xmin = xmax = 0;
12114     }
12115   }
12116 
12117   /// Returns current vertical text bounds.
12118   void getVBounds (out float ymin, out float ymax) nothrow @trusted @nogc {
12119     pragma(inline, true);
12120     if (stash !is null) {
12121       ymin = miny;
12122       ymax = maxy;
12123     } else {
12124       ymin = ymax = 0;
12125     }
12126   }
12127 
12128   /// Returns font line height.
12129   float lineHeight () nothrow @trusted @nogc {
12130     pragma(inline, true);
12131     return (stash !is null ? stash.fonts[state.font].lineh*cast(short)(state.size*10.0f)/10.0f : 0);
12132   }
12133 
12134   /// Returns font ascender (positive).
12135   float ascender () nothrow @trusted @nogc {
12136     pragma(inline, true);
12137     return (stash !is null ? stash.fonts[state.font].ascender*cast(short)(state.size*10.0f)/10.0f : 0);
12138   }
12139 
12140   /// Returns font descender (negative).
12141   float descender () nothrow @trusted @nogc {
12142     pragma(inline, true);
12143     return (stash !is null ? stash.fonts[state.font].descender*cast(short)(state.size*10.0f)/10.0f : 0);
12144   }
12145 }
12146 
12147 
12148 // ////////////////////////////////////////////////////////////////////////// //
12149 // backgl
12150 // ////////////////////////////////////////////////////////////////////////// //
12151 import core.stdc.stdlib : malloc, realloc, free;
12152 import core.stdc.string : memcpy, memset;
12153 
12154 static if (__VERSION__ < 2076) {
12155   private auto DGNoThrowNoGC(T) (scope T t) /*if (isFunctionPointer!T || isDelegate!T)*/ {
12156     import std.traits;
12157     enum attrs = functionAttributes!T|FunctionAttribute.nogc|FunctionAttribute.nothrow_;
12158     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
12159   }
12160 }
12161 
12162 
12163 //import arsd.simpledisplay;
12164 version(nanovg_bindbc_opengl_bindings) {
12165   import bindbc.opengl;
12166 } else version(nanovg_builtin_opengl_bindings) {
12167   import arsd.simpledisplay;
12168 } else {
12169   import iv.glbinds;
12170 }
12171 
12172 private:
12173 // sdpy is missing that yet
12174 static if (!is(typeof(GL_STENCIL_BUFFER_BIT))) enum uint GL_STENCIL_BUFFER_BIT = 0x00000400;
12175 
12176 
12177 
12178 version(bindbc){
12179   private extern(System) nothrow @nogc:
12180   // this definition doesn't exist in regular OpenGL (?)
12181   enum uint GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9U;
12182   private void nanovgInitOpenGL () {
12183     // i'm not aware of calling the load multiple times having negative side effects, so i don't do an initialization check
12184     GLSupport support = loadOpenGL();
12185     if (support == GLSupport.noLibrary)
12186       assert(0, "OpenGL initialization failed: shared library failed to load");
12187     else if (support == GLSupport.badLibrary)
12188       assert(0, "OpenGL initialization failed: a context-independent symbol failed to load");
12189     else if (support == GLSupport.noContext)
12190       assert(0, "OpenGL initialization failed: a context needs to be created prior to initialization");
12191   }
12192 } else { // OpenGL API missing from simpledisplay
12193   private extern(System) nothrow @nogc {
12194     alias GLvoid = void;
12195     alias GLboolean = ubyte;
12196     alias GLuint = uint;
12197     alias GLenum = uint;
12198     alias GLchar = char;
12199     alias GLsizei = int;
12200     alias GLfloat = float;
12201     alias GLintptr = size_t;
12202     alias GLsizeiptr = ptrdiff_t;
12203 
12204     enum uint GL_STENCIL_BUFFER_BIT = 0x00000400;
12205 
12206     enum uint GL_INVALID_ENUM = 0x0500;
12207 
12208     enum uint GL_ZERO = 0;
12209     enum uint GL_ONE = 1;
12210 
12211     enum uint GL_FLOAT = 0x1406;
12212 
12213     enum uint GL_STREAM_DRAW = 0x88E0;
12214 
12215     enum uint GL_CCW = 0x0901;
12216 
12217     enum uint GL_STENCIL_TEST = 0x0B90;
12218     enum uint GL_SCISSOR_TEST = 0x0C11;
12219 
12220     enum uint GL_EQUAL = 0x0202;
12221     enum uint GL_NOTEQUAL = 0x0205;
12222 
12223     enum uint GL_ALWAYS = 0x0207;
12224     enum uint GL_KEEP = 0x1E00;
12225 
12226     enum uint GL_INCR = 0x1E02;
12227 
12228     enum uint GL_INCR_WRAP = 0x8507;
12229     enum uint GL_DECR_WRAP = 0x8508;
12230 
12231     enum uint GL_CULL_FACE = 0x0B44;
12232     enum uint GL_BACK = 0x0405;
12233 
12234     enum uint GL_FRAGMENT_SHADER = 0x8B30;
12235     enum uint GL_VERTEX_SHADER = 0x8B31;
12236 
12237     enum uint GL_COMPILE_STATUS = 0x8B81;
12238     enum uint GL_LINK_STATUS = 0x8B82;
12239 
12240     enum uint GL_UNPACK_ALIGNMENT = 0x0CF5;
12241     enum uint GL_UNPACK_ROW_LENGTH = 0x0CF2;
12242     enum uint GL_UNPACK_SKIP_PIXELS = 0x0CF4;
12243     enum uint GL_UNPACK_SKIP_ROWS = 0x0CF3;
12244 
12245     enum uint GL_GENERATE_MIPMAP = 0x8191;
12246     enum uint GL_LINEAR_MIPMAP_LINEAR = 0x2703;
12247 
12248     enum uint GL_RED = 0x1903;
12249 
12250     enum uint GL_TEXTURE0 = 0x84C0U;
12251     enum uint GL_TEXTURE1 = 0x84C1U;
12252 
12253     enum uint GL_ARRAY_BUFFER = 0x8892;
12254 
12255     enum uint GL_SRC_COLOR = 0x0300;
12256     enum uint GL_ONE_MINUS_SRC_COLOR = 0x0301;
12257     enum uint GL_SRC_ALPHA = 0x0302;
12258     enum uint GL_ONE_MINUS_SRC_ALPHA = 0x0303;
12259     enum uint GL_DST_ALPHA = 0x0304;
12260     enum uint GL_ONE_MINUS_DST_ALPHA = 0x0305;
12261     enum uint GL_DST_COLOR = 0x0306;
12262     enum uint GL_ONE_MINUS_DST_COLOR = 0x0307;
12263     enum uint GL_SRC_ALPHA_SATURATE = 0x0308;
12264 
12265     enum uint GL_INVERT = 0x150AU;
12266 
12267     enum uint GL_DEPTH_STENCIL = 0x84F9U;
12268     enum uint GL_UNSIGNED_INT_24_8 = 0x84FAU;
12269 
12270     enum uint GL_FRAMEBUFFER = 0x8D40U;
12271     enum uint GL_COLOR_ATTACHMENT0 = 0x8CE0U;
12272     enum uint GL_DEPTH_STENCIL_ATTACHMENT = 0x821AU;
12273 
12274     enum uint GL_FRAMEBUFFER_COMPLETE = 0x8CD5U;
12275     enum uint GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6U;
12276     enum uint GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7U;
12277     enum uint GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9U;
12278     enum uint GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDDU;
12279 
12280     enum uint GL_COLOR_LOGIC_OP = 0x0BF2U;
12281     enum uint GL_CLEAR = 0x1500U;
12282     enum uint GL_COPY = 0x1503U;
12283     enum uint GL_XOR = 0x1506U;
12284 
12285     enum uint GL_FRAMEBUFFER_BINDING = 0x8CA6U;
12286 
12287     enum uint GL_TEXTURE_LOD_BIAS = 0x8501;
12288 
12289     /*
12290     version(Windows) {
12291       private void* kglLoad (const(char)* name) {
12292         void* res = glGetProcAddress(name);
12293         if (res is null) {
12294           import core.sys.windows.windef, core.sys.windows.winbase;
12295           static HINSTANCE dll = null;
12296           if (dll is null) {
12297             dll = LoadLibraryA("opengl32.dll");
12298             if (dll is null) return null; // <32, but idc
12299             return GetProcAddress(dll, name);
12300           }
12301         }
12302       }
12303     } else {
12304       alias kglLoad = glGetProcAddress;
12305     }
12306     */
12307 
12308     alias glbfn_glGenVertexArrays = void function(GLsizei, GLuint*);
12309     __gshared glbfn_glGenVertexArrays glGenVertexArrays_NVGLZ; alias glGenVertexArrays = glGenVertexArrays_NVGLZ;
12310     alias glbfn_glBindVertexArray = void function(GLuint);
12311     __gshared glbfn_glBindVertexArray glBindVertexArray_NVGLZ; alias glBindVertexArray = glBindVertexArray_NVGLZ;
12312     alias glbfn_glDeleteVertexArrays = void function(GLsizei, const(GLuint)*);
12313     __gshared glbfn_glDeleteVertexArrays glDeleteVertexArrays_NVGLZ; alias glDeleteVertexArrays = glDeleteVertexArrays_NVGLZ;
12314     alias glbfn_glGenerateMipmap = void function(GLenum);
12315     __gshared glbfn_glGenerateMipmap glGenerateMipmap_NVGLZ; alias glGenerateMipmap = glGenerateMipmap_NVGLZ;
12316     alias glbfn_glBufferSubData = void function(GLenum, GLintptr, GLsizeiptr, const(GLvoid)*);
12317     __gshared glbfn_glBufferSubData glBufferSubData_NVGLZ; alias glBufferSubData = glBufferSubData_NVGLZ;
12318 
12319     alias glbfn_glStencilMask = void function(GLuint);
12320     __gshared glbfn_glStencilMask glStencilMask_NVGLZ; alias glStencilMask = glStencilMask_NVGLZ;
12321     alias glbfn_glStencilFunc = void function(GLenum, GLint, GLuint);
12322     __gshared glbfn_glStencilFunc glStencilFunc_NVGLZ; alias glStencilFunc = glStencilFunc_NVGLZ;
12323     alias glbfn_glGetShaderInfoLog = void function(GLuint, GLsizei, GLsizei*, GLchar*);
12324     __gshared glbfn_glGetShaderInfoLog glGetShaderInfoLog_NVGLZ; alias glGetShaderInfoLog = glGetShaderInfoLog_NVGLZ;
12325     alias glbfn_glGetProgramInfoLog = void function(GLuint, GLsizei, GLsizei*, GLchar*);
12326     __gshared glbfn_glGetProgramInfoLog glGetProgramInfoLog_NVGLZ; alias glGetProgramInfoLog = glGetProgramInfoLog_NVGLZ;
12327     alias glbfn_glCreateProgram = GLuint function();
12328     __gshared glbfn_glCreateProgram glCreateProgram_NVGLZ; alias glCreateProgram = glCreateProgram_NVGLZ;
12329     alias glbfn_glCreateShader = GLuint function(GLenum);
12330     __gshared glbfn_glCreateShader glCreateShader_NVGLZ; alias glCreateShader = glCreateShader_NVGLZ;
12331     alias glbfn_glShaderSource = void function(GLuint, GLsizei, const(GLchar*)*, const(GLint)*);
12332     __gshared glbfn_glShaderSource glShaderSource_NVGLZ; alias glShaderSource = glShaderSource_NVGLZ;
12333     alias glbfn_glCompileShader = void function(GLuint);
12334     __gshared glbfn_glCompileShader glCompileShader_NVGLZ; alias glCompileShader = glCompileShader_NVGLZ;
12335     alias glbfn_glGetShaderiv = void function(GLuint, GLenum, GLint*);
12336     __gshared glbfn_glGetShaderiv glGetShaderiv_NVGLZ; alias glGetShaderiv = glGetShaderiv_NVGLZ;
12337     alias glbfn_glAttachShader = void function(GLuint, GLuint);
12338     __gshared glbfn_glAttachShader glAttachShader_NVGLZ; alias glAttachShader = glAttachShader_NVGLZ;
12339     alias glbfn_glBindAttribLocation = void function(GLuint, GLuint, const(GLchar)*);
12340     __gshared glbfn_glBindAttribLocation glBindAttribLocation_NVGLZ; alias glBindAttribLocation = glBindAttribLocation_NVGLZ;
12341     alias glbfn_glLinkProgram = void function(GLuint);
12342     __gshared glbfn_glLinkProgram glLinkProgram_NVGLZ; alias glLinkProgram = glLinkProgram_NVGLZ;
12343     alias glbfn_glGetProgramiv = void function(GLuint, GLenum, GLint*);
12344     __gshared glbfn_glGetProgramiv glGetProgramiv_NVGLZ; alias glGetProgramiv = glGetProgramiv_NVGLZ;
12345     alias glbfn_glDeleteProgram = void function(GLuint);
12346     __gshared glbfn_glDeleteProgram glDeleteProgram_NVGLZ; alias glDeleteProgram = glDeleteProgram_NVGLZ;
12347     alias glbfn_glDeleteShader = void function(GLuint);
12348     __gshared glbfn_glDeleteShader glDeleteShader_NVGLZ; alias glDeleteShader = glDeleteShader_NVGLZ;
12349     alias glbfn_glGetUniformLocation = GLint function(GLuint, const(GLchar)*);
12350     __gshared glbfn_glGetUniformLocation glGetUniformLocation_NVGLZ; alias glGetUniformLocation = glGetUniformLocation_NVGLZ;
12351     alias glbfn_glGenBuffers = void function(GLsizei, GLuint*);
12352     __gshared glbfn_glGenBuffers glGenBuffers_NVGLZ; alias glGenBuffers = glGenBuffers_NVGLZ;
12353     alias glbfn_glPixelStorei = void function(GLenum, GLint);
12354     __gshared glbfn_glPixelStorei glPixelStorei_NVGLZ; alias glPixelStorei = glPixelStorei_NVGLZ;
12355     alias glbfn_glUniform4fv = void function(GLint, GLsizei, const(GLfloat)*);
12356     __gshared glbfn_glUniform4fv glUniform4fv_NVGLZ; alias glUniform4fv = glUniform4fv_NVGLZ;
12357     alias glbfn_glColorMask = void function(GLboolean, GLboolean, GLboolean, GLboolean);
12358     __gshared glbfn_glColorMask glColorMask_NVGLZ; alias glColorMask = glColorMask_NVGLZ;
12359     alias glbfn_glStencilOpSeparate = void function(GLenum, GLenum, GLenum, GLenum);
12360     __gshared glbfn_glStencilOpSeparate glStencilOpSeparate_NVGLZ; alias glStencilOpSeparate = glStencilOpSeparate_NVGLZ;
12361     alias glbfn_glDrawArrays = void function(GLenum, GLint, GLsizei);
12362     __gshared glbfn_glDrawArrays glDrawArrays_NVGLZ; alias glDrawArrays = glDrawArrays_NVGLZ;
12363     alias glbfn_glStencilOp = void function(GLenum, GLenum, GLenum);
12364     __gshared glbfn_glStencilOp glStencilOp_NVGLZ; alias glStencilOp = glStencilOp_NVGLZ;
12365     alias glbfn_glUseProgram = void function(GLuint);
12366     __gshared glbfn_glUseProgram glUseProgram_NVGLZ; alias glUseProgram = glUseProgram_NVGLZ;
12367     alias glbfn_glCullFace = void function(GLenum);
12368     __gshared glbfn_glCullFace glCullFace_NVGLZ; alias glCullFace = glCullFace_NVGLZ;
12369     alias glbfn_glFrontFace = void function(GLenum);
12370     __gshared glbfn_glFrontFace glFrontFace_NVGLZ; alias glFrontFace = glFrontFace_NVGLZ;
12371     alias glbfn_glActiveTexture = void function(GLenum);
12372     __gshared glbfn_glActiveTexture glActiveTexture_NVGLZ; alias glActiveTexture = glActiveTexture_NVGLZ;
12373     alias glbfn_glBindBuffer = void function(GLenum, GLuint);
12374     __gshared glbfn_glBindBuffer glBindBuffer_NVGLZ; alias glBindBuffer = glBindBuffer_NVGLZ;
12375     alias glbfn_glBufferData = void function(GLenum, GLsizeiptr, const(void)*, GLenum);
12376     __gshared glbfn_glBufferData glBufferData_NVGLZ; alias glBufferData = glBufferData_NVGLZ;
12377     alias glbfn_glEnableVertexAttribArray = void function(GLuint);
12378     __gshared glbfn_glEnableVertexAttribArray glEnableVertexAttribArray_NVGLZ; alias glEnableVertexAttribArray = glEnableVertexAttribArray_NVGLZ;
12379     alias glbfn_glVertexAttribPointer = void function(GLuint, GLint, GLenum, GLboolean, GLsizei, const(void)*);
12380     __gshared glbfn_glVertexAttribPointer glVertexAttribPointer_NVGLZ; alias glVertexAttribPointer = glVertexAttribPointer_NVGLZ;
12381     alias glbfn_glUniform1i = void function(GLint, GLint);
12382     __gshared glbfn_glUniform1i glUniform1i_NVGLZ; alias glUniform1i = glUniform1i_NVGLZ;
12383     alias glbfn_glUniform2fv = void function(GLint, GLsizei, const(GLfloat)*);
12384     __gshared glbfn_glUniform2fv glUniform2fv_NVGLZ; alias glUniform2fv = glUniform2fv_NVGLZ;
12385     alias glbfn_glDisableVertexAttribArray = void function(GLuint);
12386     __gshared glbfn_glDisableVertexAttribArray glDisableVertexAttribArray_NVGLZ; alias glDisableVertexAttribArray = glDisableVertexAttribArray_NVGLZ;
12387     alias glbfn_glDeleteBuffers = void function(GLsizei, const(GLuint)*);
12388     __gshared glbfn_glDeleteBuffers glDeleteBuffers_NVGLZ; alias glDeleteBuffers = glDeleteBuffers_NVGLZ;
12389     alias glbfn_glBlendFuncSeparate = void function(GLenum, GLenum, GLenum, GLenum);
12390     __gshared glbfn_glBlendFuncSeparate glBlendFuncSeparate_NVGLZ; alias glBlendFuncSeparate = glBlendFuncSeparate_NVGLZ;
12391 
12392     alias glbfn_glLogicOp = void function (GLenum opcode);
12393     __gshared glbfn_glLogicOp glLogicOp_NVGLZ; alias glLogicOp = glLogicOp_NVGLZ;
12394     alias glbfn_glFramebufferTexture2D = void function (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
12395     __gshared glbfn_glFramebufferTexture2D glFramebufferTexture2D_NVGLZ; alias glFramebufferTexture2D = glFramebufferTexture2D_NVGLZ;
12396     alias glbfn_glDeleteFramebuffers = void function (GLsizei n, const(GLuint)* framebuffers);
12397     __gshared glbfn_glDeleteFramebuffers glDeleteFramebuffers_NVGLZ; alias glDeleteFramebuffers = glDeleteFramebuffers_NVGLZ;
12398     alias glbfn_glGenFramebuffers = void function (GLsizei n, GLuint* framebuffers);
12399     __gshared glbfn_glGenFramebuffers glGenFramebuffers_NVGLZ; alias glGenFramebuffers = glGenFramebuffers_NVGLZ;
12400     alias glbfn_glCheckFramebufferStatus = GLenum function (GLenum target);
12401     __gshared glbfn_glCheckFramebufferStatus glCheckFramebufferStatus_NVGLZ; alias glCheckFramebufferStatus = glCheckFramebufferStatus_NVGLZ;
12402     alias glbfn_glBindFramebuffer = void function (GLenum target, GLuint framebuffer);
12403     __gshared glbfn_glBindFramebuffer glBindFramebuffer_NVGLZ; alias glBindFramebuffer = glBindFramebuffer_NVGLZ;
12404 
12405     alias glbfn_glGetIntegerv = void function (GLenum pname, GLint* data);
12406     __gshared glbfn_glGetIntegerv glGetIntegerv_NVGLZ; alias glGetIntegerv = glGetIntegerv_NVGLZ;
12407 
12408     private void nanovgInitOpenGL () {
12409       __gshared bool initialized = false;
12410       if (initialized) return;
12411       glGenVertexArrays_NVGLZ = cast(glbfn_glGenVertexArrays)glbindGetProcAddress(`glGenVertexArrays`);
12412       if (glGenVertexArrays_NVGLZ is null) assert(0, `OpenGL function 'glGenVertexArrays' not found!`);
12413       glBindVertexArray_NVGLZ = cast(glbfn_glBindVertexArray)glbindGetProcAddress(`glBindVertexArray`);
12414       if (glBindVertexArray_NVGLZ is null) assert(0, `OpenGL function 'glBindVertexArray' not found!`);
12415       glDeleteVertexArrays_NVGLZ = cast(glbfn_glDeleteVertexArrays)glbindGetProcAddress(`glDeleteVertexArrays`);
12416       if (glDeleteVertexArrays_NVGLZ is null) assert(0, `OpenGL function 'glDeleteVertexArrays' not found!`);
12417       glGenerateMipmap_NVGLZ = cast(glbfn_glGenerateMipmap)glbindGetProcAddress(`glGenerateMipmap`);
12418       if (glGenerateMipmap_NVGLZ is null) assert(0, `OpenGL function 'glGenerateMipmap' not found!`);
12419       glBufferSubData_NVGLZ = cast(glbfn_glBufferSubData)glbindGetProcAddress(`glBufferSubData`);
12420       if (glBufferSubData_NVGLZ is null) assert(0, `OpenGL function 'glBufferSubData' not found!`);
12421 
12422       glStencilMask_NVGLZ = cast(glbfn_glStencilMask)glbindGetProcAddress(`glStencilMask`);
12423       if (glStencilMask_NVGLZ is null) assert(0, `OpenGL function 'glStencilMask' not found!`);
12424       glStencilFunc_NVGLZ = cast(glbfn_glStencilFunc)glbindGetProcAddress(`glStencilFunc`);
12425       if (glStencilFunc_NVGLZ is null) assert(0, `OpenGL function 'glStencilFunc' not found!`);
12426       glGetShaderInfoLog_NVGLZ = cast(glbfn_glGetShaderInfoLog)glbindGetProcAddress(`glGetShaderInfoLog`);
12427       if (glGetShaderInfoLog_NVGLZ is null) assert(0, `OpenGL function 'glGetShaderInfoLog' not found!`);
12428       glGetProgramInfoLog_NVGLZ = cast(glbfn_glGetProgramInfoLog)glbindGetProcAddress(`glGetProgramInfoLog`);
12429       if (glGetProgramInfoLog_NVGLZ is null) assert(0, `OpenGL function 'glGetProgramInfoLog' not found!`);
12430       glCreateProgram_NVGLZ = cast(glbfn_glCreateProgram)glbindGetProcAddress(`glCreateProgram`);
12431       if (glCreateProgram_NVGLZ is null) assert(0, `OpenGL function 'glCreateProgram' not found!`);
12432       glCreateShader_NVGLZ = cast(glbfn_glCreateShader)glbindGetProcAddress(`glCreateShader`);
12433       if (glCreateShader_NVGLZ is null) assert(0, `OpenGL function 'glCreateShader' not found!`);
12434       glShaderSource_NVGLZ = cast(glbfn_glShaderSource)glbindGetProcAddress(`glShaderSource`);
12435       if (glShaderSource_NVGLZ is null) assert(0, `OpenGL function 'glShaderSource' not found!`);
12436       glCompileShader_NVGLZ = cast(glbfn_glCompileShader)glbindGetProcAddress(`glCompileShader`);
12437       if (glCompileShader_NVGLZ is null) assert(0, `OpenGL function 'glCompileShader' not found!`);
12438       glGetShaderiv_NVGLZ = cast(glbfn_glGetShaderiv)glbindGetProcAddress(`glGetShaderiv`);
12439       if (glGetShaderiv_NVGLZ is null) assert(0, `OpenGL function 'glGetShaderiv' not found!`);
12440       glAttachShader_NVGLZ = cast(glbfn_glAttachShader)glbindGetProcAddress(`glAttachShader`);
12441       if (glAttachShader_NVGLZ is null) assert(0, `OpenGL function 'glAttachShader' not found!`);
12442       glBindAttribLocation_NVGLZ = cast(glbfn_glBindAttribLocation)glbindGetProcAddress(`glBindAttribLocation`);
12443       if (glBindAttribLocation_NVGLZ is null) assert(0, `OpenGL function 'glBindAttribLocation' not found!`);
12444       glLinkProgram_NVGLZ = cast(glbfn_glLinkProgram)glbindGetProcAddress(`glLinkProgram`);
12445       if (glLinkProgram_NVGLZ is null) assert(0, `OpenGL function 'glLinkProgram' not found!`);
12446       glGetProgramiv_NVGLZ = cast(glbfn_glGetProgramiv)glbindGetProcAddress(`glGetProgramiv`);
12447       if (glGetProgramiv_NVGLZ is null) assert(0, `OpenGL function 'glGetProgramiv' not found!`);
12448       glDeleteProgram_NVGLZ = cast(glbfn_glDeleteProgram)glbindGetProcAddress(`glDeleteProgram`);
12449       if (glDeleteProgram_NVGLZ is null) assert(0, `OpenGL function 'glDeleteProgram' not found!`);
12450       glDeleteShader_NVGLZ = cast(glbfn_glDeleteShader)glbindGetProcAddress(`glDeleteShader`);
12451       if (glDeleteShader_NVGLZ is null) assert(0, `OpenGL function 'glDeleteShader' not found!`);
12452       glGetUniformLocation_NVGLZ = cast(glbfn_glGetUniformLocation)glbindGetProcAddress(`glGetUniformLocation`);
12453       if (glGetUniformLocation_NVGLZ is null) assert(0, `OpenGL function 'glGetUniformLocation' not found!`);
12454       glGenBuffers_NVGLZ = cast(glbfn_glGenBuffers)glbindGetProcAddress(`glGenBuffers`);
12455       if (glGenBuffers_NVGLZ is null) assert(0, `OpenGL function 'glGenBuffers' not found!`);
12456       glPixelStorei_NVGLZ = cast(glbfn_glPixelStorei)glbindGetProcAddress(`glPixelStorei`);
12457       if (glPixelStorei_NVGLZ is null) assert(0, `OpenGL function 'glPixelStorei' not found!`);
12458       glUniform4fv_NVGLZ = cast(glbfn_glUniform4fv)glbindGetProcAddress(`glUniform4fv`);
12459       if (glUniform4fv_NVGLZ is null) assert(0, `OpenGL function 'glUniform4fv' not found!`);
12460       glColorMask_NVGLZ = cast(glbfn_glColorMask)glbindGetProcAddress(`glColorMask`);
12461       if (glColorMask_NVGLZ is null) assert(0, `OpenGL function 'glColorMask' not found!`);
12462       glStencilOpSeparate_NVGLZ = cast(glbfn_glStencilOpSeparate)glbindGetProcAddress(`glStencilOpSeparate`);
12463       if (glStencilOpSeparate_NVGLZ is null) assert(0, `OpenGL function 'glStencilOpSeparate' not found!`);
12464       glDrawArrays_NVGLZ = cast(glbfn_glDrawArrays)glbindGetProcAddress(`glDrawArrays`);
12465       if (glDrawArrays_NVGLZ is null) assert(0, `OpenGL function 'glDrawArrays' not found!`);
12466       glStencilOp_NVGLZ = cast(glbfn_glStencilOp)glbindGetProcAddress(`glStencilOp`);
12467       if (glStencilOp_NVGLZ is null) assert(0, `OpenGL function 'glStencilOp' not found!`);
12468       glUseProgram_NVGLZ = cast(glbfn_glUseProgram)glbindGetProcAddress(`glUseProgram`);
12469       if (glUseProgram_NVGLZ is null) assert(0, `OpenGL function 'glUseProgram' not found!`);
12470       glCullFace_NVGLZ = cast(glbfn_glCullFace)glbindGetProcAddress(`glCullFace`);
12471       if (glCullFace_NVGLZ is null) assert(0, `OpenGL function 'glCullFace' not found!`);
12472       glFrontFace_NVGLZ = cast(glbfn_glFrontFace)glbindGetProcAddress(`glFrontFace`);
12473       if (glFrontFace_NVGLZ is null) assert(0, `OpenGL function 'glFrontFace' not found!`);
12474       glActiveTexture_NVGLZ = cast(glbfn_glActiveTexture)glbindGetProcAddress(`glActiveTexture`);
12475       if (glActiveTexture_NVGLZ is null) assert(0, `OpenGL function 'glActiveTexture' not found!`);
12476       glBindBuffer_NVGLZ = cast(glbfn_glBindBuffer)glbindGetProcAddress(`glBindBuffer`);
12477       if (glBindBuffer_NVGLZ is null) assert(0, `OpenGL function 'glBindBuffer' not found!`);
12478       glBufferData_NVGLZ = cast(glbfn_glBufferData)glbindGetProcAddress(`glBufferData`);
12479       if (glBufferData_NVGLZ is null) assert(0, `OpenGL function 'glBufferData' not found!`);
12480       glEnableVertexAttribArray_NVGLZ = cast(glbfn_glEnableVertexAttribArray)glbindGetProcAddress(`glEnableVertexAttribArray`);
12481       if (glEnableVertexAttribArray_NVGLZ is null) assert(0, `OpenGL function 'glEnableVertexAttribArray' not found!`);
12482       glVertexAttribPointer_NVGLZ = cast(glbfn_glVertexAttribPointer)glbindGetProcAddress(`glVertexAttribPointer`);
12483       if (glVertexAttribPointer_NVGLZ is null) assert(0, `OpenGL function 'glVertexAttribPointer' not found!`);
12484       glUniform1i_NVGLZ = cast(glbfn_glUniform1i)glbindGetProcAddress(`glUniform1i`);
12485       if (glUniform1i_NVGLZ is null) assert(0, `OpenGL function 'glUniform1i' not found!`);
12486       glUniform2fv_NVGLZ = cast(glbfn_glUniform2fv)glbindGetProcAddress(`glUniform2fv`);
12487       if (glUniform2fv_NVGLZ is null) assert(0, `OpenGL function 'glUniform2fv' not found!`);
12488       glDisableVertexAttribArray_NVGLZ = cast(glbfn_glDisableVertexAttribArray)glbindGetProcAddress(`glDisableVertexAttribArray`);
12489       if (glDisableVertexAttribArray_NVGLZ is null) assert(0, `OpenGL function 'glDisableVertexAttribArray' not found!`);
12490       glDeleteBuffers_NVGLZ = cast(glbfn_glDeleteBuffers)glbindGetProcAddress(`glDeleteBuffers`);
12491       if (glDeleteBuffers_NVGLZ is null) assert(0, `OpenGL function 'glDeleteBuffers' not found!`);
12492       glBlendFuncSeparate_NVGLZ = cast(glbfn_glBlendFuncSeparate)glbindGetProcAddress(`glBlendFuncSeparate`);
12493       if (glBlendFuncSeparate_NVGLZ is null) assert(0, `OpenGL function 'glBlendFuncSeparate' not found!`);
12494 
12495       glLogicOp_NVGLZ = cast(glbfn_glLogicOp)glbindGetProcAddress(`glLogicOp`);
12496       if (glLogicOp_NVGLZ is null) assert(0, `OpenGL function 'glLogicOp' not found!`);
12497       glFramebufferTexture2D_NVGLZ = cast(glbfn_glFramebufferTexture2D)glbindGetProcAddress(`glFramebufferTexture2D`);
12498       if (glFramebufferTexture2D_NVGLZ is null) assert(0, `OpenGL function 'glFramebufferTexture2D' not found!`);
12499       glDeleteFramebuffers_NVGLZ = cast(glbfn_glDeleteFramebuffers)glbindGetProcAddress(`glDeleteFramebuffers`);
12500       if (glDeleteFramebuffers_NVGLZ is null) assert(0, `OpenGL function 'glDeleteFramebuffers' not found!`);
12501       glGenFramebuffers_NVGLZ = cast(glbfn_glGenFramebuffers)glbindGetProcAddress(`glGenFramebuffers`);
12502       if (glGenFramebuffers_NVGLZ is null) assert(0, `OpenGL function 'glGenFramebuffers' not found!`);
12503       glCheckFramebufferStatus_NVGLZ = cast(glbfn_glCheckFramebufferStatus)glbindGetProcAddress(`glCheckFramebufferStatus`);
12504       if (glCheckFramebufferStatus_NVGLZ is null) assert(0, `OpenGL function 'glCheckFramebufferStatus' not found!`);
12505       glBindFramebuffer_NVGLZ = cast(glbfn_glBindFramebuffer)glbindGetProcAddress(`glBindFramebuffer`);
12506       if (glBindFramebuffer_NVGLZ is null) assert(0, `OpenGL function 'glBindFramebuffer' not found!`);
12507 
12508       glGetIntegerv_NVGLZ = cast(glbfn_glGetIntegerv)glbindGetProcAddress(`glGetIntegerv`);
12509       if (glGetIntegerv_NVGLZ is null) assert(0, `OpenGL function 'glGetIntegerv' not found!`);
12510       initialized = true;
12511     }
12512   }
12513 }
12514 
12515 
12516 
12517 /// Context creation flags.
12518 /// Group: context_management
12519 public enum NVGContextFlag : int {
12520   /// Nothing special, i.e. empty flag.
12521   None = 0,
12522   /// Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA).
12523   Antialias = 1U<<0,
12524   /** Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little
12525     * slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. */
12526   StencilStrokes = 1U<<1,
12527   /// Flag indicating that additional debug checks are done.
12528   Debug = 1U<<2,
12529   /// Filter (antialias) fonts
12530   FontAA = 1U<<7,
12531   /// Don't filter (antialias) fonts
12532   FontNoAA = 1U<<8,
12533   /// You can use this as a substitute for default flags, for cases like this: `nvgCreateContext(NVGContextFlag.Default, NVGContextFlag.Debug);`.
12534   Default = 1U<<31,
12535 }
12536 
12537 public enum NANOVG_GL_USE_STATE_FILTER = true;
12538 
12539 /// Returns flags for glClear().
12540 /// Group: context_management
12541 public uint glNVGClearFlags () pure nothrow @safe @nogc {
12542   pragma(inline, true);
12543   return (GL_COLOR_BUFFER_BIT|/*GL_DEPTH_BUFFER_BIT|*/GL_STENCIL_BUFFER_BIT);
12544 }
12545 
12546 
12547 // ////////////////////////////////////////////////////////////////////////// //
12548 private:
12549 
12550 version = nanovega_shared_stencil;
12551 //version = nanovega_debug_clipping;
12552 
12553 enum GLNVGuniformLoc {
12554   ViewSize,
12555   Tex,
12556   Frag,
12557   TMat,
12558   TTr,
12559   ClipTex,
12560 }
12561 
12562 alias GLNVGshaderType = int;
12563 enum /*GLNVGshaderType*/ {
12564   NSVG_SHADER_FILLCOLOR,
12565   NSVG_SHADER_FILLGRAD,
12566   NSVG_SHADER_FILLIMG,
12567   NSVG_SHADER_SIMPLE, // also used for clipfill
12568   NSVG_SHADER_IMG,
12569 }
12570 
12571 struct GLNVGshader {
12572   GLuint prog;
12573   GLuint frag;
12574   GLuint vert;
12575   GLint[GLNVGuniformLoc.max+1] loc;
12576 }
12577 
12578 struct GLNVGtexture {
12579   int id;
12580   GLuint tex;
12581   int width, height;
12582   NVGtexture type;
12583   int flags;
12584   shared int rc; // this can be 0 with tex != 0 -- postponed deletion
12585   int nextfree;
12586 }
12587 
12588 struct GLNVGblend {
12589   bool simple;
12590   GLenum srcRGB;
12591   GLenum dstRGB;
12592   GLenum srcAlpha;
12593   GLenum dstAlpha;
12594 }
12595 
12596 alias GLNVGcallType = int;
12597 enum /*GLNVGcallType*/ {
12598   GLNVG_NONE = 0,
12599   GLNVG_FILL,
12600   GLNVG_CONVEXFILL,
12601   GLNVG_STROKE,
12602   GLNVG_TRIANGLES,
12603   GLNVG_AFFINE, // change affine transformation matrix
12604   GLNVG_PUSHCLIP,
12605   GLNVG_POPCLIP,
12606   GLNVG_RESETCLIP,
12607   GLNVG_CLIP_DDUMP_ON,
12608   GLNVG_CLIP_DDUMP_OFF,
12609 }
12610 
12611 struct GLNVGcall {
12612   int type;
12613   int evenOdd; // for fill
12614   int image;
12615   int pathOffset;
12616   int pathCount;
12617   int triangleOffset;
12618   int triangleCount;
12619   int uniformOffset;
12620   NVGMatrix affine;
12621   GLNVGblend blendFunc;
12622   NVGClipMode clipmode;
12623 }
12624 
12625 struct GLNVGpath {
12626   int fillOffset;
12627   int fillCount;
12628   int strokeOffset;
12629   int strokeCount;
12630 }
12631 
12632 align(1) struct GLNVGfragUniforms {
12633 align(1):
12634   enum UNIFORM_ARRAY_SIZE = 13;
12635   // note: after modifying layout or size of uniform array,
12636   // don't forget to also update the fragment shader source!
12637   align(1) union {
12638   align(1):
12639     align(1) struct {
12640     align(1):
12641       float[12] scissorMat; // matrices are actually 3 vec4s
12642       float[12] paintMat;
12643       NVGColor innerCol;
12644       NVGColor middleCol;
12645       NVGColor outerCol;
12646       float[2] scissorExt;
12647       float[2] scissorScale;
12648       float[2] extent;
12649       float radius;
12650       float feather;
12651       float strokeMult;
12652       float strokeThr;
12653       float texType;
12654       float type;
12655       float doclip;
12656       float midp; // for gradients
12657       float unused2, unused3;
12658     }
12659     float[4][UNIFORM_ARRAY_SIZE] uniformArray;
12660   }
12661 }
12662 
12663 enum GLMaskState {
12664   DontMask = -1,
12665   Uninitialized = 0,
12666   Initialized = 1,
12667   JustCleared = 2,
12668 }
12669 
12670 final class GLNVGTextureLocker {}
12671 
12672 struct GLNVGcontext {
12673   private import core.thread : ThreadID;
12674 
12675   GLNVGshader shader;
12676   GLNVGtexture* textures;
12677   float[2] view;
12678   int freetexid; // -1: none
12679   int ntextures;
12680   int ctextures;
12681   GLuint vertBuf;
12682   int fragSize;
12683   int flags;
12684   // FBOs for masks
12685   GLuint[NVG_MAX_STATES] fbo;
12686   GLuint[2][NVG_MAX_STATES] fboTex; // FBO textures: [0] is color, [1] is stencil
12687   int fboWidth, fboHeight;
12688   GLMaskState[NVG_MAX_STATES] maskStack;
12689   int msp; // mask stack pointer; starts from `0`; points to next free item; see below for logic description
12690   int lastClipFBO; // -666: cache invalidated; -1: don't mask
12691   int lastClipUniOfs;
12692   bool doClipUnion; // specal mode
12693   GLNVGshader shaderFillFBO;
12694   GLNVGshader shaderCopyFBO;
12695 
12696   bool inFrame; // will be `true` if we can perform OpenGL operations (used in texture deletion)
12697   shared bool mustCleanTextures; // will be `true` if we should delete some textures
12698   ThreadID mainTID;
12699   uint mainFBO;
12700 
12701   // Per frame buffers
12702   GLNVGcall* calls;
12703   int ccalls;
12704   int ncalls;
12705   GLNVGpath* paths;
12706   int cpaths;
12707   int npaths;
12708   NVGVertex* verts;
12709   int cverts;
12710   int nverts;
12711   ubyte* uniforms;
12712   int cuniforms;
12713   int nuniforms;
12714   NVGMatrix lastAffine;
12715 
12716   // cached state
12717   static if (NANOVG_GL_USE_STATE_FILTER) {
12718     GLuint boundTexture;
12719     GLuint stencilMask;
12720     GLenum stencilFunc;
12721     GLint stencilFuncRef;
12722     GLuint stencilFuncMask;
12723     GLNVGblend blendFunc;
12724   }
12725 }
12726 
12727 int glnvg__maxi() (int a, int b) { pragma(inline, true); return (a > b ? a : b); }
12728 
12729 void glnvg__bindTexture (GLNVGcontext* gl, GLuint tex) nothrow @trusted @nogc {
12730   static if (NANOVG_GL_USE_STATE_FILTER) {
12731     if (gl.boundTexture != tex) {
12732       gl.boundTexture = tex;
12733       glBindTexture(GL_TEXTURE_2D, tex);
12734     }
12735   } else {
12736     glBindTexture(GL_TEXTURE_2D, tex);
12737   }
12738 }
12739 
12740 void glnvg__stencilMask (GLNVGcontext* gl, GLuint mask) nothrow @trusted @nogc {
12741   static if (NANOVG_GL_USE_STATE_FILTER) {
12742     if (gl.stencilMask != mask) {
12743       gl.stencilMask = mask;
12744       glStencilMask(mask);
12745     }
12746   } else {
12747     glStencilMask(mask);
12748   }
12749 }
12750 
12751 void glnvg__stencilFunc (GLNVGcontext* gl, GLenum func, GLint ref_, GLuint mask) nothrow @trusted @nogc {
12752   static if (NANOVG_GL_USE_STATE_FILTER) {
12753     if (gl.stencilFunc != func || gl.stencilFuncRef != ref_ || gl.stencilFuncMask != mask) {
12754       gl.stencilFunc = func;
12755       gl.stencilFuncRef = ref_;
12756       gl.stencilFuncMask = mask;
12757       glStencilFunc(func, ref_, mask);
12758     }
12759   } else {
12760     glStencilFunc(func, ref_, mask);
12761   }
12762 }
12763 
12764 // texture id is never zero
12765 // sets refcount to one
12766 GLNVGtexture* glnvg__allocTexture (GLNVGcontext* gl) nothrow @trusted @nogc {
12767   GLNVGtexture* tex = null;
12768 
12769   int tid = gl.freetexid;
12770   if (tid == -1) {
12771     if (gl.ntextures >= gl.ctextures) {
12772       assert(gl.ntextures == gl.ctextures);
12773       //pragma(msg, GLNVGtexture.sizeof*32);
12774       int ctextures = (gl.ctextures == 0 ? 32 : gl.ctextures+gl.ctextures/2); // 1.5x overallocate
12775       GLNVGtexture* textures = cast(GLNVGtexture*)realloc(gl.textures, GLNVGtexture.sizeof*ctextures);
12776       if (textures is null) assert(0, "NanoVega: out of memory for textures");
12777       memset(&textures[gl.ctextures], 0, (ctextures-gl.ctextures)*GLNVGtexture.sizeof);
12778       version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("allocated more textures (n=%d; c=%d; nc=%d)\n", gl.ntextures, gl.ctextures, ctextures); }}
12779       gl.textures = textures;
12780       gl.ctextures = ctextures;
12781     }
12782     assert(gl.ntextures+1 <= gl.ctextures);
12783     tid = gl.ntextures++;
12784     version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("  got next free texture id %d, ntextures=%d\n", tid+1, gl.ntextures); }}
12785   } else {
12786     gl.freetexid = gl.textures[tid].nextfree;
12787   }
12788   assert(tid <= gl.ntextures);
12789 
12790   assert(gl.textures[tid].id == 0);
12791   tex = &gl.textures[tid];
12792   memset(tex, 0, (*tex).sizeof);
12793   tex.id = tid+1;
12794   tex.rc = 1;
12795   tex.nextfree = -1;
12796 
12797   version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("allocated texture with id %d (%d)\n", tex.id, tid+1); }}
12798 
12799   return tex;
12800 }
12801 
12802 GLNVGtexture* glnvg__findTexture (GLNVGcontext* gl, int id) nothrow @trusted @nogc {
12803   if (id <= 0 || id > gl.ntextures) return null;
12804   if (gl.textures[id-1].id == 0) return null; // free one
12805   assert(gl.textures[id-1].id == id);
12806   return &gl.textures[id-1];
12807 }
12808 
12809 bool glnvg__deleteTexture (GLNVGcontext* gl, ref int id) nothrow @trusted @nogc {
12810   if (id <= 0 || id > gl.ntextures) return false;
12811   auto tx = &gl.textures[id-1];
12812   if (tx.id == 0) { id = 0; return false; } // free one
12813   assert(tx.id == id);
12814   assert(tx.tex != 0);
12815   version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("decrefing texture with id %d (%d)\n", tx.id, id); }}
12816   import core.atomic : atomicOp;
12817   if (atomicOp!"-="(tx.rc, 1) == 0) {
12818     import core.thread : ThreadID;
12819     ThreadID mytid;
12820     static if (__VERSION__ < 2076) {
12821       DGNoThrowNoGC(() {
12822         import core.thread; mytid = Thread.getThis.id;
12823       })();
12824     } else {
12825       try { import core.thread; mytid = Thread.getThis.id; } catch (Exception e) {}
12826     }
12827     if (gl.mainTID == mytid && gl.inFrame) {
12828       // can delete it right now
12829       if ((tx.flags&NVGImageFlag.NoDelete) == 0) glDeleteTextures(1, &tx.tex);
12830       version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("*** deleted texture with id %d (%d); glid=%u\n", tx.id, id, tx.tex); }}
12831       memset(tx, 0, (*tx).sizeof);
12832       //{ import core.stdc.stdio; printf("deleting texture with id %d\n", id); }
12833       tx.nextfree = gl.freetexid;
12834       gl.freetexid = id-1;
12835     } else {
12836       // alas, we aren't doing frame business, so we should postpone deletion
12837       version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("*** POSTPONED texture deletion with id %d (%d); glid=%u\n", tx.id, id, tx.tex); }}
12838       version(aliced) {
12839         synchronized(GLNVGTextureLocker.classinfo) {
12840           tx.id = 0; // mark it as dead
12841           gl.mustCleanTextures = true; // set "need cleanup" flag
12842         }
12843       } else {
12844         try {
12845           synchronized(GLNVGTextureLocker.classinfo) {
12846             tx.id = 0; // mark it as dead
12847             gl.mustCleanTextures = true; // set "need cleanup" flag
12848           }
12849         } catch (Exception e) {}
12850       }
12851     }
12852   }
12853   id = 0;
12854   return true;
12855 }
12856 
12857 void glnvg__dumpShaderError (GLuint shader, const(char)* name, const(char)* type) nothrow @trusted @nogc {
12858   import core.stdc.stdio : fprintf, stderr;
12859   GLchar[512+1] str = 0;
12860   GLsizei len = 0;
12861   glGetShaderInfoLog(shader, 512, &len, str.ptr);
12862   if (len > 512) len = 512;
12863   str[len] = '\0';
12864   fprintf(stderr, "Shader %s/%s error:\n%s\n", name, type, str.ptr);
12865 }
12866 
12867 void glnvg__dumpProgramError (GLuint prog, const(char)* name) nothrow @trusted @nogc {
12868   import core.stdc.stdio : fprintf, stderr;
12869   GLchar[512+1] str = 0;
12870   GLsizei len = 0;
12871   glGetProgramInfoLog(prog, 512, &len, str.ptr);
12872   if (len > 512) len = 512;
12873   str[len] = '\0';
12874   fprintf(stderr, "Program %s error:\n%s\n", name, str.ptr);
12875 }
12876 
12877 void glnvg__resetError(bool force=false) (GLNVGcontext* gl) nothrow @trusted @nogc {
12878   static if (!force) {
12879     if ((gl.flags&NVGContextFlag.Debug) == 0) return;
12880   }
12881   glGetError();
12882 }
12883 
12884 void glnvg__checkError(bool force=false) (GLNVGcontext* gl, const(char)* str) nothrow @trusted @nogc {
12885   GLenum err;
12886   static if (!force) {
12887     if ((gl.flags&NVGContextFlag.Debug) == 0) return;
12888   }
12889   err = glGetError();
12890   if (err != GL_NO_ERROR) {
12891     import core.stdc.stdio : fprintf, stderr;
12892     fprintf(stderr, "Error %08x after %s\n", err, str);
12893     return;
12894   }
12895 }
12896 
12897 bool glnvg__createShader (GLNVGshader* shader, const(char)* name, const(char)* header, const(char)* opts, const(char)* vshader, const(char)* fshader) nothrow @trusted @nogc {
12898   GLint status;
12899   GLuint prog, vert, frag;
12900   const(char)*[3] str;
12901 
12902   memset(shader, 0, (*shader).sizeof);
12903 
12904   prog = glCreateProgram();
12905   vert = glCreateShader(GL_VERTEX_SHADER);
12906   frag = glCreateShader(GL_FRAGMENT_SHADER);
12907   str[0] = header;
12908   str[1] = (opts !is null ? opts : "");
12909   str[2] = vshader;
12910   glShaderSource(vert, 3, cast(const(char)**)str.ptr, null);
12911 
12912   glCompileShader(vert);
12913   glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
12914   if (status != GL_TRUE) {
12915     glnvg__dumpShaderError(vert, name, "vert");
12916     return false;
12917   }
12918 
12919   str[0] = header;
12920   str[1] = (opts !is null ? opts : "");
12921   str[2] = fshader;
12922   glShaderSource(frag, 3, cast(const(char)**)str.ptr, null);
12923 
12924   glCompileShader(frag);
12925   glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
12926   if (status != GL_TRUE) {
12927     glnvg__dumpShaderError(frag, name, "frag");
12928     return false;
12929   }
12930 
12931   glAttachShader(prog, vert);
12932   glAttachShader(prog, frag);
12933 
12934   glBindAttribLocation(prog, 0, "vertex");
12935   glBindAttribLocation(prog, 1, "tcoord");
12936 
12937   glLinkProgram(prog);
12938   glGetProgramiv(prog, GL_LINK_STATUS, &status);
12939   if (status != GL_TRUE) {
12940     glnvg__dumpProgramError(prog, name);
12941     return false;
12942   }
12943 
12944   shader.prog = prog;
12945   shader.vert = vert;
12946   shader.frag = frag;
12947 
12948   return true;
12949 }
12950 
12951 void glnvg__deleteShader (GLNVGshader* shader) nothrow @trusted @nogc {
12952   if (shader.prog != 0) glDeleteProgram(shader.prog);
12953   if (shader.vert != 0) glDeleteShader(shader.vert);
12954   if (shader.frag != 0) glDeleteShader(shader.frag);
12955 }
12956 
12957 void glnvg__getUniforms (GLNVGshader* shader) nothrow @trusted @nogc {
12958   shader.loc[GLNVGuniformLoc.ViewSize] = glGetUniformLocation(shader.prog, "viewSize");
12959   shader.loc[GLNVGuniformLoc.Tex] = glGetUniformLocation(shader.prog, "tex");
12960   shader.loc[GLNVGuniformLoc.Frag] = glGetUniformLocation(shader.prog, "frag");
12961   shader.loc[GLNVGuniformLoc.TMat] = glGetUniformLocation(shader.prog, "tmat");
12962   shader.loc[GLNVGuniformLoc.TTr] = glGetUniformLocation(shader.prog, "ttr");
12963   shader.loc[GLNVGuniformLoc.ClipTex] = glGetUniformLocation(shader.prog, "clipTex");
12964 }
12965 
12966 void glnvg__killFBOs (GLNVGcontext* gl) nothrow @trusted @nogc {
12967   foreach (immutable fidx, ref GLuint fbo; gl.fbo[]) {
12968     if (fbo != 0) {
12969       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
12970       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
12971       foreach (ref GLuint tid; gl.fboTex.ptr[fidx][]) if (tid != 0) { glDeleteTextures(1, &tid); tid = 0; }
12972       glDeleteFramebuffers(1, &fbo);
12973       fbo = 0;
12974     }
12975   }
12976   gl.fboWidth = gl.fboHeight = 0;
12977 }
12978 
12979 // returns `true` is new FBO was created
12980 // will not unbind buffer, if it was created
12981 bool glnvg__allocFBO (GLNVGcontext* gl, int fidx, bool doclear=true) nothrow @trusted @nogc {
12982   assert(fidx >= 0 && fidx < gl.fbo.length);
12983   assert(gl.fboWidth > 0);
12984   assert(gl.fboHeight > 0);
12985 
12986   if (gl.fbo.ptr[fidx] != 0) return false; // nothing to do, this FBO is already initialized
12987 
12988   glnvg__resetError(gl);
12989 
12990   // allocate FBO object
12991   GLuint fbo = 0;
12992   glGenFramebuffers(1, &fbo);
12993   if (fbo == 0) assert(0, "NanoVega: cannot create FBO");
12994   glnvg__checkError(gl, "glnvg__allocFBO: glGenFramebuffers");
12995   glBindFramebuffer(GL_FRAMEBUFFER, fbo);
12996   //scope(exit) glBindFramebuffer(GL_FRAMEBUFFER, 0);
12997 
12998   // attach 2D texture to this FBO
12999   GLuint tidColor = 0;
13000   glGenTextures(1, &tidColor);
13001   if (tidColor == 0) assert(0, "NanoVega: cannot create RGBA texture for FBO");
13002   glBindTexture(GL_TEXTURE_2D, tidColor);
13003   //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
13004   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
13005   glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_S");
13006   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
13007   glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_T");
13008   //FIXME: linear or nearest?
13009   //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
13010   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
13011   glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MIN_FILTER");
13012   //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
13013   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
13014   glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MAG_FILTER");
13015   // empty texture
13016   //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
13017   // create texture with only one color channel
13018   glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, gl.fboWidth, gl.fboHeight, 0, GL_RED, GL_UNSIGNED_BYTE, null);
13019   //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
13020   glnvg__checkError(gl, "glnvg__allocFBO: glTexImage2D (color)");
13021   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tidColor, 0);
13022   glnvg__checkError(gl, "glnvg__allocFBO: glFramebufferTexture2D (color)");
13023 
13024   // attach stencil texture to this FBO
13025   GLuint tidStencil = 0;
13026   version(nanovega_shared_stencil) {
13027     if (gl.fboTex.ptr[0].ptr[0] == 0) {
13028       glGenTextures(1, &tidStencil);
13029       if (tidStencil == 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
13030       gl.fboTex.ptr[0].ptr[0] = tidStencil;
13031     } else {
13032       tidStencil = gl.fboTex.ptr[0].ptr[0];
13033     }
13034     if (fidx != 0) gl.fboTex.ptr[fidx].ptr[1] = 0; // stencil texture is shared among FBOs
13035   } else {
13036     glGenTextures(1, &tidStencil);
13037     if (tidStencil == 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
13038     gl.fboTex.ptr[0].ptr[0] = tidStencil;
13039   }
13040   glBindTexture(GL_TEXTURE_2D, tidStencil);
13041   glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, gl.fboWidth, gl.fboHeight, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
13042   glnvg__checkError(gl, "glnvg__allocFBO: glTexImage2D (stencil)");
13043   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tidStencil, 0);
13044   glnvg__checkError(gl, "glnvg__allocFBO: glFramebufferTexture2D (stencil)");
13045 
13046   {
13047     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
13048     if (status != GL_FRAMEBUFFER_COMPLETE) {
13049       version(all) {
13050         import core.stdc.stdio;
13051         if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) printf("fucked attachement\n");
13052         if (status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) printf("fucked dimensions\n");
13053         if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) printf("missing attachement\n");
13054         if (status == GL_FRAMEBUFFER_UNSUPPORTED) printf("unsupported\n");
13055       }
13056       assert(0, "NanoVega: framebuffer creation failed");
13057     }
13058   }
13059 
13060   // clear 'em all
13061   if (doclear) {
13062     glClearColor(0, 0, 0, 0);
13063     glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
13064   }
13065 
13066   // save texture ids
13067   gl.fbo.ptr[fidx] = fbo;
13068   gl.fboTex.ptr[fidx].ptr[0] = tidColor;
13069   version(nanovega_shared_stencil) {} else {
13070     gl.fboTex.ptr[fidx].ptr[1] = tidStencil;
13071   }
13072 
13073   static if (NANOVG_GL_USE_STATE_FILTER) glBindTexture(GL_TEXTURE_2D, gl.boundTexture);
13074 
13075   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): created with index %d\n", gl.msp-1, fidx); }
13076 
13077   return true;
13078 }
13079 
13080 // will not unbind buffer
13081 void glnvg__clearFBO (GLNVGcontext* gl, int fidx) nothrow @trusted @nogc {
13082   assert(fidx >= 0 && fidx < gl.fbo.length);
13083   assert(gl.fboWidth > 0);
13084   assert(gl.fboHeight > 0);
13085   assert(gl.fbo.ptr[fidx] != 0);
13086   glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[fidx]);
13087   glClearColor(0, 0, 0, 0);
13088   glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
13089   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): cleared with index %d\n", gl.msp-1, fidx); }
13090 }
13091 
13092 // will not unbind buffer
13093 void glnvg__copyFBOToFrom (GLNVGcontext* gl, int didx, int sidx) nothrow @trusted @nogc {
13094   import core.stdc.string : memset;
13095   assert(didx >= 0 && didx < gl.fbo.length);
13096   assert(sidx >= 0 && sidx < gl.fbo.length);
13097   assert(gl.fboWidth > 0);
13098   assert(gl.fboHeight > 0);
13099   assert(gl.fbo.ptr[didx] != 0);
13100   assert(gl.fbo.ptr[sidx] != 0);
13101   if (didx == sidx) return;
13102 
13103   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): copy FBO: %d -> %d\n", gl.msp-1, sidx, didx); }
13104 
13105   glUseProgram(gl.shaderCopyFBO.prog);
13106 
13107   glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[didx]);
13108   glDisable(GL_CULL_FACE);
13109   glDisable(GL_BLEND);
13110   glDisable(GL_SCISSOR_TEST);
13111   glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[sidx].ptr[0]);
13112   // copy texture by drawing full quad
13113   enum x = 0;
13114   enum y = 0;
13115   immutable int w = gl.fboWidth;
13116   immutable int h = gl.fboHeight;
13117   immutable(NVGVertex[4]) vertices =
13118    [NVGVertex(x, y), // top-left
13119     NVGVertex(w, y), // top-right
13120     NVGVertex(w, h), // bottom-right
13121     NVGVertex(x, h)]; // bottom-left
13122 
13123   glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.sizeof, &vertices);
13124   glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
13125 
13126   // restore state (but don't unbind FBO)
13127   static if (NANOVG_GL_USE_STATE_FILTER) glBindTexture(GL_TEXTURE_2D, gl.boundTexture);
13128   glEnable(GL_CULL_FACE);
13129   glEnable(GL_BLEND);
13130   glUseProgram(gl.shader.prog);
13131 }
13132 
13133 void glnvg__resetFBOClipTextureCache (GLNVGcontext* gl) nothrow @trusted @nogc {
13134   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): texture cache invalidated (%d)\n", gl.msp-1, gl.lastClipFBO); }
13135   /*
13136   if (gl.lastClipFBO >= 0) {
13137     glActiveTexture(GL_TEXTURE1);
13138     glBindTexture(GL_TEXTURE_2D, 0);
13139     glActiveTexture(GL_TEXTURE0);
13140   }
13141   */
13142   gl.lastClipFBO = -666;
13143 }
13144 
13145 void glnvg__setFBOClipTexture (GLNVGcontext* gl, GLNVGfragUniforms* frag) nothrow @trusted @nogc {
13146   //assert(gl.msp > 0 && gl.msp <= gl.maskStack.length);
13147   if (gl.lastClipFBO != -666) {
13148     // cached
13149     version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): cached (%d)\n", gl.msp-1, gl.lastClipFBO); }
13150     frag.doclip = (gl.lastClipFBO >= 0 ? 1 : 0);
13151     return;
13152   }
13153 
13154   // no cache
13155   int fboidx = -1;
13156   mainloop: foreach_reverse (immutable sp, GLMaskState mst; gl.maskStack.ptr[0..gl.msp]/*; reverse*/) {
13157     final switch (mst) {
13158       case GLMaskState.DontMask: fboidx = -1; break mainloop;
13159       case GLMaskState.Uninitialized: break;
13160       case GLMaskState.Initialized: fboidx = cast(int)sp; break mainloop;
13161       case GLMaskState.JustCleared: assert(0, "NanoVega: `glnvg__setFBOClipTexture()` internal error");
13162     }
13163   }
13164 
13165   if (fboidx < 0) {
13166     // don't mask
13167     gl.lastClipFBO = -1;
13168     frag.doclip = 0;
13169   } else {
13170     // do masking
13171     assert(gl.fbo.ptr[fboidx] != 0);
13172     gl.lastClipFBO = fboidx;
13173     frag.doclip = 1;
13174   }
13175 
13176   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): new cache (new:%d)\n", gl.msp-1, gl.lastClipFBO); }
13177 
13178   if (gl.lastClipFBO >= 0) {
13179     assert(gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
13180     glActiveTexture(GL_TEXTURE1);
13181     glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
13182     glActiveTexture(GL_TEXTURE0);
13183   }
13184 }
13185 
13186 // returns index in `gl.fbo`, or -1 for "don't mask"
13187 int glnvg__generateFBOClipTexture (GLNVGcontext* gl) nothrow @trusted @nogc {
13188   assert(gl.msp > 0 && gl.msp <= gl.maskStack.length);
13189   // we need initialized FBO, even for "don't mask" case
13190   // for this, look back in stack, and either copy initialized FBO,
13191   // or stop at first uninitialized one, and clear it
13192   if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.Initialized) {
13193     // shortcut
13194     version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): generation of new texture is skipped (already initialized)\n", gl.msp-1); }
13195     glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[gl.msp-1]);
13196     return gl.msp-1;
13197   }
13198   foreach_reverse (immutable sp; 0..gl.msp/*; reverse*/) {
13199     final switch (gl.maskStack.ptr[sp]) {
13200       case GLMaskState.DontMask:
13201         // clear it
13202         version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): generating new clean texture\n", gl.msp-1); }
13203         if (!glnvg__allocFBO(gl, gl.msp-1)) glnvg__clearFBO(gl, gl.msp-1);
13204         gl.maskStack.ptr[gl.msp-1] = GLMaskState.JustCleared;
13205         return gl.msp-1;
13206       case GLMaskState.Uninitialized: break; // do nothing
13207       case GLMaskState.Initialized:
13208         // i found her! copy to TOS
13209         version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): copying texture from %d\n", gl.msp-1, cast(int)sp); }
13210         glnvg__allocFBO(gl, gl.msp-1, false);
13211         glnvg__copyFBOToFrom(gl, gl.msp-1, sp);
13212         gl.maskStack.ptr[gl.msp-1] = GLMaskState.Initialized;
13213         return gl.msp-1;
13214       case GLMaskState.JustCleared: assert(0, "NanoVega: `glnvg__generateFBOClipTexture()` internal error");
13215     }
13216   }
13217   // nothing was initialized, lol
13218   version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): generating new clean texture (first one)\n", gl.msp-1); }
13219   if (!glnvg__allocFBO(gl, gl.msp-1)) glnvg__clearFBO(gl, gl.msp-1);
13220   gl.maskStack.ptr[gl.msp-1] = GLMaskState.JustCleared;
13221   return gl.msp-1;
13222 }
13223 
13224 void glnvg__renderPushClip (void* uptr) nothrow @trusted @nogc {
13225   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13226   GLNVGcall* call = glnvg__allocCall(gl);
13227   if (call is null) return;
13228   call.type = GLNVG_PUSHCLIP;
13229 }
13230 
13231 void glnvg__renderPopClip (void* uptr) nothrow @trusted @nogc {
13232   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13233   GLNVGcall* call = glnvg__allocCall(gl);
13234   if (call is null) return;
13235   call.type = GLNVG_POPCLIP;
13236 }
13237 
13238 void glnvg__renderResetClip (void* uptr) nothrow @trusted @nogc {
13239   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13240   GLNVGcall* call = glnvg__allocCall(gl);
13241   if (call is null) return;
13242   call.type = GLNVG_RESETCLIP;
13243 }
13244 
13245 void glnvg__clipDebugDump (void* uptr, bool doit) nothrow @trusted @nogc {
13246   version(nanovega_debug_clipping) {
13247     GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13248     GLNVGcall* call = glnvg__allocCall(gl);
13249     call.type = (doit ? GLNVG_CLIP_DDUMP_ON : GLNVG_CLIP_DDUMP_OFF);
13250   }
13251 }
13252 
13253 bool glnvg__renderCreate (void* uptr) nothrow @trusted @nogc {
13254   import core.stdc.stdio : snprintf;
13255 
13256   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13257   enum align_ = 4;
13258 
13259   char[64] shaderHeader = void;
13260   //enum shaderHeader = "#define UNIFORM_ARRAY_SIZE 12\n";
13261   snprintf(shaderHeader.ptr, shaderHeader.length, "#define UNIFORM_ARRAY_SIZE %u\n", cast(uint)GLNVGfragUniforms.UNIFORM_ARRAY_SIZE);
13262 
13263   enum fillVertShader = q{
13264     uniform vec2 viewSize;
13265     attribute vec2 vertex;
13266     attribute vec2 tcoord;
13267     varying vec2 ftcoord;
13268     varying vec2 fpos;
13269     uniform vec4 tmat; /* abcd of affine matrix: xyzw */
13270     uniform vec2 ttr; /* tx and ty of affine matrix */
13271     void main (void) {
13272       /* affine transformation */
13273       float nx = vertex.x*tmat.x+vertex.y*tmat.z+ttr.x;
13274       float ny = vertex.x*tmat.y+vertex.y*tmat.w+ttr.y;
13275       ftcoord = tcoord;
13276       fpos = vec2(nx, ny);
13277       gl_Position = vec4(2.0*nx/viewSize.x-1.0, 1.0-2.0*ny/viewSize.y, 0, 1);
13278     }
13279   };
13280 
13281   enum fillFragShader = `
13282     uniform vec4 frag[UNIFORM_ARRAY_SIZE];
13283     uniform sampler2D tex;
13284     uniform sampler2D clipTex;
13285     uniform vec2 viewSize;
13286     varying vec2 ftcoord;
13287     varying vec2 fpos;
13288     #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)
13289     #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)
13290     #define innerCol frag[6]
13291     #define middleCol frag[7]
13292     #define outerCol frag[7+1]
13293     #define scissorExt frag[8+1].xy
13294     #define scissorScale frag[8+1].zw
13295     #define extent frag[9+1].xy
13296     #define radius frag[9+1].z
13297     #define feather frag[9+1].w
13298     #define strokeMult frag[10+1].x
13299     #define strokeThr frag[10+1].y
13300     #define texType int(frag[10+1].z)
13301     #define type int(frag[10+1].w)
13302     #define doclip int(frag[11+1].x)
13303     #define midp frag[11+1].y
13304 
13305     float sdroundrect (in vec2 pt, in vec2 ext, in float rad) {
13306       vec2 ext2 = ext-vec2(rad, rad);
13307       vec2 d = abs(pt)-ext2;
13308       return min(max(d.x, d.y), 0.0)+length(max(d, 0.0))-rad;
13309     }
13310 
13311     // Scissoring
13312     float scissorMask (in vec2 p) {
13313       vec2 sc = (abs((scissorMat*vec3(p, 1.0)).xy)-scissorExt);
13314       sc = vec2(0.5, 0.5)-sc*scissorScale;
13315       return clamp(sc.x, 0.0, 1.0)*clamp(sc.y, 0.0, 1.0);
13316     }
13317 
13318     #ifdef EDGE_AA
13319     // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.
13320     float strokeMask () {
13321       return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult)*min(1.0, ftcoord.y);
13322     }
13323     #endif
13324 
13325     void main (void) {
13326       // clipping
13327       if (doclip != 0) {
13328         /*vec4 clr = texelFetch(clipTex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);*/
13329         vec4 clr = texture2D(clipTex, vec2(gl_FragCoord.x/viewSize.x, gl_FragCoord.y/viewSize.y));
13330         if (clr.r == 0.0) discard;
13331       }
13332       float scissor = scissorMask(fpos);
13333       if (scissor <= 0.0) discard; //k8: is it really faster?
13334       #ifdef EDGE_AA
13335       float strokeAlpha = strokeMask();
13336       if (strokeAlpha < strokeThr) discard;
13337       #else
13338       float strokeAlpha = 1.0;
13339       #endif
13340       // rendering
13341       vec4 color;
13342       if (type == 0) { /* NSVG_SHADER_FILLCOLOR */
13343         color = innerCol;
13344         // Combine alpha
13345         color *= strokeAlpha*scissor;
13346       } else if (type == 1) { /* NSVG_SHADER_FILLGRAD */
13347         // Gradient
13348         // Calculate gradient color using box gradient
13349         vec2 pt = (paintMat*vec3(fpos, 1.0)).xy;
13350         float d = clamp((sdroundrect(pt, extent, radius)+feather*0.5)/feather, 0.0, 1.0);
13351         if (midp <= 0.0) {
13352           color = mix(innerCol, outerCol, d);
13353         } else {
13354           float gdst = min(midp, 1.0);
13355           if (d < gdst) {
13356             color = mix(innerCol, middleCol, d/gdst);
13357           } else {
13358             color = mix(middleCol, outerCol, (d-gdst)/gdst);
13359           }
13360         }
13361         // Combine alpha
13362         color *= strokeAlpha*scissor;
13363       } else if (type == 2) { /* NSVG_SHADER_FILLIMG */
13364         // Image
13365         // Calculate color from texture
13366         vec2 pt = (paintMat*vec3(fpos, 1.0)).xy/extent;
13367         color = texture2D(tex, pt);
13368         if (texType == 1) color = vec4(color.xyz*color.w, color.w);
13369         if (texType == 2) color = vec4(color.x);
13370         // Apply color tint and alpha
13371         color *= innerCol;
13372         // Combine alpha
13373         color *= strokeAlpha*scissor;
13374       } else if (type == 3) { /* NSVG_SHADER_SIMPLE */
13375         // Stencil fill
13376         color = vec4(1, 1, 1, 1);
13377       } else if (type == 4) { /* NSVG_SHADER_IMG */
13378         // Textured tris
13379         color = texture2D(tex, ftcoord);
13380         if (texType == 1) color = vec4(color.xyz*color.w, color.w);
13381         if (texType == 2) color = vec4(color.x);
13382         color *= scissor;
13383         color *= innerCol; // Apply color tint
13384       }
13385       gl_FragColor = color;
13386     }
13387   `;
13388 
13389   enum clipVertShaderFill = q{
13390     uniform vec2 viewSize;
13391     attribute vec2 vertex;
13392     uniform vec4 tmat; /* abcd of affine matrix: xyzw */
13393     uniform vec2 ttr; /* tx and ty of affine matrix */
13394     void main (void) {
13395       /* affine transformation */
13396       float nx = vertex.x*tmat.x+vertex.y*tmat.z+ttr.x;
13397       float ny = vertex.x*tmat.y+vertex.y*tmat.w+ttr.y;
13398       gl_Position = vec4(2.0*nx/viewSize.x-1.0, 1.0-2.0*ny/viewSize.y, 0, 1);
13399     }
13400   };
13401 
13402   enum clipFragShaderFill = q{
13403     uniform vec2 viewSize;
13404     void main (void) {
13405       gl_FragColor = vec4(1, 1, 1, 1);
13406     }
13407   };
13408 
13409   enum clipVertShaderCopy = q{
13410     uniform vec2 viewSize;
13411     attribute vec2 vertex;
13412     void main (void) {
13413       gl_Position = vec4(2.0*vertex.x/viewSize.x-1.0, 1.0-2.0*vertex.y/viewSize.y, 0, 1);
13414     }
13415   };
13416 
13417   enum clipFragShaderCopy = q{
13418     uniform sampler2D tex;
13419     uniform vec2 viewSize;
13420     void main (void) {
13421       //gl_FragColor = texelFetch(tex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);
13422       gl_FragColor = texture2D(tex, vec2(gl_FragCoord.x/viewSize.x, gl_FragCoord.y/viewSize.y));
13423     }
13424   };
13425 
13426   glnvg__checkError(gl, "init");
13427 
13428   string defines = (gl.flags&NVGContextFlag.Antialias ? "#define EDGE_AA 1\n" : null);
13429   if (!glnvg__createShader(&gl.shader, "shader", shaderHeader.ptr, defines.ptr, fillVertShader, fillFragShader)) return false;
13430   if (!glnvg__createShader(&gl.shaderFillFBO, "shaderFillFBO", shaderHeader.ptr, defines.ptr, clipVertShaderFill, clipFragShaderFill)) return false;
13431   if (!glnvg__createShader(&gl.shaderCopyFBO, "shaderCopyFBO", shaderHeader.ptr, defines.ptr, clipVertShaderCopy, clipFragShaderCopy)) return false;
13432 
13433   glnvg__checkError(gl, "uniform locations");
13434   glnvg__getUniforms(&gl.shader);
13435   glnvg__getUniforms(&gl.shaderFillFBO);
13436   glnvg__getUniforms(&gl.shaderCopyFBO);
13437 
13438   // Create dynamic vertex array
13439   glGenBuffers(1, &gl.vertBuf);
13440 
13441   gl.fragSize = GLNVGfragUniforms.sizeof+align_-GLNVGfragUniforms.sizeof%align_;
13442 
13443   glnvg__checkError(gl, "create done");
13444 
13445   glFinish();
13446 
13447   return true;
13448 }
13449 
13450 int glnvg__renderCreateTexture (void* uptr, NVGtexture type, int w, int h, int imageFlags, const(ubyte)* data) nothrow @trusted @nogc {
13451   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13452   GLNVGtexture* tex = glnvg__allocTexture(gl);
13453 
13454   if (tex is null) return 0;
13455 
13456   glGenTextures(1, &tex.tex);
13457   tex.width = w;
13458   tex.height = h;
13459   tex.type = type;
13460   tex.flags = imageFlags;
13461   glnvg__bindTexture(gl, tex.tex);
13462 
13463   version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("created texture with id %d; glid=%u\n", tex.id, tex.tex); }}
13464 
13465   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
13466   glPixelStorei(GL_UNPACK_ROW_LENGTH, tex.width);
13467   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
13468   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
13469 
13470 
13471 
13472   immutable ttype = (type == NVGtexture.RGBA ? GL_RGBA : GL_RED);
13473   glTexImage2D(GL_TEXTURE_2D, 0, ttype, w, h, 0, ttype, GL_UNSIGNED_BYTE, data);
13474   // GL 3.0 and later have support for a dedicated function for generating mipmaps
13475   // it needs to be called after the glTexImage2D call
13476   if (imageFlags & NVGImageFlag.GenerateMipmaps)
13477     glGenerateMipmap(GL_TEXTURE_2D);
13478 
13479   immutable tfmin =
13480     (imageFlags & NVGImageFlag.GenerateMipmaps ? GL_LINEAR_MIPMAP_LINEAR :
13481      imageFlags & NVGImageFlag.NoFiltering ? GL_NEAREST :
13482      GL_LINEAR);
13483   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.0);
13484   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tfmin);
13485   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (imageFlags&NVGImageFlag.NoFiltering ? GL_NEAREST : GL_LINEAR));
13486 
13487   int flag;
13488   if (imageFlags&NVGImageFlag.RepeatX)
13489     flag = GL_REPEAT;
13490   else if (imageFlags&NVGImageFlag.ClampToBorderX)
13491     flag = GL_CLAMP_TO_BORDER;
13492   else 
13493     flag = GL_CLAMP_TO_EDGE;
13494   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, flag);
13495 
13496 
13497   if (imageFlags&NVGImageFlag.RepeatY)
13498     flag = GL_REPEAT;
13499   else if (imageFlags&NVGImageFlag.ClampToBorderY)
13500     flag = GL_CLAMP_TO_BORDER;
13501   else 
13502     flag = GL_CLAMP_TO_EDGE;
13503   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, flag);
13504 
13505   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
13506   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
13507   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
13508   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
13509 
13510   glnvg__checkError(gl, "create tex");
13511   glnvg__bindTexture(gl, 0);
13512 
13513   return tex.id;
13514 }
13515 
13516 bool glnvg__renderDeleteTexture (void* uptr, int image) nothrow @trusted @nogc {
13517   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13518   return glnvg__deleteTexture(gl, image);
13519 }
13520 
13521 bool glnvg__renderTextureIncRef (void* uptr, int image) nothrow @trusted @nogc {
13522   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13523   GLNVGtexture* tex = glnvg__findTexture(gl, image);
13524   if (tex is null) {
13525     version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("CANNOT incref texture with id %d\n", image); }}
13526     return false;
13527   }
13528   import core.atomic : atomicOp;
13529   atomicOp!"+="(tex.rc, 1);
13530   version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("texture #%d: incref; newref=%d\n", image, tex.rc); }}
13531   return true;
13532 }
13533 
13534 bool glnvg__renderUpdateTexture (void* uptr, int image, int x, int y, int w, int h, const(ubyte)* data) nothrow @trusted @nogc {
13535   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13536   GLNVGtexture* tex = glnvg__findTexture(gl, image);
13537 
13538   if (tex is null) {
13539     version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("CANNOT update texture with id %d\n", image); }}
13540     return false;
13541   }
13542 
13543   version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("updated texture with id %d; glid=%u\n", tex.id, image, tex.tex); }}
13544 
13545   glnvg__bindTexture(gl, tex.tex);
13546 
13547   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
13548   glPixelStorei(GL_UNPACK_ROW_LENGTH, tex.width);
13549   glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);
13550   glPixelStorei(GL_UNPACK_SKIP_ROWS, y);
13551 
13552   immutable ttype = (tex.type == NVGtexture.RGBA ? GL_RGBA : GL_RED);
13553   glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, ttype, GL_UNSIGNED_BYTE, data);
13554 
13555   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
13556   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
13557   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
13558   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
13559 
13560   glnvg__bindTexture(gl, 0);
13561 
13562   return true;
13563 }
13564 
13565 bool glnvg__renderGetTextureSize (void* uptr, int image, int* w, int* h) nothrow @trusted @nogc {
13566   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13567   GLNVGtexture* tex = glnvg__findTexture(gl, image);
13568   if (tex is null) {
13569     if (w !is null) *w = 0;
13570     if (h !is null) *h = 0;
13571     return false;
13572   } else {
13573     if (w !is null) *w = tex.width;
13574     if (h !is null) *h = tex.height;
13575     return true;
13576   }
13577 }
13578 
13579 void glnvg__xformToMat3x4 (float[] m3, const(float)[] t) nothrow @trusted @nogc {
13580   assert(t.length >= 6);
13581   assert(m3.length >= 12);
13582   m3.ptr[0] = t.ptr[0];
13583   m3.ptr[1] = t.ptr[1];
13584   m3.ptr[2] = 0.0f;
13585   m3.ptr[3] = 0.0f;
13586   m3.ptr[4] = t.ptr[2];
13587   m3.ptr[5] = t.ptr[3];
13588   m3.ptr[6] = 0.0f;
13589   m3.ptr[7] = 0.0f;
13590   m3.ptr[8] = t.ptr[4];
13591   m3.ptr[9] = t.ptr[5];
13592   m3.ptr[10] = 1.0f;
13593   m3.ptr[11] = 0.0f;
13594 }
13595 
13596 NVGColor glnvg__premulColor() (in auto ref NVGColor c) nothrow @trusted @nogc {
13597   //pragma(inline, true);
13598   NVGColor res = void;
13599   res.r = c.r*c.a;
13600   res.g = c.g*c.a;
13601   res.b = c.b*c.a;
13602   res.a = c.a;
13603   return res;
13604 }
13605 
13606 bool glnvg__convertPaint (GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGPaint* paint, NVGscissor* scissor, float width, float fringe, float strokeThr) nothrow @trusted @nogc {
13607   import core.stdc.math : sqrtf;
13608   GLNVGtexture* tex = null;
13609   NVGMatrix invxform = void;
13610 
13611   memset(frag, 0, (*frag).sizeof);
13612 
13613   frag.innerCol = glnvg__premulColor(paint.innerColor);
13614   frag.middleCol = glnvg__premulColor(paint.middleColor);
13615   frag.outerCol = glnvg__premulColor(paint.outerColor);
13616   frag.midp = paint.midp;
13617 
13618   if (scissor.extent.ptr[0] < -0.5f || scissor.extent.ptr[1] < -0.5f) {
13619     memset(frag.scissorMat.ptr, 0, frag.scissorMat.sizeof);
13620     frag.scissorExt.ptr[0] = 1.0f;
13621     frag.scissorExt.ptr[1] = 1.0f;
13622     frag.scissorScale.ptr[0] = 1.0f;
13623     frag.scissorScale.ptr[1] = 1.0f;
13624   } else {
13625     //nvgTransformInverse(invxform[], scissor.xform[]);
13626     invxform = scissor.xform.inverted;
13627     glnvg__xformToMat3x4(frag.scissorMat[], invxform.mat[]);
13628     frag.scissorExt.ptr[0] = scissor.extent.ptr[0];
13629     frag.scissorExt.ptr[1] = scissor.extent.ptr[1];
13630     frag.scissorScale.ptr[0] = sqrtf(scissor.xform.mat.ptr[0]*scissor.xform.mat.ptr[0]+scissor.xform.mat.ptr[2]*scissor.xform.mat.ptr[2])/fringe;
13631     frag.scissorScale.ptr[1] = sqrtf(scissor.xform.mat.ptr[1]*scissor.xform.mat.ptr[1]+scissor.xform.mat.ptr[3]*scissor.xform.mat.ptr[3])/fringe;
13632   }
13633 
13634   memcpy(frag.extent.ptr, paint.extent.ptr, frag.extent.sizeof);
13635   frag.strokeMult = (width*0.5f+fringe*0.5f)/fringe;
13636   frag.strokeThr = strokeThr;
13637 
13638   if (paint.image.valid) {
13639     tex = glnvg__findTexture(gl, paint.image.id);
13640     if (tex is null) return false;
13641     if ((tex.flags&NVGImageFlag.FlipY) != 0) {
13642       /*
13643       NVGMatrix flipped;
13644       nvgTransformScale(flipped[], 1.0f, -1.0f);
13645       nvgTransformMultiply(flipped[], paint.xform[]);
13646       nvgTransformInverse(invxform[], flipped[]);
13647       */
13648       /*
13649       NVGMatrix m1 = void, m2 = void;
13650       nvgTransformTranslate(m1[], 0.0f, frag.extent.ptr[1]*0.5f);
13651       nvgTransformMultiply(m1[], paint.xform[]);
13652       nvgTransformScale(m2[], 1.0f, -1.0f);
13653       nvgTransformMultiply(m2[], m1[]);
13654       nvgTransformTranslate(m1[], 0.0f, -frag.extent.ptr[1]*0.5f);
13655       nvgTransformMultiply(m1[], m2[]);
13656       nvgTransformInverse(invxform[], m1[]);
13657       */
13658       NVGMatrix m1 = NVGMatrix.Translated(0.0f, frag.extent.ptr[1]*0.5f);
13659       m1.mul(paint.xform);
13660       NVGMatrix m2 = NVGMatrix.Scaled(1.0f, -1.0f);
13661       m2.mul(m1);
13662       m1 = NVGMatrix.Translated(0.0f, -frag.extent.ptr[1]*0.5f);
13663       m1.mul(m2);
13664       invxform = m1.inverted;
13665     } else {
13666       //nvgTransformInverse(invxform[], paint.xform[]);
13667       invxform = paint.xform.inverted;
13668     }
13669     frag.type = NSVG_SHADER_FILLIMG;
13670 
13671     if (tex.type == NVGtexture.RGBA) {
13672       frag.texType = (tex.flags&NVGImageFlag.Premultiplied ? 0 : 1);
13673     } else {
13674       frag.texType = 2;
13675     }
13676     //printf("frag.texType = %d\n", frag.texType);
13677   } else {
13678     frag.type = (paint.simpleColor ? NSVG_SHADER_FILLCOLOR : NSVG_SHADER_FILLGRAD);
13679     frag.radius = paint.radius;
13680     frag.feather = paint.feather;
13681     //nvgTransformInverse(invxform[], paint.xform[]);
13682     invxform = paint.xform.inverted;
13683   }
13684 
13685   glnvg__xformToMat3x4(frag.paintMat[], invxform.mat[]);
13686 
13687   return true;
13688 }
13689 
13690 void glnvg__setUniforms (GLNVGcontext* gl, int uniformOffset, int image) nothrow @trusted @nogc {
13691   GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
13692   glnvg__setFBOClipTexture(gl, frag);
13693   glUniform4fv(gl.shader.loc[GLNVGuniformLoc.Frag], frag.UNIFORM_ARRAY_SIZE, &(frag.uniformArray.ptr[0].ptr[0]));
13694   glnvg__checkError(gl, "glnvg__setUniforms");
13695   if (image != 0) {
13696     GLNVGtexture* tex = glnvg__findTexture(gl, image);
13697     glnvg__bindTexture(gl, (tex !is null ? tex.tex : 0));
13698     glnvg__checkError(gl, "tex paint tex");
13699   } else {
13700     glnvg__bindTexture(gl, 0);
13701   }
13702 }
13703 
13704 void glnvg__finishClip (GLNVGcontext* gl, NVGClipMode clipmode) nothrow @trusted @nogc {
13705   assert(clipmode != NVGClipMode.None);
13706 
13707   // fill FBO, clear stencil buffer
13708   //TODO: optimize with bounds?
13709   version(all) {
13710     //glnvg__resetAffine(gl);
13711     //glUseProgram(gl.shaderFillFBO.prog);
13712     glDisable(GL_CULL_FACE);
13713     glDisable(GL_BLEND);
13714     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
13715     glEnable(GL_STENCIL_TEST);
13716     if (gl.doClipUnion) {
13717       // for "and" we should clear everything that is NOT stencil-masked
13718       glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
13719       glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
13720     } else {
13721       glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x00, 0xff);
13722       glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
13723     }
13724 
13725     immutable(NVGVertex[4]) vertices =
13726      [NVGVertex(0, 0, 0, 0),
13727       NVGVertex(0, gl.fboHeight, 0, 0),
13728       NVGVertex(gl.fboWidth, gl.fboHeight, 0, 0),
13729       NVGVertex(gl.fboWidth, 0, 0, 0)];
13730 
13731     glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.sizeof, &vertices);
13732     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
13733 
13734     //glnvg__restoreAffine(gl);
13735   }
13736 
13737   glBindFramebuffer(GL_FRAMEBUFFER, gl.mainFBO);
13738   glDisable(GL_COLOR_LOGIC_OP);
13739   //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // done above
13740   glEnable(GL_BLEND);
13741   glDisable(GL_STENCIL_TEST);
13742   glEnable(GL_CULL_FACE);
13743   glUseProgram(gl.shader.prog);
13744 
13745   // set current FBO as used one
13746   assert(gl.msp > 0 && gl.fbo.ptr[gl.msp-1] > 0 && gl.fboTex.ptr[gl.msp-1].ptr[0] > 0);
13747   if (gl.lastClipFBO != gl.msp-1) {
13748     version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): new cache from changed mask (old:%d; new:%d)\n", gl.msp-1, gl.lastClipFBO, gl.msp-1); }
13749     gl.lastClipFBO = gl.msp-1;
13750     glActiveTexture(GL_TEXTURE1);
13751     glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
13752     glActiveTexture(GL_TEXTURE0);
13753   }
13754 }
13755 
13756 void glnvg__setClipUniforms (GLNVGcontext* gl, int uniformOffset, NVGClipMode clipmode) nothrow @trusted @nogc {
13757   assert(clipmode != NVGClipMode.None);
13758   GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
13759   // save uniform offset for `glnvg__finishClip()`
13760   gl.lastClipUniOfs = uniformOffset;
13761   // get FBO index, bind this FBO
13762   immutable int clipTexId = glnvg__generateFBOClipTexture(gl);
13763   assert(clipTexId >= 0);
13764   glUseProgram(gl.shaderFillFBO.prog);
13765   glnvg__checkError(gl, "use");
13766   glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[clipTexId]);
13767   // set logic op for clip
13768   gl.doClipUnion = false;
13769   if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.JustCleared) {
13770     // it is cleared to zero, we can just draw a path
13771     glDisable(GL_COLOR_LOGIC_OP);
13772     gl.maskStack.ptr[gl.msp-1] = GLMaskState.Initialized;
13773   } else {
13774     glEnable(GL_COLOR_LOGIC_OP);
13775     final switch (clipmode) {
13776       case NVGClipMode.None: assert(0, "wtf?!");
13777       case NVGClipMode.Union: glLogicOp(GL_CLEAR); gl.doClipUnion = true; break; // use `GL_CLEAR` to avoid adding another shader mode
13778       case NVGClipMode.Or: glLogicOp(GL_COPY); break; // GL_OR
13779       case NVGClipMode.Xor: glLogicOp(GL_XOR); break;
13780       case NVGClipMode.Sub: glLogicOp(GL_CLEAR); break;
13781       case NVGClipMode.Replace: glLogicOp(GL_COPY); break;
13782     }
13783   }
13784   // set affine matrix
13785   glUniform4fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.TMat], 1, gl.lastAffine.mat.ptr);
13786   glnvg__checkError(gl, "affine 0");
13787   glUniform2fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.TTr], 1, gl.lastAffine.mat.ptr+4);
13788   glnvg__checkError(gl, "affine 1");
13789   // setup common OpenGL parameters
13790   glDisable(GL_BLEND);
13791   glDisable(GL_CULL_FACE);
13792   glEnable(GL_STENCIL_TEST);
13793   glnvg__stencilMask(gl, 0xff);
13794   glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
13795   glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
13796   glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
13797 }
13798 
13799 void glnvg__renderViewport (void* uptr, int width, int height) nothrow @trusted @nogc {
13800   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13801   gl.inFrame = true;
13802   gl.view.ptr[0] = cast(float)width;
13803   gl.view.ptr[1] = cast(float)height;
13804   // kill FBOs if we need to create new ones (flushing will recreate 'em if necessary)
13805   if (width != gl.fboWidth || height != gl.fboHeight) {
13806     glnvg__killFBOs(gl);
13807     gl.fboWidth = width;
13808     gl.fboHeight = height;
13809   }
13810   gl.msp = 1;
13811   gl.maskStack.ptr[0] = GLMaskState.DontMask;
13812   // texture cleanup
13813   import core.atomic : atomicLoad;
13814   if (atomicLoad(gl.mustCleanTextures)) {
13815     try {
13816       import core.thread : Thread;
13817       static if (__VERSION__ < 2076) {
13818         DGNoThrowNoGC(() {
13819           if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
13820         })();
13821       } else {
13822         if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
13823       }
13824       synchronized(GLNVGTextureLocker.classinfo) {
13825         gl.mustCleanTextures = false;
13826         foreach (immutable tidx, ref GLNVGtexture tex; gl.textures[0..gl.ntextures]) {
13827           // no need to use atomic ops here, as we're locked
13828           if (tex.rc == 0 && tex.tex != 0 && tex.id == 0) {
13829             version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("*** cleaned up texture with glid=%u\n", tex.tex); }}
13830             import core.stdc.string : memset;
13831             if ((tex.flags&NVGImageFlag.NoDelete) == 0) glDeleteTextures(1, &tex.tex);
13832             memset(&tex, 0, tex.sizeof);
13833             tex.nextfree = gl.freetexid;
13834             gl.freetexid = cast(int)tidx;
13835           }
13836         }
13837       }
13838     } catch (Exception e) {}
13839   }
13840 }
13841 
13842 void glnvg__fill (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
13843   GLNVGpath* paths = &gl.paths[call.pathOffset];
13844   int npaths = call.pathCount;
13845 
13846   if (call.clipmode == NVGClipMode.None) {
13847     // Draw shapes
13848     glEnable(GL_STENCIL_TEST);
13849     glnvg__stencilMask(gl, 0xffU);
13850     glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xffU);
13851 
13852     glnvg__setUniforms(gl, call.uniformOffset, 0);
13853     glnvg__checkError(gl, "fill simple");
13854 
13855     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
13856     if (call.evenOdd) {
13857       //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
13858       //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
13859       glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
13860     } else {
13861       glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
13862       glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
13863     }
13864     glDisable(GL_CULL_FACE);
13865     foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13866     glEnable(GL_CULL_FACE);
13867 
13868     // Draw anti-aliased pixels
13869     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
13870     glnvg__setUniforms(gl, call.uniformOffset+gl.fragSize, call.image);
13871     glnvg__checkError(gl, "fill fill");
13872 
13873     if (gl.flags&NVGContextFlag.Antialias) {
13874       glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xffU);
13875       glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
13876       // Draw fringes
13877       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13878     }
13879 
13880     // Draw fill
13881     glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xffU);
13882     glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
13883     if (call.evenOdd) {
13884       glDisable(GL_CULL_FACE);
13885       glDrawArrays(GL_TRIANGLE_STRIP, call.triangleOffset, call.triangleCount);
13886       //foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13887       glEnable(GL_CULL_FACE);
13888     } else {
13889       glDrawArrays(GL_TRIANGLE_STRIP, call.triangleOffset, call.triangleCount);
13890     }
13891 
13892     glDisable(GL_STENCIL_TEST);
13893   } else {
13894     glnvg__setClipUniforms(gl, call.uniformOffset/*+gl.fragSize*/, call.clipmode); // this activates our FBO
13895     glnvg__checkError(gl, "fillclip simple");
13896     glnvg__stencilFunc(gl, GL_ALWAYS, 0x00, 0xffU);
13897     if (call.evenOdd) {
13898       //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
13899       //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
13900       glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
13901     } else {
13902       glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
13903       glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
13904     }
13905     foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13906     glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
13907   }
13908 }
13909 
13910 void glnvg__convexFill (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
13911   GLNVGpath* paths = &gl.paths[call.pathOffset];
13912   int npaths = call.pathCount;
13913 
13914   if (call.clipmode == NVGClipMode.None) {
13915     glnvg__setUniforms(gl, call.uniformOffset, call.image);
13916     glnvg__checkError(gl, "convex fill");
13917     if (call.evenOdd) glDisable(GL_CULL_FACE);
13918     foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13919     if (gl.flags&NVGContextFlag.Antialias) {
13920       // Draw fringes
13921       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13922     }
13923     if (call.evenOdd) glEnable(GL_CULL_FACE);
13924   } else {
13925     glnvg__setClipUniforms(gl, call.uniformOffset, call.clipmode); // this activates our FBO
13926     glnvg__checkError(gl, "clip convex fill");
13927     foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13928     if (gl.flags&NVGContextFlag.Antialias) {
13929       // Draw fringes
13930       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13931     }
13932     glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
13933   }
13934 }
13935 
13936 void glnvg__stroke (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
13937   GLNVGpath* paths = &gl.paths[call.pathOffset];
13938   int npaths = call.pathCount;
13939 
13940   if (call.clipmode == NVGClipMode.None) {
13941     if (gl.flags&NVGContextFlag.StencilStrokes) {
13942       glEnable(GL_STENCIL_TEST);
13943       glnvg__stencilMask(gl, 0xff);
13944 
13945       // Fill the stroke base without overlap
13946       glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff);
13947       glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
13948       glnvg__setUniforms(gl, call.uniformOffset+gl.fragSize, call.image);
13949       glnvg__checkError(gl, "stroke fill 0");
13950       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13951 
13952       // Draw anti-aliased pixels.
13953       glnvg__setUniforms(gl, call.uniformOffset, call.image);
13954       glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
13955       glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
13956       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13957 
13958       // Clear stencil buffer.
13959       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
13960       glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
13961       glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
13962       glnvg__checkError(gl, "stroke fill 1");
13963       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13964       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
13965 
13966       glDisable(GL_STENCIL_TEST);
13967 
13968       //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, strokeWidth, fringe, 1.0f-0.5f/255.0f);
13969     } else {
13970       glnvg__setUniforms(gl, call.uniformOffset, call.image);
13971       glnvg__checkError(gl, "stroke fill");
13972       // Draw Strokes
13973       foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13974     }
13975   } else {
13976     glnvg__setClipUniforms(gl, call.uniformOffset/*+gl.fragSize*/, call.clipmode);
13977     glnvg__checkError(gl, "stroke fill 0");
13978     foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
13979     glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
13980   }
13981 }
13982 
13983 void glnvg__triangles (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
13984   if (call.clipmode == NVGClipMode.None) {
13985     glnvg__setUniforms(gl, call.uniformOffset, call.image);
13986     glnvg__checkError(gl, "triangles fill");
13987     glDrawArrays(GL_TRIANGLES, call.triangleOffset, call.triangleCount);
13988   } else {
13989     //TODO(?): use texture as mask?
13990   }
13991 }
13992 
13993 void glnvg__affine (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
13994   glUniform4fv(gl.shader.loc[GLNVGuniformLoc.TMat], 1, call.affine.mat.ptr);
13995   glnvg__checkError(gl, "affine");
13996   glUniform2fv(gl.shader.loc[GLNVGuniformLoc.TTr], 1, call.affine.mat.ptr+4);
13997   glnvg__checkError(gl, "affine");
13998   //glnvg__setUniforms(gl, call.uniformOffset, call.image);
13999 }
14000 
14001 void glnvg__renderCancelInternal (GLNVGcontext* gl, bool clearTextures) nothrow @trusted @nogc {
14002   scope(exit) gl.inFrame = false;
14003   if (clearTextures && gl.inFrame) {
14004     try {
14005       import core.thread : Thread;
14006       static if (__VERSION__ < 2076) {
14007         DGNoThrowNoGC(() {
14008           if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
14009         })();
14010       } else {
14011         if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
14012       }
14013     } catch (Exception e) {}
14014     foreach (ref GLNVGcall c; gl.calls[0..gl.ncalls]) if (c.image > 0) glnvg__deleteTexture(gl, c.image);
14015   }
14016   gl.nverts = 0;
14017   gl.npaths = 0;
14018   gl.ncalls = 0;
14019   gl.nuniforms = 0;
14020   gl.msp = 1;
14021   gl.maskStack.ptr[0] = GLMaskState.DontMask;
14022 }
14023 
14024 void glnvg__renderCancel (void* uptr) nothrow @trusted @nogc {
14025   glnvg__renderCancelInternal(cast(GLNVGcontext*)uptr, true);
14026 }
14027 
14028 GLenum glnvg_convertBlendFuncFactor (NVGBlendFactor factor) pure nothrow @trusted @nogc {
14029   if (factor == NVGBlendFactor.Zero) return GL_ZERO;
14030   if (factor == NVGBlendFactor.One) return GL_ONE;
14031   if (factor == NVGBlendFactor.SrcColor) return GL_SRC_COLOR;
14032   if (factor == NVGBlendFactor.OneMinusSrcColor) return GL_ONE_MINUS_SRC_COLOR;
14033   if (factor == NVGBlendFactor.DstColor) return GL_DST_COLOR;
14034   if (factor == NVGBlendFactor.OneMinusDstColor) return GL_ONE_MINUS_DST_COLOR;
14035   if (factor == NVGBlendFactor.SrcAlpha) return GL_SRC_ALPHA;
14036   if (factor == NVGBlendFactor.OneMinusSrcAlpha) return GL_ONE_MINUS_SRC_ALPHA;
14037   if (factor == NVGBlendFactor.DstAlpha) return GL_DST_ALPHA;
14038   if (factor == NVGBlendFactor.OneMinusDstAlpha) return GL_ONE_MINUS_DST_ALPHA;
14039   if (factor == NVGBlendFactor.SrcAlphaSaturate) return GL_SRC_ALPHA_SATURATE;
14040   return GL_INVALID_ENUM;
14041 }
14042 
14043 GLNVGblend glnvg__buildBlendFunc (NVGCompositeOperationState op) pure nothrow @trusted @nogc {
14044   GLNVGblend res;
14045   res.simple = op.simple;
14046   res.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB);
14047   res.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB);
14048   res.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha);
14049   res.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha);
14050   if (res.simple) {
14051     if (res.srcAlpha == GL_INVALID_ENUM || res.dstAlpha == GL_INVALID_ENUM) {
14052       res.srcRGB = res.srcAlpha = res.dstRGB = res.dstAlpha = GL_INVALID_ENUM;
14053     }
14054   } else {
14055     if (res.srcRGB == GL_INVALID_ENUM || res.dstRGB == GL_INVALID_ENUM || res.srcAlpha == GL_INVALID_ENUM || res.dstAlpha == GL_INVALID_ENUM) {
14056       res.simple = true;
14057       res.srcRGB = res.srcAlpha = res.dstRGB = res.dstAlpha = GL_INVALID_ENUM;
14058     }
14059   }
14060   return res;
14061 }
14062 
14063 void glnvg__blendCompositeOperation() (GLNVGcontext* gl, in auto ref GLNVGblend op) nothrow @trusted @nogc {
14064   //glBlendFuncSeparate(glnvg_convertBlendFuncFactor(op.srcRGB), glnvg_convertBlendFuncFactor(op.dstRGB), glnvg_convertBlendFuncFactor(op.srcAlpha), glnvg_convertBlendFuncFactor(op.dstAlpha));
14065   static if (NANOVG_GL_USE_STATE_FILTER) {
14066     if (gl.blendFunc.simple == op.simple) {
14067       if (op.simple) {
14068         if (gl.blendFunc.srcAlpha == op.srcAlpha && gl.blendFunc.dstAlpha == op.dstAlpha) return;
14069       } else {
14070         if (gl.blendFunc.srcRGB == op.srcRGB && gl.blendFunc.dstRGB == op.dstRGB && gl.blendFunc.srcAlpha == op.srcAlpha && gl.blendFunc.dstAlpha == op.dstAlpha) return;
14071       }
14072     }
14073     gl.blendFunc = op;
14074   }
14075   if (op.simple) {
14076     if (op.srcAlpha == GL_INVALID_ENUM || op.dstAlpha == GL_INVALID_ENUM) {
14077       glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
14078     } else {
14079       glBlendFunc(op.srcAlpha, op.dstAlpha);
14080     }
14081   } else {
14082     if (op.srcRGB == GL_INVALID_ENUM || op.dstRGB == GL_INVALID_ENUM || op.srcAlpha == GL_INVALID_ENUM || op.dstAlpha == GL_INVALID_ENUM) {
14083       glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
14084     } else {
14085       glBlendFuncSeparate(op.srcRGB, op.dstRGB, op.srcAlpha, op.dstAlpha);
14086     }
14087   }
14088 }
14089 
14090 void glnvg__renderSetAffine (void* uptr, in ref NVGMatrix mat) nothrow @trusted @nogc {
14091   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14092   GLNVGcall* call;
14093   // if last operation was GLNVG_AFFINE, simply replace the matrix
14094   if (gl.ncalls > 0 && gl.calls[gl.ncalls-1].type == GLNVG_AFFINE) {
14095     call = &gl.calls[gl.ncalls-1];
14096   } else {
14097     call = glnvg__allocCall(gl);
14098     if (call is null) return;
14099     call.type = GLNVG_AFFINE;
14100   }
14101   call.affine.mat.ptr[0..6] = mat.mat.ptr[0..6];
14102 }
14103 
14104 version(nanovega_debug_clipping) public __gshared bool nanovegaClipDebugDump = false;
14105 
14106 void glnvg__renderFlush (void* uptr) nothrow @trusted @nogc {
14107   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14108   if (!gl.inFrame) assert(0, "NanoVega: internal driver error");
14109   try {
14110     import core.thread : Thread;
14111     static if (__VERSION__ < 2076) {
14112       DGNoThrowNoGC(() {
14113         if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
14114       })();
14115     } else {
14116       if (gl.mainTID != Thread.getThis.id) assert(0, "NanoVega: cannot use context in alien thread");
14117     }
14118   } catch (Exception e) {}
14119   scope(exit) gl.inFrame = false;
14120 
14121   glnvg__resetError!true(gl);
14122   {
14123     int vv = 0;
14124     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &vv);
14125     if (glGetError() || vv < 0) vv = 0;
14126     gl.mainFBO = cast(uint)vv;
14127   }
14128 
14129   enum ShaderType { None, Fill, Clip }
14130   auto lastShader = ShaderType.None;
14131   if (gl.ncalls > 0) {
14132     gl.msp = 1;
14133     gl.maskStack.ptr[0] = GLMaskState.DontMask;
14134 
14135     // Setup require GL state.
14136     glUseProgram(gl.shader.prog);
14137 
14138     glActiveTexture(GL_TEXTURE1);
14139     glBindTexture(GL_TEXTURE_2D, 0);
14140     glActiveTexture(GL_TEXTURE0);
14141     glnvg__resetFBOClipTextureCache(gl);
14142 
14143     //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
14144     static if (NANOVG_GL_USE_STATE_FILTER) {
14145       gl.blendFunc.simple = true;
14146       gl.blendFunc.srcRGB = gl.blendFunc.dstRGB = gl.blendFunc.srcAlpha = gl.blendFunc.dstAlpha = GL_INVALID_ENUM;
14147     }
14148     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // just in case
14149     glEnable(GL_CULL_FACE);
14150     glCullFace(GL_BACK);
14151     glFrontFace(GL_CCW);
14152     glEnable(GL_BLEND);
14153     glDisable(GL_DEPTH_TEST);
14154     glDisable(GL_SCISSOR_TEST);
14155     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
14156     glStencilMask(0xffffffff);
14157     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
14158     glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
14159     glActiveTexture(GL_TEXTURE0);
14160     glBindTexture(GL_TEXTURE_2D, 0);
14161     static if (NANOVG_GL_USE_STATE_FILTER) {
14162       gl.boundTexture = 0;
14163       gl.stencilMask = 0xffffffff;
14164       gl.stencilFunc = GL_ALWAYS;
14165       gl.stencilFuncRef = 0;
14166       gl.stencilFuncMask = 0xffffffff;
14167     }
14168     glnvg__checkError(gl, "OpenGL setup");
14169 
14170     // Upload vertex data
14171     glBindBuffer(GL_ARRAY_BUFFER, gl.vertBuf);
14172     // ensure that there's space for at least 4 vertices in case we need to draw a quad (e. g. framebuffer copy)
14173     glBufferData(GL_ARRAY_BUFFER, (gl.nverts < 4 ? 4 : gl.nverts) * NVGVertex.sizeof, gl.verts, GL_STREAM_DRAW);
14174     glEnableVertexAttribArray(0);
14175     glEnableVertexAttribArray(1);
14176     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, NVGVertex.sizeof, cast(const(GLvoid)*)cast(usize)0);
14177     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, NVGVertex.sizeof, cast(const(GLvoid)*)(0+2*float.sizeof));
14178     glnvg__checkError(gl, "vertex data uploading");
14179 
14180     // Set view and texture just once per frame.
14181     glUniform1i(gl.shader.loc[GLNVGuniformLoc.Tex], 0);
14182     if (gl.shader.loc[GLNVGuniformLoc.ClipTex] != -1) {
14183       //{ import core.stdc.stdio; printf("%d\n", gl.shader.loc[GLNVGuniformLoc.ClipTex]); }
14184       glUniform1i(gl.shader.loc[GLNVGuniformLoc.ClipTex], 1);
14185     }
14186     if (gl.shader.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shader.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
14187     glnvg__checkError(gl, "render shader setup");
14188 
14189     // Reset affine transformations.
14190     glUniform4fv(gl.shader.loc[GLNVGuniformLoc.TMat], 1, NVGMatrix.IdentityMat.ptr);
14191     glUniform2fv(gl.shader.loc[GLNVGuniformLoc.TTr], 1, NVGMatrix.IdentityMat.ptr+4);
14192     glnvg__checkError(gl, "affine setup");
14193 
14194     // set clip shaders params
14195     // fill
14196     glUseProgram(gl.shaderFillFBO.prog);
14197     glnvg__checkError(gl, "clip shaders setup (fill 0)");
14198     if (gl.shaderFillFBO.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
14199     glnvg__checkError(gl, "clip shaders setup (fill 1)");
14200     // copy
14201     glUseProgram(gl.shaderCopyFBO.prog);
14202     glnvg__checkError(gl, "clip shaders setup (copy 0)");
14203     if (gl.shaderCopyFBO.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shaderCopyFBO.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
14204     glnvg__checkError(gl, "clip shaders setup (copy 1)");
14205     //glUniform1i(gl.shaderFillFBO.loc[GLNVGuniformLoc.Tex], 0);
14206     glUniform1i(gl.shaderCopyFBO.loc[GLNVGuniformLoc.Tex], 0);
14207     glnvg__checkError(gl, "clip shaders setup (copy 2)");
14208     // restore render shader
14209     glUseProgram(gl.shader.prog);
14210 
14211     //{ import core.stdc.stdio; printf("ViewSize=%u %u %u\n", gl.shader.loc[GLNVGuniformLoc.ViewSize], gl.shaderFillFBO.loc[GLNVGuniformLoc.ViewSize], gl.shaderCopyFBO.loc[GLNVGuniformLoc.ViewSize]); }
14212 
14213     gl.lastAffine.identity;
14214 
14215     foreach (int i; 0..gl.ncalls) {
14216       GLNVGcall* call = &gl.calls[i];
14217       switch (call.type) {
14218         case GLNVG_FILL: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__fill(gl, call); break;
14219         case GLNVG_CONVEXFILL: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__convexFill(gl, call); break;
14220         case GLNVG_STROKE: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__stroke(gl, call); break;
14221         case GLNVG_TRIANGLES: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__triangles(gl, call); break;
14222         case GLNVG_AFFINE: gl.lastAffine = call.affine; glnvg__affine(gl, call); break;
14223         // clip region management
14224         case GLNVG_PUSHCLIP:
14225           version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): push clip (cache:%d); current state is %d\n", gl.msp-1, gl.lastClipFBO, gl.maskStack.ptr[gl.msp-1]); }
14226           if (gl.msp >= gl.maskStack.length) assert(0, "NanoVega: mask stack overflow in OpenGL backend");
14227           if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.DontMask) {
14228             gl.maskStack.ptr[gl.msp++] = GLMaskState.DontMask;
14229           } else {
14230             gl.maskStack.ptr[gl.msp++] = GLMaskState.Uninitialized;
14231           }
14232           // no need to reset FBO cache here, as nothing was changed
14233           break;
14234         case GLNVG_POPCLIP:
14235           if (gl.msp <= 1) assert(0, "NanoVega: mask stack underflow in OpenGL backend");
14236           version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): pop clip (cache:%d); current state is %d; previous state is %d\n", gl.msp-1, gl.lastClipFBO, gl.maskStack.ptr[gl.msp-1], gl.maskStack.ptr[gl.msp-2]); }
14237           --gl.msp;
14238           assert(gl.msp > 0);
14239           //{ import core.stdc.stdio; printf("popped; new msp is %d; state is %d\n", gl.msp, gl.maskStack.ptr[gl.msp]); }
14240           // check popped item
14241           final switch (gl.maskStack.ptr[gl.msp]) {
14242             case GLMaskState.DontMask:
14243               // if last FBO was "don't mask", reset cache if current is not "don't mask"
14244               if (gl.maskStack.ptr[gl.msp-1] != GLMaskState.DontMask) {
14245                 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("  +++ need to reset FBO cache\n"); }
14246                 glnvg__resetFBOClipTextureCache(gl);
14247               }
14248               break;
14249             case GLMaskState.Uninitialized:
14250               // if last FBO texture was uninitialized, it means that nothing was changed,
14251               // so we can keep using cached FBO
14252               break;
14253             case GLMaskState.Initialized:
14254               // if last FBO was initialized, it means that something was definitely changed
14255               version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("  +++ need to reset FBO cache\n"); }
14256               glnvg__resetFBOClipTextureCache(gl);
14257               break;
14258             case GLMaskState.JustCleared: assert(0, "NanoVega: internal FBO stack error");
14259           }
14260           break;
14261         case GLNVG_RESETCLIP:
14262           // mark current mask as "don't mask"
14263           version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): reset clip (cache:%d); current state is %d\n", gl.msp-1, gl.lastClipFBO, gl.maskStack.ptr[gl.msp-1]); }
14264           if (gl.msp > 0) {
14265             if (gl.maskStack.ptr[gl.msp-1] != GLMaskState.DontMask) {
14266               gl.maskStack.ptr[gl.msp-1] = GLMaskState.DontMask;
14267               version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("  +++ need to reset FBO cache\n"); }
14268               glnvg__resetFBOClipTextureCache(gl);
14269             }
14270           }
14271           break;
14272         case GLNVG_CLIP_DDUMP_ON:
14273           version(nanovega_debug_clipping) nanovegaClipDebugDump = true;
14274           break;
14275         case GLNVG_CLIP_DDUMP_OFF:
14276           version(nanovega_debug_clipping) nanovegaClipDebugDump = false;
14277           break;
14278         case GLNVG_NONE: break;
14279         default:
14280           {
14281             import core.stdc.stdio; stderr.fprintf("NanoVega FATAL: invalid command in OpenGL backend: %d\n", call.type);
14282           }
14283           assert(0, "NanoVega: invalid command in OpenGL backend (fatal internal error)");
14284       }
14285       // and free texture, why not
14286       glnvg__deleteTexture(gl, call.image);
14287     }
14288 
14289     glDisableVertexAttribArray(0);
14290     glDisableVertexAttribArray(1);
14291     glDisable(GL_CULL_FACE);
14292     glBindBuffer(GL_ARRAY_BUFFER, 0);
14293     glUseProgram(0);
14294     glnvg__bindTexture(gl, 0);
14295   }
14296 
14297   // this will do all necessary cleanup
14298   glnvg__renderCancelInternal(gl, false); // no need to clear textures
14299 }
14300 
14301 int glnvg__maxVertCount (const(NVGpath)* paths, int npaths) nothrow @trusted @nogc {
14302   int count = 0;
14303   foreach (int i; 0..npaths) {
14304     count += paths[i].nfill;
14305     count += paths[i].nstroke;
14306   }
14307   return count;
14308 }
14309 
14310 GLNVGcall* glnvg__allocCall (GLNVGcontext* gl) nothrow @trusted @nogc {
14311   GLNVGcall* ret = null;
14312   if (gl.ncalls+1 > gl.ccalls) {
14313     GLNVGcall* calls;
14314     int ccalls = glnvg__maxi(gl.ncalls+1, 128)+gl.ccalls/2; // 1.5x Overallocate
14315     calls = cast(GLNVGcall*)realloc(gl.calls, GLNVGcall.sizeof*ccalls);
14316     if (calls is null) return null;
14317     gl.calls = calls;
14318     gl.ccalls = ccalls;
14319   }
14320   ret = &gl.calls[gl.ncalls++];
14321   memset(ret, 0, GLNVGcall.sizeof);
14322   return ret;
14323 }
14324 
14325 int glnvg__allocPaths (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
14326   int ret = 0;
14327   if (gl.npaths+n > gl.cpaths) {
14328     GLNVGpath* paths;
14329     int cpaths = glnvg__maxi(gl.npaths+n, 128)+gl.cpaths/2; // 1.5x Overallocate
14330     paths = cast(GLNVGpath*)realloc(gl.paths, GLNVGpath.sizeof*cpaths);
14331     if (paths is null) return -1;
14332     gl.paths = paths;
14333     gl.cpaths = cpaths;
14334   }
14335   ret = gl.npaths;
14336   gl.npaths += n;
14337   return ret;
14338 }
14339 
14340 int glnvg__allocVerts (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
14341   int ret = 0;
14342   if (gl.nverts+n > gl.cverts) {
14343     NVGVertex* verts;
14344     int cverts = glnvg__maxi(gl.nverts+n, 4096)+gl.cverts/2; // 1.5x Overallocate
14345     verts = cast(NVGVertex*)realloc(gl.verts, NVGVertex.sizeof*cverts);
14346     if (verts is null) return -1;
14347     gl.verts = verts;
14348     gl.cverts = cverts;
14349   }
14350   ret = gl.nverts;
14351   gl.nverts += n;
14352   return ret;
14353 }
14354 
14355 int glnvg__allocFragUniforms (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
14356   int ret = 0, structSize = gl.fragSize;
14357   if (gl.nuniforms+n > gl.cuniforms) {
14358     ubyte* uniforms;
14359     int cuniforms = glnvg__maxi(gl.nuniforms+n, 128)+gl.cuniforms/2; // 1.5x Overallocate
14360     uniforms = cast(ubyte*)realloc(gl.uniforms, structSize*cuniforms);
14361     if (uniforms is null) return -1;
14362     gl.uniforms = uniforms;
14363     gl.cuniforms = cuniforms;
14364   }
14365   ret = gl.nuniforms*structSize;
14366   gl.nuniforms += n;
14367   return ret;
14368 }
14369 
14370 GLNVGfragUniforms* nvg__fragUniformPtr (GLNVGcontext* gl, int i) nothrow @trusted @nogc {
14371   return cast(GLNVGfragUniforms*)&gl.uniforms[i];
14372 }
14373 
14374 void glnvg__vset (NVGVertex* vtx, float x, float y, float u, float v) nothrow @trusted @nogc {
14375   vtx.x = x;
14376   vtx.y = y;
14377   vtx.u = u;
14378   vtx.v = v;
14379 }
14380 
14381 void glnvg__renderFill (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, float fringe, const(float)* bounds, const(NVGpath)* paths, int npaths, bool evenOdd) nothrow @trusted @nogc {
14382   if (npaths < 1) return;
14383 
14384   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14385   GLNVGcall* call = glnvg__allocCall(gl);
14386   NVGVertex* quad;
14387   GLNVGfragUniforms* frag;
14388   int maxverts, offset;
14389 
14390   if (call is null) return;
14391 
14392   call.type = GLNVG_FILL;
14393   call.evenOdd = evenOdd;
14394   call.clipmode = clipmode;
14395   //if (clipmode != NVGClipMode.None) { import core.stdc.stdio; printf("CLIP!\n"); }
14396   call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
14397   call.triangleCount = 4;
14398   call.pathOffset = glnvg__allocPaths(gl, npaths);
14399   if (call.pathOffset == -1) goto error;
14400   call.pathCount = npaths;
14401   call.image = paint.image.id;
14402   if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
14403 
14404   if (npaths == 1 && paths[0].convex) {
14405     call.type = GLNVG_CONVEXFILL;
14406     call.triangleCount = 0; // Bounding box fill quad not needed for convex fill
14407   }
14408 
14409   // Allocate vertices for all the paths.
14410   maxverts = glnvg__maxVertCount(paths, npaths)+call.triangleCount;
14411   offset = glnvg__allocVerts(gl, maxverts);
14412   if (offset == -1) goto error;
14413 
14414   foreach (int i; 0..npaths) {
14415     GLNVGpath* copy = &gl.paths[call.pathOffset+i];
14416     const(NVGpath)* path = &paths[i];
14417     memset(copy, 0, GLNVGpath.sizeof);
14418     if (path.nfill > 0) {
14419       copy.fillOffset = offset;
14420       copy.fillCount = path.nfill;
14421       memcpy(&gl.verts[offset], path.fill, NVGVertex.sizeof*path.nfill);
14422       offset += path.nfill;
14423     }
14424     if (path.nstroke > 0) {
14425       copy.strokeOffset = offset;
14426       copy.strokeCount = path.nstroke;
14427       memcpy(&gl.verts[offset], path.stroke, NVGVertex.sizeof*path.nstroke);
14428       offset += path.nstroke;
14429     }
14430   }
14431 
14432   // Setup uniforms for draw calls
14433   if (call.type == GLNVG_FILL) {
14434     import core.stdc.string : memcpy;
14435     // Quad
14436     call.triangleOffset = offset;
14437     quad = &gl.verts[call.triangleOffset];
14438     glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f);
14439     glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f);
14440     glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f);
14441     glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f);
14442     // Get uniform
14443     call.uniformOffset = glnvg__allocFragUniforms(gl, 2);
14444     if (call.uniformOffset == -1) goto error;
14445     // Simple shader for stencil
14446     frag = nvg__fragUniformPtr(gl, call.uniformOffset);
14447     memset(frag, 0, (*frag).sizeof);
14448     glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, fringe, fringe, -1.0f);
14449     memcpy(nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), frag, (*frag).sizeof);
14450     frag.strokeThr = -1.0f;
14451     frag.type = NSVG_SHADER_SIMPLE;
14452     // Fill shader
14453     //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, fringe, fringe, -1.0f);
14454   } else {
14455     call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
14456     if (call.uniformOffset == -1) goto error;
14457     // Fill shader
14458     glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, fringe, fringe, -1.0f);
14459   }
14460 
14461   return;
14462 
14463 error:
14464   // We get here if call alloc was ok, but something else is not.
14465   // Roll back the last call to prevent drawing it.
14466   if (gl.ncalls > 0) --gl.ncalls;
14467 }
14468 
14469 void glnvg__renderStroke (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const(NVGpath)* paths, int npaths) nothrow @trusted @nogc {
14470   if (npaths < 1) return;
14471 
14472   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14473   GLNVGcall* call = glnvg__allocCall(gl);
14474   int maxverts, offset;
14475 
14476   if (call is null) return;
14477 
14478   call.type = GLNVG_STROKE;
14479   call.clipmode = clipmode;
14480   call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
14481   call.pathOffset = glnvg__allocPaths(gl, npaths);
14482   if (call.pathOffset == -1) goto error;
14483   call.pathCount = npaths;
14484   call.image = paint.image.id;
14485   if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
14486 
14487   // Allocate vertices for all the paths.
14488   maxverts = glnvg__maxVertCount(paths, npaths);
14489   offset = glnvg__allocVerts(gl, maxverts);
14490   if (offset == -1) goto error;
14491 
14492   foreach (int i; 0..npaths) {
14493     GLNVGpath* copy = &gl.paths[call.pathOffset+i];
14494     const(NVGpath)* path = &paths[i];
14495     memset(copy, 0, GLNVGpath.sizeof);
14496     if (path.nstroke) {
14497       copy.strokeOffset = offset;
14498       copy.strokeCount = path.nstroke;
14499       memcpy(&gl.verts[offset], path.stroke, NVGVertex.sizeof*path.nstroke);
14500       offset += path.nstroke;
14501     }
14502   }
14503 
14504   if (gl.flags&NVGContextFlag.StencilStrokes) {
14505     // Fill shader
14506     call.uniformOffset = glnvg__allocFragUniforms(gl, 2);
14507     if (call.uniformOffset == -1) goto error;
14508     glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
14509     glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, strokeWidth, fringe, 1.0f-0.5f/255.0f);
14510   } else {
14511     // Fill shader
14512     call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
14513     if (call.uniformOffset == -1) goto error;
14514     glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
14515   }
14516 
14517   return;
14518 
14519 error:
14520   // We get here if call alloc was ok, but something else is not.
14521   // Roll back the last call to prevent drawing it.
14522   if (gl.ncalls > 0) --gl.ncalls;
14523 }
14524 
14525 void glnvg__renderTriangles (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, const(NVGVertex)* verts, int nverts) nothrow @trusted @nogc {
14526   if (nverts < 1) return;
14527 
14528   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14529   GLNVGcall* call = glnvg__allocCall(gl);
14530   GLNVGfragUniforms* frag;
14531 
14532   if (call is null) return;
14533 
14534   call.type = GLNVG_TRIANGLES;
14535   call.clipmode = clipmode;
14536   call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
14537   call.image = paint.image.id;
14538   if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
14539 
14540   // Allocate vertices for all the paths.
14541   call.triangleOffset = glnvg__allocVerts(gl, nverts);
14542   if (call.triangleOffset == -1) goto error;
14543   call.triangleCount = nverts;
14544 
14545   memcpy(&gl.verts[call.triangleOffset], verts, NVGVertex.sizeof*nverts);
14546 
14547   // Fill shader
14548   call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
14549   if (call.uniformOffset == -1) goto error;
14550   frag = nvg__fragUniformPtr(gl, call.uniformOffset);
14551   glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
14552   frag.type = NSVG_SHADER_IMG;
14553 
14554   return;
14555 
14556 error:
14557   // We get here if call alloc was ok, but something else is not.
14558   // Roll back the last call to prevent drawing it.
14559   if (gl.ncalls > 0) --gl.ncalls;
14560 }
14561 
14562 void glnvg__renderDelete (void* uptr) nothrow @trusted @nogc {
14563   GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
14564   if (gl is null) return;
14565 
14566   glnvg__killFBOs(gl);
14567   glnvg__deleteShader(&gl.shader);
14568   glnvg__deleteShader(&gl.shaderFillFBO);
14569   glnvg__deleteShader(&gl.shaderCopyFBO);
14570 
14571   if (gl.vertBuf != 0) glDeleteBuffers(1, &gl.vertBuf);
14572 
14573   foreach (ref GLNVGtexture tex; gl.textures[0..gl.ntextures]) {
14574     if (tex.id != 0 && (tex.flags&NVGImageFlag.NoDelete) == 0) {
14575       assert(tex.tex != 0);
14576       glDeleteTextures(1, &tex.tex);
14577     }
14578   }
14579   free(gl.textures);
14580 
14581   free(gl.paths);
14582   free(gl.verts);
14583   free(gl.uniforms);
14584   free(gl.calls);
14585 
14586   free(gl);
14587 }
14588 
14589 
14590 /** Creates NanoVega contexts for OpenGL2+.
14591  *
14592  * Specify creation flags as additional arguments, like this:
14593  * `nvgCreateContext(NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes);`
14594  *
14595  * If you won't specify any flags, defaults will be used:
14596  * `[NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes]`.
14597  *
14598  * Group: context_management
14599  */
14600 public NVGContext nvgCreateContext (const(NVGContextFlag)[] flagList...) nothrow @trusted @nogc {
14601   version(aliced) {
14602     enum DefaultFlags = NVGContextFlag.Antialias|NVGContextFlag.StencilStrokes|NVGContextFlag.FontNoAA;
14603   } else {
14604     enum DefaultFlags = NVGContextFlag.Antialias|NVGContextFlag.StencilStrokes;
14605   }
14606   uint flags = 0;
14607   if (flagList.length != 0) {
14608     foreach (immutable flg; flagList) flags |= (flg != NVGContextFlag.Default ? flg : DefaultFlags);
14609   } else {
14610     flags = DefaultFlags;
14611   }
14612   NVGparams params = void;
14613   NVGContext ctx = null;
14614   version(nanovg_builtin_opengl_bindings) nanovgInitOpenGL(); // why not?
14615   version(nanovg_bindbc_opengl_bindings) nanovgInitOpenGL();
14616   GLNVGcontext* gl = cast(GLNVGcontext*)malloc(GLNVGcontext.sizeof);
14617   if (gl is null) goto error;
14618   memset(gl, 0, GLNVGcontext.sizeof);
14619 
14620   memset(&params, 0, params.sizeof);
14621   params.renderCreate = &glnvg__renderCreate;
14622   params.renderCreateTexture = &glnvg__renderCreateTexture;
14623   params.renderTextureIncRef = &glnvg__renderTextureIncRef;
14624   params.renderDeleteTexture = &glnvg__renderDeleteTexture;
14625   params.renderUpdateTexture = &glnvg__renderUpdateTexture;
14626   params.renderGetTextureSize = &glnvg__renderGetTextureSize;
14627   params.renderViewport = &glnvg__renderViewport;
14628   params.renderCancel = &glnvg__renderCancel;
14629   params.renderFlush = &glnvg__renderFlush;
14630   params.renderPushClip = &glnvg__renderPushClip;
14631   params.renderPopClip = &glnvg__renderPopClip;
14632   params.renderResetClip = &glnvg__renderResetClip;
14633   params.renderFill = &glnvg__renderFill;
14634   params.renderStroke = &glnvg__renderStroke;
14635   params.renderTriangles = &glnvg__renderTriangles;
14636   params.renderSetAffine = &glnvg__renderSetAffine;
14637   params.renderDelete = &glnvg__renderDelete;
14638   params.userPtr = gl;
14639   params.edgeAntiAlias = (flags&NVGContextFlag.Antialias ? true : false);
14640   if (flags&(NVGContextFlag.FontAA|NVGContextFlag.FontNoAA)) {
14641     params.fontAA = (flags&NVGContextFlag.FontNoAA ? NVG_INVERT_FONT_AA : !NVG_INVERT_FONT_AA);
14642   } else {
14643     params.fontAA = NVG_INVERT_FONT_AA;
14644   }
14645 
14646   gl.flags = flags;
14647   gl.freetexid = -1;
14648 
14649   ctx = createInternal(&params);
14650   if (ctx is null) goto error;
14651 
14652   static if (__VERSION__ < 2076) {
14653     DGNoThrowNoGC(() { import core.thread; gl.mainTID = Thread.getThis.id; })();
14654   } else {
14655     try { import core.thread; gl.mainTID = Thread.getThis.id; } catch (Exception e) {}
14656   }
14657 
14658   return ctx;
14659 
14660 error:
14661   // 'gl' is freed by nvgDeleteInternal.
14662   if (ctx !is null) ctx.deleteInternal();
14663   return null;
14664 }
14665 
14666 /// Create NanoVega OpenGL image from texture id.
14667 /// Group: images
14668 public int glCreateImageFromHandleGL2 (NVGContext ctx, GLuint textureId, int w, int h, int imageFlags) nothrow @trusted @nogc {
14669   GLNVGcontext* gl = cast(GLNVGcontext*)ctx.internalParams().userPtr;
14670   GLNVGtexture* tex = glnvg__allocTexture(gl);
14671 
14672   if (tex is null) return 0;
14673 
14674   tex.type = NVGtexture.RGBA;
14675   tex.tex = textureId;
14676   tex.flags = imageFlags;
14677   tex.width = w;
14678   tex.height = h;
14679 
14680   return tex.id;
14681 }
14682 
14683 /// Returns OpenGL texture id for NanoVega image.
14684 /// Group: images
14685 public GLuint glImageHandleGL2 (NVGContext ctx, int image) nothrow @trusted @nogc {
14686   GLNVGcontext* gl = cast(GLNVGcontext*)ctx.internalParams().userPtr;
14687   GLNVGtexture* tex = glnvg__findTexture(gl, image);
14688   return tex.tex;
14689 }
14690 
14691 
14692 // ////////////////////////////////////////////////////////////////////////// //
14693 private:
14694 
14695 static if (NanoVegaHasFontConfig) {
14696   version(nanovg_builtin_fontconfig_bindings) {
14697     pragma(lib, "fontconfig");
14698 
14699     private extern(C) nothrow @trusted @nogc {
14700       enum FC_FILE = "file"; /* String */
14701       alias FcBool = int;
14702       alias FcChar8 = char;
14703       struct FcConfig;
14704       struct FcPattern;
14705       alias FcMatchKind = int;
14706       enum : FcMatchKind {
14707         FcMatchPattern,
14708         FcMatchFont,
14709         FcMatchScan
14710       }
14711       alias FcResult = int;
14712       enum : FcResult {
14713         FcResultMatch,
14714         FcResultNoMatch,
14715         FcResultTypeMismatch,
14716         FcResultNoId,
14717         FcResultOutOfMemory
14718       }
14719       FcBool FcInit ();
14720       FcBool FcConfigSubstituteWithPat (FcConfig* config, FcPattern* p, FcPattern* p_pat, FcMatchKind kind);
14721       void FcDefaultSubstitute (FcPattern* pattern);
14722       FcBool FcConfigSubstitute (FcConfig* config, FcPattern* p, FcMatchKind kind);
14723       FcPattern* FcFontMatch (FcConfig* config, FcPattern* p, FcResult* result);
14724       FcPattern* FcNameParse (const(FcChar8)* name);
14725       void FcPatternDestroy (FcPattern* p);
14726       FcResult FcPatternGetString (const(FcPattern)* p, const(char)* object, int n, FcChar8** s);
14727     }
14728   }
14729 
14730   __gshared bool fontconfigAvailable = false;
14731   // initialize fontconfig
14732   shared static this () {
14733     if (FcInit()) {
14734       fontconfigAvailable = true;
14735     } else {
14736       import core.stdc.stdio : stderr, fprintf;
14737       stderr.fprintf("***NanoVega WARNING: cannot init fontconfig!\n");
14738     }
14739   }
14740 }
14741 
14742 
14743 // ////////////////////////////////////////////////////////////////////////// //
14744 public enum BaphometDims = 512.0f; // baphomet icon is 512x512 ([0..511])
14745 
14746 private static immutable ubyte[7641] baphometPath = [
14747   0x01,0x04,0x06,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x08,0xa0,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,
14748   0x00,0x80,0xff,0x43,0xa2,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x08,0x00,0x80,0xff,
14749   0x43,0x7a,0x89,0xe5,0x42,0xa0,0x1d,0xc6,0x43,0x00,0x00,0x00,0x00,0x30,0x89,0x7f,0x43,0x00,0x00,0x00,
14750   0x00,0x08,0x7a,0x89,0xe5,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x89,0xe5,0x42,0x00,0x00,
14751   0x00,0x00,0x30,0x89,0x7f,0x43,0x08,0x00,0x00,0x00,0x00,0xa2,0x1d,0xc6,0x43,0x7a,0x89,0xe5,0x42,0x00,
14752   0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x09,0x06,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
14753   0x43,0x08,0x16,0x68,0xb3,0x43,0x72,0x87,0xdd,0x43,0x71,0x87,0xdd,0x43,0x17,0x68,0xb3,0x43,0x71,0x87,
14754   0xdd,0x43,0x30,0x89,0x7f,0x43,0x08,0x71,0x87,0xdd,0x43,0xd2,0x2f,0x18,0x43,0x16,0x68,0xb3,0x43,0x35,
14755   0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x35,0xe2,0x87,0x42,0x08,0xd1,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,
14756   0x35,0xe2,0x87,0x42,0xd2,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x08,0x35,0xe2,0x87,
14757   0x42,0x17,0x68,0xb3,0x43,0xd1,0x2f,0x18,0x43,0x72,0x87,0xdd,0x43,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
14758   0x43,0x09,0x06,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x07,0xa4,0x3f,0x7f,0x43,0x0b,0x86,0xdc,0x43,
14759   0x07,0x6c,0xb9,0xb2,0x43,0xe8,0xd1,0xca,0x42,0x07,0x6e,0x4d,0xa0,0x42,0xa9,0x10,0x9c,0x43,0x07,0xb7,
14760   0x40,0xd7,0x43,0xa9,0x10,0x9c,0x43,0x07,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x09,0x06,0x98,0x42,
14761   0x74,0x43,0xb1,0x8d,0x68,0x43,0x08,0xd7,0x24,0x79,0x43,0xba,0x83,0x6e,0x43,0xa9,0x16,0x7c,0x43,0x56,
14762   0xa1,0x76,0x43,0x74,0x2a,0x7d,0x43,0x44,0x73,0x80,0x43,0x08,0x55,0xd1,0x7e,0x43,0xe3,0xea,0x76,0x43,
14763   0xbc,0x18,0x81,0x43,0x7f,0xa8,0x6e,0x43,0x8f,0x0a,0x84,0x43,0x02,0xfc,0x68,0x43,0x09,0x06,0x92,0x29,
14764   0x8d,0x43,0x73,0xc3,0x67,0x43,0x08,0xa4,0xd9,0x8e,0x43,0xf2,0xa6,0x7a,0x43,0x8f,0x22,0x88,0x43,0x75,
14765   0x2a,0x7d,0x43,0x42,0x7f,0x82,0x43,0x08,0xc8,0x88,0x43,0x09,0x06,0xc1,0x79,0x74,0x43,0x50,0x64,0x89,
14766   0x43,0x08,0x68,0x2d,0x72,0x43,0xee,0x21,0x81,0x43,0xcd,0x97,0x55,0x43,0xe6,0xf1,0x7b,0x43,0x91,0xec,
14767   0x5d,0x43,0xa8,0xc7,0x6a,0x43,0x09,0x06,0xfa,0xa5,0x52,0x43,0x60,0x97,0x7c,0x43,0x08,0x19,0xff,0x50,
14768   0x43,0xe9,0x6e,0x8a,0x43,0xb0,0xbd,0x70,0x43,0x4c,0x51,0x82,0x43,0x04,0xeb,0x69,0x43,0x66,0x0f,0x8e,
14769   0x43,0x09,0x06,0x17,0xbf,0x71,0x43,0x2c,0x58,0x94,0x43,0x08,0x1c,0x96,0x6e,0x43,0x61,0x68,0x99,0x43,
14770   0x2d,0x3a,0x6e,0x43,0xc8,0x81,0x9e,0x43,0xb7,0x9b,0x72,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0x30,0xdb,
14771   0x82,0x43,0xdb,0xe9,0x93,0x43,0x08,0x11,0x82,0x84,0x43,0x61,0x68,0x99,0x43,0xe8,0x4a,0x84,0x43,0x8e,
14772   0xa6,0x9e,0x43,0x42,0x7f,0x82,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0xc4,0x02,0x85,0x43,0xd1,0x0b,0x92,
14773   0x43,0x08,0xd6,0xb2,0x86,0x43,0x34,0x1e,0x92,0x43,0x4f,0x58,0x87,0x43,0xa4,0xf1,0x92,0x43,0x03,0xd9,
14774   0x87,0x43,0x7b,0xc6,0x94,0x43,0x09,0x06,0x87,0x3e,0x64,0x43,0x31,0x3b,0x93,0x43,0x08,0x3b,0xbf,0x64,
14775   0x43,0x6f,0xf9,0x91,0x43,0x96,0x0b,0x67,0x43,0xc5,0x4a,0x91,0x43,0xcf,0xfe,0x6a,0x43,0x31,0x2f,0x91,
14776   0x43,0x09,0x06,0x16,0x74,0xb5,0x43,0x08,0xec,0x8e,0x43,0x08,0x1b,0x4b,0xb2,0x43,0xee,0x5d,0x8b,0x43,
14777   0x48,0x4d,0xad,0x43,0x12,0xa6,0x8a,0x43,0xf3,0xd7,0xa7,0x43,0x74,0xb8,0x8a,0x43,0x08,0x8c,0xb2,0xa0,
14778   0x43,0xcd,0xf8,0x8a,0x43,0x68,0x46,0x9b,0x43,0x79,0x8f,0x87,0x43,0x49,0xc9,0x96,0x43,0xe9,0x3e,0x82,
14779   0x43,0x08,0x60,0x5c,0x97,0x43,0xa1,0xde,0x8b,0x43,0x4e,0xa0,0x93,0x43,0x31,0x3b,0x93,0x43,0x9f,0xea,
14780   0x8d,0x43,0x27,0x8d,0x99,0x43,0x08,0x07,0xe0,0x8c,0x43,0x06,0x34,0x9b,0x43,0x38,0xe9,0x8c,0x43,0x46,
14781   0x0a,0x9e,0x43,0x3d,0xcc,0x8b,0x43,0xb2,0x06,0xa2,0x43,0x08,0xf1,0x40,0x8a,0x43,0xb0,0x12,0xa4,0x43,
14782   0x39,0xd1,0x88,0x43,0x76,0x43,0xa6,0x43,0xfa,0x06,0x88,0x43,0xa4,0x75,0xa9,0x43,0x08,0x19,0x6c,0x88,
14783   0x43,0x9f,0x9e,0xac,0x43,0x66,0xeb,0x87,0x43,0x44,0x76,0xb0,0x43,0x6b,0xce,0x86,0x43,0x3b,0xbc,0xb4,
14784   0x43,0x08,0xa9,0x8c,0x85,0x43,0x06,0xd0,0xb5,0x43,0xfa,0xee,0x83,0x43,0x74,0xa3,0xb6,0x43,0x3d,0x90,
14785   0x81,0x43,0x31,0xf6,0xb6,0x43,0x08,0x9d,0x61,0x7d,0x43,0xee,0x48,0xb7,0x43,0x3b,0x1f,0x75,0x43,0xcf,
14786   0xe3,0xb6,0x43,0xee,0x6f,0x6d,0x43,0x68,0xe2,0xb5,0x43,0x08,0xd4,0xed,0x6b,0x43,0x87,0x2f,0xb2,0x43,
14787   0x0e,0xc9,0x6b,0x43,0xa7,0x7c,0xae,0x43,0x98,0xfa,0x67,0x43,0xab,0x53,0xab,0x43,0x08,0x25,0x2c,0x64,
14788   0x43,0x33,0xa2,0xa8,0x43,0x40,0x96,0x61,0x43,0xc3,0xc2,0xa5,0x43,0x64,0xde,0x60,0x43,0xfa,0xa2,0xa2,
14789   0x43,0x08,0xb0,0x5d,0x60,0x43,0x06,0x4c,0x9f,0x43,0x9a,0xca,0x5f,0x43,0x38,0x3d,0x9b,0x43,0x3b,0x8f,
14790   0x5c,0x43,0x85,0xb0,0x98,0x43,0x08,0x42,0x36,0x51,0x43,0x3d,0xf0,0x91,0x43,0xcd,0x4f,0x49,0x43,0xdb,
14791   0xb9,0x8b,0x43,0xe0,0xdb,0x44,0x43,0x42,0x8b,0x84,0x43,0x08,0x7e,0xc9,0x44,0x43,0x8a,0x57,0x8d,0x43,
14792   0xbc,0x6c,0x0f,0x43,0x23,0x62,0x8e,0x43,0xf5,0x17,0x07,0x43,0xc5,0x3e,0x8f,0x43,0x09,0x06,0xe0,0xea,
14793   0x76,0x43,0xab,0xef,0xc5,0x43,0x08,0x12,0x00,0x79,0x43,0xab,0xcb,0xbf,0x43,0x79,0xb9,0x6d,0x43,0x7e,
14794   0x8d,0xba,0x43,0xee,0x6f,0x6d,0x43,0x98,0xeb,0xb5,0x43,0x08,0xe0,0x02,0x7b,0x43,0x5f,0x1c,0xb8,0x43,
14795   0x85,0x2c,0x82,0x43,0xe9,0x65,0xb8,0x43,0xd6,0xb2,0x86,0x43,0xc6,0x05,0xb5,0x43,0x08,0x03,0xcd,0x85,
14796   0x43,0x5a,0x39,0xb9,0x43,0xe4,0x4f,0x81,0x43,0xdb,0xd4,0xbf,0x43,0xdf,0x6c,0x82,0x43,0xbc,0x93,0xc5,
14797   0x43,0x09,0x06,0xf0,0xd0,0x22,0x43,0x5d,0x19,0x08,0x43,0x08,0xbc,0xab,0x49,0x43,0x4a,0x35,0x29,0x43,
14798   0xcb,0xf7,0x65,0x43,0xce,0x37,0x45,0x43,0x0e,0x99,0x63,0x43,0x67,0xc6,0x5c,0x43,0x09,0x06,0x05,0x94,
14799   0xab,0x43,0xc2,0x13,0x04,0x43,0x08,0x9f,0x26,0x98,0x43,0x11,0x42,0x25,0x43,0x97,0x00,0x8a,0x43,0x32,
14800   0x32,0x41,0x43,0xf5,0x2f,0x8b,0x43,0xc7,0xc0,0x58,0x43,0x09,0x06,0x8f,0x85,0x48,0x43,0xe0,0xa8,0x8c,
14801   0x43,0x08,0x55,0xaa,0x48,0x43,0xe0,0xa8,0x8c,0x43,0x6b,0x3d,0x49,0x43,0xc1,0x43,0x8c,0x43,0x31,0x62,
14802   0x49,0x43,0xc1,0x43,0x8c,0x43,0x08,0x2f,0xe3,0x2f,0x43,0xad,0xe7,0x98,0x43,0xff,0x0d,0x0d,0x43,0xad,
14803   0xf3,0x9a,0x43,0xf0,0xaf,0xcc,0x42,0x74,0x00,0x97,0x43,0x08,0xbb,0xa2,0xf7,0x42,0x93,0x4d,0x93,0x43,
14804   0x5e,0x19,0x08,0x43,0x5a,0x2a,0x87,0x43,0x23,0x6e,0x10,0x43,0x42,0x97,0x86,0x43,0x08,0xca,0xe8,0x33,
14805   0x43,0x1b,0x3c,0x80,0x43,0x80,0xe8,0x4d,0x43,0xda,0xf4,0x70,0x43,0xae,0x0e,0x4f,0x43,0x2b,0x1b,0x65,
14806   0x43,0x08,0x66,0x96,0x54,0x43,0xa3,0xe1,0x3b,0x43,0x4e,0xc4,0x19,0x43,0xa0,0x1a,0x16,0x43,0x10,0xe2,
14807   0x14,0x43,0x26,0x14,0xe0,0x42,0x08,0x5c,0x91,0x1c,0x43,0xcb,0x27,0xee,0x42,0xa9,0x40,0x24,0x43,0x71,
14808   0x3b,0xfc,0x42,0xf3,0xef,0x2b,0x43,0x8b,0x27,0x05,0x43,0x08,0xe2,0x4b,0x2c,0x43,0x48,0x86,0x07,0x43,
14809   0x79,0x62,0x2f,0x43,0x05,0xe5,0x09,0x43,0x55,0x32,0x34,0x43,0xa0,0xd2,0x09,0x43,0x08,0x74,0xa3,0x36,
14810   0x43,0x3a,0xd1,0x08,0x43,0x7e,0x81,0x38,0x43,0x09,0xd4,0x0a,0x43,0x0d,0xba,0x39,0x43,0xa0,0xea,0x0d,
14811   0x43,0x08,0x6f,0xe4,0x3d,0x43,0x43,0xc7,0x0e,0x43,0xd6,0xe5,0x3e,0x43,0xc4,0x4a,0x11,0x43,0x55,0x7a,
14812   0x40,0x43,0x59,0x72,0x13,0x43,0x08,0x55,0x92,0x44,0x43,0xbf,0x73,0x14,0x43,0x23,0x95,0x46,0x43,0xa5,
14813   0x09,0x17,0x43,0xe0,0xf3,0x48,0x43,0xfe,0x55,0x19,0x43,0x08,0xcd,0x4f,0x49,0x43,0xaa,0x10,0x1c,0x43,
14814   0x61,0x77,0x4b,0x43,0xfe,0x6d,0x1d,0x43,0x80,0xe8,0x4d,0x43,0x2b,0x94,0x1e,0x43,0x08,0x58,0xc9,0x51,
14815   0x43,0x41,0x27,0x1f,0x43,0x9b,0x82,0x53,0x43,0x35,0x72,0x20,0x43,0x53,0xf2,0x54,0x43,0x88,0xcf,0x21,
14816   0x43,0x08,0x7b,0x29,0x55,0x43,0xe8,0x0a,0x25,0x43,0xb2,0x2d,0x58,0x43,0xef,0xe8,0x26,0x43,0x9b,0xb2,
14817   0x5b,0x43,0xd0,0x8f,0x28,0x43,0x08,0x5f,0xef,0x5f,0x43,0xeb,0x11,0x2a,0x43,0xfd,0xdc,0x5f,0x43,0x6e,
14818   0x95,0x2c,0x43,0x3b,0xa7,0x60,0x43,0x2b,0xf4,0x2e,0x43,0x08,0x06,0xbb,0x61,0x43,0xfd,0xe5,0x31,0x43,
14819   0xe7,0x61,0x63,0x43,0xef,0x30,0x33,0x43,0x53,0x52,0x65,0x43,0xa3,0xb1,0x33,0x43,0x08,0x12,0xa0,0x68,
14820   0x43,0x7f,0x69,0x34,0x43,0x40,0xc6,0x69,0x43,0x64,0xff,0x36,0x43,0x7e,0x90,0x6a,0x43,0x71,0xcc,0x39,
14821   0x43,0x08,0xbc,0x5a,0x6b,0x43,0x51,0x73,0x3b,0x43,0xc1,0x49,0x6c,0x43,0xa5,0xd0,0x3c,0x43,0xe0,0xba,
14822   0x6e,0x43,0xb8,0x74,0x3c,0x43,0x08,0x6b,0x1c,0x73,0x43,0x13,0xc1,0x3e,0x43,0x40,0xf6,0x71,0x43,0xce,
14823   0x1f,0x41,0x43,0x55,0x89,0x72,0x43,0x8d,0x7e,0x43,0x43,0x08,0x68,0x2d,0x72,0x43,0x89,0xae,0x4b,0x43,
14824   0xc1,0x79,0x74,0x43,0xcb,0x78,0x4c,0x43,0x55,0xa1,0x76,0x43,0x5b,0xb1,0x4d,0x43,0x08,0xa2,0x38,0x7a,
14825   0x43,0xd1,0x56,0x4e,0x43,0x85,0xb6,0x78,0x43,0xb1,0x15,0x54,0x43,0x83,0xc7,0x77,0x43,0x89,0x0e,0x5c,
14826   0x43,0x08,0xcf,0x46,0x77,0x43,0x0f,0x81,0x5f,0x43,0x1a,0xde,0x7a,0x43,0xce,0xc7,0x5d,0x43,0x42,0x73,
14827   0x80,0x43,0x99,0xc3,0x5a,0x43,0x08,0x85,0x2c,0x82,0x43,0xf6,0xe6,0x59,0x43,0x81,0x3d,0x81,0x43,0x16,
14828   0x10,0x50,0x43,0xd6,0x8e,0x80,0x43,0x5b,0x99,0x49,0x43,0x08,0xc4,0xea,0x80,0x43,0x22,0x95,0x46,0x43,
14829   0xfa,0xe2,0x81,0x43,0xda,0xec,0x43,0x43,0x78,0x77,0x83,0x43,0xe4,0xb2,0x41,0x43,0x08,0x8a,0x27,0x85,
14830   0x43,0x86,0x77,0x3e,0x43,0x0c,0x9f,0x85,0x43,0x07,0xf4,0x3b,0x43,0x8f,0x16,0x86,0x43,0xe6,0x82,0x39,
14831   0x43,0x08,0x85,0x44,0x86,0x43,0x37,0xd9,0x35,0x43,0x1e,0x4f,0x87,0x43,0xe1,0x7b,0x34,0x43,0xdf,0x90,
14832   0x88,0x43,0xb6,0x55,0x33,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0xe5,0x31,0x43,0xfa,0x12,0x8a,0x43,0xbf,
14833   0x03,0x2d,0x43,0x19,0x78,0x8a,0x43,0x45,0x5e,0x2c,0x43,0x08,0x03,0xf1,0x8b,0x43,0xac,0x47,0x29,0x43,
14834   0x2f,0x17,0x8d,0x43,0x45,0x46,0x28,0x43,0xc8,0x21,0x8e,0x43,0x30,0xb3,0x27,0x43,0x08,0xa9,0xc8,0x8f,
14835   0x43,0xef,0xe8,0x26,0x43,0xbf,0x5b,0x90,0x43,0x5b,0xc1,0x24,0x43,0x10,0xca,0x90,0x43,0xa0,0x62,0x22,
14836   0x43,0x08,0x26,0x5d,0x91,0x43,0xbb,0xcc,0x1f,0x43,0xf0,0x70,0x92,0x43,0x78,0x13,0x1e,0x43,0x77,0xd7,
14837   0x93,0x43,0x73,0x24,0x1d,0x43,0x08,0x65,0x3f,0x96,0x43,0xce,0x58,0x1b,0x43,0xbe,0x7f,0x96,0x43,0xbf,
14838   0x8b,0x18,0x43,0x60,0x5c,0x97,0x43,0xb6,0xad,0x16,0x43,0x08,0xba,0xa8,0x99,0x43,0x78,0xcb,0x11,0x43,
14839   0x49,0xe1,0x9a,0x43,0x78,0xcb,0x11,0x43,0x01,0x51,0x9c,0x43,0x73,0xdc,0x10,0x43,0x08,0x72,0x24,0x9d,
14840   0x43,0xd2,0xff,0x0f,0x43,0x1c,0xd3,0x9d,0x43,0x07,0xec,0x0e,0x43,0xeb,0xc9,0x9d,0x43,0xe8,0x7a,0x0c,
14841   0x43,0x08,0x60,0x80,0x9d,0x43,0xd7,0xbe,0x08,0x43,0x4d,0xe8,0x9f,0x43,0x86,0x50,0x08,0x43,0x25,0xbd,
14842   0xa1,0x43,0x5b,0x2a,0x07,0x43,0x08,0x99,0x7f,0xa3,0x43,0xc9,0xf1,0x05,0x43,0x48,0x1d,0xa5,0x43,0x86,
14843   0x38,0x04,0x43,0x6c,0x71,0xa6,0x43,0x18,0x59,0x01,0x43,0x08,0x32,0x96,0xa6,0x43,0x6e,0x64,0xff,0x42,
14844   0x48,0x29,0xa7,0x43,0xed,0xcf,0xfd,0x42,0x5f,0xbc,0xa7,0x43,0x71,0x3b,0xfc,0x42,0x08,0xf3,0xe3,0xa9,
14845   0x43,0xf7,0x7d,0xf7,0x42,0xd8,0x6d,0xaa,0x43,0x45,0xe5,0xf2,0x42,0x48,0x41,0xab,0x43,0xcb,0x27,0xee,
14846   0x42,0x08,0x24,0xf9,0xab,0x43,0x52,0x6a,0xe9,0x42,0xee,0x0c,0xad,0x43,0x4c,0x8c,0xe7,0x42,0x1b,0x33,
14847   0xae,0x43,0xcc,0xf7,0xe5,0x42,0x08,0xaa,0x6b,0xaf,0x43,0xe8,0x61,0xe3,0x42,0x90,0xf5,0xaf,0x43,0xc9,
14848   0xf0,0xe0,0x42,0xe0,0x63,0xb0,0x43,0xe5,0x5a,0xde,0x42,0x08,0xaa,0x83,0xb3,0x43,0x29,0x2d,0x09,0x43,
14849   0x6a,0xfe,0x8e,0x43,0xb8,0x74,0x3c,0x43,0xd5,0x06,0x95,0x43,0xe6,0x79,0x67,0x43,0x08,0x2f,0x53,0x97,
14850   0x43,0xe9,0xb0,0x74,0x43,0xa8,0x28,0xa0,0x43,0x43,0xfd,0x76,0x43,0x83,0x28,0xad,0x43,0x17,0x59,0x81,
14851   0x43,0x08,0x3d,0xe7,0xbf,0x43,0x4b,0x8d,0x8c,0x43,0xae,0x96,0xba,0x43,0x66,0x27,0x92,0x43,0x15,0xe0,
14852   0xc7,0x43,0x6f,0x11,0x96,0x43,0x08,0x7e,0x5d,0xb2,0x43,0xdb,0x01,0x98,0x43,0x9e,0x56,0xa0,0x43,0x80,
14853   0xc1,0x97,0x43,0x69,0x2e,0x97,0x43,0x31,0x17,0x8d,0x43,0x09,0x06,0xab,0xa7,0x39,0x43,0x67,0x0f,0x0e,
14854   0x43,0x08,0xdb,0xbc,0x3b,0x43,0xe8,0x92,0x10,0x43,0xb5,0x85,0x3b,0x43,0x97,0x3c,0x14,0x43,0xab,0xa7,
14855   0x39,0x43,0x0c,0x0b,0x18,0x43,0x09,0x06,0xca,0x30,0x40,0x43,0x30,0x3b,0x13,0x43,0x08,0x17,0xc8,0x43,
14856   0x43,0xa5,0x09,0x17,0x43,0x7e,0xc9,0x44,0x43,0x1a,0xd8,0x1a,0x43,0x9d,0x22,0x43,0x43,0x8d,0xa6,0x1e,
14857   0x43,0x09,0x06,0xc8,0x78,0x4c,0x43,0xed,0xc9,0x1d,0x43,0x08,0x0b,0x32,0x4e,0x43,0x22,0xce,0x20,0x43,
14858   0x23,0xc5,0x4e,0x43,0x58,0xd2,0x23,0x43,0x0b,0x32,0x4e,0x43,0x2b,0xc4,0x26,0x43,0x09,0x06,0xec,0x08,
14859   0x58,0x43,0xc7,0xb1,0x26,0x43,0x08,0x02,0x9c,0x58,0x43,0xef,0x00,0x2b,0x43,0xd9,0x64,0x58,0x43,0x02,
14860   0xbd,0x2e,0x43,0x10,0x51,0x57,0x43,0x37,0xc1,0x31,0x43,0x09,0x06,0xcb,0xdf,0x61,0x43,0x4a,0x65,0x31,
14861   0x43,0x08,0xbe,0x2a,0x63,0x43,0xbd,0x33,0x35,0x43,0x32,0xe1,0x62,0x43,0x56,0x4a,0x38,0x43,0xde,0x83,
14862   0x61,0x43,0x3c,0xe0,0x3a,0x43,0x09,0x06,0x1c,0x7e,0x6a,0x43,0x5b,0x39,0x39,0x43,0x08,0x31,0x11,0x6b,
14863   0x43,0x0c,0xd2,0x3d,0x43,0x1c,0x7e,0x6a,0x43,0x13,0xd9,0x42,0x43,0xd9,0xc4,0x68,0x43,0xcb,0x60,0x48,
14864   0x43,0x09,0x06,0xe5,0xc1,0x73,0x43,0x16,0xf8,0x4b,0x43,0x08,0xa6,0xf7,0x72,0x43,0xb1,0xfd,0x4f,0x43,
14865   0x3b,0x07,0x71,0x43,0x4a,0x14,0x53,0x43,0xa2,0xf0,0x6d,0x43,0x7c,0x29,0x55,0x43,0x09,0x06,0x00,0x8d,
14866   0xa6,0x43,0xef,0x21,0x01,0x43,0x08,0x52,0xfb,0xa6,0x43,0xce,0xc8,0x02,0x43,0xe6,0x16,0xa7,0x43,0x51,
14867   0x4c,0x05,0x43,0x3b,0x68,0xa6,0x43,0x4c,0x75,0x08,0x43,0x09,0x06,0xde,0x20,0xa1,0x43,0x86,0x50,0x08,
14868   0x43,0x08,0xd4,0x4e,0xa1,0x43,0xd3,0xe7,0x0b,0x43,0xb5,0xe9,0xa0,0x43,0x59,0x5a,0x0f,0x43,0xba,0xcc,
14869   0x9f,0x43,0x54,0x83,0x12,0x43,0x09,0x06,0x77,0xfb,0x99,0x43,0x6c,0x16,0x13,0x43,0x08,0xde,0xfc,0x9a,
14870   0x43,0x4a,0xbd,0x14,0x43,0x06,0x34,0x9b,0x43,0xfe,0x55,0x19,0x43,0x13,0xe9,0x99,0x43,0x41,0x27,0x1f,
14871   0x43,0x09,0x06,0x46,0xce,0x93,0x43,0x26,0xa5,0x1d,0x43,0x08,0xe7,0xaa,0x94,0x43,0xbb,0xcc,0x1f,0x43,
14872   0x18,0xb4,0x94,0x43,0xa8,0x40,0x24,0x43,0xe2,0xbb,0x93,0x43,0x21,0xfe,0x28,0x43,0x09,0x06,0xb1,0x8e,
14873   0x8d,0x43,0xa8,0x58,0x28,0x43,0x08,0x19,0x90,0x8e,0x43,0x54,0x13,0x2b,0x43,0xa4,0xd9,0x8e,0x43,0x84,
14874   0x40,0x31,0x43,0x46,0xaa,0x8d,0x43,0x29,0x24,0x37,0x43,0x09,0x06,0xd6,0xbe,0x88,0x43,0xef,0x30,0x33,
14875   0x43,0x08,0x0c,0xb7,0x89,0x43,0x0e,0xa2,0x35,0x43,0xc0,0x37,0x8a,0x43,0x7a,0xaa,0x3b,0x43,0xbb,0x48,
14876   0x89,0x43,0xbb,0x7b,0x41,0x43,0x09,0x06,0x3a,0xad,0x82,0x43,0xc4,0x59,0x43,0x43,0x08,0xd2,0xb7,0x83,
14877   0x43,0x2b,0x5b,0x44,0x43,0x35,0xd6,0x85,0x43,0x48,0xf5,0x49,0x43,0x42,0x97,0x86,0x43,0xc4,0xa1,0x4f,
14878   0x43,0x09,0x06,0x9c,0xb3,0x80,0x43,0x48,0x55,0x5a,0x43,0x08,0xff,0xc5,0x80,0x43,0x09,0x73,0x55,0x43,
14879   0x93,0xe1,0x80,0x43,0x0f,0x39,0x53,0x43,0xf1,0xbe,0x7e,0x43,0x18,0xe7,0x4c,0x43,0x09,0x06,0xe0,0x02,
14880   0x7b,0x43,0x92,0xec,0x5d,0x43,0x08,0x09,0x3a,0x7b,0x43,0xf0,0xf7,0x58,0x43,0x09,0x3a,0x7b,0x43,0xe6,
14881   0x31,0x5b,0x43,0xe0,0x02,0x7b,0x43,0xa8,0x4f,0x56,0x43,0x09,0x06,0x39,0x4f,0x7d,0x43,0x3e,0x8f,0x5c,
14882   0x43,0x08,0xe9,0xe0,0x7c,0x43,0x03,0x9c,0x58,0x43,0x1e,0x2b,0x81,0x43,0x7f,0x30,0x5a,0x43,0xff,0x73,
14883   0x7d,0x43,0xf6,0xb6,0x51,0x43,0x09,0x06,0x5c,0xb8,0x52,0x43,0x28,0x21,0x87,0x43,0x08,0xae,0x3e,0x57,
14884   0x43,0x12,0x9a,0x88,0x43,0x23,0xf5,0x56,0x43,0x04,0xf1,0x8b,0x43,0x25,0xfc,0x5b,0x43,0x85,0x74,0x8e,
14885   0x43,0x08,0x2f,0xf2,0x61,0x43,0x8e,0x52,0x90,0x43,0xd9,0xdc,0x6c,0x43,0x85,0x74,0x8e,0x43,0xc6,0x20,
14886   0x69,0x43,0x3d,0xd8,0x8d,0x43,0x08,0x6d,0x8c,0x5a,0x43,0xf5,0x3b,0x8d,0x43,0x3d,0x77,0x58,0x43,0xa1,
14887   0xc6,0x87,0x43,0xf8,0xed,0x5e,0x43,0x5e,0x0d,0x86,0x43,0x09,0x06,0xde,0xcc,0x92,0x43,0xf7,0x17,0x87,
14888   0x43,0x08,0xb6,0x89,0x90,0x43,0xae,0x87,0x88,0x43,0x4a,0xa5,0x90,0x43,0xa1,0xde,0x8b,0x43,0xf9,0x2a,
14889   0x8e,0x43,0x23,0x62,0x8e,0x43,0x08,0xf5,0x2f,0x8b,0x43,0x5c,0x49,0x90,0x43,0x35,0xd6,0x85,0x43,0x8e,
14890   0x46,0x8e,0x43,0x3d,0xb4,0x87,0x43,0x47,0xaa,0x8d,0x43,0x08,0x6a,0xfe,0x8e,0x43,0xff,0x0d,0x8d,0x43,
14891   0xbb,0x6c,0x8f,0x43,0xf7,0x17,0x87,0x43,0x5c,0x31,0x8c,0x43,0xb2,0x5e,0x85,0x43,0x09,0x06,0x60,0x38,
14892   0x91,0x43,0x69,0x5d,0x7a,0x43,0x08,0x34,0x1e,0x92,0x43,0x1e,0x5b,0x89,0x43,0x04,0x63,0x7e,0x43,0x5e,
14893   0x01,0x84,0x43,0x59,0x2a,0x87,0x43,0x0d,0xcf,0x8d,0x43,0x09,0x03,0x04,0x06,0x5a,0x18,0x63,0x43,0x82,
14894   0x79,0x8b,0x43,0x08,0x25,0x2c,0x64,0x43,0x82,0x79,0x8b,0x43,0x2a,0x1b,0x65,0x43,0x9d,0xef,0x8a,0x43,
14895   0x2a,0x1b,0x65,0x43,0xc1,0x37,0x8a,0x43,0x08,0x2a,0x1b,0x65,0x43,0x17,0x89,0x89,0x43,0x25,0x2c,0x64,
14896   0x43,0x31,0xff,0x88,0x43,0x5a,0x18,0x63,0x43,0x31,0xff,0x88,0x43,0x08,0xf3,0x16,0x62,0x43,0x31,0xff,
14897   0x88,0x43,0xee,0x27,0x61,0x43,0x17,0x89,0x89,0x43,0xee,0x27,0x61,0x43,0xc1,0x37,0x8a,0x43,0x08,0xee,
14898   0x27,0x61,0x43,0x9d,0xef,0x8a,0x43,0xf3,0x16,0x62,0x43,0x82,0x79,0x8b,0x43,0x5a,0x18,0x63,0x43,0x82,
14899   0x79,0x8b,0x43,0x09,0x06,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x08,0x34,0xee,0x89,0x43,0x82,0x79,
14900   0x8b,0x43,0x85,0x5c,0x8a,0x43,0x9d,0xef,0x8a,0x43,0x85,0x5c,0x8a,0x43,0xc1,0x37,0x8a,0x43,0x08,0x85,
14901   0x5c,0x8a,0x43,0x17,0x89,0x89,0x43,0x34,0xee,0x89,0x43,0x31,0xff,0x88,0x43,0x4f,0x64,0x89,0x43,0x31,
14902   0xff,0x88,0x43,0x08,0x9c,0xe3,0x88,0x43,0x31,0xff,0x88,0x43,0x19,0x6c,0x88,0x43,0x17,0x89,0x89,0x43,
14903   0x19,0x6c,0x88,0x43,0xc1,0x37,0x8a,0x43,0x08,0x19,0x6c,0x88,0x43,0x9d,0xef,0x8a,0x43,0x9c,0xe3,0x88,
14904   0x43,0x82,0x79,0x8b,0x43,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x09,0x02,0x04,0x06,0x19,0x60,0x86,
14905   0x43,0xec,0xed,0xa3,0x43,0x08,0x35,0xd6,0x85,0x43,0x76,0x43,0xa6,0x43,0x93,0xe1,0x80,0x43,0x57,0x02,
14906   0xac,0x43,0x61,0xd8,0x80,0x43,0x87,0x17,0xae,0x43,0x08,0xa5,0x85,0x80,0x43,0xc3,0xfe,0xaf,0x43,0xce,
14907   0xbc,0x80,0x43,0x83,0x40,0xb1,0x43,0xa5,0x91,0x82,0x43,0x79,0x6e,0xb1,0x43,0x08,0x23,0x26,0x84,0x43,
14908   0x40,0x93,0xb1,0x43,0x30,0xe7,0x84,0x43,0xbe,0x1b,0xb1,0x43,0x11,0x82,0x84,0x43,0xab,0x6b,0xaf,0x43,
14909   0x08,0xb7,0x41,0x84,0x43,0x3b,0x98,0xae,0x43,0xb7,0x41,0x84,0x43,0xc3,0xf2,0xad,0x43,0xa1,0xae,0x83,
14910   0x43,0x83,0x28,0xad,0x43,0x08,0xb2,0x52,0x83,0x43,0x80,0x39,0xac,0x43,0x81,0x49,0x83,0x43,0xf0,0x00,
14911   0xab,0x43,0xe4,0x67,0x85,0x43,0x76,0x4f,0xa8,0x43,0x08,0x9c,0xd7,0x86,0x43,0xd1,0x83,0xa6,0x43,0xec,
14912   0x45,0x87,0x43,0x01,0x75,0xa2,0x43,0x19,0x60,0x86,0x43,0xec,0xed,0xa3,0x43,0x09,0x06,0xd9,0xdc,0x6c,
14913   0x43,0x14,0x25,0xa4,0x43,0x08,0xa2,0xf0,0x6d,0x43,0x9f,0x7a,0xa6,0x43,0x47,0xec,0x77,0x43,0x80,0x39,
14914   0xac,0x43,0xa9,0xfe,0x77,0x43,0xb0,0x4e,0xae,0x43,0x08,0x23,0xa4,0x78,0x43,0xea,0x35,0xb0,0x43,0xd2,
14915   0x35,0x78,0x43,0xab,0x77,0xb1,0x43,0xc1,0x79,0x74,0x43,0xa2,0xa5,0xb1,0x43,0x08,0xc6,0x50,0x71,0x43,
14916   0x68,0xca,0xb1,0x43,0xab,0xce,0x6f,0x43,0xe7,0x52,0xb1,0x43,0xea,0x98,0x70,0x43,0xd4,0xa2,0xaf,0x43,
14917   0x08,0x9d,0x19,0x71,0x43,0x96,0xd8,0xae,0x43,0x9d,0x19,0x71,0x43,0xec,0x29,0xae,0x43,0xca,0x3f,0x72,
14918   0x43,0xab,0x5f,0xad,0x43,0x08,0xa6,0xf7,0x72,0x43,0xa7,0x70,0xac,0x43,0x09,0x0a,0x73,0x43,0x17,0x38,
14919   0xab,0x43,0x44,0xcd,0x6e,0x43,0x9f,0x86,0xa8,0x43,0x08,0xd4,0xed,0x6b,0x43,0xf8,0xba,0xa6,0x43,0x31,
14920   0x11,0x6b,0x43,0x2a,0xac,0xa2,0x43,0xd9,0xdc,0x6c,0x43,0x14,0x25,0xa4,0x43,0x09,0x01,0x05,0x06,0x66,
14921   0x5d,0x7a,0x43,0x74,0xeb,0xc2,0x43,0x08,0x09,0x22,0x77,0x43,0x50,0xbb,0xc7,0x43,0xe9,0xe0,0x7c,0x43,
14922   0xf5,0x86,0xc9,0x43,0x8f,0x94,0x7a,0x43,0xc5,0x95,0xcd,0x43,0x09,0x06,0x08,0x98,0x80,0x43,0x6b,0x19,
14923   0xc3,0x43,0x08,0xb7,0x35,0x82,0x43,0x79,0xf2,0xc7,0x43,0xf1,0xbe,0x7e,0x43,0x1e,0xbe,0xc9,0x43,0x73,
14924   0x7c,0x80,0x43,0xec,0xcc,0xcd,0x43,0x09,0x06,0x28,0xab,0x7d,0x43,0xae,0xde,0xc6,0x43,0x08,0x1e,0xcd,
14925   0x7b,0x43,0x8a,0xa2,0xc9,0x43,0x30,0x89,0x7f,0x43,0x5c,0x94,0xcc,0x43,0x28,0xab,0x7d,0x43,0x42,0x2a,
14926   0xcf,0x43,0x09,0x01,0x05,0x06,0x24,0x14,0xe0,0x42,0xf5,0x77,0x97,0x43,0x08,0xf7,0x1d,0xe7,0x42,0x74,
14927   0x00,0x97,0x43,0x4d,0x93,0xec,0x42,0xdb,0xf5,0x95,0x43,0x29,0x4b,0xed,0x42,0xcd,0x34,0x95,0x43,0x09,
14928   0x06,0x29,0x7b,0xf5,0x42,0x6f,0x1d,0x98,0x43,0x08,0xe4,0xf1,0xfb,0x42,0x61,0x5c,0x97,0x43,0xdb,0x7d,
14929   0x01,0x43,0xb2,0xbe,0x95,0x43,0x55,0x23,0x02,0x43,0xe7,0xaa,0x94,0x43,0x09,0x06,0x98,0xdc,0x03,0x43,
14930   0xbe,0x8b,0x98,0x43,0x08,0x66,0xdf,0x05,0x43,0x47,0xe6,0x97,0x43,0xae,0x87,0x08,0x43,0x98,0x48,0x96,
14931   0x43,0x61,0x08,0x09,0x43,0xd6,0x06,0x95,0x43,0x09,0x06,0x31,0x0b,0x0b,0x43,0x8e,0x82,0x98,0x43,0x08,
14932   0xdb,0xc5,0x0d,0x43,0x80,0xc1,0x97,0x43,0xd6,0xee,0x10,0x43,0xa9,0xec,0x95,0x43,0x79,0xcb,0x11,0x43,
14933   0x55,0x8f,0x94,0x43,0x09,0x06,0xd1,0x2f,0x18,0x43,0xdb,0x01,0x98,0x43,0x08,0xad,0xe7,0x18,0x43,0x38,
14934   0x25,0x97,0x43,0x8a,0x9f,0x19,0x43,0x80,0xb5,0x95,0x43,0xd6,0x1e,0x19,0x43,0xe0,0xd8,0x94,0x43,0x09,
14935   0x06,0x9a,0x5b,0x1d,0x43,0x58,0x8a,0x97,0x43,0x08,0x01,0x5d,0x1e,0x43,0xf1,0x88,0x96,0x43,0x2f,0x83,
14936   0x1f,0x43,0x19,0xb4,0x94,0x43,0x19,0xf0,0x1e,0x43,0x6f,0x05,0x94,0x43,0x09,0x06,0x0b,0x53,0x24,0x43,
14937   0xae,0xdb,0x96,0x43,0x08,0x25,0xd5,0x25,0x43,0x50,0xac,0x95,0x43,0x53,0xfb,0x26,0x43,0x8a,0x7b,0x93,
14938   0x43,0x76,0x43,0x26,0x43,0xb7,0x95,0x92,0x43,0x09,0x06,0x76,0x5b,0x2a,0x43,0x47,0xda,0x95,0x43,0x08,
14939   0xf3,0xef,0x2b,0x43,0x10,0xe2,0x94,0x43,0x6d,0x95,0x2c,0x43,0xae,0xc3,0x92,0x43,0x68,0xa6,0x2b,0x43,
14940   0x47,0xc2,0x91,0x43,0x09,0x06,0x36,0xc1,0x31,0x43,0x2c,0x58,0x94,0x43,0x08,0x8c,0x1e,0x33,0x43,0x31,
14941   0x3b,0x93,0x43,0x79,0x7a,0x33,0x43,0xff,0x25,0x91,0x43,0xd9,0x9d,0x32,0x43,0xc1,0x5b,0x90,0x43,0x09,
14942   0x06,0x25,0x35,0x36,0x43,0x31,0x3b,0x93,0x43,0x08,0x3f,0xb7,0x37,0x43,0xc1,0x67,0x92,0x43,0xe0,0x93,
14943   0x38,0x43,0xae,0xb7,0x90,0x43,0x7e,0x81,0x38,0x43,0x0d,0xdb,0x8f,0x43,0x09,0x06,0xb5,0x85,0x3b,0x43,
14944   0xe4,0xaf,0x91,0x43,0x08,0xcf,0x07,0x3d,0x43,0x9d,0x13,0x91,0x43,0xbc,0x63,0x3d,0x43,0x47,0xb6,0x8f,
14945   0x43,0xe5,0x9a,0x3d,0x43,0x74,0xd0,0x8e,0x43,0x09,0x06,0xae,0xc6,0x42,0x43,0xa4,0xd9,0x8e,0x43,0x08,
14946   0xca,0x48,0x44,0x43,0xfa,0x2a,0x8e,0x43,0xa2,0x11,0x44,0x43,0x9d,0xfb,0x8c,0x43,0x55,0x92,0x44,0x43,
14947   0x0d,0xc3,0x8b,0x43,0x09,0x06,0x39,0x10,0xc3,0x43,0x34,0x36,0x96,0x43,0x08,0x92,0x44,0xc1,0x43,0xe4,
14948   0xc7,0x95,0x43,0x6f,0xf0,0xbf,0x43,0x4b,0xbd,0x94,0x43,0x47,0xb9,0xbf,0x43,0x0b,0xf3,0x93,0x43,0x09,
14949   0x06,0x8f,0x49,0xbe,0x43,0xb7,0xad,0x96,0x43,0x08,0x11,0xb5,0xbc,0x43,0x77,0xe3,0x95,0x43,0x9c,0xf2,
14950   0xba,0x43,0xfa,0x4e,0x94,0x43,0xae,0x96,0xba,0x43,0x31,0x3b,0x93,0x43,0x09,0x06,0xdb,0xb0,0xb9,0x43,
14951   0x10,0xee,0x96,0x43,0x08,0x42,0xa6,0xb8,0x43,0xc8,0x51,0x96,0x43,0x50,0x5b,0xb7,0x43,0x19,0xb4,0x94,
14952   0x43,0xf7,0x1a,0xb7,0x43,0x58,0x72,0x93,0x43,0x09,0x06,0xf2,0x2b,0xb6,0x43,0x10,0xee,0x96,0x43,0x08,
14953   0x9d,0xce,0xb4,0x43,0x04,0x2d,0x96,0x43,0xed,0x30,0xb3,0x43,0x2c,0x58,0x94,0x43,0xce,0xcb,0xb2,0x43,
14954   0xd6,0xfa,0x92,0x43,0x09,0x06,0x5a,0x09,0xb1,0x43,0x19,0xc0,0x96,0x43,0x08,0x6c,0xad,0xb0,0x43,0x77,
14955   0xe3,0x95,0x43,0x7e,0x51,0xb0,0x43,0xc0,0x73,0x94,0x43,0xd8,0x91,0xb0,0x43,0x1e,0x97,0x93,0x43,0x09,
14956   0x06,0x48,0x4d,0xad,0x43,0xbe,0x7f,0x96,0x43,0x08,0x95,0xcc,0xac,0x43,0x58,0x7e,0x95,0x43,0x4d,0x30,
14957   0xac,0x43,0x80,0xa9,0x93,0x43,0xd8,0x79,0xac,0x43,0xd6,0xfa,0x92,0x43,0x09,0x06,0x90,0xd1,0xa9,0x43,
14958   0x14,0xd1,0x95,0x43,0x08,0x83,0x10,0xa9,0x43,0xb7,0xa1,0x94,0x43,0x3b,0x74,0xa8,0x43,0xf1,0x70,0x92,
14959   0x43,0x29,0xd0,0xa8,0x43,0x1e,0x8b,0x91,0x43,0x09,0x06,0x5a,0xcd,0xa6,0x43,0x8a,0x87,0x95,0x43,0x08,
14960   0x1c,0x03,0xa6,0x43,0x23,0x86,0x94,0x43,0x5f,0xb0,0xa5,0x43,0xc1,0x67,0x92,0x43,0xe1,0x27,0xa6,0x43,
14961   0x8a,0x6f,0x91,0x43,0x09,0x06,0xd4,0x5a,0xa3,0x43,0x2c,0x58,0x94,0x43,0x08,0x29,0xac,0xa2,0x43,0x31,
14962   0x3b,0x93,0x43,0x32,0x7e,0xa2,0x43,0xff,0x25,0x91,0x43,0x83,0xec,0xa2,0x43,0x8e,0x52,0x90,0x43,0x09,
14963   0x06,0xf8,0x96,0xa0,0x43,0x1e,0x97,0x93,0x43,0x08,0xeb,0xd5,0x9f,0x43,0x7b,0xba,0x92,0x43,0x99,0x67,
14964   0x9f,0x43,0x9d,0x13,0x91,0x43,0x99,0x67,0x9f,0x43,0xfa,0x36,0x90,0x43,0x09,0x06,0xeb,0xc9,0x9d,0x43,
14965   0xc8,0x39,0x92,0x43,0x08,0xde,0x08,0x9d,0x43,0xb2,0xa6,0x91,0x43,0xe6,0xda,0x9c,0x43,0x2c,0x40,0x90,
14966   0x43,0x52,0xbf,0x9c,0x43,0x5a,0x5a,0x8f,0x43,0x09,0x06,0x37,0x3d,0x9b,0x43,0x85,0x80,0x90,0x43,0x08,
14967   0x2a,0x7c,0x9a,0x43,0xdb,0xd1,0x8f,0x43,0xf0,0xa0,0x9a,0x43,0x7d,0xa2,0x8e,0x43,0x65,0x57,0x9a,0x43,
14968   0xee,0x69,0x8d,0x43,0x09,0x02,0x04,0x06,0x2a,0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x08,0x0d,0x8a,0x31,
14969   0x42,0x9f,0x0e,0x94,0x43,0xf3,0x1f,0x34,0x42,0x3d,0xfc,0x93,0x43,0x63,0xff,0x36,0x42,0xa9,0xe0,0x93,
14970   0x43,0x08,0xb5,0x34,0x5d,0x42,0x0b,0xf3,0x93,0x43,0x6d,0xa4,0x5e,0x42,0x03,0x39,0x98,0x43,0xe7,0x31,
14971   0x5b,0x42,0x93,0x89,0x9d,0x43,0x08,0x02,0x9c,0x58,0x42,0xd4,0x5a,0xa3,0x43,0x38,0x70,0x53,0x42,0x14,
14972   0x49,0xaa,0x43,0xf8,0xed,0x5e,0x42,0x83,0x28,0xad,0x43,0x08,0xea,0x68,0x68,0x42,0x20,0x22,0xaf,0x43,
14973   0x12,0xb8,0x6c,0x42,0xb5,0x49,0xb1,0x43,0x2a,0x4b,0x6d,0x42,0x0d,0x96,0xb3,0x43,0x07,0x2a,0x4b,0x6d,
14974   0x42,0xc6,0x05,0xb5,0x43,0x08,0x87,0x6e,0x6c,0x42,0x68,0xee,0xb7,0x43,0x1c,0x66,0x66,0x42,0x31,0x0e,
14975   0xbb,0x43,0x57,0x11,0x5e,0x42,0x8f,0x49,0xbe,0x43,0x08,0x66,0x96,0x54,0x42,0xb9,0x5c,0xb8,0x43,0x2c,
14976   0x2b,0x3c,0x42,0x68,0xd6,0xb3,0x43,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x07,0x2a,0xf4,0x2e,0x42,
14977   0x61,0xa4,0xa3,0x43,0x08,0x55,0x1a,0x30,0x42,0xf0,0xd0,0xa2,0x43,0xf8,0xf6,0x30,0x42,0xb2,0x06,0xa2,
14978   0x43,0x98,0xd3,0x31,0x42,0xd6,0x4e,0xa1,0x43,0x08,0x1c,0x6f,0x38,0x42,0x2a,0x94,0x9e,0x43,0xc1,0x22,
14979   0x36,0x42,0xf5,0x9b,0x9d,0x43,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x57,
14980   0xa2,0x9b,0x43,0x08,0xab,0x8f,0x35,0x42,0x8a,0xab,0x9b,0x43,0xe9,0x71,0x3a,0x42,0xb2,0xe2,0x9b,0x43,
14981   0xb7,0x74,0x3c,0x42,0x34,0x5a,0x9c,0x43,0x08,0x23,0x7d,0x42,0x42,0x0b,0x2f,0x9e,0x43,0xe5,0x9a,0x3d,
14982   0x42,0x38,0x6d,0xa3,0x43,0x36,0xd9,0x35,0x42,0xf3,0xd7,0xa7,0x43,0x08,0x12,0x61,0x2e,0x42,0xb0,0x42,
14983   0xac,0x43,0x63,0xff,0x36,0x42,0xdd,0x74,0xaf,0x43,0x1e,0xa6,0x45,0x42,0x44,0x82,0xb2,0x43,0x08,0x74,
14984   0x1b,0x4b,0x42,0x79,0x7a,0xb3,0x43,0x10,0x21,0x4f,0x42,0x2a,0x18,0xb5,0x43,0xdb,0x4c,0x54,0x42,0x91,
14985   0x19,0xb6,0x43,0x08,0xee,0x3f,0x65,0x42,0x5f,0x28,0xba,0x43,0xa7,0xaf,0x66,0x42,0xb9,0x50,0xb6,0x43,
14986   0x14,0x58,0x5c,0x42,0xca,0xdc,0xb1,0x43,0x08,0x2c,0x8b,0x4c,0x42,0x4e,0x30,0xac,0x43,0x19,0xcf,0x48,
14987   0x42,0x2a,0xd0,0xa8,0x43,0xbc,0xab,0x49,0x42,0xa9,0x4c,0xa6,0x43,0x08,0x61,0x5f,0x47,0x42,0xfa,0xa2,
14988   0xa2,0x43,0xa7,0xaf,0x66,0x42,0x85,0x98,0x94,0x43,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x07,0x2a,
14989   0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x09,0x06,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x08,0xdc,0xe3,
14990   0xf1,0x41,0xe9,0x9e,0x92,0x43,0xd2,0xe7,0x0b,0x42,0xd6,0x06,0x95,0x43,0x2a,0xf4,0x2e,0x42,0x04,0x21,
14991   0x94,0x43,0x07,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x08,0x87,0x17,0x2e,0x42,0xc3,0x62,0x95,0x43,
14992   0xe7,0x3a,0x2d,0x42,0xf5,0x6b,0x95,0x43,0x44,0x5e,0x2c,0x42,0xf5,0x6b,0x95,0x43,0x08,0xd1,0x47,0x1c,
14993   0x42,0x19,0xc0,0x96,0x43,0x66,0xdf,0x05,0x42,0x38,0x19,0x95,0x43,0x12,0x6a,0x00,0x42,0xb2,0xbe,0x95,
14994   0x43,0x08,0xbb,0x6b,0xea,0x41,0xd6,0x12,0x97,0x43,0x2d,0x82,0xfa,0x41,0x61,0x74,0x9b,0x43,0x7e,0x72,
14995   0x06,0x42,0x8a,0xab,0x9b,0x43,0x08,0xc8,0x39,0x12,0x42,0x4e,0xd0,0x9b,0x43,0x53,0xe3,0x22,0x42,0xc3,
14996   0x86,0x9b,0x43,0x2a,0xf4,0x2e,0x42,0x57,0xa2,0x9b,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,
14997   0x08,0x01,0xa5,0x2a,0x42,0xa4,0x2d,0x9d,0x43,0x96,0x9c,0x24,0x42,0x06,0x40,0x9d,0x43,0x8a,0xb7,0x1d,
14998   0x42,0x9a,0x5b,0x9d,0x43,0x08,0x6b,0x16,0x13,0x42,0xcd,0x64,0x9d,0x43,0x42,0xc7,0x0e,0x42,0x9a,0x5b,
14999   0x9d,0x43,0x23,0x26,0x04,0x42,0xcd,0x64,0x9d,0x43,0x08,0xe6,0x91,0xeb,0x41,0x38,0x49,0x9d,0x43,0x73,
15000   0x7b,0xdb,0x41,0xf5,0x83,0x99,0x43,0x7f,0x60,0xe2,0x41,0x0b,0x0b,0x98,0x43,0x08,0x7f,0x60,0xe2,0x41,
15001   0xec,0x99,0x95,0x43,0xe3,0x5a,0xde,0x41,0xbe,0x7f,0x96,0x43,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,
15002   0x07,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x09,0x06,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x08,
15003   0xd4,0x7e,0x29,0x42,0xab,0x6b,0xaf,0x43,0x4e,0x0c,0x26,0x42,0x44,0x6a,0xae,0x43,0x38,0x79,0x25,0x42,
15004   0xd4,0x96,0xad,0x43,0x08,0x25,0xbd,0x21,0x42,0xe2,0x4b,0xac,0x43,0x49,0x35,0x29,0x42,0x9a,0x97,0xa7,
15005   0x43,0x2a,0xf4,0x2e,0x42,0x61,0xa4,0xa3,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x09,0x06,
15006   0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x08,0x86,0x20,0x80,0x43,0x57,0x41,0xe6,0x43,0x7d,0x4e,0x80,
15007   0x43,0x25,0x38,0xe6,0x43,0xa5,0x85,0x80,0x43,0xf3,0x2e,0xe6,0x43,0x08,0x35,0xca,0x83,0x43,0xd4,0xc9,
15008   0xe5,0x43,0x9c,0xd7,0x86,0x43,0x44,0x91,0xe4,0x43,0xd5,0xca,0x8a,0x43,0x91,0x1c,0xe6,0x43,0x08,0x53,
15009   0x5f,0x8c,0x43,0xf8,0x1d,0xe7,0x43,0x2f,0x17,0x8d,0x43,0x4e,0x7b,0xe8,0x43,0x92,0x29,0x8d,0x43,0x2f,
15010   0x22,0xea,0x43,0x07,0x92,0x29,0x8d,0x43,0x44,0xb5,0xea,0x43,0x08,0xfe,0x0d,0x8d,0x43,0x2a,0x4b,0xed,
15011   0x43,0xe3,0x8b,0x8b,0x43,0x55,0x7d,0xf0,0x43,0xec,0x51,0x89,0x43,0x72,0x0b,0xf4,0x43,0x08,0xcd,0xd4,
15012   0x84,0x43,0x9d,0x55,0xfb,0x43,0xc9,0xe5,0x83,0x43,0x74,0x1e,0xfb,0x43,0x73,0x94,0x84,0x43,0x5a,0x90,
15013   0xf7,0x43,0x08,0xe8,0x62,0x88,0x43,0xfd,0x30,0xee,0x43,0x39,0xc5,0x86,0x43,0xdd,0xbf,0xeb,0x43,0x35,
15014   0xbe,0x81,0x43,0x40,0xde,0xed,0x43,0x08,0x4f,0x34,0x81,0x43,0x36,0x0c,0xee,0x43,0x08,0x98,0x80,0x43,
15015   0xfd,0x30,0xee,0x43,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x40,0xec,
15016   0x43,0x08,0x35,0xbe,0x81,0x43,0x06,0xf7,0xeb,0x43,0x15,0x65,0x83,0x43,0x49,0xa4,0xeb,0x43,0x1e,0x43,
15017   0x85,0x43,0xbe,0x5a,0xeb,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0x18,0xea,0x43,0x42,0x97,0x86,0x43,0x5f,
15018   0x67,0xf4,0x43,0xa9,0x98,0x87,0x43,0xd4,0x1d,0xf4,0x43,0x08,0x5c,0x25,0x8a,0x43,0xcf,0x16,0xef,0x43,
15019   0x46,0xaa,0x8d,0x43,0x5a,0x3c,0xe9,0x43,0x19,0x6c,0x88,0x43,0x53,0x5e,0xe7,0x43,0x08,0xc4,0x02,0x85,
15020   0x43,0x96,0x0b,0xe7,0x43,0x85,0x2c,0x82,0x43,0x83,0x67,0xe7,0x43,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,
15021   0x43,0x07,0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x09,0x06,0xfd,0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,
15022   0x08,0xfa,0x6c,0x78,0x43,0xd1,0xc2,0xe0,0x43,0x25,0x5c,0x6c,0x43,0x25,0x44,0xe8,0x43,0x1d,0xe5,0x7f,
15023   0x43,0x87,0x4a,0xe6,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,0x43,0x08,0xa6,0x27,0x7b,0x43,0x91,
15024   0x28,0xe8,0x43,0xbc,0xa2,0x77,0x43,0xb0,0x8d,0xe8,0x43,0xc6,0x68,0x75,0x43,0x57,0x4d,0xe8,0x43,0x08,
15025   0xe0,0xd2,0x72,0x43,0xab,0x9e,0xe7,0x43,0x50,0x9a,0x71,0x43,0x2a,0x27,0xe7,0x43,0xea,0x98,0x70,0x43,
15026   0x57,0x35,0xe4,0x43,0x08,0x94,0x3b,0x6f,0x43,0x14,0x7c,0xe2,0x43,0xff,0x13,0x6d,0x43,0x06,0xbb,0xe1,
15027   0x43,0xcf,0xfe,0x6a,0x43,0x06,0xbb,0xe1,0x43,0x08,0x44,0x9d,0x66,0x43,0x77,0x8e,0xe2,0x43,0x3b,0xef,
15028   0x6c,0x43,0x91,0x10,0xe4,0x43,0xfd,0x24,0x6c,0x43,0xb0,0x81,0xe6,0x43,0x08,0x96,0x23,0x6b,0x43,0xee,
15029   0x57,0xe9,0x43,0xca,0x0f,0x6a,0x43,0x5f,0x37,0xec,0x43,0x55,0x71,0x6e,0x43,0x9f,0x01,0xed,0x43,0x08,
15030   0xdb,0xfb,0x75,0x43,0x3b,0xef,0xec,0x43,0x09,0x3a,0x7b,0x43,0xb0,0xa5,0xec,0x43,0x1d,0xe5,0x7f,0x43,
15031   0x91,0x40,0xec,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x08,0xa9,0x16,0x7c,0x43,0xb0,0xb1,
15032   0xee,0x43,0x47,0xec,0x77,0x43,0xd9,0xe8,0xee,0x43,0x1e,0x9d,0x73,0x43,0xcf,0x16,0xef,0x43,0x08,0x0e,
15033   0xc9,0x6b,0x43,0xee,0x7b,0xef,0x43,0x7e,0x90,0x6a,0x43,0xfd,0x30,0xee,0x43,0x01,0xfc,0x68,0x43,0x4e,
15034   0x93,0xec,0x43,0x08,0x31,0xf9,0x66,0x43,0x4e,0x87,0xea,0x43,0x31,0x11,0x6b,0x43,0xd4,0xd5,0xe7,0x43,
15035   0xd9,0xc4,0x68,0x43,0xd4,0xc9,0xe5,0x43,0x08,0xe5,0x79,0x67,0x43,0x77,0x9a,0xe4,0x43,0x44,0x9d,0x66,
15036   0x43,0xab,0x86,0xe3,0x43,0x7e,0x78,0x66,0x43,0x0b,0xaa,0xe2,0x43,0x07,0x7e,0x78,0x66,0x43,0x57,0x29,
15037   0xe2,0x43,0x08,0xa7,0xaf,0x66,0x43,0xbe,0x1e,0xe1,0x43,0x87,0x56,0x68,0x43,0x77,0x82,0xe0,0x43,0xfd,
15038   0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,0x09,0x06,0xc4,0x41,0xbf,0x43,0x85,0xc0,0x72,0x42,0x08,0x73,0xdf,
15039   0xc0,0x43,0xf4,0x76,0x72,0x42,0x97,0x33,0xc2,0x43,0x85,0xc0,0x72,0x42,0xb2,0xb5,0xc3,0x43,0x64,0x56,
15040   0x75,0x42,0x08,0x03,0x24,0xc4,0x43,0x5e,0x7f,0x78,0x42,0xfa,0x51,0xc4,0x43,0x01,0x85,0x7c,0x42,0x5c,
15041   0x64,0xc4,0x43,0xa0,0xb3,0x80,0x42,0x07,0x5c,0x64,0xc4,0x43,0x10,0x93,0x83,0x42,0x08,0xc8,0x48,0xc4,
15042   0x43,0x1c,0x78,0x8a,0x42,0x27,0x6c,0xc3,0x43,0xaf,0xcf,0x94,0x42,0x23,0x7d,0xc2,0x43,0x99,0x9c,0xa4,
15043   0x42,0x08,0x3d,0xe7,0xbf,0x43,0xfb,0xfd,0xb5,0x42,0xb3,0x9d,0xbf,0x43,0x88,0x17,0xae,0x42,0xc4,0x41,
15044   0xbf,0x43,0x69,0x76,0xa3,0x42,0x07,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x08,0x4f,0x8b,0xbf,0x43,
15045   0xed,0x81,0x91,0x42,0xe4,0xa6,0xbf,0x43,0x5d,0x61,0x94,0x42,0xfa,0x39,0xc0,0x43,0x3b,0x49,0x9d,0x42,
15046   0x08,0x2b,0x43,0xc0,0x43,0x28,0xed,0xa9,0x42,0x61,0x3b,0xc1,0x43,0x00,0x9e,0xa5,0x42,0xe4,0xb2,0xc1,
15047   0x43,0x5d,0x91,0x9c,0x42,0x08,0x78,0xce,0xc1,0x43,0xfd,0x36,0x90,0x42,0x22,0x89,0xc4,0x43,0x81,0x72,
15048   0x86,0x42,0xae,0xc6,0xc2,0x43,0xa0,0xb3,0x80,0x42,0x08,0x54,0x86,0xc2,0x43,0x58,0xd1,0x7e,0x42,0x30,
15049   0x32,0xc1,0x43,0xce,0x5e,0x7b,0x42,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x07,0xc4,0x41,0xbf,0x43,
15050   0x85,0xc0,0x72,0x42,0x09,0x06,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x08,0x35,0xfd,0xbb,0x43,0xa4,
15051   0xa1,0x5c,0x42,0x5e,0x34,0xbc,0x43,0x9d,0x2a,0x70,0x42,0x5e,0x40,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,
15052   0x4c,0x9c,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,0xef,0xbe,0x43,0x0e,0x0a,0x73,0x42,0xc4,0x41,0xbf,0x43,
15053   0x85,0xc0,0x72,0x42,0x07,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x08,0xcd,0x13,0xbf,0x43,0xe8,0xf1,
15054   0x7b,0x42,0xd6,0xe5,0xbe,0x43,0x71,0x3b,0x7c,0x42,0xdf,0xb7,0xbe,0x43,0x71,0x3b,0x7c,0x42,0x08,0x08,
15055   0xe3,0xbc,0x43,0xa4,0x61,0x7d,0x42,0x28,0x3c,0xbb,0x43,0x91,0x45,0x69,0x42,0x28,0x3c,0xbb,0x43,0x58,
15056   0x71,0x6e,0x42,0x08,0xce,0xfb,0xba,0x43,0xd5,0x35,0x78,0x42,0x59,0x45,0xbb,0x43,0x58,0x23,0x82,0x42,
15057   0xa1,0xe1,0xbb,0x43,0xd7,0xbe,0x88,0x42,0x08,0xc9,0x18,0xbc,0x43,0xaf,0x9f,0x8c,0x42,0x1e,0x76,0xbd,
15058   0x43,0x51,0x7c,0x8d,0x42,0xd6,0xe5,0xbe,0x43,0xf4,0x58,0x8e,0x42,0x08,0x9c,0x0a,0xbf,0x43,0x45,0xc7,
15059   0x8e,0x42,0x30,0x26,0xbf,0x43,0x96,0x35,0x8f,0x42,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x07,0xc4,
15060   0x41,0xbf,0x43,0x69,0x76,0xa3,0x42,0x08,0x08,0xef,0xbe,0x43,0xb1,0xd6,0x99,0x42,0xe8,0x89,0xbe,0x43,
15061   0xde,0xc5,0x8d,0x42,0xc0,0x46,0xbc,0x43,0xc2,0x5b,0x90,0x42,0x08,0x9c,0xf2,0xba,0x43,0x86,0x80,0x90,
15062   0x42,0xf2,0x43,0xba,0x43,0xe8,0x73,0x87,0x42,0x8f,0x31,0xba,0x43,0xb6,0xf4,0x7d,0x42,0x07,0x8f,0x31,
15063   0xba,0x43,0x21,0xc6,0x76,0x42,0x08,0xc0,0x3a,0xba,0x43,0x5f,0x48,0x6b,0x42,0xae,0x96,0xba,0x43,0xe3,
15064   0x83,0x61,0x42,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x09,0x06,0xea,0x74,0xea,0x43,0x61,0x44,0x93,
15065   0x43,0x08,0x24,0x5c,0xec,0x43,0x31,0x3b,0x93,0x43,0xfb,0x30,0xee,0x43,0x93,0x4d,0x93,0x43,0x0d,0xe1,
15066   0xef,0x43,0x80,0xa9,0x93,0x43,0x08,0x8f,0x58,0xf0,0x43,0xd1,0x17,0x94,0x43,0xb7,0x8f,0xf0,0x43,0x10,
15067   0xe2,0x94,0x43,0xea,0x98,0xf0,0x43,0xa9,0xec,0x95,0x43,0x07,0xea,0x98,0xf0,0x43,0x38,0x25,0x97,0x43,
15068   0x08,0x23,0x74,0xf0,0x43,0x9f,0x32,0x9a,0x43,0x5a,0x60,0xef,0x43,0x53,0xcb,0x9e,0x43,0x2d,0x3a,0xee,
15069   0x43,0xfd,0x91,0xa3,0x43,0x08,0xa2,0xf0,0xed,0x43,0xdd,0x38,0xa5,0x43,0x17,0xa7,0xed,0x43,0xbe,0xdf,
15070   0xa6,0x43,0x5a,0x54,0xed,0x43,0x9f,0x86,0xa8,0x43,0x08,0xfc,0x24,0xec,0x43,0xca,0xc4,0xad,0x43,0x48,
15071   0xa4,0xeb,0x43,0x40,0x6f,0xab,0x43,0x28,0x3f,0xeb,0x43,0x1c,0x0f,0xa8,0x43,0x08,0x1f,0x6d,0xeb,0x43,
15072   0x72,0x48,0xa3,0x43,0x67,0x09,0xec,0x43,0xd1,0x53,0x9e,0x43,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,0x43,
15073   0x07,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x08,0x7e,0x90,0xea,0x43,0x8a,0x9f,0x99,0x43,0x12,0xac,
15074   0xea,0x43,0xbc,0xa8,0x99,0x43,0xa7,0xc7,0xea,0x43,0xbc,0xa8,0x99,0x43,0x08,0x51,0x76,0xeb,0x43,0x9f,
15075   0x32,0x9a,0x43,0x5e,0x37,0xec,0x43,0x49,0xed,0x9c,0x43,0xb0,0xa5,0xec,0x43,0x2a,0xa0,0xa0,0x43,0x08,
15076   0x09,0xe6,0xec,0x43,0xd1,0x77,0xa4,0x43,0x28,0x4b,0xed,0x43,0x61,0xa4,0xa3,0x43,0xab,0xc2,0xed,0x43,
15077   0x8e,0xb2,0xa0,0x43,0x08,0x70,0xe7,0xed,0x43,0xde,0x08,0x9d,0x43,0x87,0x86,0xf0,0x43,0x2f,0x53,0x97,
15078   0x43,0x87,0x7a,0xee,0x43,0xec,0x99,0x95,0x43,0x08,0xca,0x27,0xee,0x43,0xff,0x3d,0x95,0x43,0x74,0xca,
15079   0xec,0x43,0x55,0x8f,0x94,0x43,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x07,0xea,0x74,0xea,0x43,0x61,
15080   0x44,0x93,0x43,0x09,0x06,0x05,0xd3,0xe5,0x43,0x19,0x9c,0x90,0x43,0x08,0x09,0xc2,0xe6,0x43,0xd1,0xff,
15081   0x8f,0x43,0x4d,0x6f,0xe6,0x43,0x74,0xe8,0x92,0x43,0x3b,0xd7,0xe8,0x43,0xc3,0x56,0x93,0x43,0x08,0x1f,
15082   0x61,0xe9,0x43,0x93,0x4d,0x93,0x43,0x05,0xeb,0xe9,0x43,0x93,0x4d,0x93,0x43,0xea,0x74,0xea,0x43,0x61,
15083   0x44,0x93,0x43,0x07,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x08,0x24,0x50,0xea,0x43,0xe7,0xaa,0x94,
15084   0x43,0x2d,0x22,0xea,0x43,0xe7,0xaa,0x94,0x43,0x36,0xf4,0xe9,0x43,0xe7,0xaa,0x94,0x43,0x08,0xa2,0xcc,
15085   0xe7,0x43,0xe0,0xd8,0x94,0x43,0xd4,0xc9,0xe5,0x43,0x19,0xa8,0x92,0x43,0xd4,0xc9,0xe5,0x43,0x27,0x69,
15086   0x93,0x43,0x08,0x17,0x77,0xe5,0x43,0xe0,0xd8,0x94,0x43,0x67,0xe5,0xe5,0x43,0x47,0xda,0x95,0x43,0x43,
15087   0x9d,0xe6,0x43,0xe2,0xd3,0x97,0x43,0x08,0x9d,0xdd,0xe6,0x43,0xad,0xe7,0x98,0x43,0x09,0xce,0xe8,0x43,
15088   0xff,0x55,0x99,0x43,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x07,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,
15089   0x43,0x08,0x71,0xcf,0xe9,0x43,0x53,0xb3,0x9a,0x43,0xa7,0xbb,0xe8,0x43,0xdb,0x0d,0x9a,0x43,0xc6,0x14,
15090   0xe7,0x43,0xdb,0x0d,0x9a,0x43,0x08,0x48,0x80,0xe5,0x43,0xdb,0x0d,0x9a,0x43,0x0a,0xb6,0xe4,0x43,0xc3,
15091   0x6e,0x97,0x43,0x76,0x9a,0xe4,0x43,0x74,0xf4,0x94,0x43,0x07,0x76,0x9a,0xe4,0x43,0x79,0xd7,0x93,0x43,
15092   0x08,0xd8,0xac,0xe4,0x43,0x66,0x27,0x92,0x43,0x29,0x1b,0xe5,0x43,0xe0,0xc0,0x90,0x43,0x05,0xd3,0xe5,
15093   0x43,0x19,0x9c,0x90,0x43,0x09,0x06,0x1b,0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x08,0x71,0x0b,0xf4,0x42,
15094   0x00,0x0e,0x8d,0x42,0x8c,0x0f,0x01,0x43,0x3e,0xc0,0x89,0x42,0xf3,0x28,0x06,0x43,0x48,0x9e,0x8b,0x42,
15095   0x08,0x15,0x89,0x09,0x43,0x00,0x0e,0x8d,0x42,0xe0,0x9c,0x0a,0x43,0xc1,0x8b,0x98,0x42,0xa6,0xc1,0x0a,
15096   0x43,0x02,0xa5,0xaa,0x42,0x07,0xa6,0xc1,0x0a,0x43,0xf9,0xf6,0xb0,0x42,0x08,0xa6,0xc1,0x0a,0x43,0x47,
15097   0x8e,0xb4,0x42,0x42,0xaf,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0xe0,0x9c,0x0a,0x43,0xba,0x74,0xbc,0x42,0x08,
15098   0xa1,0xd2,0x09,0x43,0x40,0x47,0xd0,0x42,0x0d,0xab,0x07,0x43,0x91,0xb5,0xd0,0x42,0x3b,0xb9,0x04,0x43,
15099   0xec,0x71,0xba,0x42,0x08,0xe5,0x5b,0x03,0x43,0xe3,0x33,0xa8,0x42,0x63,0xd8,0x00,0x43,0xce,0x70,0x9f,
15100   0x42,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x07,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,0x42,0x08,0xed,
15101   0x6f,0xed,0x42,0x73,0x24,0x9d,0x42,0xd8,0x0c,0xf5,0x42,0x99,0x6c,0x9c,0x42,0x27,0xab,0xfd,0x42,0xea,
15102   0xda,0x9c,0x42,0x08,0x36,0xca,0x03,0x43,0x2b,0x94,0x9e,0x42,0x68,0xc7,0x01,0x43,0x8f,0xbe,0xa2,0x42,
15103   0xfa,0x06,0x08,0x43,0x73,0xb4,0xb5,0x42,0x08,0x8e,0x2e,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0x9d,0xe3,0x08,
15104   0x43,0xd7,0x1e,0x99,0x42,0x28,0x15,0x05,0x43,0x32,0x3b,0x93,0x42,0x08,0x63,0xf0,0x04,0x43,0x70,0xed,
15105   0x8f,0x42,0x71,0x0b,0xf4,0x42,0x32,0x3b,0x93,0x42,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x07,0x1b,
15106   0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x09,0x06,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,0x08,0x8e,0x55,
15107   0xc0,0x42,0xb8,0x4d,0x86,0x42,0x60,0xbf,0xd7,0x42,0x3e,0xf0,0x91,0x42,0x63,0xf6,0xe4,0x42,0x70,0xed,
15108   0x8f,0x42,0x08,0x7a,0x89,0xe5,0x42,0xac,0xc8,0x8f,0x42,0xcc,0xf7,0xe5,0x42,0xac,0xc8,0x8f,0x42,0x1b,
15109   0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x07,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x08,0x63,0xf6,0xe4,
15110   0x42,0x3b,0x19,0x95,0x42,0xe6,0x61,0xe3,0x42,0x00,0x3e,0x95,0x42,0xf4,0x16,0xe2,0x42,0xc4,0x62,0x95,
15111   0x42,0x08,0x6e,0x74,0xd6,0x42,0x15,0xd1,0x95,0x42,0x97,0x63,0xca,0x42,0xaf,0xcf,0x94,0x42,0xfb,0x2d,
15112   0xbe,0x42,0x86,0x80,0x90,0x42,0x08,0x97,0x03,0xba,0x42,0xce,0x10,0x8f,0x42,0x5e,0x28,0xba,0x42,0x3e,
15113   0xf0,0x91,0x42,0xf2,0x4f,0xbc,0x42,0x45,0xf7,0x96,0x42,0x08,0x27,0x54,0xbf,0x42,0x73,0x24,0x9d,0x42,
15114   0xa5,0xe8,0xc0,0x42,0x86,0xe0,0xa0,0x42,0xe4,0xca,0xc5,0x42,0xed,0x11,0xaa,0x42,0x08,0x54,0xaa,0xc8,
15115   0x42,0x86,0x40,0xb1,0x42,0x59,0x81,0xc5,0x42,0xa1,0x11,0xc4,0x42,0x3e,0xe7,0xbf,0x42,0xfb,0x8d,0xce,
15116   0x42,0x08,0xb4,0x6d,0xb7,0x42,0x30,0xc2,0xd9,0x42,0x46,0xf5,0xc9,0x42,0xdf,0x53,0xd9,0x42,0x38,0x40,
15117   0xcb,0x42,0x62,0x8f,0xcf,0x42,0x08,0x7d,0xf9,0xcc,0x42,0xec,0xa1,0xc2,0x42,0x07,0x43,0xcd,0x42,0x6c,
15118   0xdd,0xb8,0x42,0x2b,0x8b,0xcc,0x42,0x92,0xf5,0xaf,0x42,0x08,0xf9,0x8d,0xce,0x42,0x41,0x57,0xa7,0x42,
15119   0x5b,0xb8,0xd2,0x42,0xae,0x2f,0xa5,0x42,0x18,0x2f,0xd9,0x42,0x13,0x2a,0xa1,0x42,0x08,0x41,0x7e,0xdd,
15120   0x42,0xe3,0x03,0xa0,0x42,0x2e,0xf2,0xe1,0x42,0x7c,0x02,0x9f,0x42,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,
15121   0x42,0x07,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x08,0x4d,0x63,0xe4,0x42,0x00,0x9e,0xa5,0x42,0xf4,
15122   0x16,0xe2,0x42,0x15,0x31,0xa6,0x42,0x99,0xca,0xdf,0x42,0x2b,0xc4,0xa6,0x42,0x08,0xc0,0x82,0xc6,0x42,
15123   0xc4,0xc2,0xa5,0x42,0x57,0xe1,0xd5,0x42,0x91,0xb5,0xd0,0x42,0x54,0xda,0xd0,0x42,0x97,0x93,0xd2,0x42,
15124   0x08,0x9c,0x3a,0xc7,0x42,0x17,0x58,0xdc,0x42,0x9c,0x0a,0xbf,0x42,0x6e,0xa4,0xde,0x42,0x90,0x25,0xb8,
15125   0x42,0xdf,0x53,0xd9,0x42,0x08,0x59,0x21,0xb5,0x42,0xf2,0xdf,0xd4,0x42,0x51,0x43,0xb3,0x42,0x91,0xb5,
15126   0xd0,0x42,0xc5,0x29,0xbb,0x42,0x0e,0x1a,0xca,0x42,0x08,0x65,0x36,0xc4,0x42,0xd0,0x07,0xbd,0x42,0x3e,
15127   0xe7,0xbf,0x42,0x37,0x09,0xbe,0x42,0x0c,0xea,0xc1,0x42,0xcd,0xd0,0xaf,0x42,0x08,0x2b,0x5b,0xc4,0x42,
15128   0x18,0x08,0xa3,0x42,0x67,0xa6,0xab,0x42,0x99,0x3c,0x94,0x42,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,
15129   0x09,];
15130 
15131 private struct ThePath {
15132 public:
15133   enum Command {
15134     Bounds, // always first, has 4 args (x0, y0, x1, y1)
15135     StrokeMode,
15136     FillMode,
15137     StrokeFillMode,
15138     NormalStroke,
15139     ThinStroke,
15140     MoveTo,
15141     LineTo,
15142     CubicTo, // cubic bezier
15143     EndPath,
15144   }
15145 
15146 public:
15147   const(ubyte)[] path;
15148   uint ppos;
15149 
15150 public:
15151   this (const(void)[] apath) pure nothrow @trusted @nogc {
15152     path = cast(const(ubyte)[])apath;
15153   }
15154 
15155   @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (ppos >= path.length); }
15156 
15157   Command getCommand () nothrow @trusted @nogc {
15158     pragma(inline, true);
15159     if (ppos >= cast(uint)path.length) assert(0, "invalid path");
15160     return cast(Command)(path.ptr[ppos++]);
15161   }
15162 
15163   // number of (x,y) pairs for this command
15164   static int argCount (in Command cmd) nothrow @safe @nogc {
15165     version(aliced) pragma(inline, true);
15166          if (cmd == Command.Bounds) return 2;
15167     else if (cmd == Command.MoveTo || cmd == Command.LineTo) return 1;
15168     else if (cmd == Command.CubicTo) return 3;
15169     else return 0;
15170   }
15171 
15172   void skipArgs (int argc) nothrow @trusted @nogc {
15173     pragma(inline, true);
15174     ppos += cast(uint)(float.sizeof*2*argc);
15175   }
15176 
15177   float getFloat () nothrow @trusted @nogc {
15178     pragma(inline, true);
15179     if (ppos >= cast(uint)path.length || cast(uint)path.length-ppos < float.sizeof) assert(0, "invalid path");
15180     version(LittleEndian) {
15181       float res = *cast(const(float)*)(&path.ptr[ppos]);
15182       ppos += cast(uint)float.sizeof;
15183       return res;
15184     } else {
15185       static assert(float.sizeof == 4);
15186       uint xp = path.ptr[ppos]|(path.ptr[ppos+1]<<8)|(path.ptr[ppos+2]<<16)|(path.ptr[ppos+3]<<24);
15187       ppos += cast(uint)float.sizeof;
15188       return *cast(const(float)*)(&xp);
15189     }
15190   }
15191 }
15192 
15193 // this will add baphomet's background path to the current NanoVega path, so you can fill it.
15194 public void addBaphometBack (NVGContext nvg, float ofsx=0, float ofsy=0, float scalex=1, float scaley=1) nothrow @trusted @nogc {
15195   if (nvg is null) return;
15196 
15197   auto path = ThePath(baphometPath);
15198 
15199   float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
15200   float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
15201 
15202   bool inPath = false;
15203   while (!path.empty) {
15204     auto cmd = path.getCommand();
15205     switch (cmd) {
15206       case ThePath.Command.MoveTo:
15207         inPath = true;
15208         immutable float ex = getScaledX();
15209         immutable float ey = getScaledY();
15210         nvg.moveTo(ex, ey);
15211         break;
15212       case ThePath.Command.LineTo:
15213         inPath = true;
15214         immutable float ex = getScaledX();
15215         immutable float ey = getScaledY();
15216         nvg.lineTo(ex, ey);
15217         break;
15218       case ThePath.Command.CubicTo: // cubic bezier
15219         inPath = true;
15220         immutable float x1 = getScaledX();
15221         immutable float y1 = getScaledY();
15222         immutable float x2 = getScaledX();
15223         immutable float y2 = getScaledY();
15224         immutable float ex = getScaledX();
15225         immutable float ey = getScaledY();
15226         nvg.bezierTo(x1, y1, x2, y2, ex, ey);
15227         break;
15228       case ThePath.Command.EndPath:
15229         if (inPath) return;
15230         break;
15231       default:
15232         path.skipArgs(path.argCount(cmd));
15233         break;
15234     }
15235   }
15236 }
15237 
15238 // this will add baphomet's pupil paths to the current NanoVega path, so you can fill it.
15239 public void addBaphometPupils(bool left=true, bool right=true) (NVGContext nvg, float ofsx=0, float ofsy=0, float scalex=1, float scaley=1) nothrow @trusted @nogc {
15240   // pupils starts with "fill-and-stroke" mode
15241   if (nvg is null) return;
15242 
15243   auto path = ThePath(baphometPath);
15244 
15245   float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
15246   float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
15247 
15248   bool inPath = false;
15249   bool pupLeft = true;
15250   while (!path.empty) {
15251     auto cmd = path.getCommand();
15252     switch (cmd) {
15253       case ThePath.Command.StrokeFillMode: inPath = true; break;
15254       case ThePath.Command.MoveTo:
15255         if (!inPath) goto default;
15256         static if (!left) { if (pupLeft) goto default; }
15257         static if (!right) { if (!pupLeft) goto default; }
15258         immutable float ex = getScaledX();
15259         immutable float ey = getScaledY();
15260         nvg.moveTo(ex, ey);
15261         break;
15262       case ThePath.Command.LineTo:
15263         if (!inPath) goto default;
15264         static if (!left) { if (pupLeft) goto default; }
15265         static if (!right) { if (!pupLeft) goto default; }
15266         immutable float ex = getScaledX();
15267         immutable float ey = getScaledY();
15268         nvg.lineTo(ex, ey);
15269         break;
15270       case ThePath.Command.CubicTo: // cubic bezier
15271         if (!inPath) goto default;
15272         static if (!left) { if (pupLeft) goto default; }
15273         static if (!right) { if (!pupLeft) goto default; }
15274         immutable float x1 = getScaledX();
15275         immutable float y1 = getScaledY();
15276         immutable float x2 = getScaledX();
15277         immutable float y2 = getScaledY();
15278         immutable float ex = getScaledX();
15279         immutable float ey = getScaledY();
15280         nvg.bezierTo(x1, y1, x2, y2, ex, ey);
15281         break;
15282       case ThePath.Command.EndPath:
15283         if (inPath) {
15284           if (pupLeft) pupLeft = false; else return;
15285         }
15286         break;
15287       default:
15288         path.skipArgs(path.argCount(cmd));
15289         break;
15290     }
15291   }
15292 }
15293 
15294 // mode: 'f' to allow fills; 's' to allow strokes; 'w' to allow stroke widths; 'c' to replace fills with strokes
15295 public void renderBaphomet(string mode="fs") (NVGContext nvg, float ofsx=0, float ofsy=0, float scalex=1, float scaley=1) nothrow @trusted @nogc {
15296   template hasChar(char ch, string s) {
15297          static if (s.length == 0) enum hasChar = false;
15298     else static if (s[0] == ch) enum hasChar = true;
15299     else enum hasChar = hasChar!(ch, s[1..$]);
15300   }
15301   enum AllowStroke = hasChar!('s', mode);
15302   enum AllowFill = hasChar!('f', mode);
15303   enum AllowWidth = hasChar!('w', mode);
15304   enum Contour = hasChar!('c', mode);
15305   //static assert(AllowWidth || AllowFill);
15306 
15307   if (nvg is null) return;
15308 
15309   auto path = ThePath(baphometPath);
15310 
15311   float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
15312   float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
15313 
15314   int mode = 0;
15315   int sw = ThePath.Command.NormalStroke;
15316   nvg.beginPath();
15317   while (!path.empty) {
15318     auto cmd = path.getCommand();
15319     switch (cmd) {
15320       case ThePath.Command.StrokeMode: mode = ThePath.Command.StrokeMode; break;
15321       case ThePath.Command.FillMode: mode = ThePath.Command.FillMode; break;
15322       case ThePath.Command.StrokeFillMode: mode = ThePath.Command.StrokeFillMode; break;
15323       case ThePath.Command.NormalStroke: sw = ThePath.Command.NormalStroke; break;
15324       case ThePath.Command.ThinStroke: sw = ThePath.Command.ThinStroke; break;
15325       case ThePath.Command.MoveTo:
15326         immutable float ex = getScaledX();
15327         immutable float ey = getScaledY();
15328         nvg.moveTo(ex, ey);
15329         break;
15330       case ThePath.Command.LineTo:
15331         immutable float ex = getScaledX();
15332         immutable float ey = getScaledY();
15333         nvg.lineTo(ex, ey);
15334         break;
15335       case ThePath.Command.CubicTo: // cubic bezier
15336         immutable float x1 = getScaledX();
15337         immutable float y1 = getScaledY();
15338         immutable float x2 = getScaledX();
15339         immutable float y2 = getScaledY();
15340         immutable float ex = getScaledX();
15341         immutable float ey = getScaledY();
15342         nvg.bezierTo(x1, y1, x2, y2, ex, ey);
15343         break;
15344       case ThePath.Command.EndPath:
15345         if (mode == ThePath.Command.FillMode || mode == ThePath.Command.StrokeFillMode) {
15346           static if (AllowFill || Contour) {
15347             static if (Contour) {
15348               if (mode == ThePath.Command.FillMode) { nvg.strokeWidth = 1; nvg.stroke(); }
15349             } else {
15350               nvg.fill();
15351             }
15352           }
15353         }
15354         if (mode == ThePath.Command.StrokeMode || mode == ThePath.Command.StrokeFillMode) {
15355           static if (AllowStroke || Contour) {
15356             static if (AllowWidth) {
15357                    if (sw == ThePath.Command.NormalStroke) nvg.strokeWidth = 1;
15358               else if (sw == ThePath.Command.ThinStroke) nvg.strokeWidth = 0.5;
15359               else assert(0, "wtf?!");
15360             }
15361             nvg.stroke();
15362           }
15363         }
15364         nvg.newPath();
15365         break;
15366       default:
15367         path.skipArgs(path.argCount(cmd));
15368         break;
15369     }
15370   }
15371   nvg.newPath();
15372 }