iv.nanovega: added link to McSeem Bezier Rasteriser page
[iv.d.git] / nanovega / nanovega.d
blobf7bd6f9166f63131b2d5c3a52ed5b570317b62ea
1 //.
2 // Copyright (c) 2013 Mikko Mononen memon@inside.org
3 //
4 // This software is provided 'as-is', without any express or implied
5 // warranty. In no event will the authors be held liable for any damages
6 // arising from the use of this software.
7 // Permission is granted to anyone to use this software for any purpose,
8 // including commercial applications, and to alter it and redistribute it
9 // freely, subject to the following restrictions:
10 // 1. The origin of this software must not be misrepresented; you must not
11 // claim that you wrote the original software. If you use this software
12 // in a product, an acknowledgment in the product documentation would be
13 // appreciated but is not required.
14 // 2. Altered source versions must be plainly marked as such, and must not be
15 // misrepresented as being the original software.
16 // 3. This notice may not be removed or altered from any source distribution.
18 /* Invisible Vector Library
19 * ported by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
20 * Understanding is not required. Only obedience.
21 * yes, this D port is GPLed.
23 * This program is free software: you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation, either version 3 of the License, or
26 * (at your option) any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36 /**
37 The NanoVega API is modeled loosely on HTML5 canvas API.
38 If you know canvas, you're up to speed with NanoVega in no time.
41 Creating drawing context
42 ========================
44 The drawing context is created using platform specific constructor function.
46 ---
47 NVGContext vg = nvgCreateContext();
48 ---
51 Drawing shapes with NanoVega
52 ============================
54 Drawing a simple shape using NanoVega consists of four steps:
55 $(LIST
56 * begin a new shape,
57 * define the path to draw,
58 * set fill or stroke,
59 * and finally fill or stroke the path.
62 ---
63 vg.beginPath();
64 vg.rect(100, 100, 120, 30);
65 vg.fillColor(nvgRGBA(255, 192, 0, 255));
66 vg.fill();
67 ---
69 Calling [beginPath] will clear any existing paths and start drawing from blank slate.
70 There are number of number of functions to define the path to draw, such as rectangle,
71 rounded rectangle and ellipse, or you can use the common moveTo, lineTo, bezierTo and
72 arcTo API to compose the paths step by step.
75 Understanding Composite Paths
76 =============================
78 Because of the way the rendering backend is built in NanoVega, drawing a composite path,
79 that is path consisting from multiple paths defining holes and fills, is a bit more
80 involved. NanoVega uses non-zero filling rule and by default, and paths are wound in counter
81 clockwise order. Keep that in mind when drawing using the low level draw API. In order to
82 wind one of the predefined shapes as a hole, you should call `pathWinding(NVGSolidity.Hole)`,
83 or `pathWinding(NVGSolidity.Solid)` $(B after) defining the path.
85 ---
86 vg.beginPath();
87 vg.rect(100, 100, 120, 30);
88 vg.circle(120, 120, 5);
89 vg.pathWinding(NVGSolidity.Hole); // mark circle as a hole
90 vg.fillColor(nvgRGBA(255, 192, 0, 255));
91 vg.fill();
92 ---
95 Rendering is wrong, what to do?
96 ===============================
98 $(LIST
99 * make sure you have created NanoVega context using [nvgCreateContext] call
100 * make sure you have initialised OpenGL with $(B stencil buffer)
101 * make sure you have cleared stencil buffer
102 * make sure all rendering calls happen between [beginFrame] and [endFrame]
103 * to enable more checks for OpenGL errors, add `NVGContextFlag.Debug` flag to [nvgCreateContext]
107 OpenGL state touched by the backend
108 ===================================
110 The OpenGL back-end touches following states:
112 When textures are uploaded or updated, the following pixel store is set to defaults:
113 `GL_UNPACK_ALIGNMENT`, `GL_UNPACK_ROW_LENGTH`, `GL_UNPACK_SKIP_PIXELS`, `GL_UNPACK_SKIP_ROWS`.
114 Texture binding is also affected. Texture updates can happen when the user loads images,
115 or when new font glyphs are added. Glyphs are added as needed between calls to [beginFrame]
116 and [endFrame].
118 The data for the whole frame is buffered and flushed in [endFrame].
119 The following code illustrates the OpenGL state touched by the rendering code:
122 glUseProgram(prog);
123 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
124 glEnable(GL_CULL_FACE);
125 glCullFace(GL_BACK);
126 glFrontFace(GL_CCW);
127 glEnable(GL_BLEND);
128 glDisable(GL_DEPTH_TEST);
129 glDisable(GL_SCISSOR_TEST);
130 glDisable(GL_COLOR_LOGIC_OP);
131 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
132 glStencilMask(0xffffffff);
133 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
134 glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
135 glActiveTexture(GL_TEXTURE1);
136 glActiveTexture(GL_TEXTURE0);
137 glBindBuffer(GL_UNIFORM_BUFFER, buf);
138 glBindVertexArray(arr);
139 glBindBuffer(GL_ARRAY_BUFFER, buf);
140 glBindTexture(GL_TEXTURE_2D, tex);
141 glUniformBlockBinding(... , GLNVG_FRAG_BINDING);
144 Symbol_groups:
146 context_management =
147 ## Context Management
149 Functions to create and destory NanoVega context.
151 frame_management =
152 ## Frame Management
154 To start drawing with NanoVega context, you have to "begin frame", and then
155 "end frame" to flush your rendering commands to GPU.
157 composite_operation =
158 ## Composite Operation
160 The composite operations in NanoVega are modeled after HTML Canvas API, and
161 the blend func is based on OpenGL (see corresponding manuals for more info).
162 The colors in the blending state have premultiplied alpha.
164 color_utils =
165 ## Color Utils
167 Colors in NanoVega are stored as ARGB. Zero alpha means "transparent color".
169 matrices =
170 ## Matrices and Transformations
172 The paths, gradients, patterns and scissor region are transformed by an transformation
173 matrix at the time when they are passed to the API.
174 The current transformation matrix is an affine matrix:
176 ----------------------
177 [sx kx tx]
178 [ky sy ty]
179 [ 0 0 1]
180 ----------------------
182 Where: (sx, sy) define scaling, (kx, ky) skewing, and (tx, ty) translation.
183 The last row is assumed to be (0, 0, 1) and is not stored.
185 Apart from [resetTransform], each transformation function first creates
186 specific transformation matrix and pre-multiplies the current transformation by it.
188 Current coordinate system (transformation) can be saved and restored using [save] and [restore].
190 The following functions can be used to make calculations on 2x3 transformation matrices.
191 A 2x3 matrix is represented as float[6].
193 state_handling =
194 ## State Handling
196 NanoVega contains state which represents how paths will be rendered.
197 The state contains transform, fill and stroke styles, text and font styles,
198 and scissor clipping.
200 render_styles =
201 ## Render Styles
203 Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.
204 Solid color is simply defined as a color value, different kinds of paints can be created
205 using [linearGradient], [boxGradient], [radialGradient] and [imagePattern].
207 Current render style can be saved and restored using [save] and [restore].
209 Note that if you want "almost perfect" pixel rendering, you should set aspect ratio to 1,
210 and use `integerCoord+0.5f` as pixel coordinates.
212 render_transformations =
213 ## Render Transformations
215 Transformation matrix management for the current rendering style. Transformations are applied in
216 backwards order. I.e. if you first translate, and then rotate, your path will be rotated around
217 it's origin, and then translated to the destination point.
219 scissoring =
220 ## Scissoring
222 Scissoring allows you to clip the rendering into a rectangle. This is useful for various
223 user interface cases like rendering a text edit or a timeline.
225 images =
226 ## Images
228 NanoVega allows you to load image files in various formats (if arsd loaders are in place) to be used for rendering.
229 In addition you can upload your own image.
230 The parameter imageFlagsList is a list of flags defined in [NVGImageFlag].
232 If you will use your image as fill pattern, it will be scaled by default. To make it repeat, pass
233 [NVGImageFlag.RepeatX] and [NVGImageFlag.RepeatY] flags to image creation function respectively.
235 paints =
236 ## Paints
238 NanoVega supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.
239 These can be used as paints for strokes and fills.
241 gpu_affine =
242 ## Render-Time Affine Transformations
244 It is possible to set affine transformation matrix for GPU. That matrix will
245 be applied by the shader code. This can be used to quickly translate and rotate
246 saved paths. Call this $(B only) between [beginFrame] and [endFrame].
248 Note that [beginFrame] resets this matrix to identity one.
250 $(WARNING Don't use this for scaling or skewing, or your image will be heavily distorted!)
252 paths =
253 ## Paths
255 Drawing a new shape starts with [beginPath], it clears all the currently defined paths.
256 Then you define one or more paths and sub-paths which describe the shape. The are functions
257 to draw common shapes like rectangles and circles, and lower level step-by-step functions,
258 which allow to define a path curve by curve.
260 NanoVega uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise
261 winding and holes should have counter clockwise order. To specify winding of a path you can
262 call [pathWinding]. This is useful especially for the common shapes, which are drawn CCW.
264 Finally you can fill the path using current fill style by calling [fill], and stroke it
265 with current stroke style by calling [stroke].
267 The curve segments and sub-paths are transformed by the current transform.
269 picking_api =
270 ## Picking API
272 This is picking API that works directly on paths, without rasterizing them first.
274 [beginFrame] resets picking state. Then you can create paths as usual, but
275 there is a possibility to perform hit checks $(B before) rasterizing a path.
276 Call either id assigning functions ([currFillHitId]/[currStrokeHitId]), or
277 immediate hit test functions ([hitTestCurrFill]/[hitTestCurrStroke])
278 before rasterizing (i.e. calling [fill] or [stroke]) to perform hover
279 effects, for example.
281 Also note that picking API is ignoring GPU affine transformation matrix.
282 You can "untransform" picking coordinates before checking with [gpuUntransformPoint].
284 $(WARNING Picking API completely ignores clipping. If you want to check for
285 clip regions, you have to manuall register them as fill/stroke pathes,
286 and perform the necessary logic. See [hitTestForId] function.)
288 clipping =
289 ## Cliping with paths
291 If scissoring is not enough for you, you can clip rendering with arbitrary path,
292 or with combination of paths. Clip region is saved by [save] and restored by
293 [restore] NanoVega functions. You can combine clip paths with various logic
294 operations, see [NVGClipMode].
296 Note that both [clip] and [clipStroke] are ignoring scissoring (i.e. clip mask
297 is created as if there was no scissor set). Actual rendering is affected by
298 scissors, though.
300 text_api =
301 ## Text
303 NanoVega allows you to load .ttf files and use the font to render text.
304 You have to load some font, and set proper font size before doing anything
305 with text, as there is no "default" font provided by NanoVega. Also, don't
306 forget to check return value of `createFont()`, 'cause NanoVega won't fail
307 if it cannot load font, it will silently try to render nothing.
309 The appearance of the text can be defined by setting the current text style
310 and by specifying the fill color. Common text and font settings such as
311 font size, letter spacing and text align are supported. Font blur allows you
312 to create simple text effects such as drop shadows.
314 At render time the font face can be set based on the font handles or name.
316 Font measure functions return values in local space, the calculations are
317 carried in the same resolution as the final rendering. This is done because
318 the text glyph positions are snapped to the nearest pixels sharp rendering.
320 The local space means that values are not rotated or scale as per the current
321 transformation. For example if you set font size to 12, which would mean that
322 line height is 16, then regardless of the current scaling and rotation, the
323 returned line height is always 16. Some measures may vary because of the scaling
324 since aforementioned pixel snapping.
326 While this may sound a little odd, the setup allows you to always render the
327 same way regardless of scaling. I.e. following works regardless of scaling:
329 ----------------------
330 string txt = "Text me up.";
331 vg.textBounds(x, y, txt, bounds);
332 vg.beginPath();
333 vg.roundedRect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1], 6);
334 vg.fill();
335 ----------------------
337 Note: currently only solid color fill is supported for text.
339 path_recording =
340 ## Recording and Replaying Pathes
342 $(WARNING This API is hightly experimental, and is subject to change.
343 While I will try to keep it compatible in future NanoVega
344 versions, no promises are made. Also note that NanoVega
345 rendering is quite fast, so you prolly don't need this
346 functionality. If you really want to render-once-and-copy,
347 consider rendering to FBO, and use imaging API to blit
348 FBO texture instead. Note that NanoVega supports alot of
349 blit/copy modes.)
351 It is posible to record render commands and replay them later. This will allow
352 you to skip possible time-consuming tesselation stage. Potential uses of this
353 feature is, for example, rendering alot of similar complex paths, like game
354 tiles, or enemy sprites.
356 Path replaying has some limitations, though: you cannot change stroke width,
357 fringe size, tesselation tolerance, or rescale path. But you can change fill
358 color/pattern, stroke color, translate and/or rotate saved paths.
360 Note that text rendering commands are not saved, as technically text rendering
361 is not a path.
363 To translate or rotate a record, use [affineGPU] API call.
365 To record render commands, you must create new path set with [newPathSet]
366 function, then start recording with [startRecording]. You can cancel current
367 recording with [cancelRecording], or commit (save) recording with [stopRecording].
369 You can resume recording with [startRecording] after [stopRecording] call.
370 Calling [cancelRecording] will cancel only current recording session (i.e. it
371 will forget everything from the very latest [startRecording], not the whole
372 record).
374 Finishing frame with [endFrame] will automatically commit current recording, and
375 calling [cancelFrame] will cancel recording by calling [cancelRecording].
377 Note that commit recording will clear current picking scene (but cancelling won't).
379 Calling [startRecording] without commiting or cancelling recoriding will commit.
381 $(WARNING Text output is not recorded now. Neither is scissor, so if you are using
382 scissoring or text in your pathes (UI, for example), things will not
383 work as you may expect.)
385 module iv.nanovega.nanovega;
386 private:
388 version(aliced) {
389 import iv.meta;
390 import iv.vfs;
391 } else {
392 private alias usize = size_t;
393 // i fear phobos!
394 private template Unqual(T) {
395 static if (is(T U == immutable U)) alias Unqual = U;
396 else static if (is(T U == shared inout const U)) alias Unqual = U;
397 else static if (is(T U == shared inout U)) alias Unqual = U;
398 else static if (is(T U == shared const U)) alias Unqual = U;
399 else static if (is(T U == shared U)) alias Unqual = U;
400 else static if (is(T U == inout const U)) alias Unqual = U;
401 else static if (is(T U == inout U)) alias Unqual = U;
402 else static if (is(T U == const U)) alias Unqual = U;
403 else alias Unqual = T;
405 private template isAnyCharType(T, bool unqual=false) {
406 static if (unqual) private alias UT = Unqual!T; else private alias UT = T;
407 enum isAnyCharType = is(UT == char) || is(UT == wchar) || is(UT == dchar);
409 private template isWideCharType(T, bool unqual=false) {
410 static if (unqual) private alias UT = Unqual!T; else private alias UT = T;
411 enum isWideCharType = is(UT == wchar) || is(UT == dchar);
414 version(nanovg_disable_vfs) {
415 enum NanoVegaHasIVVFS = false;
416 } else {
417 static if (is(typeof((){import iv.vfs;}))) {
418 enum NanoVegaHasIVVFS = true;
419 import iv.vfs;
420 } else {
421 enum NanoVegaHasIVVFS = false;
425 // ////////////////////////////////////////////////////////////////////////// //
426 // engine
427 // ////////////////////////////////////////////////////////////////////////// //
428 import core.stdc.stdlib : malloc, realloc, free;
429 import core.stdc.string : memset, memcpy, strlen;
430 import std.math : PI;
432 version(Posix) {
433 version = nanovg_use_freetype;
434 } else {
435 version = nanovg_disable_fontconfig;
437 version(aliced) {
438 version = nanovg_default_no_font_aa;
439 version = nanovg_builtin_fontconfig_bindings;
440 version = nanovg_builtin_freetype_bindings;
441 version = nanovg_builtin_opengl_bindings; // use `arsd.simpledisplay` to get basic bindings
442 } else {
443 version = nanovg_builtin_fontconfig_bindings;
444 version = nanovg_builtin_freetype_bindings;
445 version = nanovg_builtin_opengl_bindings; // use `arsd.simpledisplay` to get basic bindings
448 version(nanovg_disable_fontconfig) {
449 public enum NanoVegaHasFontConfig = false;
450 } else {
451 public enum NanoVegaHasFontConfig = true;
452 version(nanovg_builtin_fontconfig_bindings) {} else import iv.fontconfig;
455 //version = nanovg_bench_flatten;
457 public:
458 alias NVG_PI = PI;
460 enum NanoVegaHasArsdColor = (is(typeof((){ import arsd.color; })));
461 enum NanoVegaHasArsdImage = (is(typeof((){ import arsd.color; import arsd.image; })));
463 static if (NanoVegaHasArsdColor) private import arsd.color;
464 static if (NanoVegaHasArsdImage) {
465 private import arsd.image;
466 } else {
467 void stbi_set_unpremultiply_on_load (int flag_true_if_should_unpremultiply) {}
468 void stbi_convert_iphone_png_to_rgb (int flag_true_if_should_convert) {}
469 ubyte* stbi_load (const(char)* filename, int* x, int* y, int* comp, int req_comp) { return null; }
470 ubyte* stbi_load_from_memory (const(void)* buffer, int len, int* x, int* y, int* comp, int req_comp) { return null; }
471 void stbi_image_free (void* retval_from_stbi_load) {}
474 version(nanovg_default_no_font_aa) {
475 __gshared bool NVG_INVERT_FONT_AA = false;
476 } else {
477 __gshared bool NVG_INVERT_FONT_AA = true;
481 /// this is branchless for ints on x86, and even for longs on x86_64
482 public ubyte nvgClampToByte(T) (T n) pure nothrow @safe @nogc if (__traits(isIntegral, T)) {
483 static if (__VERSION__ > 2067) pragma(inline, true);
484 static if (T.sizeof == 2 || T.sizeof == 4) {
485 static if (__traits(isUnsigned, T)) {
486 return cast(ubyte)(n&0xff|(255-((-cast(int)(n < 256))>>24)));
487 } else {
488 n &= -cast(int)(n >= 0);
489 return cast(ubyte)(n|((255-cast(int)n)>>31));
491 } else static if (T.sizeof == 1) {
492 static assert(__traits(isUnsigned, T), "clampToByte: signed byte? no, really?");
493 return cast(ubyte)n;
494 } else static if (T.sizeof == 8) {
495 static if (__traits(isUnsigned, T)) {
496 return cast(ubyte)(n&0xff|(255-((-cast(long)(n < 256))>>56)));
497 } else {
498 n &= -cast(long)(n >= 0);
499 return cast(ubyte)(n|((255-cast(long)n)>>63));
501 } else {
502 static assert(false, "clampToByte: integer too big");
507 /// NanoVega RGBA color
508 /// Group: color_utils
509 public align(1) struct NVGColor {
510 align(1):
511 public:
512 float[4] rgba = 0; /// default color is transparent (a=1 is opaque)
514 public:
515 @property string toString () const @safe { import std.string : format; return "NVGColor(%s,%s,%s,%s)".format(r, g, b, a); }
517 public:
518 enum transparent = NVGColor(0.0f, 0.0f, 0.0f, 0.0f);
519 enum k8orange = NVGColor(1.0f, 0.5f, 0.0f, 1.0f);
521 enum aliceblue = NVGColor(240, 248, 255);
522 enum antiquewhite = NVGColor(250, 235, 215);
523 enum aqua = NVGColor(0, 255, 255);
524 enum aquamarine = NVGColor(127, 255, 212);
525 enum azure = NVGColor(240, 255, 255);
526 enum beige = NVGColor(245, 245, 220);
527 enum bisque = NVGColor(255, 228, 196);
528 enum black = NVGColor(0, 0, 0); // basic color
529 enum blanchedalmond = NVGColor(255, 235, 205);
530 enum blue = NVGColor(0, 0, 255); // basic color
531 enum blueviolet = NVGColor(138, 43, 226);
532 enum brown = NVGColor(165, 42, 42);
533 enum burlywood = NVGColor(222, 184, 135);
534 enum cadetblue = NVGColor(95, 158, 160);
535 enum chartreuse = NVGColor(127, 255, 0);
536 enum chocolate = NVGColor(210, 105, 30);
537 enum coral = NVGColor(255, 127, 80);
538 enum cornflowerblue = NVGColor(100, 149, 237);
539 enum cornsilk = NVGColor(255, 248, 220);
540 enum crimson = NVGColor(220, 20, 60);
541 enum cyan = NVGColor(0, 255, 255); // basic color
542 enum darkblue = NVGColor(0, 0, 139);
543 enum darkcyan = NVGColor(0, 139, 139);
544 enum darkgoldenrod = NVGColor(184, 134, 11);
545 enum darkgray = NVGColor(169, 169, 169);
546 enum darkgreen = NVGColor(0, 100, 0);
547 enum darkgrey = NVGColor(169, 169, 169);
548 enum darkkhaki = NVGColor(189, 183, 107);
549 enum darkmagenta = NVGColor(139, 0, 139);
550 enum darkolivegreen = NVGColor(85, 107, 47);
551 enum darkorange = NVGColor(255, 140, 0);
552 enum darkorchid = NVGColor(153, 50, 204);
553 enum darkred = NVGColor(139, 0, 0);
554 enum darksalmon = NVGColor(233, 150, 122);
555 enum darkseagreen = NVGColor(143, 188, 143);
556 enum darkslateblue = NVGColor(72, 61, 139);
557 enum darkslategray = NVGColor(47, 79, 79);
558 enum darkslategrey = NVGColor(47, 79, 79);
559 enum darkturquoise = NVGColor(0, 206, 209);
560 enum darkviolet = NVGColor(148, 0, 211);
561 enum deeppink = NVGColor(255, 20, 147);
562 enum deepskyblue = NVGColor(0, 191, 255);
563 enum dimgray = NVGColor(105, 105, 105);
564 enum dimgrey = NVGColor(105, 105, 105);
565 enum dodgerblue = NVGColor(30, 144, 255);
566 enum firebrick = NVGColor(178, 34, 34);
567 enum floralwhite = NVGColor(255, 250, 240);
568 enum forestgreen = NVGColor(34, 139, 34);
569 enum fuchsia = NVGColor(255, 0, 255);
570 enum gainsboro = NVGColor(220, 220, 220);
571 enum ghostwhite = NVGColor(248, 248, 255);
572 enum gold = NVGColor(255, 215, 0);
573 enum goldenrod = NVGColor(218, 165, 32);
574 enum gray = NVGColor(128, 128, 128); // basic color
575 enum green = NVGColor(0, 128, 0); // basic color
576 enum greenyellow = NVGColor(173, 255, 47);
577 enum grey = NVGColor(128, 128, 128); // basic color
578 enum honeydew = NVGColor(240, 255, 240);
579 enum hotpink = NVGColor(255, 105, 180);
580 enum indianred = NVGColor(205, 92, 92);
581 enum indigo = NVGColor(75, 0, 130);
582 enum ivory = NVGColor(255, 255, 240);
583 enum khaki = NVGColor(240, 230, 140);
584 enum lavender = NVGColor(230, 230, 250);
585 enum lavenderblush = NVGColor(255, 240, 245);
586 enum lawngreen = NVGColor(124, 252, 0);
587 enum lemonchiffon = NVGColor(255, 250, 205);
588 enum lightblue = NVGColor(173, 216, 230);
589 enum lightcoral = NVGColor(240, 128, 128);
590 enum lightcyan = NVGColor(224, 255, 255);
591 enum lightgoldenrodyellow = NVGColor(250, 250, 210);
592 enum lightgray = NVGColor(211, 211, 211);
593 enum lightgreen = NVGColor(144, 238, 144);
594 enum lightgrey = NVGColor(211, 211, 211);
595 enum lightpink = NVGColor(255, 182, 193);
596 enum lightsalmon = NVGColor(255, 160, 122);
597 enum lightseagreen = NVGColor(32, 178, 170);
598 enum lightskyblue = NVGColor(135, 206, 250);
599 enum lightslategray = NVGColor(119, 136, 153);
600 enum lightslategrey = NVGColor(119, 136, 153);
601 enum lightsteelblue = NVGColor(176, 196, 222);
602 enum lightyellow = NVGColor(255, 255, 224);
603 enum lime = NVGColor(0, 255, 0);
604 enum limegreen = NVGColor(50, 205, 50);
605 enum linen = NVGColor(250, 240, 230);
606 enum magenta = NVGColor(255, 0, 255); // basic color
607 enum maroon = NVGColor(128, 0, 0);
608 enum mediumaquamarine = NVGColor(102, 205, 170);
609 enum mediumblue = NVGColor(0, 0, 205);
610 enum mediumorchid = NVGColor(186, 85, 211);
611 enum mediumpurple = NVGColor(147, 112, 219);
612 enum mediumseagreen = NVGColor(60, 179, 113);
613 enum mediumslateblue = NVGColor(123, 104, 238);
614 enum mediumspringgreen = NVGColor(0, 250, 154);
615 enum mediumturquoise = NVGColor(72, 209, 204);
616 enum mediumvioletred = NVGColor(199, 21, 133);
617 enum midnightblue = NVGColor(25, 25, 112);
618 enum mintcream = NVGColor(245, 255, 250);
619 enum mistyrose = NVGColor(255, 228, 225);
620 enum moccasin = NVGColor(255, 228, 181);
621 enum navajowhite = NVGColor(255, 222, 173);
622 enum navy = NVGColor(0, 0, 128);
623 enum oldlace = NVGColor(253, 245, 230);
624 enum olive = NVGColor(128, 128, 0);
625 enum olivedrab = NVGColor(107, 142, 35);
626 enum orange = NVGColor(255, 165, 0);
627 enum orangered = NVGColor(255, 69, 0);
628 enum orchid = NVGColor(218, 112, 214);
629 enum palegoldenrod = NVGColor(238, 232, 170);
630 enum palegreen = NVGColor(152, 251, 152);
631 enum paleturquoise = NVGColor(175, 238, 238);
632 enum palevioletred = NVGColor(219, 112, 147);
633 enum papayawhip = NVGColor(255, 239, 213);
634 enum peachpuff = NVGColor(255, 218, 185);
635 enum peru = NVGColor(205, 133, 63);
636 enum pink = NVGColor(255, 192, 203);
637 enum plum = NVGColor(221, 160, 221);
638 enum powderblue = NVGColor(176, 224, 230);
639 enum purple = NVGColor(128, 0, 128);
640 enum red = NVGColor(255, 0, 0); // basic color
641 enum rosybrown = NVGColor(188, 143, 143);
642 enum royalblue = NVGColor(65, 105, 225);
643 enum saddlebrown = NVGColor(139, 69, 19);
644 enum salmon = NVGColor(250, 128, 114);
645 enum sandybrown = NVGColor(244, 164, 96);
646 enum seagreen = NVGColor(46, 139, 87);
647 enum seashell = NVGColor(255, 245, 238);
648 enum sienna = NVGColor(160, 82, 45);
649 enum silver = NVGColor(192, 192, 192);
650 enum skyblue = NVGColor(135, 206, 235);
651 enum slateblue = NVGColor(106, 90, 205);
652 enum slategray = NVGColor(112, 128, 144);
653 enum slategrey = NVGColor(112, 128, 144);
654 enum snow = NVGColor(255, 250, 250);
655 enum springgreen = NVGColor(0, 255, 127);
656 enum steelblue = NVGColor(70, 130, 180);
657 enum tan = NVGColor(210, 180, 140);
658 enum teal = NVGColor(0, 128, 128);
659 enum thistle = NVGColor(216, 191, 216);
660 enum tomato = NVGColor(255, 99, 71);
661 enum turquoise = NVGColor(64, 224, 208);
662 enum violet = NVGColor(238, 130, 238);
663 enum wheat = NVGColor(245, 222, 179);
664 enum white = NVGColor(255, 255, 255); // basic color
665 enum whitesmoke = NVGColor(245, 245, 245);
666 enum yellow = NVGColor(255, 255, 0); // basic color
667 enum yellowgreen = NVGColor(154, 205, 50);
669 nothrow @safe @nogc:
670 public:
672 this (ubyte ar, ubyte ag, ubyte ab, ubyte aa=255) pure {
673 pragma(inline, true);
674 r = ar/255.0f;
675 g = ag/255.0f;
676 b = ab/255.0f;
677 a = aa/255.0f;
681 this (float ar, float ag, float ab, float aa=1.0f) pure {
682 pragma(inline, true);
683 r = ar;
684 g = ag;
685 b = ab;
686 a = aa;
689 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
690 this (uint c) pure {
691 pragma(inline, true);
692 r = (c&0xff)/255.0f;
693 g = ((c>>8)&0xff)/255.0f;
694 b = ((c>>16)&0xff)/255.0f;
695 a = ((c>>24)&0xff)/255.0f;
698 /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
699 this (const(char)[] srgb) {
700 static int c2d (char ch) pure nothrow @safe @nogc {
701 pragma(inline, true);
702 return
703 ch >= '0' && ch <= '9' ? ch-'0' :
704 ch >= 'A' && ch <= 'F' ? ch-'A'+10 :
705 ch >= 'a' && ch <= 'f' ? ch-'a'+10 :
708 int[8] digs;
709 int dc = -1;
710 foreach (immutable char ch; srgb) {
711 if (ch <= ' ') continue;
712 if (ch == '#') {
713 if (dc != -1) { dc = -1; break; }
714 dc = 0;
715 } else {
716 if (dc >= digs.length) { dc = -1; break; }
717 if ((digs[dc++] = c2d(ch)) < 0) { dc = -1; break; }
720 switch (dc) {
721 case 3: // rgb
722 a = 1.0f;
723 r = digs[0]/15.0f;
724 g = digs[1]/15.0f;
725 b = digs[2]/15.0f;
726 break;
727 case 4: // argb
728 a = digs[0]/15.0f;
729 r = digs[1]/15.0f;
730 g = digs[2]/15.0f;
731 b = digs[3]/15.0f;
732 break;
733 case 6: // rrggbb
734 a = 1.0f;
735 r = (digs[0]*16+digs[1])/255.0f;
736 g = (digs[2]*16+digs[3])/255.0f;
737 b = (digs[4]*16+digs[5])/255.0f;
738 break;
739 case 8: // aarrggbb
740 a = (digs[0]*16+digs[1])/255.0f;
741 r = (digs[2]*16+digs[3])/255.0f;
742 g = (digs[4]*16+digs[5])/255.0f;
743 b = (digs[6]*16+digs[7])/255.0f;
744 break;
745 default:
746 break;
750 /// Is this color completely opaque?
751 @property bool isOpaque () const pure nothrow @trusted @nogc { pragma(inline, true); return (rgba.ptr[3] >= 1.0f); }
752 /// Is this color completely transparent?
753 @property bool isTransparent () const pure nothrow @trusted @nogc { pragma(inline, true); return (rgba.ptr[3] <= 0.0f); }
755 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
756 @property uint asUint () const pure {
757 pragma(inline, true);
758 return
759 cast(uint)(r*255)|
760 (cast(uint)(g*255)<<8)|
761 (cast(uint)(b*255)<<16)|
762 (cast(uint)(a*255)<<24);
765 alias asUintABGR = asUint; /// Ditto.
767 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
768 static NVGColor fromUint (uint c) pure { pragma(inline, true); return NVGColor(c); }
770 alias fromUintABGR = fromUint; /// Ditto.
772 /// AARRGGBB
773 @property uint asUintARGB () const pure {
774 pragma(inline, true);
775 return
776 cast(uint)(b*255)|
777 (cast(uint)(g*255)<<8)|
778 (cast(uint)(r*255)<<16)|
779 (cast(uint)(a*255)<<24);
782 /// AARRGGBB
783 static NVGColor fromUintARGB (uint c) pure { pragma(inline, true); return NVGColor((c>>16)&0xff, (c>>8)&0xff, c&0xff, (c>>24)&0xff); }
785 @property ref inout(float) r () inout pure @trusted { pragma(inline, true); return rgba.ptr[0]; } ///
786 @property ref inout(float) g () inout pure @trusted { pragma(inline, true); return rgba.ptr[1]; } ///
787 @property ref inout(float) b () inout pure @trusted { pragma(inline, true); return rgba.ptr[2]; } ///
788 @property ref inout(float) a () inout pure @trusted { pragma(inline, true); return rgba.ptr[3]; } ///
790 ref NVGColor applyTint() (in auto ref NVGColor tint) nothrow @trusted @nogc {
791 if (tint.a == 0) return this;
792 foreach (immutable idx, ref float v; rgba[0..4]) {
793 v = nvg__clamp(v*tint.rgba.ptr[idx], 0.0f, 1.0f);
795 return this;
798 NVGHSL asHSL() (bool useWeightedLightness=false) const { pragma(inline, true); return NVGHSL.fromColor(this, useWeightedLightness); } ///
799 static fromHSL() (in auto ref NVGHSL hsl) { pragma(inline, true); return hsl.asColor; } ///
801 static if (NanoVegaHasArsdColor) {
802 Color toArsd () const { pragma(inline, true); return Color(cast(int)(r*255), cast(int)(g*255), cast(int)(b*255), cast(int)(a*255)); } ///
803 static NVGColor fromArsd (in Color c) { pragma(inline, true); return NVGColor(c.r, c.g, c.b, c.a); } ///
805 this (in Color c) {
806 version(aliced) pragma(inline, true);
807 r = c.r/255.0f;
808 g = c.g/255.0f;
809 b = c.b/255.0f;
810 a = c.a/255.0f;
816 /// NanoVega A-HSL color
817 /// Group: color_utils
818 public align(1) struct NVGHSL {
819 align(1):
820 float h=0, s=0, l=1, a=1; ///
822 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)); }
824 nothrow @safe @nogc:
825 public:
827 this (float ah, float as, float al, float aa=1) pure { pragma(inline, true); h = ah; s = as; l = al; a = aa; }
829 NVGColor asColor () const { pragma(inline, true); return nvgHSLA(h, s, l, a); } ///
831 // taken from Adam's arsd.color
832 /** Converts an RGB color into an HSL triplet.
833 * [useWeightedLightness] will try to get a better value for luminosity for the human eye,
834 * which is more sensitive to green than red and more to red than blue.
835 * If it is false, it just does average of the rgb. */
836 static NVGHSL fromColor() (in auto ref NVGColor c, bool useWeightedLightness=false) pure {
837 NVGHSL res;
838 res.a = c.a;
839 float r1 = c.r;
840 float g1 = c.g;
841 float b1 = c.b;
843 float maxColor = r1;
844 if (g1 > maxColor) maxColor = g1;
845 if (b1 > maxColor) maxColor = b1;
846 float minColor = r1;
847 if (g1 < minColor) minColor = g1;
848 if (b1 < minColor) minColor = b1;
850 res.l = (maxColor+minColor)/2;
851 if (useWeightedLightness) {
852 // the colors don't affect the eye equally
853 // this is a little more accurate than plain HSL numbers
854 res.l = 0.2126*r1+0.7152*g1+0.0722*b1;
856 if (maxColor != minColor) {
857 if (res.l < 0.5) {
858 res.s = (maxColor-minColor)/(maxColor+minColor);
859 } else {
860 res.s = (maxColor-minColor)/(2.0-maxColor-minColor);
862 if (r1 == maxColor) {
863 res.h = (g1-b1)/(maxColor-minColor);
864 } else if(g1 == maxColor) {
865 res.h = 2.0+(b1-r1)/(maxColor-minColor);
866 } else {
867 res.h = 4.0+(r1-g1)/(maxColor-minColor);
871 res.h = res.h*60;
872 if (res.h < 0) res.h += 360;
873 res.h /= 360;
875 return res;
880 //version = nanovega_debug_image_manager;
881 //version = nanovega_debug_image_manager_rc;
883 /** NanoVega image handle.
885 * This is refcounted struct, so you don't need to do anything special to free it once it is allocated.
887 * Group: images
889 struct NVGImage {
890 private:
891 NVGContext ctx;
892 int id; // backend image id
894 public:
896 this() (in auto ref NVGImage src) nothrow @trusted @nogc {
897 version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p created from %p (imgid=%d)\n", &this, src, src.id); }
898 ctx = cast(NVGContext)src.ctx;
899 id = src.id;
900 if (ctx !is null) ctx.nvg__imageIncRef(id);
904 ~this () nothrow @trusted @nogc { version(aliced) pragma(inline, true); clear(); }
907 this (this) nothrow @trusted @nogc {
908 if (ctx !is null) {
909 version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p postblit (imgid=%d)\n", &this, id); }
910 ctx.nvg__imageIncRef(id);
915 void opAssign() (in auto ref NVGImage src) nothrow @trusted @nogc {
916 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); }
917 if (src.ctx !is null) (cast(NVGContext)src.ctx).nvg__imageIncRef(src.id);
918 if (ctx !is null) ctx.nvg__imageDecRef(id);
919 ctx = cast(NVGContext)src.ctx;
920 id = src.id;
923 /// Is this image valid?
924 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (id > 0 && ctx.valid); }
926 /// Is this image valid?
927 @property bool isSameContext (const(NVGContext) actx) const pure nothrow @safe @nogc { pragma(inline, true); return (actx !is null && ctx is actx); }
929 /// Returns image width, or zero for invalid image.
930 int width () const nothrow @trusted @nogc {
931 int w = 0;
932 if (valid) {
933 int h = void;
934 ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, id, &w, &h);
936 return w;
939 /// Returns image height, or zero for invalid image.
940 int height () const nothrow @trusted @nogc {
941 int h = 0;
942 if (valid) {
943 int w = void;
944 ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, id, &w, &h);
946 return h;
949 /// Free this image.
950 void clear () nothrow @trusted @nogc {
951 if (ctx !is null) {
952 version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("NVGImage %p cleared (imgid=%d)\n", &this, id); }
953 ctx.nvg__imageDecRef(id);
954 ctx = null;
955 id = 0;
961 /// Paint parameters for various fills. Don't change anything here!
962 /// Group: render_styles
963 public struct NVGPaint {
964 NVGMatrix xform;
965 float[2] extent = 0.0f;
966 float radius = 0.0f;
967 float feather = 0.0f;
968 NVGColor innerColor; /// this can be used to modulate images (fill/font)
969 NVGColor outerColor;
970 NVGImage image;
971 bool simpleColor; /// if `true`, only innerColor is used, and this is solid-color paint
973 this() (in auto ref NVGPaint p) nothrow @trusted @nogc {
974 xform = p.xform;
975 extent[] = p.extent[];
976 radius = p.radius;
977 feather = p.feather;
978 innerColor = p.innerColor;
979 outerColor = p.outerColor;
980 image = p.image;
981 simpleColor = p.simpleColor;
984 void opAssign() (in auto ref NVGPaint p) nothrow @trusted @nogc {
985 xform = p.xform;
986 extent[] = p.extent[];
987 radius = p.radius;
988 feather = p.feather;
989 innerColor = p.innerColor;
990 outerColor = p.outerColor;
991 image = p.image;
992 simpleColor = p.simpleColor;
995 void clear () nothrow @trusted @nogc {
996 version(aliced) pragma(inline, true);
997 import core.stdc.string : memset;
998 image.clear();
999 memset(&this, 0, this.sizeof);
1000 simpleColor = true;
1004 /// Path winding.
1005 /// Group: paths
1006 public enum NVGWinding {
1007 CCW = 1, /// Winding for solid shapes
1008 CW = 2, /// Winding for holes
1011 /// Path solidity.
1012 /// Group: paths
1013 public enum NVGSolidity {
1014 Solid = 1, /// Solid shape (CCW winding).
1015 Hole = 2, /// Hole (CW winding).
1018 /// Line cap style.
1019 /// Group: render_styles
1020 public enum NVGLineCap {
1021 Butt, ///
1022 Round, ///
1023 Square, ///
1024 Bevel, ///
1025 Miter, ///
1028 /// Text align.
1029 /// Group: text_api
1030 public align(1) struct NVGTextAlign {
1031 align(1):
1032 /// Horizontal align.
1033 enum H : ubyte {
1034 Left = 0, /// Default, align text horizontally to left.
1035 Center = 1, /// Align text horizontally to center.
1036 Right = 2, /// Align text horizontally to right.
1039 /// Vertical align.
1040 enum V : ubyte {
1041 Baseline = 0, /// Default, align text vertically to baseline.
1042 Top = 1, /// Align text vertically to top.
1043 Middle = 2, /// Align text vertically to middle.
1044 Bottom = 3, /// Align text vertically to bottom.
1047 pure nothrow @safe @nogc:
1048 public:
1049 this (H h) { pragma(inline, true); value = h; } ///
1050 this (V v) { pragma(inline, true); value = cast(ubyte)(v<<4); } ///
1051 this (H h, V v) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1052 this (V v, H h) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1053 void reset () { pragma(inline, true); value = 0; } ///
1054 void reset (H h, V v) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1055 void reset (V v, H h) { pragma(inline, true); value = cast(ubyte)(h|(v<<4)); } ///
1056 @property:
1057 bool left () const { pragma(inline, true); return ((value&0x0f) == H.Left); } ///
1058 void left (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Left : 0)); } ///
1059 bool center () const { pragma(inline, true); return ((value&0x0f) == H.Center); } ///
1060 void center (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Center : 0)); } ///
1061 bool right () const { pragma(inline, true); return ((value&0x0f) == H.Right); } ///
1062 void right (bool v) { pragma(inline, true); value = cast(ubyte)((value&0xf0)|(v ? H.Right : 0)); } ///
1064 bool baseline () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Baseline); } ///
1065 void baseline (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Baseline<<4 : 0)); } ///
1066 bool top () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Top); } ///
1067 void top (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Top<<4 : 0)); } ///
1068 bool middle () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Middle); } ///
1069 void middle (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Middle<<4 : 0)); } ///
1070 bool bottom () const { pragma(inline, true); return (((value>>4)&0x0f) == V.Bottom); } ///
1071 void bottom (bool v) { pragma(inline, true); value = cast(ubyte)((value&0x0f)|(v ? V.Bottom<<4 : 0)); } ///
1073 H horizontal () const { pragma(inline, true); return cast(H)(value&0x0f); } ///
1074 void horizontal (H v) { pragma(inline, true); value = (value&0xf0)|v; } ///
1076 V vertical () const { pragma(inline, true); return cast(V)((value>>4)&0x0f); } ///
1077 void vertical (V v) { pragma(inline, true); value = (value&0x0f)|cast(ubyte)(v<<4); } ///
1079 private:
1080 ubyte value = 0; // low nibble: horizontal; high nibble: vertical
1083 /// Blending type.
1084 /// Group: composite_operation
1085 public enum NVGBlendFactor {
1086 Zero = 1<<0, ///
1087 One = 1<<1, ///
1088 SrcColor = 1<<2, ///
1089 OneMinusSrcColor = 1<<3, ///
1090 DstColor = 1<<4, ///
1091 OneMinusDstColor = 1<<5, ///
1092 SrcAlpha = 1<<6, ///
1093 OneMinusSrcAlpha = 1<<7, ///
1094 DstAlpha = 1<<8, ///
1095 OneMinusDstAlpha = 1<<9, ///
1096 SrcAlphaSaturate = 1<<10, ///
1099 /// Composite operation (HTML5-alike).
1100 /// Group: composite_operation
1101 public enum NVGCompositeOperation {
1102 SourceOver, ///
1103 SourceIn, ///
1104 SourceOut, ///
1105 SourceAtop, ///
1106 DestinationOver, ///
1107 DestinationIn, ///
1108 DestinationOut, ///
1109 DestinationAtop, ///
1110 Lighter, ///
1111 Copy, ///
1112 Xor, ///
1115 /// Composite operation state.
1116 /// Group: composite_operation
1117 public struct NVGCompositeOperationState {
1118 bool simple; /// `true`: use `glBlendFunc()` instead of `glBlendFuncSeparate()`
1119 NVGBlendFactor srcRGB; ///
1120 NVGBlendFactor dstRGB; ///
1121 NVGBlendFactor srcAlpha; ///
1122 NVGBlendFactor dstAlpha; ///
1125 /// Mask combining more
1126 /// Group: clipping
1127 public enum NVGClipMode {
1128 None, /// normal rendering (i.e. render path instead of modifying clip region)
1129 Union, /// old mask will be masked with the current one; this is the default mode for [clip]
1130 Or, /// new mask will be added to the current one (logical `OR` operation);
1131 Xor, /// new mask will be logically `XOR`ed with the current one
1132 Sub, /// "subtract" current path from mask
1133 Replace, /// replace current mask
1134 Add = Or, /// Synonym
1137 /// Glyph position info.
1138 /// Group: text_api
1139 public struct NVGGlyphPosition {
1140 usize strpos; /// Position of the glyph in the input string.
1141 float x; /// The x-coordinate of the logical glyph position.
1142 float minx, maxx; /// The bounds of the glyph shape.
1145 /// Text row storage.
1146 /// Group: text_api
1147 public struct NVGTextRow(CT) if (isAnyCharType!CT) {
1148 alias CharType = CT;
1149 const(CT)[] s;
1150 int start; /// Index in the input text where the row starts.
1151 int end; /// Index in the input text where the row ends (one past the last character).
1152 float width; /// Logical width of the row.
1153 float minx, maxx; /// Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.
1154 /// Get rest of the string.
1155 @property const(CT)[] rest () const pure nothrow @trusted @nogc { pragma(inline, true); return (end <= s.length ? s[end..$] : null); }
1156 /// Get current row.
1157 @property const(CT)[] row () const pure nothrow @trusted @nogc { pragma(inline, true); return s[start..end]; }
1158 @property const(CT)[] string () const pure nothrow @trusted @nogc { pragma(inline, true); return s; }
1159 @property void string(CT) (const(CT)[] v) pure nothrow @trusted @nogc { pragma(inline, true); s = v; }
1162 /// Image creation flags.
1163 /// Group: images
1164 public enum NVGImageFlag : uint {
1165 None = 0, /// Nothing special.
1166 GenerateMipmaps = 1<<0, /// Generate mipmaps during creation of the image.
1167 RepeatX = 1<<1, /// Repeat image in X direction.
1168 RepeatY = 1<<2, /// Repeat image in Y direction.
1169 FlipY = 1<<3, /// Flips (inverses) image in Y direction when rendered.
1170 Premultiplied = 1<<4, /// Image data has premultiplied alpha.
1171 NoFiltering = 1<<8, /// use GL_NEAREST instead of GL_LINEAR
1172 Nearest = NoFiltering, /// compatibility with original NanoVG
1175 alias NVGImageFlags = NVGImageFlag; /// Backwards compatibility for [NVGImageFlag].
1178 // ////////////////////////////////////////////////////////////////////////// //
1179 private:
1181 static T* xdup(T) (const(T)* ptr, int count) nothrow @trusted @nogc {
1182 import core.stdc.stdlib : malloc;
1183 import core.stdc.string : memcpy;
1184 if (count == 0) return null;
1185 T* res = cast(T*)malloc(T.sizeof*count);
1186 if (res is null) assert(0, "NanoVega: out of memory");
1187 memcpy(res, ptr, T.sizeof*count);
1188 return res;
1191 // Internal Render API
1192 enum NVGtexture {
1193 Alpha = 0x01,
1194 RGBA = 0x02,
1197 struct NVGscissor {
1198 NVGMatrix xform;
1199 float[2] extent = -1.0f;
1202 struct NVGvertex {
1203 float x, y, u, v;
1206 struct NVGpath {
1207 int first;
1208 int count;
1209 bool closed;
1210 int nbevel;
1211 NVGvertex* fill;
1212 int nfill;
1213 NVGvertex* stroke;
1214 int nstroke;
1215 NVGWinding winding;
1216 int convex;
1217 bool cloned;
1219 @disable this (this); // no copies
1221 void clear () nothrow @trusted @nogc {
1222 import core.stdc.stdlib : free;
1223 if (cloned) {
1224 if (stroke !is null && stroke !is fill) free(stroke);
1225 if (fill !is null) free(fill);
1227 this = this.init;
1230 // won't clear current path
1231 void copyFrom (const NVGpath* src) nothrow @trusted @nogc {
1232 import core.stdc.string : memcpy;
1233 assert(src !is null);
1234 memcpy(&this, src, NVGpath.sizeof);
1235 this.fill = xdup(src.fill, src.nfill);
1236 if (src.stroke is src.fill) {
1237 this.stroke = this.fill;
1238 } else {
1239 this.stroke = xdup(src.stroke, src.nstroke);
1241 this.cloned = true;
1246 struct NVGparams {
1247 void* userPtr;
1248 bool edgeAntiAlias;
1249 bool fontAA;
1250 bool function (void* uptr) nothrow @trusted @nogc renderCreate;
1251 int function (void* uptr, NVGtexture type, int w, int h, int imageFlags, const(ubyte)* data) nothrow @trusted @nogc renderCreateTexture;
1252 bool function (void* uptr, int image) nothrow @trusted @nogc renderTextureIncRef;
1253 bool function (void* uptr, int image) nothrow @trusted @nogc renderDeleteTexture;
1254 bool function (void* uptr, int image, int x, int y, int w, int h, const(ubyte)* data) nothrow @trusted @nogc renderUpdateTexture;
1255 bool function (void* uptr, int image, int* w, int* h) nothrow @trusted @nogc renderGetTextureSize;
1256 void function (void* uptr, int width, int height) nothrow @trusted @nogc renderViewport; // called in [beginFrame]
1257 void function (void* uptr) nothrow @trusted @nogc renderCancel;
1258 void function (void* uptr) nothrow @trusted @nogc renderFlush;
1259 void function (void* uptr) nothrow @trusted @nogc renderPushClip; // backend should support stack of at least [NVG_MAX_STATES] elements
1260 void function (void* uptr) nothrow @trusted @nogc renderPopClip; // backend should support stack of at least [NVG_MAX_STATES] elements
1261 void function (void* uptr) nothrow @trusted @nogc renderResetClip; // reset current clip region to `non-clipped`
1262 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;
1263 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;
1264 void function (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, const(NVGvertex)* verts, int nverts) nothrow @trusted @nogc renderTriangles;
1265 void function (void* uptr, in ref NVGMatrix mat) nothrow @trusted @nogc renderSetAffine;
1266 void function (void* uptr) nothrow @trusted @nogc renderDelete;
1269 // ////////////////////////////////////////////////////////////////////////// //
1270 private:
1272 enum NVG_INIT_FONTIMAGE_SIZE = 512;
1273 enum NVG_MAX_FONTIMAGE_SIZE = 2048;
1274 enum NVG_MAX_FONTIMAGES = 4;
1276 enum NVG_INIT_COMMANDS_SIZE = 256;
1277 enum NVG_INIT_POINTS_SIZE = 128;
1278 enum NVG_INIT_PATHS_SIZE = 16;
1279 enum NVG_INIT_VERTS_SIZE = 256;
1280 enum NVG_MAX_STATES = 32;
1282 enum NVG_KAPPA90 = 0.5522847493f; // Length proportional to radius of a cubic bezier handle for 90deg arcs.
1283 enum NVG_MIN_FEATHER = 0.001f; // it should be greater than zero, 'cause it is used in shader for divisions
1285 enum Command {
1286 MoveTo = 0,
1287 LineTo = 1,
1288 BezierTo = 2,
1289 Close = 3,
1290 Winding = 4,
1293 enum PointFlag : int {
1294 Corner = 0x01,
1295 Left = 0x02,
1296 Bevel = 0x04,
1297 InnerBevelPR = 0x08,
1300 struct NVGstate {
1301 NVGCompositeOperationState compositeOperation;
1302 bool shapeAntiAlias = true;
1303 NVGPaint fill;
1304 NVGPaint stroke;
1305 float strokeWidth = 1.0f;
1306 float miterLimit = 10.0f;
1307 NVGLineCap lineJoin = NVGLineCap.Miter;
1308 NVGLineCap lineCap = NVGLineCap.Butt;
1309 float alpha = 1.0f;
1310 NVGMatrix xform;
1311 NVGscissor scissor;
1312 float fontSize = 16.0f;
1313 float letterSpacing = 0.0f;
1314 float lineHeight = 1.0f;
1315 float fontBlur = 0.0f;
1316 NVGTextAlign textAlign;
1317 int fontId = 0;
1318 bool evenOddMode = false; // use even-odd filling rule (required for some svgs); otherwise use non-zero fill
1320 void clearPaint () nothrow @trusted @nogc {
1321 fill.clear();
1322 stroke.clear();
1326 struct NVGpoint {
1327 float x, y;
1328 float dx, dy;
1329 float len;
1330 float dmx, dmy;
1331 ubyte flags;
1334 struct NVGpathCache {
1335 NVGpoint* points;
1336 int npoints;
1337 int cpoints;
1338 NVGpath* paths;
1339 int npaths;
1340 int cpaths;
1341 NVGvertex* verts;
1342 int nverts;
1343 int cverts;
1344 float[4] bounds;
1345 // this is required for saved paths
1346 bool strokeReady;
1347 bool fillReady;
1348 float strokeAlphaMul;
1349 float strokeWidth;
1350 float fringeWidth;
1351 bool evenOddMode;
1352 NVGClipMode clipmode;
1353 // non-saved path will not have this
1354 float* commands;
1355 int ncommands;
1357 @disable this (this); // no copies
1359 // won't clear current path
1360 void copyFrom (const NVGpathCache* src) nothrow @trusted @nogc {
1361 import core.stdc.stdlib : malloc;
1362 import core.stdc.string : memcpy, memset;
1363 assert(src !is null);
1364 memcpy(&this, src, NVGpathCache.sizeof);
1365 this.points = xdup(src.points, src.npoints);
1366 this.cpoints = src.npoints;
1367 this.verts = xdup(src.verts, src.nverts);
1368 this.cverts = src.nverts;
1369 this.commands = xdup(src.commands, src.ncommands);
1370 if (src.npaths > 0) {
1371 this.paths = cast(NVGpath*)malloc(src.npaths*NVGpath.sizeof);
1372 memset(this.paths, 0, npaths*NVGpath.sizeof);
1373 foreach (immutable pidx; 0..npaths) this.paths[pidx].copyFrom(&src.paths[pidx]);
1374 } else {
1375 this.npaths = this.cpaths = 0;
1379 void clear () nothrow @trusted @nogc {
1380 import core.stdc.stdlib : free;
1381 if (paths !is null) {
1382 foreach (ref p; paths[0..npaths]) p.clear();
1383 free(paths);
1385 if (points !is null) free(points);
1386 if (verts !is null) free(verts);
1387 if (commands !is null) free(commands);
1388 this = this.init;
1392 /// Pointer to opaque NanoVega context structure.
1393 /// Group: context_management
1394 public alias NVGContext = NVGcontextinternal*;
1396 // Returns FontStash context of the given NanoVega context.
1397 public FONScontext* fonsContext (NVGContext ctx) { return (ctx !is null ? ctx.fs : null); }
1399 /** Bezier curve rasterizer.
1401 * De Casteljau Bezier rasterizer is faster, but currently rasterizing curves with cusps sligtly wrong.
1402 * It doesn't really matter in practice.
1404 * AFD tesselator is somewhat slower, but does cusps better.
1406 * McSeem rasterizer should have the best quality, bit it is the slowest method. Basically, you will
1407 * never notice any visial difference (and this code is not really debugged), so you probably should
1408 * not use it. It is there for further experiments.
1410 public enum NVGTesselation {
1411 DeCasteljau, /// default: standard well-known tesselation algorithm
1412 AFD, /// adaptive forward differencing
1413 DeCasteljauMcSeem, /// standard well-known tesselation algorithm, with improvements from Maxim Shemanarev; slowest one, but should give best results
1416 /// Default tesselator for Bezier curves.
1417 public __gshared NVGTesselation NVG_DEFAULT_TESSELATOR = NVGTesselation.DeCasteljau;
1420 // some public info
1422 /// valid only inside [beginFrame]/[endFrame]
1423 /// Group: context_management
1424 public int width (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.mWidth : 0); }
1426 /// valid only inside [beginFrame]/[endFrame]
1427 /// Group: context_management
1428 public int height (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.mHeight : 0); }
1430 /// valid only inside [beginFrame]/[endFrame]
1431 /// Group: context_management
1432 public float devicePixelRatio (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.mDeviceRatio : float.nan); }
1434 // path autoregistration
1436 /// [pickid] to stop autoregistration.
1437 /// Group: context_management
1438 public enum NVGNoPick = -1;
1440 /// >=0: this pickid will be assigned to all filled/stroked paths
1441 /// Group: context_management
1442 public int pickid (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.pathPickId : NVGNoPick); }
1444 /// >=0: this pickid will be assigned to all filled/stroked paths
1445 /// Group: context_management
1446 public void pickid (NVGContext ctx, int v) nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null) ctx.pathPickId = v; }
1448 /// pick autoregistration mode; see [NVGPickKind]
1449 /// Group: context_management
1450 public uint pickmode (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.pathPickRegistered&NVGPickKind.All : 0); }
1452 /// pick autoregistration mode; see [NVGPickKind]
1453 /// Group: context_management
1454 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); }
1456 // tesselator options
1459 /// Group: context_management
1460 public NVGTesselation tesselation (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null ? ctx.tesselatortype : NVGTesselation.DeCasteljau); }
1463 /// Group: context_management
1464 public void tesselation (NVGContext ctx, NVGTesselation v) nothrow @trusted @nogc { pragma(inline, true); if (ctx !is null) ctx.tesselatortype = v; }
1467 private struct NVGcontextinternal {
1468 private:
1469 NVGparams params;
1470 float* commands;
1471 int ccommands;
1472 int ncommands;
1473 float commandx, commandy;
1474 NVGstate[NVG_MAX_STATES] states;
1475 int nstates;
1476 NVGpathCache* cache;
1477 public float tessTol;
1478 public float angleTol; // 0.0f -- angle tolerance for McSeem Bezier rasterizer
1479 public float cuspLimit; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
1480 float distTol;
1481 public float fringeWidth;
1482 float devicePxRatio;
1483 FONScontext* fs;
1484 NVGImage[NVG_MAX_FONTIMAGES] fontImages;
1485 int fontImageIdx;
1486 int drawCallCount;
1487 int fillTriCount;
1488 int strokeTriCount;
1489 int textTriCount;
1490 NVGTesselation tesselatortype;
1491 // picking API
1492 NVGpickScene* pickScene;
1493 int pathPickId; // >=0: register all paths for picking using this id
1494 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
1495 // path recording
1496 NVGPathSet recset;
1497 int recstart; // used to cancel recording
1498 bool recblockdraw;
1499 // internals
1500 NVGMatrix gpuAffine;
1501 int mWidth, mHeight;
1502 float mDeviceRatio;
1503 // image manager
1504 int imageCount; // number of alive images in this context
1505 bool contextAlive; // context can be dead, but still contain some images
1507 @disable this (this); // no copies
1510 void nvg__imageIncRef (NVGContext ctx, int imgid) nothrow @trusted @nogc {
1511 if (ctx !is null && imgid > 0) {
1512 ++ctx.imageCount;
1513 version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("image[++]ref: context %p: %d image refs (%d)\n", ctx, ctx.imageCount, imgid); }
1514 if (ctx.contextAlive) ctx.params.renderTextureIncRef(ctx.params.userPtr, imgid);
1518 void nvg__imageDecRef (NVGContext ctx, int imgid) nothrow @trusted @nogc {
1519 if (ctx !is null && imgid > 0) {
1520 assert(ctx.imageCount > 0);
1521 --ctx.imageCount;
1522 version(nanovega_debug_image_manager_rc) { import core.stdc.stdio; printf("image[--]ref: context %p: %d image refs (%d)\n", ctx, ctx.imageCount, imgid); }
1523 if (ctx.contextAlive) ctx.params.renderDeleteTexture(ctx.params.userPtr, imgid);
1524 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); }
1525 if (!ctx.contextAlive && ctx.imageCount == 0) {
1526 // it is finally safe to free context memory
1527 import core.stdc.stdlib : free;
1528 version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("killed zombie context %p\n", ctx); }
1529 free(ctx);
1535 public import core.stdc.math :
1536 nvg__sqrtf = sqrtf,
1537 nvg__modf = fmodf,
1538 nvg__sinf = sinf,
1539 nvg__cosf = cosf,
1540 nvg__tanf = tanf,
1541 nvg__atan2f = atan2f,
1542 nvg__acosf = acosf,
1543 nvg__ceilf = ceilf;
1545 version(Windows) {
1546 public int nvg__lrintf (float f) nothrow @trusted @nogc { pragma(inline, true); return cast(int)(f+0.5); }
1547 } else {
1548 public import core.stdc.math : nvg__lrintf = lrintf;
1551 public auto nvg__min(T) (T a, T b) { pragma(inline, true); return (a < b ? a : b); }
1552 public auto nvg__max(T) (T a, T b) { pragma(inline, true); return (a > b ? a : b); }
1553 public auto nvg__clamp(T) (T a, T mn, T mx) { pragma(inline, true); return (a < mn ? mn : (a > mx ? mx : a)); }
1554 //float nvg__absf() (float a) { pragma(inline, true); return (a >= 0.0f ? a : -a); }
1555 public auto nvg__sign(T) (T a) { pragma(inline, true); return (a >= cast(T)0 ? cast(T)1 : cast(T)(-1)); }
1556 public float nvg__cross() (float dx0, float dy0, float dx1, float dy1) { pragma(inline, true); return (dx1*dy0-dx0*dy1); }
1558 public import core.stdc.math : nvg__absf = fabsf;
1561 float nvg__normalize (float* x, float* y) nothrow @safe @nogc {
1562 float d = nvg__sqrtf((*x)*(*x)+(*y)*(*y));
1563 if (d > 1e-6f) {
1564 immutable float id = 1.0f/d;
1565 *x *= id;
1566 *y *= id;
1568 return d;
1571 void nvg__deletePathCache (ref NVGpathCache* c) nothrow @trusted @nogc {
1572 if (c !is null) {
1573 c.clear();
1574 free(c);
1578 NVGpathCache* nvg__allocPathCache () nothrow @trusted @nogc {
1579 NVGpathCache* c = cast(NVGpathCache*)malloc(NVGpathCache.sizeof);
1580 if (c is null) goto error;
1581 memset(c, 0, NVGpathCache.sizeof);
1583 c.points = cast(NVGpoint*)malloc(NVGpoint.sizeof*NVG_INIT_POINTS_SIZE);
1584 if (c.points is null) goto error;
1585 assert(c.npoints == 0);
1586 c.cpoints = NVG_INIT_POINTS_SIZE;
1588 c.paths = cast(NVGpath*)malloc(NVGpath.sizeof*NVG_INIT_PATHS_SIZE);
1589 if (c.paths is null) goto error;
1590 assert(c.npaths == 0);
1591 c.cpaths = NVG_INIT_PATHS_SIZE;
1593 c.verts = cast(NVGvertex*)malloc(NVGvertex.sizeof*NVG_INIT_VERTS_SIZE);
1594 if (c.verts is null) goto error;
1595 assert(c.nverts == 0);
1596 c.cverts = NVG_INIT_VERTS_SIZE;
1598 return c;
1600 error:
1601 nvg__deletePathCache(c);
1602 return null;
1605 void nvg__setDevicePixelRatio (NVGContext ctx, float ratio) pure nothrow @safe @nogc {
1606 ctx.tessTol = 0.25f/ratio;
1607 ctx.distTol = 0.01f/ratio;
1608 ctx.fringeWidth = 1.0f/ratio;
1609 ctx.devicePxRatio = ratio;
1612 NVGCompositeOperationState nvg__compositeOperationState (NVGCompositeOperation op) pure nothrow @safe @nogc {
1613 NVGCompositeOperationState state;
1614 NVGBlendFactor sfactor, dfactor;
1616 if (op == NVGCompositeOperation.SourceOver) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.OneMinusSrcAlpha;}
1617 else if (op == NVGCompositeOperation.SourceIn) { sfactor = NVGBlendFactor.DstAlpha; dfactor = NVGBlendFactor.Zero; }
1618 else if (op == NVGCompositeOperation.SourceOut) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.Zero; }
1619 else if (op == NVGCompositeOperation.SourceAtop) { sfactor = NVGBlendFactor.DstAlpha; dfactor = NVGBlendFactor.OneMinusSrcAlpha; }
1620 else if (op == NVGCompositeOperation.DestinationOver) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.One; }
1621 else if (op == NVGCompositeOperation.DestinationIn) { sfactor = NVGBlendFactor.Zero; dfactor = NVGBlendFactor.SrcAlpha; }
1622 else if (op == NVGCompositeOperation.DestinationOut) { sfactor = NVGBlendFactor.Zero; dfactor = NVGBlendFactor.OneMinusSrcAlpha; }
1623 else if (op == NVGCompositeOperation.DestinationAtop) { sfactor = NVGBlendFactor.OneMinusDstAlpha; dfactor = NVGBlendFactor.SrcAlpha; }
1624 else if (op == NVGCompositeOperation.Lighter) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.One; }
1625 else if (op == NVGCompositeOperation.Copy) { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.Zero; }
1626 else if (op == NVGCompositeOperation.Xor) {
1627 state.simple = false;
1628 state.srcRGB = NVGBlendFactor.OneMinusDstColor;
1629 state.srcAlpha = NVGBlendFactor.OneMinusDstAlpha;
1630 state.dstRGB = NVGBlendFactor.OneMinusSrcColor;
1631 state.dstAlpha = NVGBlendFactor.OneMinusSrcAlpha;
1632 return state;
1634 else { sfactor = NVGBlendFactor.One; dfactor = NVGBlendFactor.OneMinusSrcAlpha; } // default value for invalid op: SourceOver
1636 state.simple = true;
1637 state.srcAlpha = sfactor;
1638 state.dstAlpha = dfactor;
1639 return state;
1642 NVGstate* nvg__getState (NVGContext ctx) pure nothrow @trusted @nogc {
1643 pragma(inline, true);
1644 return &ctx.states.ptr[ctx.nstates-1];
1647 // Constructor called by the render back-end.
1648 NVGContext createInternal (NVGparams* params) nothrow @trusted @nogc {
1649 FONSparams fontParams = void;
1650 NVGContext ctx = cast(NVGContext)malloc(NVGcontextinternal.sizeof);
1651 if (ctx is null) goto error;
1652 memset(ctx, 0, NVGcontextinternal.sizeof);
1654 ctx.angleTol = 0; // angle tolerance for McSeem Bezier rasterizer
1655 ctx.cuspLimit = 0; // cusp limit for McSeem Bezier rasterizer (0: real cusps)
1657 ctx.contextAlive = true;
1659 ctx.params = *params;
1660 //ctx.fontImages[0..NVG_MAX_FONTIMAGES] = 0;
1662 ctx.commands = cast(float*)malloc(float.sizeof*NVG_INIT_COMMANDS_SIZE);
1663 if (ctx.commands is null) goto error;
1664 ctx.ncommands = 0;
1665 ctx.ccommands = NVG_INIT_COMMANDS_SIZE;
1667 ctx.cache = nvg__allocPathCache();
1668 if (ctx.cache is null) goto error;
1670 ctx.save();
1671 ctx.reset();
1673 nvg__setDevicePixelRatio(ctx, 1.0f);
1675 if (!ctx.params.renderCreate(ctx.params.userPtr)) goto error;
1677 // init font rendering
1678 memset(&fontParams, 0, fontParams.sizeof);
1679 fontParams.width = NVG_INIT_FONTIMAGE_SIZE;
1680 fontParams.height = NVG_INIT_FONTIMAGE_SIZE;
1681 fontParams.flags = FONS_ZERO_TOPLEFT;
1682 fontParams.renderCreate = null;
1683 fontParams.renderUpdate = null;
1684 debug(nanovega) fontParams.renderDraw = null;
1685 fontParams.renderDelete = null;
1686 fontParams.userPtr = null;
1687 ctx.fs = fonsCreateInternal(&fontParams);
1688 if (ctx.fs is null) goto error;
1690 // create font texture
1691 ctx.fontImages[0].id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.Alpha, fontParams.width, fontParams.height, (ctx.params.fontAA ? 0 : NVGImageFlag.NoFiltering), null);
1692 if (ctx.fontImages[0].id == 0) goto error;
1693 ctx.fontImages[0].ctx = ctx;
1694 ctx.nvg__imageIncRef(ctx.fontImages[0].id);
1695 ctx.fontImageIdx = 0;
1697 ctx.pathPickId = -1;
1698 ctx.tesselatortype = NVG_DEFAULT_TESSELATOR;
1700 return ctx;
1702 error:
1703 ctx.deleteInternal();
1704 return null;
1707 // Called by render backend.
1708 NVGparams* internalParams (NVGContext ctx) nothrow @trusted @nogc {
1709 return &ctx.params;
1712 // Destructor called by the render back-end.
1713 void deleteInternal (ref NVGContext ctx) nothrow @trusted @nogc {
1714 if (ctx is null) return;
1715 if (ctx.contextAlive) {
1716 if (ctx.commands !is null) free(ctx.commands);
1717 nvg__deletePathCache(ctx.cache);
1719 if (ctx.fs) fonsDeleteInternal(ctx.fs);
1721 foreach (uint i; 0..NVG_MAX_FONTIMAGES) ctx.fontImages[i].clear();
1723 if (ctx.params.renderDelete !is null) ctx.params.renderDelete(ctx.params.userPtr);
1725 if (ctx.pickScene !is null) nvg__deletePickScene(ctx.pickScene);
1727 ctx.contextAlive = false;
1729 if (ctx.imageCount == 0) {
1730 version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("destroyed context %p\n", ctx); }
1731 free(ctx);
1732 } else {
1733 version(nanovega_debug_image_manager) { import core.stdc.stdio; printf("context %p is zombie now (%d image refs)\n", ctx, ctx.imageCount); }
1738 /// Delete NanoVega context.
1739 /// Group: context_management
1740 public void kill (ref NVGContext ctx) nothrow @trusted @nogc {
1741 if (ctx !is null) {
1742 ctx.deleteInternal();
1743 ctx = null;
1747 /// Returns `true` if the given context is not `null` and can be used for painting.
1748 /// Group: context_management
1749 public bool valid (in NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx !is null && ctx.contextAlive); }
1752 // ////////////////////////////////////////////////////////////////////////// //
1753 // Frame Management
1755 /** Begin drawing a new frame.
1757 * Calls to NanoVega drawing API should be wrapped in [beginFrame] and [endFrame]
1759 * [beginFrame] defines the size of the window to render to in relation currently
1760 * set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
1761 * control the rendering on Hi-DPI devices.
1763 * For example, GLFW returns two dimension for an opened window: window size and
1764 * frame buffer size. In that case you would set windowWidth/windowHeight to the window size,
1765 * devicePixelRatio to: `windowWidth/windowHeight`.
1767 * Default ratio is `1`.
1769 * Note that fractional ratio can (and will) distort your fonts and images.
1771 * This call also resets pick marks (see picking API for non-rasterized paths),
1772 * path recording, and GPU affine transformatin matrix.
1774 * see also [glNVGClearFlags], which returns necessary flags for [glClear].
1776 * Group: frame_management
1778 public void beginFrame (NVGContext ctx, int windowWidth, int windowHeight, float devicePixelRatio=1.0f) nothrow @trusted @nogc {
1779 import std.math : isNaN;
1781 printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
1782 ctx.drawCallCount, ctx.fillTriCount, ctx.strokeTriCount, ctx.textTriCount,
1783 ctx.fillTriCount+ctx.strokeTriCount+ctx.textTriCount);
1786 if (windowWidth < 1) windowWidth = 1;
1787 if (windowHeight < 1) windowHeight = 1;
1789 if (isNaN(devicePixelRatio)) devicePixelRatio = (windowHeight > 0 ? cast(float)windowWidth/cast(float)windowHeight : 1024.0/768.0);
1791 foreach (ref NVGstate st; ctx.states[0..ctx.nstates]) st.clearPaint();
1792 ctx.nstates = 0;
1793 ctx.save();
1794 ctx.reset();
1796 nvg__setDevicePixelRatio(ctx, devicePixelRatio);
1798 ctx.params.renderViewport(ctx.params.userPtr, windowWidth, windowHeight);
1799 ctx.mWidth = windowWidth;
1800 ctx.mHeight = windowHeight;
1801 ctx.mDeviceRatio = devicePixelRatio;
1803 ctx.recset = null;
1804 ctx.recstart = -1;
1806 ctx.pathPickId = NVGNoPick;
1807 ctx.pathPickRegistered = 0;
1809 ctx.drawCallCount = 0;
1810 ctx.fillTriCount = 0;
1811 ctx.strokeTriCount = 0;
1812 ctx.textTriCount = 0;
1814 ctx.gpuAffine = NVGMatrix.Identity;
1816 nvg__pickBeginFrame(ctx, windowWidth, windowHeight);
1819 /// Cancels drawing the current frame. Cancels path recording.
1820 /// Group: frame_management
1821 public void cancelFrame (NVGContext ctx) nothrow @trusted @nogc {
1822 ctx.cancelRecording();
1823 ctx.mWidth = 0;
1824 ctx.mHeight = 0;
1825 ctx.mDeviceRatio = 0;
1826 // cancel render queue
1827 ctx.params.renderCancel(ctx.params.userPtr);
1830 /// Ends drawing the current frame (flushing remaining render state). Commits recorded paths.
1831 /// Group: frame_management
1832 public void endFrame (NVGContext ctx) nothrow @trusted @nogc {
1833 if (ctx.recset !is null) ctx.recset.takeCurrentPickScene(ctx);
1834 ctx.stopRecording();
1835 ctx.mWidth = 0;
1836 ctx.mHeight = 0;
1837 ctx.mDeviceRatio = 0;
1838 // flush render queue
1839 NVGstate* state = nvg__getState(ctx);
1840 ctx.params.renderFlush(ctx.params.userPtr);
1841 if (ctx.fontImageIdx != 0) {
1842 auto fontImage = ctx.fontImages[ctx.fontImageIdx];
1843 int j = 0, iw, ih;
1844 // delete images that smaller than current one
1845 if (!fontImage.valid) return;
1846 ctx.imageSize(fontImage, iw, ih);
1847 foreach (int i; 0..ctx.fontImageIdx) {
1848 if (ctx.fontImages[i].valid) {
1849 int nw, nh;
1850 ctx.imageSize(ctx.fontImages[i], nw, nh);
1851 if (nw < iw || nh < ih) {
1852 ctx.deleteImage(ctx.fontImages[i]);
1853 } else {
1854 ctx.fontImages[j++] = ctx.fontImages[i];
1858 // make current font image to first
1859 ctx.fontImages[j++] = ctx.fontImages[0];
1860 ctx.fontImages[0] = fontImage;
1861 ctx.fontImageIdx = 0;
1862 // clear all images after j
1863 ctx.fontImages[j..NVG_MAX_FONTIMAGES] = NVGImage.init;
1868 // ////////////////////////////////////////////////////////////////////////// //
1869 // Recording and Replaying Pathes
1871 /// Saved path set.
1872 /// Group: path_recording
1873 public alias NVGPathSet = NVGPathSetS*;
1876 //TODO: save scissor info?
1877 struct NVGPathSetS {
1878 private:
1879 // either path cache, or text item
1880 static struct Node {
1881 NVGPaint paint;
1882 NVGpathCache* path;
1885 private:
1886 Node* nodes;
1887 int nnodes, cnodes;
1888 NVGpickScene* pickscene;
1889 //int npickscenes, cpickscenes;
1890 NVGContext svctx; // used to do some sanity checks, and to free resources
1892 private:
1893 Node* allocNode () nothrow @trusted @nogc {
1894 import core.stdc.string : memset;
1895 // grow buffer if necessary
1896 if (nnodes+1 > cnodes) {
1897 import core.stdc.stdlib : realloc;
1898 int newsz = (cnodes == 0 ? 8 : cnodes <= 1024 ? cnodes*2 : cnodes+1024);
1899 nodes = cast(Node*)realloc(nodes, newsz*Node.sizeof);
1900 if (nodes is null) assert(0, "NanoVega: out of memory");
1901 //memset(svp.caches+svp.ccaches, 0, (newsz-svp.ccaches)*NVGpathCache.sizeof);
1902 cnodes = newsz;
1904 assert(nnodes < cnodes);
1905 memset(nodes+nnodes, 0, Node.sizeof);
1906 return &nodes[nnodes++];
1909 Node* allocPathNode () nothrow @trusted @nogc {
1910 import core.stdc.stdlib : malloc;
1911 import core.stdc.string : memset;
1912 auto node = allocNode();
1913 // allocate path cache
1914 auto pc = cast(NVGpathCache*)malloc(NVGpathCache.sizeof);
1915 if (pc is null) assert(0, "NanoVega: out of memory");
1916 node.path = pc;
1917 return node;
1920 void clearNode (int idx) nothrow @trusted @nogc {
1921 if (idx < 0 || idx >= nnodes) return;
1922 Node* node = &nodes[idx];
1923 if (svctx !is null && node.paint.image.valid) node.paint.image.clear();
1924 if (node.path !is null) node.path.clear();
1927 private:
1928 void takeCurrentPickScene (NVGContext ctx) nothrow @trusted @nogc {
1929 NVGpickScene* ps = ctx.pickScene;
1930 if (ps is null) return; // nothing to do
1931 if (ps.npaths == 0) return; // pick scene is empty
1932 ctx.pickScene = null;
1933 pickscene = ps;
1936 void replay (NVGContext ctx, in ref NVGColor fillTint, in ref NVGColor strokeTint) nothrow @trusted @nogc {
1937 NVGstate* state = nvg__getState(ctx);
1938 foreach (ref node; nodes[0..nnodes]) {
1939 if (auto cc = node.path) {
1940 if (cc.npaths <= 0) continue;
1942 if (cc.fillReady) {
1943 NVGPaint fillPaint = node.paint;
1945 // apply global alpha
1946 fillPaint.innerColor.a *= state.alpha;
1947 fillPaint.outerColor.a *= state.alpha;
1949 fillPaint.innerColor.applyTint(fillTint);
1950 fillPaint.outerColor.applyTint(fillTint);
1952 ctx.params.renderFill(ctx.params.userPtr, state.compositeOperation, cc.clipmode, &fillPaint, &state.scissor, cc.fringeWidth, cc.bounds.ptr, cc.paths, cc.npaths, cc.evenOddMode);
1954 // count triangles
1955 foreach (int i; 0..cc.npaths) {
1956 NVGpath* path = &cc.paths[i];
1957 ctx.fillTriCount += path.nfill-2;
1958 ctx.fillTriCount += path.nstroke-2;
1959 ctx.drawCallCount += 2;
1963 if (cc.strokeReady) {
1964 NVGPaint strokePaint = node.paint;
1966 strokePaint.innerColor.a *= cc.strokeAlphaMul;
1967 strokePaint.outerColor.a *= cc.strokeAlphaMul;
1969 // apply global alpha
1970 strokePaint.innerColor.a *= state.alpha;
1971 strokePaint.outerColor.a *= state.alpha;
1973 strokePaint.innerColor.applyTint(strokeTint);
1974 strokePaint.outerColor.applyTint(strokeTint);
1976 ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, cc.clipmode, &strokePaint, &state.scissor, cc.fringeWidth, cc.strokeWidth, cc.paths, cc.npaths);
1978 // count triangles
1979 foreach (int i; 0..cc.npaths) {
1980 NVGpath* path = &cc.paths[i];
1981 ctx.strokeTriCount += path.nstroke-2;
1982 ++ctx.drawCallCount;
1989 public:
1990 @disable this (this); // no copies
1992 // pick test
1993 // Call delegate [dg] for each path under the specified position (in no particular order).
1994 // Returns the id of the path for which delegate [dg] returned true or -1.
1995 // dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
1996 int hitTestDG(bool bestOrder=false, DG) (in float x, in float y, NVGPickKind kind, scope DG dg) if (IsGoodHitTestDG!DG || IsGoodHitTestInternalDG!DG) {
1997 if (pickscene is null) return -1;
1999 NVGpickScene* ps = pickscene;
2000 int levelwidth = 1<<(ps.nlevels-1);
2001 int cellx = nvg__clamp(cast(int)(x/ps.xdim), 0, levelwidth);
2002 int celly = nvg__clamp(cast(int)(y/ps.ydim), 0, levelwidth);
2003 int npicked = 0;
2005 for (int lvl = ps.nlevels-1; lvl >= 0; --lvl) {
2006 NVGpickPath* pp = ps.levels[lvl][celly*levelwidth+cellx];
2007 while (pp !is null) {
2008 if (nvg__pickPathTestBounds(svctx, ps, pp, x, y)) {
2009 int hit = 0;
2010 if ((kind&NVGPickKind.Stroke) && (pp.flags&NVGPathFlags.Stroke)) hit = nvg__pickPathStroke(ps, pp, x, y);
2011 if (!hit && (kind&NVGPickKind.Fill) && (pp.flags&NVGPathFlags.Fill)) hit = nvg__pickPath(ps, pp, x, y);
2012 if (hit) {
2013 static if (IsGoodHitTestDG!DG) {
2014 static if (__traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); })) {
2015 if (dg(pp.id, cast(int)pp.order)) return pp.id;
2016 } else {
2017 dg(pp.id, cast(int)pp.order);
2019 } else {
2020 static if (__traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); })) {
2021 if (dg(pp)) return pp.id;
2022 } else {
2023 dg(pp);
2028 pp = pp.next;
2030 cellx >>= 1;
2031 celly >>= 1;
2032 levelwidth >>= 1;
2035 return -1;
2038 // Fills ids with a list of the top most hit ids under the specified position.
2039 // Returns the slice of [ids].
2040 int[] hitTestAll (in float x, in float y, NVGPickKind kind, int[] ids) nothrow @trusted @nogc {
2041 if (pickscene is null || ids.length == 0) return ids[0..0];
2043 int npicked = 0;
2044 NVGpickScene* ps = pickscene;
2046 hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
2047 if (npicked == ps.cpicked) {
2048 int cpicked = ps.cpicked+ps.cpicked;
2049 NVGpickPath** picked = cast(NVGpickPath**)realloc(ps.picked, (NVGpickPath*).sizeof*ps.cpicked);
2050 if (picked is null) return true; // abort
2051 ps.cpicked = cpicked;
2052 ps.picked = picked;
2054 ps.picked[npicked] = pp;
2055 ++npicked;
2056 return false; // go on
2059 qsort(ps.picked, npicked, (NVGpickPath*).sizeof, &nvg__comparePaths);
2061 assert(npicked >= 0);
2062 if (npicked > ids.length) npicked = cast(int)ids.length;
2063 foreach (immutable nidx, ref int did; ids[0..npicked]) did = ps.picked[nidx].id;
2065 return ids[0..npicked];
2068 // Returns the id of the pickable shape containing x,y or -1 if no shape was found.
2069 int hitTest (in float x, in float y, NVGPickKind kind) nothrow @trusted @nogc {
2070 if (pickscene is null) return -1;
2072 int bestOrder = -1;
2073 int bestID = -1;
2075 hitTestDG!true(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
2076 if (pp.order > bestOrder) {
2077 bestOrder = pp.order;
2078 bestID = pp.id;
2082 return bestID;
2086 // Append current path to existing path set. Is is safe to call this with `null` [svp].
2087 void appendCurrentPathToCache (NVGContext ctx, NVGPathSet svp, in ref NVGPaint paint) nothrow @trusted @nogc {
2088 if (ctx is null || svp is null) return;
2089 if (ctx !is svp.svctx) assert(0, "NanoVega: cannot save paths from different contexts");
2090 if (ctx.ncommands == 0) {
2091 assert(ctx.cache.npaths == 0);
2092 return;
2094 if (!ctx.cache.fillReady && !ctx.cache.strokeReady) return;
2096 // tesselate current path
2097 //if (!ctx.cache.fillReady) nvg__prepareFill(ctx);
2098 //if (!ctx.cache.strokeReady) nvg__prepareStroke(ctx);
2100 auto node = svp.allocPathNode();
2101 NVGpathCache* cc = node.path;
2102 cc.copyFrom(ctx.cache);
2103 node.paint = paint;
2104 // copy path commands (we may need 'em for picking)
2105 version(all) {
2106 cc.ncommands = ctx.ncommands;
2107 if (cc.ncommands) {
2108 import core.stdc.stdlib : malloc;
2109 import core.stdc.string : memcpy;
2110 cc.commands = cast(float*)malloc(ctx.ncommands*float.sizeof);
2111 if (cc.commands is null) assert(0, "NanoVega: out of memory");
2112 memcpy(cc.commands, ctx.commands, ctx.ncommands*float.sizeof);
2113 } else {
2114 cc.commands = null;
2119 /// Create new empty path set.
2120 /// Group: path_recording
2121 public NVGPathSet newPathSet (NVGContext ctx) nothrow @trusted @nogc {
2122 import core.stdc.stdlib : malloc;
2123 import core.stdc.string : memset;
2124 if (ctx is null) return null;
2125 NVGPathSet res = cast(NVGPathSet)malloc(NVGPathSetS.sizeof);
2126 if (res is null) assert(0, "NanoVega: out of memory");
2127 memset(res, 0, NVGPathSetS.sizeof);
2128 res.svctx = ctx;
2129 return res;
2132 /// Is the given path set empty? Empty path set can be `null`.
2133 /// Group: path_recording
2134 public bool empty (NVGPathSet svp) pure nothrow @safe @nogc { pragma(inline, true); return (svp is null || svp.nnodes == 0); }
2136 /// Clear path set contents. Will release $(B some) allocated memory (this function is meant to clear something that will be reused).
2137 /// Group: path_recording
2138 public void clear (NVGPathSet svp) nothrow @trusted @nogc {
2139 if (svp !is null) {
2140 import core.stdc.stdlib : free;
2141 foreach (immutable idx; 0.. svp.nnodes) svp.clearNode(idx);
2142 svp.nnodes = 0;
2146 /// Destroy path set (frees all allocated memory).
2147 /// Group: path_recording
2148 public void kill (ref NVGPathSet svp) nothrow @trusted @nogc {
2149 if (svp !is null) {
2150 import core.stdc.stdlib : free;
2151 svp.clear();
2152 if (svp.nodes !is null) free(svp.nodes);
2153 free(svp);
2154 if (svp.pickscene !is null) nvg__deletePickScene(svp.pickscene);
2155 svp = null;
2159 /// Start path recording. [svp] should be alive until recording is cancelled or stopped.
2160 /// Group: path_recording
2161 public void startRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc {
2162 if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2163 ctx.stopRecording();
2164 ctx.recset = svp;
2165 ctx.recstart = (svp !is null ? svp.nnodes : -1);
2166 ctx.recblockdraw = false;
2169 /** Start path recording. [svp] should be alive until recording is cancelled or stopped.
2171 * This will block all rendering, so you can call your rendering functions to record paths without actual drawing.
2172 * Commiting or cancelling will re-enable rendering.
2173 * You can call this with `null` svp to block rendering without recording any paths.
2175 * Group: path_recording
2177 public void startBlockingRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc {
2178 if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2179 ctx.stopRecording();
2180 ctx.recset = svp;
2181 ctx.recstart = (svp !is null ? svp.nnodes : -1);
2182 ctx.recblockdraw = true;
2185 /// Commit recorded paths. It is safe to call this when recording is not started.
2186 /// Group: path_recording
2187 public void stopRecording (NVGContext ctx) nothrow @trusted @nogc {
2188 if (ctx.recset !is null && ctx.recset.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2189 if (ctx.recset !is null) ctx.recset.takeCurrentPickScene(ctx);
2190 ctx.recset = null;
2191 ctx.recstart = -1;
2192 ctx.recblockdraw = false;
2195 /// Cancel path recording.
2196 /// Group: path_recording
2197 public void cancelRecording (NVGContext ctx) nothrow @trusted @nogc {
2198 if (ctx.recset !is null) {
2199 if (ctx.recset.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2200 assert(ctx.recstart >= 0 && ctx.recstart <= ctx.recset.nnodes);
2201 foreach (immutable idx; ctx.recstart..ctx.recset.nnodes) ctx.recset.clearNode(idx);
2202 ctx.recset.nnodes = ctx.recstart;
2203 ctx.recset = null;
2204 ctx.recstart = -1;
2206 ctx.recblockdraw = false;
2209 /** Replay saved path set.
2211 * Replaying record while you're recording another one is undefined behavior.
2213 * Group: path_recording
2215 public void replayRecording() (NVGContext ctx, NVGPathSet svp, in auto ref NVGColor fillTint, in auto ref NVGColor strokeTint) nothrow @trusted @nogc {
2216 if (svp !is null && svp.svctx !is ctx) assert(0, "NanoVega: cannot share path set between contexts");
2217 svp.replay(ctx, fillTint, strokeTint);
2220 /// Ditto.
2221 public void replayRecording() (NVGContext ctx, NVGPathSet svp, in auto ref NVGColor fillTint) nothrow @trusted @nogc { ctx.replayRecording(svp, fillTint, NVGColor.transparent); }
2223 /// Ditto.
2224 public void replayRecording (NVGContext ctx, NVGPathSet svp) nothrow @trusted @nogc { ctx.replayRecording(svp, NVGColor.transparent, NVGColor.transparent); }
2227 // ////////////////////////////////////////////////////////////////////////// //
2228 // Composite operation
2230 /// Sets the composite operation.
2231 /// Group: composite_operation
2232 public void globalCompositeOperation (NVGContext ctx, NVGCompositeOperation op) nothrow @trusted @nogc {
2233 NVGstate* state = nvg__getState(ctx);
2234 state.compositeOperation = nvg__compositeOperationState(op);
2237 /// Sets the composite operation with custom pixel arithmetic.
2238 /// Group: composite_operation
2239 public void globalCompositeBlendFunc (NVGContext ctx, NVGBlendFactor sfactor, NVGBlendFactor dfactor) nothrow @trusted @nogc {
2240 ctx.globalCompositeBlendFuncSeparate(sfactor, dfactor, sfactor, dfactor);
2243 /// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately.
2244 /// Group: composite_operation
2245 public void globalCompositeBlendFuncSeparate (NVGContext ctx, NVGBlendFactor srcRGB, NVGBlendFactor dstRGB, NVGBlendFactor srcAlpha, NVGBlendFactor dstAlpha) nothrow @trusted @nogc {
2246 NVGCompositeOperationState op;
2247 op.simple = false;
2248 op.srcRGB = srcRGB;
2249 op.dstRGB = dstRGB;
2250 op.srcAlpha = srcAlpha;
2251 op.dstAlpha = dstAlpha;
2252 NVGstate* state = nvg__getState(ctx);
2253 state.compositeOperation = op;
2257 // ////////////////////////////////////////////////////////////////////////// //
2258 // Color utils
2260 /// Returns a color value from string form.
2261 /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
2262 /// Group: color_utils
2263 public NVGColor nvgRGB (const(char)[] srgb) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(srgb); }
2265 /// Ditto.
2266 public NVGColor nvgRGBA (const(char)[] srgb) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(srgb); }
2268 /// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).
2269 /// Group: color_utils
2270 public NVGColor nvgRGB (int r, int g, int b) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(nvgClampToByte(r), nvgClampToByte(g), nvgClampToByte(b), 255); }
2272 /// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.
2273 /// Group: color_utils
2274 public NVGColor nvgRGBf (float r, float g, float b) nothrow @trusted @nogc { pragma(inline, true); return NVGColor(r, g, b, 1.0f); }
2276 /// Returns a color value from red, green, blue and alpha values.
2277 /// Group: color_utils
2278 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)); }
2280 /// Returns a color value from red, green, blue and alpha values.
2281 /// Group: color_utils
2282 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); }
2284 /// Returns new color with transparency (alpha) set to [a].
2285 /// Group: color_utils
2286 public NVGColor nvgTransRGBA (NVGColor c, ubyte a) nothrow @trusted @nogc {
2287 pragma(inline, true);
2288 c.a = a/255.0f;
2289 return c;
2292 /// Ditto.
2293 public NVGColor nvgTransRGBAf (NVGColor c, float a) nothrow @trusted @nogc {
2294 pragma(inline, true);
2295 c.a = a;
2296 return c;
2299 /// Linearly interpolates from color c0 to c1, and returns resulting color value.
2300 /// Group: color_utils
2301 public NVGColor nvgLerpRGBA() (in auto ref NVGColor c0, in auto ref NVGColor c1, float u) nothrow @trusted @nogc {
2302 NVGColor cint = void;
2303 u = nvg__clamp(u, 0.0f, 1.0f);
2304 float oneminu = 1.0f-u;
2305 foreach (uint i; 0..4) cint.rgba.ptr[i] = c0.rgba.ptr[i]*oneminu+c1.rgba.ptr[i]*u;
2306 return cint;
2309 /* see below
2310 public NVGColor nvgHSL() (float h, float s, float l) {
2311 //pragma(inline, true); // alas
2312 return nvgHSLA(h, s, l, 255);
2316 float nvg__hue (float h, float m1, float m2) pure nothrow @safe @nogc {
2317 if (h < 0) h += 1;
2318 if (h > 1) h -= 1;
2319 if (h < 1.0f/6.0f) return m1+(m2-m1)*h*6.0f;
2320 if (h < 3.0f/6.0f) return m2;
2321 if (h < 4.0f/6.0f) return m1+(m2-m1)*(2.0f/3.0f-h)*6.0f;
2322 return m1;
2325 /// Returns color value specified by hue, saturation and lightness.
2326 /// HSL values are all in range [0..1], alpha will be set to 255.
2327 /// Group: color_utils
2328 public alias nvgHSL = nvgHSLA; // trick to allow inlining
2330 /// Returns color value specified by hue, saturation and lightness and alpha.
2331 /// HSL values are all in range [0..1], alpha in range [0..255].
2332 /// Group: color_utils
2333 public NVGColor nvgHSLA (float h, float s, float l, ubyte a=255) nothrow @trusted @nogc {
2334 pragma(inline, true);
2335 NVGColor col = void;
2336 h = nvg__modf(h, 1.0f);
2337 if (h < 0.0f) h += 1.0f;
2338 s = nvg__clamp(s, 0.0f, 1.0f);
2339 l = nvg__clamp(l, 0.0f, 1.0f);
2340 immutable float m2 = (l <= 0.5f ? l*(1+s) : l+s-l*s);
2341 immutable float m1 = 2*l-m2;
2342 col.r = nvg__clamp(nvg__hue(h+1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2343 col.g = nvg__clamp(nvg__hue(h, m1, m2), 0.0f, 1.0f);
2344 col.b = nvg__clamp(nvg__hue(h-1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2345 col.a = a/255.0f;
2346 return col;
2349 /// Returns color value specified by hue, saturation and lightness and alpha.
2350 /// HSL values and alpha are all in range [0..1].
2351 /// Group: color_utils
2352 public NVGColor nvgHSLA (float h, float s, float l, float a) nothrow @trusted @nogc {
2353 // sorry for copypasta, it is for inliner
2354 static if (__VERSION__ >= 2072) pragma(inline, true);
2355 NVGColor col = void;
2356 h = nvg__modf(h, 1.0f);
2357 if (h < 0.0f) h += 1.0f;
2358 s = nvg__clamp(s, 0.0f, 1.0f);
2359 l = nvg__clamp(l, 0.0f, 1.0f);
2360 immutable m2 = (l <= 0.5f ? l*(1+s) : l+s-l*s);
2361 immutable m1 = 2*l-m2;
2362 col.r = nvg__clamp(nvg__hue(h+1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2363 col.g = nvg__clamp(nvg__hue(h, m1, m2), 0.0f, 1.0f);
2364 col.b = nvg__clamp(nvg__hue(h-1.0f/3.0f, m1, m2), 0.0f, 1.0f);
2365 col.a = a;
2366 return col;
2370 // ////////////////////////////////////////////////////////////////////////// //
2371 // Matrices and Transformations
2373 /** Matrix class.
2375 * Group: matrices
2377 public align(1) struct NVGMatrix {
2378 align(1):
2379 private:
2380 static immutable float[6] IdentityMat = [
2381 1.0f, 0.0f,
2382 0.0f, 1.0f,
2383 0.0f, 0.0f,
2386 public:
2387 /// Matrix values. Initial value is identity matrix.
2388 float[6] mat = [
2389 1.0f, 0.0f,
2390 0.0f, 1.0f,
2391 0.0f, 0.0f,
2394 public nothrow @trusted @nogc:
2395 /// Create Matrix with the given values.
2396 this (const(float)[] amat...) {
2397 pragma(inline, true);
2398 if (amat.length >= 6) {
2399 mat.ptr[0..6] = amat.ptr[0..6];
2400 } else {
2401 mat.ptr[0..6] = 0;
2402 mat.ptr[0..amat.length] = amat[];
2406 /// Can be used to check validity of [inverted] result
2407 @property bool valid () const { import core.stdc.math : isfinite; return (isfinite(mat.ptr[0]) != 0); }
2409 /// Returns `true` if this matrix is identity matrix.
2410 @property bool isIdentity () const { version(aliced) pragma(inline, true); return (mat[] == IdentityMat[]); }
2412 /// Returns new inverse matrix.
2413 /// If inverted matrix cannot be calculated, `res.valid` fill be `false`.
2414 NVGMatrix inverted () const {
2415 NVGMatrix res = this;
2416 res.invert;
2417 return res;
2420 /// Inverts this matrix.
2421 /// If inverted matrix cannot be calculated, `this.valid` fill be `false`.
2422 ref NVGMatrix invert () {
2423 float[6] inv = void;
2424 immutable double det = cast(double)mat.ptr[0]*mat.ptr[3]-cast(double)mat.ptr[2]*mat.ptr[1];
2425 if (det > -1e-6 && det < 1e-6) {
2426 inv[] = float.nan;
2427 } else {
2428 immutable double invdet = 1.0/det;
2429 inv.ptr[0] = cast(float)(mat.ptr[3]*invdet);
2430 inv.ptr[2] = cast(float)(-mat.ptr[2]*invdet);
2431 inv.ptr[4] = cast(float)((cast(double)mat.ptr[2]*mat.ptr[5]-cast(double)mat.ptr[3]*mat.ptr[4])*invdet);
2432 inv.ptr[1] = cast(float)(-mat.ptr[1]*invdet);
2433 inv.ptr[3] = cast(float)(mat.ptr[0]*invdet);
2434 inv.ptr[5] = cast(float)((cast(double)mat.ptr[1]*mat.ptr[4]-cast(double)mat.ptr[0]*mat.ptr[5])*invdet);
2436 mat.ptr[0..6] = inv.ptr[0..6];
2437 return this;
2440 /// Sets this matrix to identity matrix.
2441 ref NVGMatrix identity () { version(aliced) pragma(inline, true); mat[] = IdentityMat[]; return this; }
2443 /// Translate this matrix.
2444 ref NVGMatrix translate (in float tx, in float ty) {
2445 version(aliced) pragma(inline, true);
2446 return this.mul(Translated(tx, ty));
2449 /// Scale this matrix.
2450 ref NVGMatrix scale (in float sx, in float sy) {
2451 version(aliced) pragma(inline, true);
2452 return this.mul(Scaled(sx, sy));
2455 /// Rotate this matrix.
2456 ref NVGMatrix rotate (in float a) {
2457 version(aliced) pragma(inline, true);
2458 return this.mul(Rotated(a));
2461 /// Skew this matrix by X axis.
2462 ref NVGMatrix skewX (in float a) {
2463 version(aliced) pragma(inline, true);
2464 return this.mul(SkewedX(a));
2467 /// Skew this matrix by Y axis.
2468 ref NVGMatrix skewY (in float a) {
2469 version(aliced) pragma(inline, true);
2470 return this.mul(SkewedY(a));
2473 /// Skew this matrix by both axes.
2474 ref NVGMatrix skewY (in float ax, in float ay) {
2475 version(aliced) pragma(inline, true);
2476 return this.mul(SkewedXY(ax, ay));
2479 /// Transform point with this matrix. `null` destinations are allowed.
2480 /// [sx] and [sy] is the source point. [dx] and [dy] may point to the same variables.
2481 void point (float* dx, float* dy, float sx, float sy) nothrow @trusted @nogc {
2482 version(aliced) pragma(inline, true);
2483 if (dx !is null) *dx = sx*mat.ptr[0]+sy*mat.ptr[2]+mat.ptr[4];
2484 if (dy !is null) *dy = sx*mat.ptr[1]+sy*mat.ptr[3]+mat.ptr[5];
2487 /// Transform point with this matrix.
2488 void point (ref float x, ref float y) nothrow @trusted @nogc {
2489 version(aliced) pragma(inline, true);
2490 immutable float nx = x*mat.ptr[0]+y*mat.ptr[2]+mat.ptr[4];
2491 immutable float ny = x*mat.ptr[1]+y*mat.ptr[3]+mat.ptr[5];
2492 x = nx;
2493 y = ny;
2496 /// Sets this matrix to the result of multiplication of `this` and [s] (this * S).
2497 ref NVGMatrix mul() (in auto ref NVGMatrix s) {
2498 immutable float t0 = mat.ptr[0]*s.mat.ptr[0]+mat.ptr[1]*s.mat.ptr[2];
2499 immutable float t2 = mat.ptr[2]*s.mat.ptr[0]+mat.ptr[3]*s.mat.ptr[2];
2500 immutable float t4 = mat.ptr[4]*s.mat.ptr[0]+mat.ptr[5]*s.mat.ptr[2]+s.mat.ptr[4];
2501 mat.ptr[1] = mat.ptr[0]*s.mat.ptr[1]+mat.ptr[1]*s.mat.ptr[3];
2502 mat.ptr[3] = mat.ptr[2]*s.mat.ptr[1]+mat.ptr[3]*s.mat.ptr[3];
2503 mat.ptr[5] = mat.ptr[4]*s.mat.ptr[1]+mat.ptr[5]*s.mat.ptr[3]+s.mat.ptr[5];
2504 mat.ptr[0] = t0;
2505 mat.ptr[2] = t2;
2506 mat.ptr[4] = t4;
2507 return this;
2510 /// Sets this matrix to the result of multiplication of [s] and `this` (S * this).
2511 /// Sets the transform to the result of multiplication of two transforms, of A = B*A.
2512 /// Group: matrices
2513 ref NVGMatrix premul() (in auto ref NVGMatrix s) {
2514 NVGMatrix s2 = s;
2515 s2.mul(this);
2516 mat[] = s2.mat[];
2517 return this;
2520 /// Multiply this matrix by [s], return result as new matrix.
2521 /// Performs operations in this left-to-right order.
2522 NVGMatrix opBinary(string op="*") (in auto ref NVGMatrix s) const {
2523 version(aliced) pragma(inline, true);
2524 NVGMatrix res = this;
2525 res.mul(s);
2526 return res;
2529 /// Multiply this matrix by [s].
2530 /// Performs operations in this left-to-right order.
2531 ref NVGMatrix opOpAssign(string op="*") (in auto ref NVGMatrix s) {
2532 version(aliced) pragma(inline, true);
2533 return this.mul(s);
2536 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.
2537 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.
2538 float rotation () const { pragma(inline, true); return nvg__atan2f(mat.ptr[1], mat.ptr[0]); } /// Returns rotation of this matrix.
2539 float tx () const { pragma(inline, true); return mat.ptr[4]; } /// Returns x translation of this matrix.
2540 float ty () const { pragma(inline, true); return mat.ptr[5]; } /// Returns y translation of this matrix.
2542 ref NVGMatrix scaleX (in float v) { pragma(inline, true); return scaleRotateTransform(v, scaleY, rotation, tx, ty); } /// Sets x scaling of this matrix.
2543 ref NVGMatrix scaleY (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, v, rotation, tx, ty); } /// Sets y scaling of this matrix.
2544 ref NVGMatrix rotation (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, scaleY, v, tx, ty); } /// Sets rotation of this matrix.
2545 ref NVGMatrix tx (in float v) { pragma(inline, true); mat.ptr[4] = v; return this; } /// Sets x translation of this matrix.
2546 ref NVGMatrix ty (in float v) { pragma(inline, true); mat.ptr[5] = v; return this; } /// Sets y translation of this matrix.
2548 /// Utility function to be used in `setXXX()`.
2549 /// This is the same as doing: `mat.identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2550 ref NVGMatrix scaleRotateTransform (in float xscale, in float yscale, in float a, in float tx, in float ty) {
2551 immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2552 mat.ptr[0] = xscale*cs; mat.ptr[1] = yscale*sn;
2553 mat.ptr[2] = xscale*-sn; mat.ptr[3] = yscale*cs;
2554 mat.ptr[4] = tx; mat.ptr[5] = ty;
2555 return this;
2558 /// This is the same as doing: `mat.identity.rotate(a).translate(tx, ty)`, only faster
2559 ref NVGMatrix rotateTransform (in float a, in float tx, in float ty) {
2560 immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2561 mat.ptr[0] = cs; mat.ptr[1] = sn;
2562 mat.ptr[2] = -sn; mat.ptr[3] = cs;
2563 mat.ptr[4] = tx; mat.ptr[5] = ty;
2564 return this;
2567 /// Returns new identity matrix.
2568 static NVGMatrix Identity () { pragma(inline, true); return NVGMatrix.init; }
2570 /// Returns new translation matrix.
2571 static NVGMatrix Translated (in float tx, in float ty) {
2572 version(aliced) pragma(inline, true);
2573 NVGMatrix res = void;
2574 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
2575 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
2576 res.mat.ptr[4] = tx; res.mat.ptr[5] = ty;
2577 return res;
2580 /// Returns new scaling matrix.
2581 static NVGMatrix Scaled (in float sx, in float sy) {
2582 version(aliced) pragma(inline, true);
2583 NVGMatrix res = void;
2584 res.mat.ptr[0] = sx; res.mat.ptr[1] = 0.0f;
2585 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = sy;
2586 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2587 return res;
2590 /// Returns new rotation matrix. Angle is specified in radians.
2591 static NVGMatrix Rotated (in float a) {
2592 version(aliced) pragma(inline, true);
2593 immutable float cs = nvg__cosf(a), sn = nvg__sinf(a);
2594 NVGMatrix res = void;
2595 res.mat.ptr[0] = cs; res.mat.ptr[1] = sn;
2596 res.mat.ptr[2] = -sn; res.mat.ptr[3] = cs;
2597 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2598 return res;
2601 /// Returns new x-skewing matrix. Angle is specified in radians.
2602 static NVGMatrix SkewedX (in float a) {
2603 version(aliced) pragma(inline, true);
2604 NVGMatrix res = void;
2605 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
2606 res.mat.ptr[2] = nvg__tanf(a); res.mat.ptr[3] = 1.0f;
2607 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2608 return res;
2611 /// Returns new y-skewing matrix. Angle is specified in radians.
2612 static NVGMatrix SkewedY (in float a) {
2613 version(aliced) pragma(inline, true);
2614 NVGMatrix res = void;
2615 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = nvg__tanf(a);
2616 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
2617 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2618 return res;
2621 /// Returns new xy-skewing matrix. Angles are specified in radians.
2622 static NVGMatrix SkewedXY (in float ax, in float ay) {
2623 version(aliced) pragma(inline, true);
2624 NVGMatrix res = void;
2625 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = nvg__tanf(ay);
2626 res.mat.ptr[2] = nvg__tanf(ax); res.mat.ptr[3] = 1.0f;
2627 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
2628 return res;
2631 /// Utility function to be used in `setXXX()`.
2632 /// This is the same as doing: `NVGMatrix.Identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2633 static NVGMatrix ScaledRotatedTransformed (in float xscale, in float yscale, in float a, in float tx, in float ty) {
2634 NVGMatrix res = void;
2635 res.scaleRotateTransform(xscale, yscale, a, tx, ty);
2636 return res;
2639 /// This is the same as doing: `NVGMatrix.Identity.rotate(a).translate(tx, ty)`, only faster
2640 static NVGMatrix RotatedTransformed (in float a, in float tx, in float ty) {
2641 NVGMatrix res = void;
2642 res.rotateTransform(a, tx, ty);
2643 return res;
2648 /// Converts degrees to radians.
2649 /// Group: matrices
2650 public float nvgDegToRad() (in float deg) pure nothrow @safe @nogc { pragma(inline, true); return deg/180.0f*NVG_PI; }
2652 /// Converts radians to degrees.
2653 /// Group: matrices
2654 public float nvgRadToDeg() (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad/NVG_PI*180.0f; }
2656 public alias nvgDegrees = nvgDegToRad; /// Use this like `42.nvgDegrees`
2657 public float nvgRadians() (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad; } /// Use this like `0.1.nvgRadians`
2660 // ////////////////////////////////////////////////////////////////////////// //
2661 void nvg__setPaintColor() (ref NVGPaint p, in auto ref NVGColor color) nothrow @trusted @nogc {
2662 p.clear();
2663 p.xform.identity;
2664 p.radius = 0.0f;
2665 p.feather = 1.0f;
2666 p.innerColor = color;
2667 p.outerColor = color;
2668 p.simpleColor = true;
2672 // ////////////////////////////////////////////////////////////////////////// //
2673 // State handling
2675 version(nanovega_debug_clipping) {
2676 public void nvgClipDumpOn (NVGContext ctx) { glnvg__clipDebugDump(ctx.params.userPtr, true); }
2677 public void nvgClipDumpOff (NVGContext ctx) { glnvg__clipDebugDump(ctx.params.userPtr, false); }
2680 /** Pushes and saves the current render state into a state stack.
2681 * A matching [restore] must be used to restore the state.
2682 * Returns `false` if state stack overflowed.
2684 * Group: state_handling
2686 public bool save (NVGContext ctx) nothrow @trusted @nogc {
2687 if (ctx.nstates >= NVG_MAX_STATES) return false;
2688 if (ctx.nstates > 0) {
2689 //memcpy(&ctx.states[ctx.nstates], &ctx.states[ctx.nstates-1], NVGstate.sizeof);
2690 ctx.states[ctx.nstates] = ctx.states[ctx.nstates-1];
2691 ctx.params.renderPushClip(ctx.params.userPtr);
2693 ++ctx.nstates;
2694 return true;
2697 /// Pops and restores current render state.
2698 /// Group: state_handling
2699 public bool restore (NVGContext ctx) nothrow @trusted @nogc {
2700 if (ctx.nstates <= 1) return false;
2701 ctx.states[ctx.nstates-1].clearPaint();
2702 ctx.params.renderPopClip(ctx.params.userPtr);
2703 --ctx.nstates;
2704 return true;
2707 /// Resets current render state to default values. Does not affect the render state stack.
2708 /// Group: state_handling
2709 public void reset (NVGContext ctx) nothrow @trusted @nogc {
2710 NVGstate* state = nvg__getState(ctx);
2711 state.clearPaint();
2713 nvg__setPaintColor(state.fill, nvgRGBA(255, 255, 255, 255));
2714 nvg__setPaintColor(state.stroke, nvgRGBA(0, 0, 0, 255));
2715 state.compositeOperation = nvg__compositeOperationState(NVGCompositeOperation.SourceOver);
2716 state.shapeAntiAlias = true;
2717 state.strokeWidth = 1.0f;
2718 state.miterLimit = 10.0f;
2719 state.lineCap = NVGLineCap.Butt;
2720 state.lineJoin = NVGLineCap.Miter;
2721 state.alpha = 1.0f;
2722 state.xform.identity;
2724 state.scissor.extent[] = -1.0f;
2726 state.fontSize = 16.0f;
2727 state.letterSpacing = 0.0f;
2728 state.lineHeight = 1.0f;
2729 state.fontBlur = 0.0f;
2730 state.textAlign.reset;
2731 state.fontId = 0;
2732 state.evenOddMode = false;
2734 ctx.params.renderResetClip(ctx.params.userPtr);
2737 /** Returns `true` if we have any room in state stack.
2738 * It is guaranteed to have at least 32 stack slots.
2740 * Group: state_handling
2742 public bool canSave (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx.nstates < NVG_MAX_STATES); }
2744 /** Returns `true` if we have any saved state.
2746 * Group: state_handling
2748 public bool canRestore (NVGContext ctx) pure nothrow @trusted @nogc { pragma(inline, true); return (ctx.nstates > 1); }
2751 // ////////////////////////////////////////////////////////////////////////// //
2752 // Render styles
2754 /// Sets filling mode to "even-odd".
2755 /// Group: render_styles
2756 public void evenOddFill (NVGContext ctx) nothrow @trusted @nogc {
2757 NVGstate* state = nvg__getState(ctx);
2758 state.evenOddMode = true;
2761 /// Sets filling mode to "non-zero" (this is default mode).
2762 /// Group: render_styles
2763 public void nonZeroFill (NVGContext ctx) nothrow @trusted @nogc {
2764 NVGstate* state = nvg__getState(ctx);
2765 state.evenOddMode = false;
2768 /// Sets whether to draw antialias for [stroke] and [fill]. It's enabled by default.
2769 /// Group: render_styles
2770 public void shapeAntiAlias (NVGContext ctx, bool enabled) {
2771 NVGstate* state = nvg__getState(ctx);
2772 state.shapeAntiAlias = enabled;
2775 /// Sets the stroke width of the stroke style.
2776 /// Group: render_styles
2777 public void strokeWidth (NVGContext ctx, float width) nothrow @trusted @nogc {
2778 NVGstate* state = nvg__getState(ctx);
2779 state.strokeWidth = width;
2782 /// Sets the miter limit of the stroke style. Miter limit controls when a sharp corner is beveled.
2783 /// Group: render_styles
2784 public void miterLimit (NVGContext ctx, float limit) nothrow @trusted @nogc {
2785 NVGstate* state = nvg__getState(ctx);
2786 state.miterLimit = limit;
2789 /// Sets how the end of the line (cap) is drawn,
2790 /// Can be one of: NVGLineCap.Butt (default), NVGLineCap.Round, NVGLineCap.Square.
2791 /// Group: render_styles
2792 public void lineCap (NVGContext ctx, NVGLineCap cap) nothrow @trusted @nogc {
2793 NVGstate* state = nvg__getState(ctx);
2794 state.lineCap = cap;
2797 /// Sets how sharp path corners are drawn.
2798 /// Can be one of NVGLineCap.Miter (default), NVGLineCap.Round, NVGLineCap.Bevel.
2799 /// Group: render_styles
2800 public void lineJoin (NVGContext ctx, NVGLineCap join) nothrow @trusted @nogc {
2801 NVGstate* state = nvg__getState(ctx);
2802 state.lineJoin = join;
2805 /// Sets the transparency applied to all rendered shapes.
2806 /// Already transparent paths will get proportionally more transparent as well.
2807 /// Group: render_styles
2808 public void globalAlpha (NVGContext ctx, float alpha) nothrow @trusted @nogc {
2809 NVGstate* state = nvg__getState(ctx);
2810 state.alpha = alpha;
2813 static if (NanoVegaHasArsdColor) {
2814 /// Sets current stroke style to a solid color.
2815 /// Group: render_styles
2816 public void strokeColor (NVGContext ctx, Color color) nothrow @trusted @nogc {
2817 NVGstate* state = nvg__getState(ctx);
2818 nvg__setPaintColor(state.stroke, NVGColor(color));
2822 /// Sets current stroke style to a solid color.
2823 /// Group: render_styles
2824 public void strokeColor (NVGContext ctx, NVGColor color) nothrow @trusted @nogc {
2825 NVGstate* state = nvg__getState(ctx);
2826 nvg__setPaintColor(state.stroke, color);
2829 /// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
2830 /// Group: render_styles
2831 public void strokePaint (NVGContext ctx, NVGPaint paint) nothrow @trusted @nogc {
2832 NVGstate* state = nvg__getState(ctx);
2833 state.stroke = paint;
2834 //nvgTransformMultiply(state.stroke.xform[], state.xform[]);
2835 state.stroke.xform.mul(state.xform);
2838 static if (NanoVegaHasArsdColor) {
2839 /// Sets current fill style to a solid color.
2840 /// Group: render_styles
2841 public void fillColor (NVGContext ctx, Color color) nothrow @trusted @nogc {
2842 NVGstate* state = nvg__getState(ctx);
2843 nvg__setPaintColor(state.fill, NVGColor(color));
2847 /// Sets current fill style to a solid color.
2848 /// Group: render_styles
2849 public void fillColor (NVGContext ctx, NVGColor color) nothrow @trusted @nogc {
2850 NVGstate* state = nvg__getState(ctx);
2851 nvg__setPaintColor(state.fill, color);
2854 /// Sets current fill style to a paint, which can be a one of the gradients or a pattern.
2855 /// Group: render_styles
2856 public void fillPaint (NVGContext ctx, NVGPaint paint) nothrow @trusted @nogc {
2857 NVGstate* state = nvg__getState(ctx);
2858 state.fill = paint;
2859 //nvgTransformMultiply(state.fill.xform[], state.xform[]);
2860 state.fill.xform.mul(state.xform);
2863 /// Returns current transformation matrix.
2864 /// Group: render_transformations
2865 public NVGMatrix currTransform (NVGContext ctx) pure nothrow @trusted @nogc {
2866 NVGstate* state = nvg__getState(ctx);
2867 return state.xform;
2870 /// Sets current transformation matrix.
2871 /// Group: render_transformations
2872 public void currTransform() (NVGContext ctx, in auto ref NVGMatrix m) nothrow @trusted @nogc {
2873 NVGstate* state = nvg__getState(ctx);
2874 state.xform = m;
2877 /// Resets current transform to an identity matrix.
2878 /// Group: render_transformations
2879 public void resetTransform (NVGContext ctx) nothrow @trusted @nogc {
2880 NVGstate* state = nvg__getState(ctx);
2881 state.xform.identity;
2884 /// Premultiplies current coordinate system by specified matrix.
2885 /// Group: render_transformations
2886 public void transform() (NVGContext ctx, in auto ref NVGMatrix mt) nothrow @trusted @nogc {
2887 NVGstate* state = nvg__getState(ctx);
2888 //nvgTransformPremultiply(state.xform[], t[]);
2889 state.xform *= mt;
2892 /// Translates current coordinate system.
2893 /// Group: render_transformations
2894 public void translate (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
2895 NVGstate* state = nvg__getState(ctx);
2896 //NVGMatrix t = void;
2897 //nvgTransformTranslate(t[], x, y);
2898 //nvgTransformPremultiply(state.xform[], t[]);
2899 state.xform.premul(NVGMatrix.Translated(x, y));
2902 /// Rotates current coordinate system. Angle is specified in radians.
2903 /// Group: render_transformations
2904 public void rotate (NVGContext ctx, in float angle) nothrow @trusted @nogc {
2905 NVGstate* state = nvg__getState(ctx);
2906 //NVGMatrix t = void;
2907 //nvgTransformRotate(t[], angle);
2908 //nvgTransformPremultiply(state.xform[], t[]);
2909 state.xform.premul(NVGMatrix.Rotated(angle));
2912 /// Skews the current coordinate system along X axis. Angle is specified in radians.
2913 /// Group: render_transformations
2914 public void skewX (NVGContext ctx, in float angle) nothrow @trusted @nogc {
2915 NVGstate* state = nvg__getState(ctx);
2916 //NVGMatrix t = void;
2917 //nvgTransformSkewX(t[], angle);
2918 //nvgTransformPremultiply(state.xform[], t[]);
2919 state.xform.premul(NVGMatrix.SkewedX(angle));
2922 /// Skews the current coordinate system along Y axis. Angle is specified in radians.
2923 /// Group: render_transformations
2924 public void skewY (NVGContext ctx, in float angle) nothrow @trusted @nogc {
2925 NVGstate* state = nvg__getState(ctx);
2926 //NVGMatrix t = void;
2927 //nvgTransformSkewY(t[], angle);
2928 //nvgTransformPremultiply(state.xform[], t[]);
2929 state.xform.premul(NVGMatrix.SkewedY(angle));
2932 /// Scales the current coordinate system.
2933 /// Group: render_transformations
2934 public void scale (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
2935 NVGstate* state = nvg__getState(ctx);
2936 //NVGMatrix t = void;
2937 //nvgTransformScale(t[], x, y);
2938 //nvgTransformPremultiply(state.xform[], t[]);
2939 state.xform.premul(NVGMatrix.Scaled(x, y));
2943 // ////////////////////////////////////////////////////////////////////////// //
2944 // Images
2946 /// Creates image by loading it from the disk from specified file name.
2947 /// Returns handle to the image or 0 on error.
2948 /// Group: images
2949 public NVGImage createImage() (NVGContext ctx, const(char)[] filename, const(NVGImageFlag)[] imageFlagsList...) {
2950 static if (NanoVegaHasArsdImage) {
2951 import arsd.image;
2952 // do we have new arsd API to load images?
2953 static if (!is(typeof(MemoryImage.fromImageFile)) || !is(typeof(MemoryImage.clearInternal))) {
2954 static assert(0, "Sorry, your ARSD is too old. Please, update it.");
2956 try {
2957 auto oimg = MemoryImage.fromImageFile(filename);
2958 if (auto img = cast(TrueColorImage)oimg) {
2959 scope(exit) oimg.clearInternal();
2960 return ctx.createImageRGBA(img.width, img.height, img.imageData.bytes[], imageFlagsList);
2961 } else {
2962 TrueColorImage img = oimg.getAsTrueColorImage;
2963 scope(exit) img.clearInternal();
2964 oimg.clearInternal(); // drop original image, as `getAsTrueColorImage()` MUST create a new one here
2965 oimg = null;
2966 return ctx.createImageRGBA(img.width, img.height, img.imageData.bytes[], imageFlagsList);
2968 } catch (Exception) {}
2969 return NVGImage.init;
2970 } else {
2971 import std.internal.cstring;
2972 ubyte* img;
2973 int w, h, n;
2974 stbi_set_unpremultiply_on_load(1);
2975 stbi_convert_iphone_png_to_rgb(1);
2976 img = stbi_load(filename.tempCString, &w, &h, &n, 4);
2977 if (img is null) {
2978 //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
2979 return NVGImage.init;
2981 auto image = ctx.createImageRGBA(w, h, img[0..w*h*4], imageFlagsList);
2982 stbi_image_free(img);
2983 return image;
2987 static if (NanoVegaHasArsdImage) {
2988 /// Creates image by loading it from the specified memory image.
2989 /// Returns handle to the image or 0 on error.
2990 /// Group: images
2991 public NVGImage createImageFromMemoryImage() (NVGContext ctx, MemoryImage img, const(NVGImageFlag)[] imageFlagsList...) {
2992 if (img is null) return NVGImage.init;
2993 if (auto tc = cast(TrueColorImage)img) {
2994 return ctx.createImageRGBA(tc.width, tc.height, tc.imageData.bytes[], imageFlagsList);
2995 } else {
2996 auto tc = img.getAsTrueColorImage;
2997 scope(exit) tc.clearInternal(); // here, it is guaranteed that `tc` is newly allocated image, so it is safe to kill it
2998 return ctx.createImageRGBA(tc.width, tc.height, tc.imageData.bytes[], imageFlagsList);
3001 } else {
3002 /// Creates image by loading it from the specified chunk of memory.
3003 /// Returns handle to the image or 0 on error.
3004 /// Group: images
3005 public NVGImage createImageMem() (NVGContext ctx, const(ubyte)* data, int ndata, const(NVGImageFlag)[] imageFlagsList...) {
3006 int w, h, n, image;
3007 ubyte* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
3008 if (img is null) {
3009 //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
3010 return NVGImage.init;
3012 image = ctx.createImageRGBA(w, h, img[0..w*h*4], imageFlagsList);
3013 stbi_image_free(img);
3014 return image;
3018 /// Creates image from specified image data.
3019 /// Returns handle to the image or 0 on error.
3020 /// Group: images
3021 public NVGImage createImageRGBA (NVGContext ctx, int w, int h, const(void)[] data, const(NVGImageFlag)[] imageFlagsList...) nothrow @trusted @nogc {
3022 if (w < 1 || h < 1 || data.length < w*h*4) return NVGImage.init;
3023 uint imageFlags = 0;
3024 foreach (immutable uint flag; imageFlagsList) imageFlags |= flag;
3025 NVGImage res;
3026 res.id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.RGBA, w, h, imageFlags, cast(const(ubyte)*)data.ptr);
3027 if (res.id > 0) {
3028 res.ctx = ctx;
3029 ctx.nvg__imageIncRef(res.id);
3031 return res;
3034 /// Updates image data specified by image handle.
3035 /// Group: images
3036 public void updateImage() (NVGContext ctx, auto ref NVGImage image, const(void)[] data) nothrow @trusted @nogc {
3037 if (image.valid) {
3038 int w, h;
3039 if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3040 ctx.params.renderGetTextureSize(ctx.params.userPtr, image.id, &w, &h);
3041 ctx.params.renderUpdateTexture(ctx.params.userPtr, image.id, 0, 0, w, h, cast(const(ubyte)*)data.ptr);
3045 /// Returns the dimensions of a created image.
3046 /// Group: images
3047 public void imageSize() (NVGContext ctx, in auto ref NVGImage image, out int w, out int h) nothrow @trusted @nogc {
3048 if (image.valid) {
3049 if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3050 ctx.params.renderGetTextureSize(cast(void*)ctx.params.userPtr, image.id, &w, &h);
3054 /// Deletes created image.
3055 /// Group: images
3056 public void deleteImage() (NVGContext ctx, ref NVGImage image) nothrow @trusted @nogc {
3057 if (ctx is null || !image.valid) return;
3058 if (image.ctx !is ctx) assert(0, "NanoVega: you cannot use image from one context in another context");
3059 image.clear();
3063 // ////////////////////////////////////////////////////////////////////////// //
3064 // Paints
3066 static if (NanoVegaHasArsdColor) {
3067 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3068 * of the linear gradient, icol specifies the start color and ocol the end color.
3069 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3071 * Group: paints
3073 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 {
3074 return ctx.linearGradient(sx, sy, ex, ey, NVGColor(icol), NVGColor(ocol));
3078 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3079 * of the linear gradient, icol specifies the start color and ocol the end color.
3080 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3082 * Group: paints
3084 public NVGPaint linearGradient (NVGContext ctx, float sx, float sy, float ex, float ey, NVGColor icol, NVGColor ocol) nothrow @trusted @nogc {
3085 enum large = 1e5f;
3087 NVGPaint p = void;
3088 memset(&p, 0, p.sizeof);
3089 p.simpleColor = false;
3091 // Calculate transform aligned to the line
3092 float dx = ex-sx;
3093 float dy = ey-sy;
3094 immutable float d = nvg__sqrtf(dx*dx+dy*dy);
3095 if (d > 0.0001f) {
3096 dx /= d;
3097 dy /= d;
3098 } else {
3099 dx = 0;
3100 dy = 1;
3103 p.xform.mat.ptr[0] = dy; p.xform.mat.ptr[1] = -dx;
3104 p.xform.mat.ptr[2] = dx; p.xform.mat.ptr[3] = dy;
3105 p.xform.mat.ptr[4] = sx-dx*large; p.xform.mat.ptr[5] = sy-dy*large;
3107 p.extent.ptr[0] = large;
3108 p.extent.ptr[1] = large+d*0.5f;
3110 p.radius = 0.0f;
3112 p.feather = nvg__max(NVG_MIN_FEATHER, d);
3114 p.innerColor = icol;
3115 p.outerColor = ocol;
3117 return p;
3120 static if (NanoVegaHasArsdColor) {
3121 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3122 * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3123 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3125 * Group: paints
3127 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 {
3128 return ctx.radialGradient(cx, cy, inr, outr, NVGColor(icol), NVGColor(ocol));
3132 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3133 * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3134 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3136 * Group: paints
3138 public NVGPaint radialGradient (NVGContext ctx, float cx, float cy, float inr, float outr, NVGColor icol, NVGColor ocol) nothrow @trusted @nogc {
3139 immutable float r = (inr+outr)*0.5f;
3140 immutable float f = (outr-inr);
3142 NVGPaint p = void;
3143 memset(&p, 0, p.sizeof);
3144 p.simpleColor = false;
3146 p.xform.identity;
3147 p.xform.mat.ptr[4] = cx;
3148 p.xform.mat.ptr[5] = cy;
3150 p.extent.ptr[0] = r;
3151 p.extent.ptr[1] = r;
3153 p.radius = r;
3155 p.feather = nvg__max(NVG_MIN_FEATHER, f);
3157 p.innerColor = icol;
3158 p.outerColor = ocol;
3160 return p;
3163 static if (NanoVegaHasArsdColor) {
3164 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3165 * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3166 * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3167 * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3168 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3170 * Group: paints
3172 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 {
3173 return ctx.boxGradient(x, y, w, h, r, f, NVGColor(icol), NVGColor(ocol));
3177 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3178 * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3179 * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3180 * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3181 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3183 * Group: paints
3185 public NVGPaint boxGradient (NVGContext ctx, float x, float y, float w, float h, float r, float f, NVGColor icol, NVGColor ocol) nothrow @trusted @nogc {
3186 NVGPaint p = void;
3187 memset(&p, 0, p.sizeof);
3188 p.simpleColor = false;
3190 p.xform.identity;
3191 p.xform.mat.ptr[4] = x+w*0.5f;
3192 p.xform.mat.ptr[5] = y+h*0.5f;
3194 p.extent.ptr[0] = w*0.5f;
3195 p.extent.ptr[1] = h*0.5f;
3197 p.radius = r;
3199 p.feather = nvg__max(NVG_MIN_FEATHER, f);
3201 p.innerColor = icol;
3202 p.outerColor = ocol;
3204 return p;
3207 /** Creates and returns an image pattern. Parameters `(cx, cy)` specify the left-top location of the image pattern,
3208 * `(w, h)` the size of one image, [angle] rotation around the top-left corner, [image] is handle to the image to render.
3209 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3211 * Group: paints
3213 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 {
3214 NVGPaint p = void;
3215 memset(&p, 0, p.sizeof);
3216 p.simpleColor = false;
3218 p.xform.identity.rotate(angle);
3219 p.xform.mat.ptr[4] = cx;
3220 p.xform.mat.ptr[5] = cy;
3222 p.extent.ptr[0] = w;
3223 p.extent.ptr[1] = h;
3225 p.image = image;
3227 p.innerColor = p.outerColor = nvgRGBAf(1, 1, 1, alpha);
3229 return p;
3232 /// Linear gradient with multiple stops.
3233 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3234 /// Group: paints
3235 public struct NVGLGS {
3236 private:
3237 NVGImage imgid;
3238 // [imagePattern] arguments
3239 float cx, cy, dim, angle;
3241 public:
3242 @disable this (this); // no copies
3243 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return imgid.valid; } ///
3244 void clear () nothrow @safe @nogc { pragma(inline, true); imgid.clear(); } ///
3247 /** Returns [NVGPaint] for linear gradient with stops, created with [createLinearGradientWithStops].
3248 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3250 * $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3251 * Group: paints
3253 public NVGPaint asPaint() (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 return p;
3259 } else {
3260 return ctx.imagePattern(lgs.cx, lgs.cy, lgs.dim, lgs.dim, lgs.angle, lgs.imgid);
3264 /// Gradient Stop Point.
3265 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3266 /// Group: paints
3267 public struct NVGGradientStop {
3268 float offset = 0; /// [0..1]
3269 NVGColor color; ///
3272 this() (in float aofs, in auto ref NVGColor aclr) nothrow @trusted @nogc { pragma(inline, true); offset = aofs; color = aclr; }
3273 this() (in float aofs, in Color aclr) nothrow @trusted @nogc { pragma(inline, true); offset = aofs; color = NVGColor(aclr); }
3276 /// Create linear gradient data suitable to use with `linearGradient(res)`.
3277 /// Don't forget to destroy the result when you don't need it anymore with `ctx.kill(res);`.
3278 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3279 /// Group: paints
3280 public NVGLGS createLinearGradientWithStops (NVGContext ctx, in float sx, in float sy, in float ex, in float ey, const(NVGGradientStop)[] stops...) nothrow @trusted @nogc {
3281 // based on the code by Jorge Acereda <jacereda@gmail.com>
3282 enum NVG_GRADIENT_SAMPLES = 1024;
3283 static void gradientSpan (uint* dst, const(NVGGradientStop)* s0, const(NVGGradientStop)* s1) nothrow @trusted @nogc {
3284 immutable float s0o = nvg__clamp(s0.offset, 0.0f, 1.0f);
3285 immutable float s1o = nvg__clamp(s1.offset, 0.0f, 1.0f);
3286 uint s = cast(uint)(s0o*NVG_GRADIENT_SAMPLES);
3287 uint e = cast(uint)(s1o*NVG_GRADIENT_SAMPLES);
3288 uint sc = 0xffffffffU;
3289 uint sh = 24;
3290 uint r = cast(uint)(s0.color.rgba[0]*sc);
3291 uint g = cast(uint)(s0.color.rgba[1]*sc);
3292 uint b = cast(uint)(s0.color.rgba[2]*sc);
3293 uint a = cast(uint)(s0.color.rgba[3]*sc);
3294 uint dr = cast(uint)((s1.color.rgba[0]*sc-r)/(e-s));
3295 uint dg = cast(uint)((s1.color.rgba[1]*sc-g)/(e-s));
3296 uint db = cast(uint)((s1.color.rgba[2]*sc-b)/(e-s));
3297 uint da = cast(uint)((s1.color.rgba[3]*sc-a)/(e-s));
3298 dst += s;
3299 foreach (immutable _; s..e) {
3300 version(BigEndian) {
3301 *dst++ = ((r>>sh)<<24)+((g>>sh)<<16)+((b>>sh)<<8)+((a>>sh)<<0);
3302 } else {
3303 *dst++ = ((a>>sh)<<24)+((b>>sh)<<16)+((g>>sh)<<8)+((r>>sh)<<0);
3305 r += dr;
3306 g += dg;
3307 b += db;
3308 a += da;
3312 NVGLGS res;
3314 uint[NVG_GRADIENT_SAMPLES] data = void;
3315 immutable float w = ex-sx;
3316 immutable float h = ey-sy;
3317 res.dim = nvg__sqrtf(w*w+h*h);
3319 res.cx = sx;
3320 res.cy = sy;
3321 res.angle =
3322 (/*nvg__absf(h) < 0.0001 ? 0 :
3323 nvg__absf(w) < 0.0001 ? 90.nvgDegrees :*/
3324 nvg__atan2f(h/*ey-sy*/, w/*ex-sx*/));
3326 if (stops.length > 0) {
3327 auto s0 = NVGGradientStop(0, nvgRGBAf(0, 0, 0, 1));
3328 auto s1 = NVGGradientStop(1, nvgRGBAf(1, 1, 1, 1));
3329 if (stops.length > 64) stops = stops[0..64];
3330 if (stops.length) {
3331 s0.color = stops[0].color;
3332 s1.color = stops[$-1].color;
3334 gradientSpan(data.ptr, &s0, (stops.length ? stops.ptr : &s1));
3335 foreach (immutable i; 0..stops.length-1) gradientSpan(data.ptr, stops.ptr+i, stops.ptr+i+1);
3336 gradientSpan(data.ptr, (stops.length ? stops.ptr+stops.length-1 : &s0), &s1);
3337 res.imgid = ctx.createImageRGBA(NVG_GRADIENT_SAMPLES, 1, data[], NVGImageFlag.RepeatX, NVGImageFlag.RepeatY);
3339 return res;
3343 // ////////////////////////////////////////////////////////////////////////// //
3344 // Scissoring
3346 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3347 /// Group: scissoring
3348 public void scissor (NVGContext ctx, in float x, in float y, float w, float h) nothrow @trusted @nogc {
3349 NVGstate* state = nvg__getState(ctx);
3351 w = nvg__max(0.0f, w);
3352 h = nvg__max(0.0f, h);
3354 state.scissor.xform.identity;
3355 state.scissor.xform.mat.ptr[4] = x+w*0.5f;
3356 state.scissor.xform.mat.ptr[5] = y+h*0.5f;
3357 //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3358 state.scissor.xform.mul(state.xform);
3360 state.scissor.extent.ptr[0] = w*0.5f;
3361 state.scissor.extent.ptr[1] = h*0.5f;
3364 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3365 /// Arguments: [x, y, w, h]*
3366 /// Group: scissoring
3367 public void scissor (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
3368 enum ArgC = 4;
3369 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [scissor] call");
3370 if (args.length < ArgC) return;
3371 NVGstate* state = nvg__getState(ctx);
3372 const(float)* aptr = args.ptr;
3373 foreach (immutable idx; 0..args.length/ArgC) {
3374 immutable x = *aptr++;
3375 immutable y = *aptr++;
3376 immutable w = nvg__max(0.0f, *aptr++);
3377 immutable h = nvg__max(0.0f, *aptr++);
3379 state.scissor.xform.identity;
3380 state.scissor.xform.mat.ptr[4] = x+w*0.5f;
3381 state.scissor.xform.mat.ptr[5] = y+h*0.5f;
3382 //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3383 state.scissor.xform.mul(state.xform);
3385 state.scissor.extent.ptr[0] = w*0.5f;
3386 state.scissor.extent.ptr[1] = h*0.5f;
3390 void nvg__isectRects (float* dst, float ax, float ay, float aw, float ah, float bx, float by, float bw, float bh) nothrow @trusted @nogc {
3391 immutable float minx = nvg__max(ax, bx);
3392 immutable float miny = nvg__max(ay, by);
3393 immutable float maxx = nvg__min(ax+aw, bx+bw);
3394 immutable float maxy = nvg__min(ay+ah, by+bh);
3395 dst[0] = minx;
3396 dst[1] = miny;
3397 dst[2] = nvg__max(0.0f, maxx-minx);
3398 dst[3] = nvg__max(0.0f, maxy-miny);
3401 /** Intersects current scissor rectangle with the specified rectangle.
3402 * The scissor rectangle is transformed by the current transform.
3403 * Note: in case the rotation of previous scissor rect differs from
3404 * the current one, the intersection will be done between the specified
3405 * rectangle and the previous scissor rectangle transformed in the current
3406 * transform space. The resulting shape is always rectangle.
3408 * Group: scissoring
3410 public void intersectScissor (NVGContext ctx, in float x, in float y, in float w, in float h) nothrow @trusted @nogc {
3411 NVGstate* state = nvg__getState(ctx);
3413 // If no previous scissor has been set, set the scissor as current scissor.
3414 if (state.scissor.extent.ptr[0] < 0) {
3415 ctx.scissor(x, y, w, h);
3416 return;
3419 NVGMatrix pxform = void;
3420 NVGMatrix invxorm = void;
3421 float[4] rect = void;
3423 // Transform the current scissor rect into current transform space.
3424 // If there is difference in rotation, this will be approximation.
3425 //memcpy(pxform.mat.ptr, state.scissor.xform.ptr, float.sizeof*6);
3426 pxform = state.scissor.xform;
3427 immutable float ex = state.scissor.extent.ptr[0];
3428 immutable float ey = state.scissor.extent.ptr[1];
3429 //nvgTransformInverse(invxorm[], state.xform[]);
3430 invxorm = state.xform.inverted;
3431 //nvgTransformMultiply(pxform[], invxorm[]);
3432 pxform.mul(invxorm);
3433 immutable float tex = ex*nvg__absf(pxform.mat.ptr[0])+ey*nvg__absf(pxform.mat.ptr[2]);
3434 immutable float tey = ex*nvg__absf(pxform.mat.ptr[1])+ey*nvg__absf(pxform.mat.ptr[3]);
3436 // Intersect rects.
3437 nvg__isectRects(rect.ptr, pxform.mat.ptr[4]-tex, pxform.mat.ptr[5]-tey, tex*2, tey*2, x, y, w, h);
3439 //ctx.scissor(rect.ptr[0], rect.ptr[1], rect.ptr[2], rect.ptr[3]);
3440 ctx.scissor(rect.ptr[0..4]);
3443 /** Intersects current scissor rectangle with the specified rectangle.
3444 * The scissor rectangle is transformed by the current transform.
3445 * Note: in case the rotation of previous scissor rect differs from
3446 * the current one, the intersection will be done between the specified
3447 * rectangle and the previous scissor rectangle transformed in the current
3448 * transform space. The resulting shape is always rectangle.
3450 * Arguments: [x, y, w, h]*
3452 * Group: scissoring
3454 public void intersectScissor (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
3455 enum ArgC = 4;
3456 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [intersectScissor] call");
3457 if (args.length < ArgC) return;
3458 const(float)* aptr = args.ptr;
3459 foreach (immutable idx; 0..args.length/ArgC) {
3460 immutable x = *aptr++;
3461 immutable y = *aptr++;
3462 immutable w = *aptr++;
3463 immutable h = *aptr++;
3464 ctx.intersectScissor(x, y, w, h);
3468 /// Reset and disables scissoring.
3469 /// Group: scissoring
3470 public void resetScissor (NVGContext ctx) nothrow @trusted @nogc {
3471 NVGstate* state = nvg__getState(ctx);
3472 state.scissor.xform.mat[] = 0.0f;
3473 state.scissor.extent[] = -1.0f;
3477 // ////////////////////////////////////////////////////////////////////////// //
3478 // Render-Time Affine Transformations
3480 /// Sets GPU affine transformatin matrix. Don't do scaling or skewing here.
3481 /// This matrix won't be saved/restored with context state save/restore operations, as it is not a part of that state.
3482 /// Group: gpu_affine
3483 public void affineGPU() (NVGContext ctx, in auto ref NVGMatrix mat) nothrow @trusted @nogc {
3484 ctx.gpuAffine = mat;
3485 ctx.params.renderSetAffine(ctx.params.userPtr, ctx.gpuAffine);
3488 /// Get current GPU affine transformatin matrix.
3489 /// Group: gpu_affine
3490 public NVGMatrix affineGPU (NVGContext ctx) nothrow @safe @nogc {
3491 pragma(inline, true);
3492 return ctx.gpuAffine;
3495 /// "Untransform" point using current GPU affine matrix.
3496 /// Group: gpu_affine
3497 public void gpuUntransformPoint (NVGContext ctx, float *dx, float *dy, in float x, in float y) nothrow @safe @nogc {
3498 if (ctx.gpuAffine.isIdentity) {
3499 if (dx !is null) *dx = x;
3500 if (dy !is null) *dy = y;
3501 } else {
3502 // inverse GPU transformation
3503 NVGMatrix igpu = ctx.gpuAffine.inverted;
3504 igpu.point(dx, dy, x, y);
3509 // ////////////////////////////////////////////////////////////////////////// //
3510 // rasterization (tesselation) code
3512 int nvg__ptEquals (float x1, float y1, float x2, float y2, float tol) pure nothrow @safe @nogc {
3513 //pragma(inline, true);
3514 immutable float dx = x2-x1;
3515 immutable float dy = y2-y1;
3516 return dx*dx+dy*dy < tol*tol;
3519 float nvg__distPtSeg (float x, float y, float px, float py, float qx, float qy) pure nothrow @safe @nogc {
3520 immutable float pqx = qx-px;
3521 immutable float pqy = qy-py;
3522 float dx = x-px;
3523 float dy = y-py;
3524 immutable float d = pqx*pqx+pqy*pqy;
3525 float t = pqx*dx+pqy*dy;
3526 if (d > 0) t /= d;
3527 if (t < 0) t = 0; else if (t > 1) t = 1;
3528 dx = px+t*pqx-x;
3529 dy = py+t*pqy-y;
3530 return dx*dx+dy*dy;
3533 void nvg__appendCommands(bool useCommand=true) (NVGContext ctx, Command acmd, const(float)[] vals...) nothrow @trusted @nogc {
3534 int nvals = cast(int)vals.length;
3535 static if (useCommand) {
3536 enum addon = 1;
3537 } else {
3538 enum addon = 0;
3539 if (nvals == 0) return; // nothing to do
3542 NVGstate* state = nvg__getState(ctx);
3544 if (ctx.ncommands+nvals+addon > ctx.ccommands) {
3545 //int ccommands = ctx.ncommands+nvals+ctx.ccommands/2;
3546 int ccommands = ((ctx.ncommands+(nvals+addon))|0xfff)+1;
3547 float* commands = cast(float*)realloc(ctx.commands, float.sizeof*ccommands);
3548 if (commands is null) assert(0, "NanoVega: out of memory");
3549 ctx.commands = commands;
3550 ctx.ccommands = ccommands;
3551 assert(ctx.ncommands+(nvals+addon) <= ctx.ccommands);
3554 static if (!useCommand) acmd = cast(Command)vals.ptr[0];
3556 if (acmd != Command.Close && acmd != Command.Winding) {
3557 //assert(nvals+addon >= 3);
3558 ctx.commandx = vals.ptr[nvals-2];
3559 ctx.commandy = vals.ptr[nvals-1];
3562 // copy commands
3563 float* vp = ctx.commands+ctx.ncommands;
3564 static if (useCommand) {
3565 vp[0] = cast(float)acmd;
3566 if (nvals > 0) memcpy(vp+1, vals.ptr, nvals*float.sizeof);
3567 } else {
3568 memcpy(vp, vals.ptr, nvals*float.sizeof);
3570 ctx.ncommands += nvals+addon;
3572 // transform commands
3573 int i = nvals+addon;
3574 while (i > 0) {
3575 int nlen = 1;
3576 final switch (cast(Command)(*vp)) {
3577 case Command.MoveTo:
3578 case Command.LineTo:
3579 assert(i >= 3);
3580 state.xform.point(vp+1, vp+2, vp[1], vp[2]);
3581 nlen = 3;
3582 break;
3583 case Command.BezierTo:
3584 assert(i >= 7);
3585 state.xform.point(vp+1, vp+2, vp[1], vp[2]);
3586 state.xform.point(vp+3, vp+4, vp[3], vp[4]);
3587 state.xform.point(vp+5, vp+6, vp[5], vp[6]);
3588 nlen = 7;
3589 break;
3590 case Command.Close:
3591 nlen = 1;
3592 break;
3593 case Command.Winding:
3594 nlen = 2;
3595 break;
3597 assert(nlen > 0 && nlen <= i);
3598 i -= nlen;
3599 vp += nlen;
3603 void nvg__clearPathCache (NVGContext ctx) nothrow @trusted @nogc {
3604 // no need to clear paths, as data is not copied there
3605 //foreach (ref p; ctx.cache.paths[0..ctx.cache.npaths]) p.clear();
3606 ctx.cache.npoints = 0;
3607 ctx.cache.npaths = 0;
3608 ctx.cache.fillReady = ctx.cache.strokeReady = false;
3609 ctx.cache.clipmode = NVGClipMode.None;
3612 NVGpath* nvg__lastPath (NVGContext ctx) nothrow @trusted @nogc {
3613 return (ctx.cache.npaths > 0 ? &ctx.cache.paths[ctx.cache.npaths-1] : null);
3616 void nvg__addPath (NVGContext ctx) nothrow @trusted @nogc {
3617 import core.stdc.stdlib : realloc;
3618 import core.stdc.string : memset;
3620 if (ctx.cache.npaths+1 > ctx.cache.cpaths) {
3621 int cpaths = ctx.cache.npaths+1+ctx.cache.cpaths/2;
3622 NVGpath* paths = cast(NVGpath*)realloc(ctx.cache.paths, NVGpath.sizeof*cpaths);
3623 if (paths is null) assert(0, "NanoVega: out of memory");
3624 ctx.cache.paths = paths;
3625 ctx.cache.cpaths = cpaths;
3628 NVGpath* path = &ctx.cache.paths[ctx.cache.npaths++];
3629 memset(path, 0, NVGpath.sizeof);
3630 path.first = ctx.cache.npoints;
3631 path.winding = NVGWinding.CCW;
3634 NVGpoint* nvg__lastPoint (NVGContext ctx) nothrow @trusted @nogc {
3635 return (ctx.cache.npoints > 0 ? &ctx.cache.points[ctx.cache.npoints-1] : null);
3638 void nvg__addPoint (NVGContext ctx, float x, float y, int flags) nothrow @trusted @nogc {
3639 NVGpath* path = nvg__lastPath(ctx);
3640 if (path is null) return;
3642 if (path.count > 0 && ctx.cache.npoints > 0) {
3643 NVGpoint* pt = nvg__lastPoint(ctx);
3644 if (nvg__ptEquals(pt.x, pt.y, x, y, ctx.distTol)) {
3645 pt.flags |= flags;
3646 return;
3650 if (ctx.cache.npoints+1 > ctx.cache.cpoints) {
3651 int cpoints = ctx.cache.npoints+1+ctx.cache.cpoints/2;
3652 NVGpoint* points = cast(NVGpoint*)realloc(ctx.cache.points, NVGpoint.sizeof*cpoints);
3653 if (points is null) return;
3654 ctx.cache.points = points;
3655 ctx.cache.cpoints = cpoints;
3658 NVGpoint* pt = &ctx.cache.points[ctx.cache.npoints];
3659 memset(pt, 0, (*pt).sizeof);
3660 pt.x = x;
3661 pt.y = y;
3662 pt.flags = cast(ubyte)flags;
3664 ++ctx.cache.npoints;
3665 ++path.count;
3668 void nvg__closePath (NVGContext ctx) nothrow @trusted @nogc {
3669 NVGpath* path = nvg__lastPath(ctx);
3670 if (path is null) return;
3671 path.closed = true;
3674 void nvg__pathWinding (NVGContext ctx, NVGWinding winding) nothrow @trusted @nogc {
3675 NVGpath* path = nvg__lastPath(ctx);
3676 if (path is null) return;
3677 path.winding = winding;
3680 float nvg__getAverageScale() (in auto ref NVGMatrix t) nothrow @trusted @nogc {
3681 immutable float sx = nvg__sqrtf(t.mat.ptr[0]*t.mat.ptr[0]+t.mat.ptr[2]*t.mat.ptr[2]);
3682 immutable float sy = nvg__sqrtf(t.mat.ptr[1]*t.mat.ptr[1]+t.mat.ptr[3]*t.mat.ptr[3]);
3683 return (sx+sy)*0.5f;
3686 NVGvertex* nvg__allocTempVerts (NVGContext ctx, int nverts) nothrow @trusted @nogc {
3687 if (nverts > ctx.cache.cverts) {
3688 int cverts = (nverts+0xff)&~0xff; // Round up to prevent allocations when things change just slightly.
3689 NVGvertex* verts = cast(NVGvertex*)realloc(ctx.cache.verts, NVGvertex.sizeof*cverts);
3690 if (verts is null) return null;
3691 ctx.cache.verts = verts;
3692 ctx.cache.cverts = cverts;
3695 return ctx.cache.verts;
3698 float nvg__triarea2 (float ax, float ay, float bx, float by, float cx, float cy) pure nothrow @safe @nogc {
3699 immutable float abx = bx-ax;
3700 immutable float aby = by-ay;
3701 immutable float acx = cx-ax;
3702 immutable float acy = cy-ay;
3703 return acx*aby-abx*acy;
3706 float nvg__polyArea (NVGpoint* pts, int npts) nothrow @trusted @nogc {
3707 float area = 0;
3708 foreach (int i; 2..npts) {
3709 NVGpoint* a = &pts[0];
3710 NVGpoint* b = &pts[i-1];
3711 NVGpoint* c = &pts[i];
3712 area += nvg__triarea2(a.x, a.y, b.x, b.y, c.x, c.y);
3714 return area*0.5f;
3717 void nvg__polyReverse (NVGpoint* pts, int npts) nothrow @trusted @nogc {
3718 NVGpoint tmp = void;
3719 int i = 0, j = npts-1;
3720 while (i < j) {
3721 tmp = pts[i];
3722 pts[i] = pts[j];
3723 pts[j] = tmp;
3724 ++i;
3725 --j;
3729 void nvg__vset (NVGvertex* vtx, float x, float y, float u, float v) nothrow @trusted @nogc {
3730 vtx.x = x;
3731 vtx.y = y;
3732 vtx.u = u;
3733 vtx.v = v;
3736 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 {
3737 if (level > 10) return;
3739 // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case)
3741 if (level == 0 && ctx.tesselatortype == NVGTesselation.Combined) {
3742 static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc {
3743 immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y);
3744 return (nvg__absf(cz*cz) <= 0.01f); // arbitrary number, seems to work ok with NanoSVG output
3746 if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) {
3747 //{ import core.stdc.stdio; printf("AFD fallback!\n"); }
3748 ctx.nvg__tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4, type);
3749 return;
3754 immutable float x12 = (x1+x2)*0.5f;
3755 immutable float y12 = (y1+y2)*0.5f;
3756 immutable float x23 = (x2+x3)*0.5f;
3757 immutable float y23 = (y2+y3)*0.5f;
3758 immutable float x34 = (x3+x4)*0.5f;
3759 immutable float y34 = (y3+y4)*0.5f;
3760 immutable float x123 = (x12+x23)*0.5f;
3761 immutable float y123 = (y12+y23)*0.5f;
3763 immutable float dx = x4-x1;
3764 immutable float dy = y4-y1;
3765 immutable float d2 = nvg__absf(((x2-x4)*dy-(y2-y4)*dx));
3766 immutable float d3 = nvg__absf(((x3-x4)*dy-(y3-y4)*dx));
3768 if ((d2+d3)*(d2+d3) < ctx.tessTol*(dx*dx+dy*dy)) {
3769 nvg__addPoint(ctx, x4, y4, type);
3770 return;
3773 immutable float x234 = (x23+x34)*0.5f;
3774 immutable float y234 = (y23+y34)*0.5f;
3775 immutable float x1234 = (x123+x234)*0.5f;
3776 immutable float y1234 = (y123+y234)*0.5f;
3779 // "taxicab" / "manhattan" check for flat curves
3780 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) {
3781 nvg__addPoint(ctx, x1234, y1234, type);
3782 return;
3785 nvg__tesselateBezier(ctx, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, 0);
3786 nvg__tesselateBezier(ctx, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, type);
3789 // based on the ideas and code of Maxim Shemanarev. Rest in Peace, bro!
3790 // see http://www.antigrain.com/research/adaptive_bezier/index.html
3791 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 {
3792 enum CollinearEPS = 0.00000001f; // 0.00001f;
3793 enum AngleTolEPS = 0.01f;
3795 static float distSquared (in float x1, in float y1, in float x2, in float y2) pure nothrow @safe @nogc {
3796 pragma(inline, true);
3797 immutable float dx = x2-x1;
3798 immutable float dy = y2-y1;
3799 return dx*dx+dy*dy;
3802 if (level == 0) {
3803 nvg__addPoint(ctx, x1, y1, 0);
3804 nvg__tesselateBezierMcSeem(ctx, x1, y1, x2, y2, x3, y3, x4, y4, 1, type);
3805 nvg__addPoint(ctx, x4, y4, type);
3806 return;
3809 if (level >= 32) return; // recurse limit; practically, it should be never reached, but...
3811 // calculate all the mid-points of the line segments
3812 immutable float x12 = (x1+x2)*0.5f;
3813 immutable float y12 = (y1+y2)*0.5f;
3814 immutable float x23 = (x2+x3)*0.5f;
3815 immutable float y23 = (y2+y3)*0.5f;
3816 immutable float x34 = (x3+x4)*0.5f;
3817 immutable float y34 = (y3+y4)*0.5f;
3818 immutable float x123 = (x12+x23)*0.5f;
3819 immutable float y123 = (y12+y23)*0.5f;
3820 immutable float x234 = (x23+x34)*0.5f;
3821 immutable float y234 = (y23+y34)*0.5f;
3822 immutable float x1234 = (x123+x234)*0.5f;
3823 immutable float y1234 = (y123+y234)*0.5f;
3825 // try to approximate the full cubic curve by a single straight line
3826 immutable float dx = x4-x1;
3827 immutable float dy = y4-y1;
3829 float d2 = nvg__absf(((x2-x4)*dy-(y2-y4)*dx));
3830 float d3 = nvg__absf(((x3-x4)*dy-(y3-y4)*dx));
3831 //immutable float da1, da2, k;
3833 final switch ((cast(int)(d2 > CollinearEPS)<<1)+cast(int)(d3 > CollinearEPS)) {
3834 case 0:
3835 // all collinear or p1 == p4
3836 float k = dx*dx+dy*dy;
3837 if (k == 0) {
3838 d2 = distSquared(x1, y1, x2, y2);
3839 d3 = distSquared(x4, y4, x3, y3);
3840 } else {
3841 k = 1.0f/k;
3842 float da1 = x2-x1;
3843 float da2 = y2-y1;
3844 d2 = k*(da1*dx+da2*dy);
3845 da1 = x3-x1;
3846 da2 = y3-y1;
3847 d3 = k*(da1*dx+da2*dy);
3848 if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {
3849 // Simple collinear case, 1---2---3---4
3850 // We can leave just two endpoints
3851 return;
3853 if (d2 <= 0) d2 = distSquared(x2, y2, x1, y1);
3854 else if (d2 >= 1) d2 = distSquared(x2, y2, x4, y4);
3855 else d2 = distSquared(x2, y2, x1+d2*dx, y1+d2*dy);
3857 if (d3 <= 0) d3 = distSquared(x3, y3, x1, y1);
3858 else if (d3 >= 1) d3 = distSquared(x3, y3, x4, y4);
3859 else d3 = distSquared(x3, y3, x1+d3*dx, y1+d3*dy);
3861 if (d2 > d3) {
3862 if (d2 < ctx.tessTol) {
3863 nvg__addPoint(ctx, x2, y2, type);
3864 return;
3866 } if (d3 < ctx.tessTol) {
3867 nvg__addPoint(ctx, x3, y3, type);
3868 return;
3870 break;
3871 case 1:
3872 // p1,p2,p4 are collinear, p3 is significant
3873 if (d3*d3 <= ctx.tessTol*(dx*dx+dy*dy)) {
3874 if (ctx.angleTol < AngleTolEPS) {
3875 nvg__addPoint(ctx, x23, y23, type);
3876 return;
3877 } else {
3878 // angle condition
3879 float da1 = nvg__absf(nvg__atan2f(y4-y3, x4-x3)-nvg__atan2f(y3-y2, x3-x2));
3880 if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
3881 if (da1 < ctx.angleTol) {
3882 nvg__addPoint(ctx, x2, y2, type);
3883 nvg__addPoint(ctx, x3, y3, type);
3884 return;
3886 if (ctx.cuspLimit != 0.0) {
3887 if (da1 > ctx.cuspLimit) {
3888 nvg__addPoint(ctx, x3, y3, type);
3889 return;
3894 break;
3895 case 2:
3896 // p1,p3,p4 are collinear, p2 is significant
3897 if (d2*d2 <= ctx.tessTol*(dx*dx+dy*dy)) {
3898 if (ctx.angleTol < AngleTolEPS) {
3899 nvg__addPoint(ctx, x23, y23, type);
3900 return;
3901 } else {
3902 // angle condition
3903 float da1 = nvg__absf(nvg__atan2f(y3-y2, x3-x2)-nvg__atan2f(y2-y1, x2-x1));
3904 if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
3905 if (da1 < ctx.angleTol) {
3906 nvg__addPoint(ctx, x2, y2, type);
3907 nvg__addPoint(ctx, x3, y3, type);
3908 return;
3910 if (ctx.cuspLimit != 0.0) {
3911 if (da1 > ctx.cuspLimit) {
3912 nvg__addPoint(ctx, x2, y2, type);
3913 return;
3918 break;
3919 case 3:
3920 // regular case
3921 if ((d2+d3)*(d2+d3) <= ctx.tessTol*(dx*dx+dy*dy)) {
3922 // if the curvature doesn't exceed the distance tolerance value, we tend to finish subdivisions
3923 if (ctx.angleTol < AngleTolEPS) {
3924 nvg__addPoint(ctx, x23, y23, type);
3925 return;
3926 } else {
3927 // angle and cusp condition
3928 immutable float k = nvg__atan2f(y3-y2, x3-x2);
3929 float da1 = nvg__absf(k-nvg__atan2f(y2-y1, x2-x1));
3930 float da2 = nvg__absf(nvg__atan2f(y4-y3, x4-x3)-k);
3931 if (da1 >= NVG_PI) da1 = 2*NVG_PI-da1;
3932 if (da2 >= NVG_PI) da2 = 2*NVG_PI-da2;
3933 if (da1+da2 < ctx.angleTol) {
3934 // finally we can stop the recursion
3935 nvg__addPoint(ctx, x23, y23, type);
3936 return;
3938 if (ctx.cuspLimit != 0.0) {
3939 if (da1 > ctx.cuspLimit) {
3940 nvg__addPoint(ctx, x2, y2, type);
3941 return;
3943 if (da2 > ctx.cuspLimit) {
3944 nvg__addPoint(ctx, x3, y3, type);
3945 return;
3950 break;
3953 // continue subdivision
3954 nvg__tesselateBezierMcSeem(ctx, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, 0);
3955 nvg__tesselateBezierMcSeem(ctx, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, type);
3959 // Adaptive forward differencing for bezier tesselation.
3960 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt.
3961 // "Adaptive forward differencing for rendering curves and surfaces."
3962 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987.
3963 // original code by Taylor Holliday <taylor@audulus.com>
3964 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 {
3965 enum AFD_ONE = (1<<10);
3967 // power basis
3968 immutable float ax = -x1+3*x2-3*x3+x4;
3969 immutable float ay = -y1+3*y2-3*y3+y4;
3970 immutable float bx = 3*x1-6*x2+3*x3;
3971 immutable float by = 3*y1-6*y2+3*y3;
3972 immutable float cx = -3*x1+3*x2;
3973 immutable float cy = -3*y1+3*y2;
3975 // Transform to forward difference basis (stepsize 1)
3976 float px = x1;
3977 float py = y1;
3978 float dx = ax+bx+cx;
3979 float dy = ay+by+cy;
3980 float ddx = 6*ax+2*bx;
3981 float ddy = 6*ay+2*by;
3982 float dddx = 6*ax;
3983 float dddy = 6*ay;
3985 //printf("dx: %f, dy: %f\n", dx, dy);
3986 //printf("ddx: %f, ddy: %f\n", ddx, ddy);
3987 //printf("dddx: %f, dddy: %f\n", dddx, dddy);
3989 int t = 0;
3990 int dt = AFD_ONE;
3992 immutable float tol = ctx.tessTol*4;
3994 while (t < AFD_ONE) {
3995 // Flatness measure.
3996 float d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
3998 // printf("d: %f, th: %f\n", d, th);
4000 // Go to higher resolution if we're moving a lot or overshooting the end.
4001 while ((d > tol && dt > 1) || (t+dt > AFD_ONE)) {
4002 // printf("up\n");
4004 // Apply L to the curve. Increase curve resolution.
4005 dx = 0.5f*dx-(1.0f/8.0f)*ddx+(1.0f/16.0f)*dddx;
4006 dy = 0.5f*dy-(1.0f/8.0f)*ddy+(1.0f/16.0f)*dddy;
4007 ddx = (1.0f/4.0f)*ddx-(1.0f/8.0f)*dddx;
4008 ddy = (1.0f/4.0f)*ddy-(1.0f/8.0f)*dddy;
4009 dddx = (1.0f/8.0f)*dddx;
4010 dddy = (1.0f/8.0f)*dddy;
4012 // Half the stepsize.
4013 dt >>= 1;
4015 // Recompute d
4016 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
4019 // Go to lower resolution if we're really flat
4020 // and we aren't going to overshoot the end.
4021 // XXX: tol/32 is just a guess for when we are too flat.
4022 while ((d > 0 && d < tol/32.0f && dt < AFD_ONE) && (t+2*dt <= AFD_ONE)) {
4023 // printf("down\n");
4025 // Apply L^(-1) to the curve. Decrease curve resolution.
4026 dx = 2*dx+ddx;
4027 dy = 2*dy+ddy;
4028 ddx = 4*ddx+4*dddx;
4029 ddy = 4*ddy+4*dddy;
4030 dddx = 8*dddx;
4031 dddy = 8*dddy;
4033 // Double the stepsize.
4034 dt <<= 1;
4036 // Recompute d
4037 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
4040 // Forward differencing.
4041 px += dx;
4042 py += dy;
4043 dx += ddx;
4044 dy += ddy;
4045 ddx += dddx;
4046 ddy += dddy;
4048 // Output a point.
4049 nvg__addPoint(ctx, px, py, (t > 0 ? type : 0));
4051 // Advance along the curve.
4052 t += dt;
4054 // Ensure we don't overshoot.
4055 assert(t <= AFD_ONE);
4059 version(nanovg_bench_flatten) import iv.timer : Timer;
4061 void nvg__flattenPaths (NVGContext ctx) nothrow @trusted @nogc {
4062 version(nanovg_bench_flatten) {
4063 Timer timer;
4064 char[128] tmbuf;
4065 int bzcount;
4067 NVGpathCache* cache = ctx.cache;
4069 if (cache.npaths > 0) return;
4071 // flatten
4072 version(nanovg_bench_flatten) timer.restart();
4073 int i = 0;
4074 while (i < ctx.ncommands) {
4075 final switch (cast(Command)ctx.commands[i]) {
4076 case Command.MoveTo:
4077 //assert(i+3 <= ctx.ncommands);
4078 nvg__addPath(ctx);
4079 const p = &ctx.commands[i+1];
4080 nvg__addPoint(ctx, p[0], p[1], PointFlag.Corner);
4081 i += 3;
4082 break;
4083 case Command.LineTo:
4084 //assert(i+3 <= ctx.ncommands);
4085 const p = &ctx.commands[i+1];
4086 nvg__addPoint(ctx, p[0], p[1], PointFlag.Corner);
4087 i += 3;
4088 break;
4089 case Command.BezierTo:
4090 //assert(i+7 <= ctx.ncommands);
4091 const last = nvg__lastPoint(ctx);
4092 if (last !is null) {
4093 const cp1 = &ctx.commands[i+1];
4094 const cp2 = &ctx.commands[i+3];
4095 const p = &ctx.commands[i+5];
4096 if (ctx.tesselatortype == NVGTesselation.DeCasteljau) {
4097 nvg__tesselateBezier(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], 0, PointFlag.Corner);
4098 } else if (ctx.tesselatortype == NVGTesselation.DeCasteljauMcSeem) {
4099 nvg__tesselateBezierMcSeem(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], 0, PointFlag.Corner);
4100 } else {
4101 nvg__tesselateBezierAFD(ctx, last.x, last.y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], PointFlag.Corner);
4103 version(nanovg_bench_flatten) ++bzcount;
4105 i += 7;
4106 break;
4107 case Command.Close:
4108 //assert(i+1 <= ctx.ncommands);
4109 nvg__closePath(ctx);
4110 i += 1;
4111 break;
4112 case Command.Winding:
4113 //assert(i+2 <= ctx.ncommands);
4114 nvg__pathWinding(ctx, cast(NVGWinding)ctx.commands[i+1]);
4115 i += 2;
4116 break;
4119 version(nanovg_bench_flatten) {{
4120 timer.stop();
4121 auto xb = timer.toBuffer(tmbuf[]);
4122 import core.stdc.stdio : printf;
4123 printf("flattening time: [%.*s] (%d beziers)\n", cast(uint)xb.length, xb.ptr, bzcount);
4126 cache.bounds.ptr[0] = cache.bounds.ptr[1] = 1e6f;
4127 cache.bounds.ptr[2] = cache.bounds.ptr[3] = -1e6f;
4129 // calculate the direction and length of line segments
4130 version(nanovg_bench_flatten) timer.restart();
4131 foreach (int j; 0..cache.npaths) {
4132 NVGpath* path = &cache.paths[j];
4133 NVGpoint* pts = &cache.points[path.first];
4135 // if the first and last points are the same, remove the last, mark as closed path
4136 NVGpoint* p0 = &pts[path.count-1];
4137 NVGpoint* p1 = &pts[0];
4138 if (nvg__ptEquals(p0.x, p0.y, p1.x, p1.y, ctx.distTol)) {
4139 --path.count;
4140 p0 = &pts[path.count-1];
4141 path.closed = true;
4144 // enforce winding
4145 if (path.count > 2) {
4146 immutable float area = nvg__polyArea(pts, path.count);
4147 if (path.winding == NVGWinding.CCW && area < 0.0f) nvg__polyReverse(pts, path.count);
4148 if (path.winding == NVGWinding.CW && area > 0.0f) nvg__polyReverse(pts, path.count);
4151 foreach (immutable _; 0..path.count) {
4152 // calculate segment direction and length
4153 p0.dx = p1.x-p0.x;
4154 p0.dy = p1.y-p0.y;
4155 p0.len = nvg__normalize(&p0.dx, &p0.dy);
4156 // update bounds
4157 cache.bounds.ptr[0] = nvg__min(cache.bounds.ptr[0], p0.x);
4158 cache.bounds.ptr[1] = nvg__min(cache.bounds.ptr[1], p0.y);
4159 cache.bounds.ptr[2] = nvg__max(cache.bounds.ptr[2], p0.x);
4160 cache.bounds.ptr[3] = nvg__max(cache.bounds.ptr[3], p0.y);
4161 // advance
4162 p0 = p1++;
4165 version(nanovg_bench_flatten) {{
4166 timer.stop();
4167 auto xb = timer.toBuffer(tmbuf[]);
4168 import core.stdc.stdio : printf;
4169 printf("segment calculation time: [%.*s]\n", cast(uint)xb.length, xb.ptr);
4173 int nvg__curveDivs (float r, float arc, float tol) nothrow @trusted @nogc {
4174 immutable float da = nvg__acosf(r/(r+tol))*2.0f;
4175 return nvg__max(2, cast(int)nvg__ceilf(arc/da));
4178 void nvg__chooseBevel (int bevel, NVGpoint* p0, NVGpoint* p1, float w, float* x0, float* y0, float* x1, float* y1) nothrow @trusted @nogc {
4179 if (bevel) {
4180 *x0 = p1.x+p0.dy*w;
4181 *y0 = p1.y-p0.dx*w;
4182 *x1 = p1.x+p1.dy*w;
4183 *y1 = p1.y-p1.dx*w;
4184 } else {
4185 *x0 = p1.x+p1.dmx*w;
4186 *y0 = p1.y+p1.dmy*w;
4187 *x1 = p1.x+p1.dmx*w;
4188 *y1 = p1.y+p1.dmy*w;
4192 NVGvertex* nvg__roundJoin (NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, float lw, float rw, float lu, float ru, int ncap, float fringe) nothrow @trusted @nogc {
4193 float dlx0 = p0.dy;
4194 float dly0 = -p0.dx;
4195 float dlx1 = p1.dy;
4196 float dly1 = -p1.dx;
4197 //NVG_NOTUSED(fringe);
4199 if (p1.flags&PointFlag.Left) {
4200 float lx0 = void, ly0 = void, lx1 = void, ly1 = void;
4201 nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, lw, &lx0, &ly0, &lx1, &ly1);
4202 immutable float a0 = nvg__atan2f(-dly0, -dlx0);
4203 float a1 = nvg__atan2f(-dly1, -dlx1);
4204 if (a1 > a0) a1 -= NVG_PI*2;
4206 nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4207 nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4209 int n = nvg__clamp(cast(int)nvg__ceilf(((a0-a1)/NVG_PI)*ncap), 2, ncap);
4210 for (int i = 0; i < n; ++i) {
4211 float u = i/cast(float)(n-1);
4212 float a = a0+u*(a1-a0);
4213 float rx = p1.x+nvg__cosf(a)*rw;
4214 float ry = p1.y+nvg__sinf(a)*rw;
4215 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4216 nvg__vset(dst, rx, ry, ru, 1); ++dst;
4219 nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4220 nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4222 } else {
4223 float rx0 = void, ry0 = void, rx1 = void, ry1 = void;
4224 nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, -rw, &rx0, &ry0, &rx1, &ry1);
4225 immutable float a0 = nvg__atan2f(dly0, dlx0);
4226 float a1 = nvg__atan2f(dly1, dlx1);
4227 if (a1 < a0) a1 += NVG_PI*2;
4229 nvg__vset(dst, p1.x+dlx0*rw, p1.y+dly0*rw, lu, 1); ++dst;
4230 nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4232 int n = nvg__clamp(cast(int)nvg__ceilf(((a1-a0)/NVG_PI)*ncap), 2, ncap);
4233 for (int i = 0; i < n; i++) {
4234 float u = i/cast(float)(n-1);
4235 float a = a0+u*(a1-a0);
4236 float lx = p1.x+nvg__cosf(a)*lw;
4237 float ly = p1.y+nvg__sinf(a)*lw;
4238 nvg__vset(dst, lx, ly, lu, 1); ++dst;
4239 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4242 nvg__vset(dst, p1.x+dlx1*rw, p1.y+dly1*rw, lu, 1); ++dst;
4243 nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4246 return dst;
4249 NVGvertex* nvg__bevelJoin (NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, float lw, float rw, float lu, float ru, float fringe) nothrow @trusted @nogc {
4250 float rx0, ry0, rx1, ry1;
4251 float lx0, ly0, lx1, ly1;
4252 float dlx0 = p0.dy;
4253 float dly0 = -p0.dx;
4254 float dlx1 = p1.dy;
4255 float dly1 = -p1.dx;
4256 //NVG_NOTUSED(fringe);
4258 if (p1.flags&PointFlag.Left) {
4259 nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, lw, &lx0, &ly0, &lx1, &ly1);
4261 nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4262 nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4264 if (p1.flags&PointFlag.Bevel) {
4265 nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4266 nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4268 nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4269 nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4270 } else {
4271 rx0 = p1.x-p1.dmx*rw;
4272 ry0 = p1.y-p1.dmy*rw;
4274 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4275 nvg__vset(dst, p1.x-dlx0*rw, p1.y-dly0*rw, ru, 1); ++dst;
4277 nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4278 nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4280 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4281 nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4284 nvg__vset(dst, lx1, ly1, lu, 1); ++dst;
4285 nvg__vset(dst, p1.x-dlx1*rw, p1.y-dly1*rw, ru, 1); ++dst;
4287 } else {
4288 nvg__chooseBevel(p1.flags&PointFlag.InnerBevelPR, p0, p1, -rw, &rx0, &ry0, &rx1, &ry1);
4290 nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4291 nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4293 if (p1.flags&PointFlag.Bevel) {
4294 nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4295 nvg__vset(dst, rx0, ry0, ru, 1); ++dst;
4297 nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4298 nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4299 } else {
4300 lx0 = p1.x+p1.dmx*lw;
4301 ly0 = p1.y+p1.dmy*lw;
4303 nvg__vset(dst, p1.x+dlx0*lw, p1.y+dly0*lw, lu, 1); ++dst;
4304 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4306 nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4307 nvg__vset(dst, lx0, ly0, lu, 1); ++dst;
4309 nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4310 nvg__vset(dst, p1.x, p1.y, 0.5f, 1); ++dst;
4313 nvg__vset(dst, p1.x+dlx1*lw, p1.y+dly1*lw, lu, 1); ++dst;
4314 nvg__vset(dst, rx1, ry1, ru, 1); ++dst;
4317 return dst;
4320 NVGvertex* nvg__buttCapStart (NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float d, float aa) nothrow @trusted @nogc {
4321 immutable float px = p.x-dx*d;
4322 immutable float py = p.y-dy*d;
4323 immutable float dlx = dy;
4324 immutable float dly = -dx;
4325 nvg__vset(dst, px+dlx*w-dx*aa, py+dly*w-dy*aa, 0, 0); ++dst;
4326 nvg__vset(dst, px-dlx*w-dx*aa, py-dly*w-dy*aa, 1, 0); ++dst;
4327 nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
4328 nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
4329 return dst;
4332 NVGvertex* nvg__buttCapEnd (NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, float d, float aa) nothrow @trusted @nogc {
4333 immutable float px = p.x+dx*d;
4334 immutable float py = p.y+dy*d;
4335 immutable float dlx = dy;
4336 immutable float dly = -dx;
4337 nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
4338 nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
4339 nvg__vset(dst, px+dlx*w+dx*aa, py+dly*w+dy*aa, 0, 0); ++dst;
4340 nvg__vset(dst, px-dlx*w+dx*aa, py-dly*w+dy*aa, 1, 0); ++dst;
4341 return dst;
4344 NVGvertex* nvg__roundCapStart (NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, float aa) nothrow @trusted @nogc {
4345 immutable float px = p.x;
4346 immutable float py = p.y;
4347 immutable float dlx = dy;
4348 immutable float dly = -dx;
4349 //NVG_NOTUSED(aa);
4350 immutable float ncpf = cast(float)(ncap-1);
4351 foreach (int i; 0..ncap) {
4352 float a = i/*/cast(float)(ncap-1)*//ncpf*NVG_PI;
4353 float ax = nvg__cosf(a)*w, ay = nvg__sinf(a)*w;
4354 nvg__vset(dst, px-dlx*ax-dx*ay, py-dly*ax-dy*ay, 0, 1); ++dst;
4355 nvg__vset(dst, px, py, 0.5f, 1); ++dst;
4357 nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
4358 nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
4359 return dst;
4362 NVGvertex* nvg__roundCapEnd (NVGvertex* dst, NVGpoint* p, float dx, float dy, float w, int ncap, float aa) nothrow @trusted @nogc {
4363 immutable float px = p.x;
4364 immutable float py = p.y;
4365 immutable float dlx = dy;
4366 immutable float dly = -dx;
4367 //NVG_NOTUSED(aa);
4368 nvg__vset(dst, px+dlx*w, py+dly*w, 0, 1); ++dst;
4369 nvg__vset(dst, px-dlx*w, py-dly*w, 1, 1); ++dst;
4370 immutable float ncpf = cast(float)(ncap-1);
4371 foreach (int i; 0..ncap) {
4372 float a = i/*cast(float)(ncap-1)*//ncpf*NVG_PI;
4373 float ax = nvg__cosf(a)*w, ay = nvg__sinf(a)*w;
4374 nvg__vset(dst, px, py, 0.5f, 1); ++dst;
4375 nvg__vset(dst, px-dlx*ax+dx*ay, py-dly*ax+dy*ay, 0, 1); ++dst;
4377 return dst;
4380 void nvg__calculateJoins (NVGContext ctx, float w, int lineJoin, float miterLimit) nothrow @trusted @nogc {
4381 NVGpathCache* cache = ctx.cache;
4382 float iw = 0.0f;
4384 if (w > 0.0f) iw = 1.0f/w;
4386 // Calculate which joins needs extra vertices to append, and gather vertex count.
4387 foreach (int i; 0..cache.npaths) {
4388 NVGpath* path = &cache.paths[i];
4389 NVGpoint* pts = &cache.points[path.first];
4390 NVGpoint* p0 = &pts[path.count-1];
4391 NVGpoint* p1 = &pts[0];
4392 int nleft = 0;
4394 path.nbevel = 0;
4396 foreach (int j; 0..path.count) {
4397 //float dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
4398 immutable float dlx0 = p0.dy;
4399 immutable float dly0 = -p0.dx;
4400 immutable float dlx1 = p1.dy;
4401 immutable float dly1 = -p1.dx;
4402 // Calculate extrusions
4403 p1.dmx = (dlx0+dlx1)*0.5f;
4404 p1.dmy = (dly0+dly1)*0.5f;
4405 immutable float dmr2 = p1.dmx*p1.dmx+p1.dmy*p1.dmy;
4406 if (dmr2 > 0.000001f) {
4407 float scale = 1.0f/dmr2;
4408 if (scale > 600.0f) scale = 600.0f;
4409 p1.dmx *= scale;
4410 p1.dmy *= scale;
4413 // Clear flags, but keep the corner.
4414 p1.flags = (p1.flags&PointFlag.Corner) ? PointFlag.Corner : 0;
4416 // Keep track of left turns.
4417 immutable float cross = p1.dx*p0.dy-p0.dx*p1.dy;
4418 if (cross > 0.0f) {
4419 nleft++;
4420 p1.flags |= PointFlag.Left;
4423 // Calculate if we should use bevel or miter for inner join.
4424 immutable float limit = nvg__max(1.01f, nvg__min(p0.len, p1.len)*iw);
4425 if ((dmr2*limit*limit) < 1.0f) p1.flags |= PointFlag.InnerBevelPR;
4427 // Check to see if the corner needs to be beveled.
4428 if (p1.flags&PointFlag.Corner) {
4429 if ((dmr2*miterLimit*miterLimit) < 1.0f || lineJoin == NVGLineCap.Bevel || lineJoin == NVGLineCap.Round) {
4430 p1.flags |= PointFlag.Bevel;
4434 if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) path.nbevel++;
4436 p0 = p1++;
4439 path.convex = (nleft == path.count) ? 1 : 0;
4443 void nvg__expandStroke (NVGContext ctx, float w, int lineCap, int lineJoin, float miterLimit) nothrow @trusted @nogc {
4444 NVGpathCache* cache = ctx.cache;
4445 immutable float aa = ctx.fringeWidth;
4446 int ncap = nvg__curveDivs(w, NVG_PI, ctx.tessTol); // Calculate divisions per half circle.
4448 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
4450 // Calculate max vertex usage.
4451 int cverts = 0;
4452 foreach (int i; 0..cache.npaths) {
4453 NVGpath* path = &cache.paths[i];
4454 immutable bool loop = path.closed;
4455 if (lineJoin == NVGLineCap.Round) {
4456 cverts += (path.count+path.nbevel*(ncap+2)+1)*2; // plus one for loop
4457 } else {
4458 cverts += (path.count+path.nbevel*5+1)*2; // plus one for loop
4460 if (!loop) {
4461 // space for caps
4462 if (lineCap == NVGLineCap.Round) {
4463 cverts += (ncap*2+2)*2;
4464 } else {
4465 cverts += (3+3)*2;
4470 NVGvertex* verts = nvg__allocTempVerts(ctx, cverts);
4471 if (verts is null) return;
4473 foreach (int i; 0..cache.npaths) {
4474 NVGpath* path = &cache.paths[i];
4475 NVGpoint* pts = &cache.points[path.first];
4476 NVGpoint* p0;
4477 NVGpoint* p1;
4478 int s, e;
4480 path.fill = null;
4481 path.nfill = 0;
4483 // Calculate fringe or stroke
4484 immutable bool loop = path.closed;
4485 NVGvertex* dst = verts;
4486 path.stroke = dst;
4488 if (loop) {
4489 // Looping
4490 p0 = &pts[path.count-1];
4491 p1 = &pts[0];
4492 s = 0;
4493 e = path.count;
4494 } else {
4495 // Add cap
4496 p0 = &pts[0];
4497 p1 = &pts[1];
4498 s = 1;
4499 e = path.count-1;
4502 if (!loop) {
4503 // Add cap
4504 float dx = p1.x-p0.x;
4505 float dy = p1.y-p0.y;
4506 nvg__normalize(&dx, &dy);
4507 if (lineCap == NVGLineCap.Butt) dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa);
4508 else if (lineCap == NVGLineCap.Butt || lineCap == NVGLineCap.Square) dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa);
4509 else if (lineCap == NVGLineCap.Round) dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa);
4512 foreach (int j; s..e) {
4513 if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) {
4514 if (lineJoin == NVGLineCap.Round) {
4515 dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa);
4516 } else {
4517 dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa);
4519 } else {
4520 nvg__vset(dst, p1.x+(p1.dmx*w), p1.y+(p1.dmy*w), 0, 1); ++dst;
4521 nvg__vset(dst, p1.x-(p1.dmx*w), p1.y-(p1.dmy*w), 1, 1); ++dst;
4523 p0 = p1++;
4526 if (loop) {
4527 // Loop it
4528 nvg__vset(dst, verts[0].x, verts[0].y, 0, 1); ++dst;
4529 nvg__vset(dst, verts[1].x, verts[1].y, 1, 1); ++dst;
4530 } else {
4531 // Add cap
4532 float dx = p1.x-p0.x;
4533 float dy = p1.y-p0.y;
4534 nvg__normalize(&dx, &dy);
4535 if (lineCap == NVGLineCap.Butt) dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa);
4536 else if (lineCap == NVGLineCap.Butt || lineCap == NVGLineCap.Square) dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa);
4537 else if (lineCap == NVGLineCap.Round) dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa);
4540 path.nstroke = cast(int)(dst-verts);
4542 verts = dst;
4546 void nvg__expandFill (NVGContext ctx, float w, int lineJoin, float miterLimit) nothrow @trusted @nogc {
4547 NVGpathCache* cache = ctx.cache;
4548 immutable float aa = ctx.fringeWidth;
4549 bool fringe = (w > 0.0f);
4551 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
4553 // Calculate max vertex usage.
4554 int cverts = 0;
4555 foreach (int i; 0..cache.npaths) {
4556 NVGpath* path = &cache.paths[i];
4557 cverts += path.count+path.nbevel+1;
4558 if (fringe) cverts += (path.count+path.nbevel*5+1)*2; // plus one for loop
4561 NVGvertex* verts = nvg__allocTempVerts(ctx, cverts);
4562 if (verts is null) return;
4564 bool convex = (cache.npaths == 1 && cache.paths[0].convex);
4566 foreach (int i; 0..cache.npaths) {
4567 NVGpath* path = &cache.paths[i];
4568 NVGpoint* pts = &cache.points[path.first];
4570 // Calculate shape vertices.
4571 immutable float woff = 0.5f*aa;
4572 NVGvertex* dst = verts;
4573 path.fill = dst;
4575 if (fringe) {
4576 // Looping
4577 NVGpoint* p0 = &pts[path.count-1];
4578 NVGpoint* p1 = &pts[0];
4579 foreach (int j; 0..path.count) {
4580 if (p1.flags&PointFlag.Bevel) {
4581 immutable float dlx0 = p0.dy;
4582 immutable float dly0 = -p0.dx;
4583 immutable float dlx1 = p1.dy;
4584 immutable float dly1 = -p1.dx;
4585 if (p1.flags&PointFlag.Left) {
4586 immutable float lx = p1.x+p1.dmx*woff;
4587 immutable float ly = p1.y+p1.dmy*woff;
4588 nvg__vset(dst, lx, ly, 0.5f, 1); ++dst;
4589 } else {
4590 immutable float lx0 = p1.x+dlx0*woff;
4591 immutable float ly0 = p1.y+dly0*woff;
4592 immutable float lx1 = p1.x+dlx1*woff;
4593 immutable float ly1 = p1.y+dly1*woff;
4594 nvg__vset(dst, lx0, ly0, 0.5f, 1); ++dst;
4595 nvg__vset(dst, lx1, ly1, 0.5f, 1); ++dst;
4597 } else {
4598 nvg__vset(dst, p1.x+(p1.dmx*woff), p1.y+(p1.dmy*woff), 0.5f, 1); ++dst;
4600 p0 = p1++;
4602 } else {
4603 foreach (int j; 0..path.count) {
4604 nvg__vset(dst, pts[j].x, pts[j].y, 0.5f, 1);
4605 ++dst;
4609 path.nfill = cast(int)(dst-verts);
4610 verts = dst;
4612 // Calculate fringe
4613 if (fringe) {
4614 float lw = w+woff;
4615 immutable float rw = w-woff;
4616 float lu = 0;
4617 immutable float ru = 1;
4618 dst = verts;
4619 path.stroke = dst;
4621 // Create only half a fringe for convex shapes so that
4622 // the shape can be rendered without stenciling.
4623 if (convex) {
4624 lw = woff; // This should generate the same vertex as fill inset above.
4625 lu = 0.5f; // Set outline fade at middle.
4628 // Looping
4629 NVGpoint* p0 = &pts[path.count-1];
4630 NVGpoint* p1 = &pts[0];
4632 foreach (int j; 0..path.count) {
4633 if ((p1.flags&(PointFlag.Bevel|PointFlag.InnerBevelPR)) != 0) {
4634 dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx.fringeWidth);
4635 } else {
4636 nvg__vset(dst, p1.x+(p1.dmx*lw), p1.y+(p1.dmy*lw), lu, 1); ++dst;
4637 nvg__vset(dst, p1.x-(p1.dmx*rw), p1.y-(p1.dmy*rw), ru, 1); ++dst;
4639 p0 = p1++;
4642 // Loop it
4643 nvg__vset(dst, verts[0].x, verts[0].y, lu, 1); ++dst;
4644 nvg__vset(dst, verts[1].x, verts[1].y, ru, 1); ++dst;
4646 path.nstroke = cast(int)(dst-verts);
4647 verts = dst;
4648 } else {
4649 path.stroke = null;
4650 path.nstroke = 0;
4656 // ////////////////////////////////////////////////////////////////////////// //
4657 // Paths
4659 /// Clears the current path and sub-paths.
4660 /// Group: paths
4661 public void beginPath (NVGContext ctx) nothrow @trusted @nogc {
4662 ctx.ncommands = 0;
4663 ctx.pathPickRegistered &= NVGPickKind.All; // reset "registered" flags
4664 nvg__clearPathCache(ctx);
4667 public alias newPath = beginPath; /// Ditto.
4669 /// Starts new sub-path with specified point as first point.
4670 /// Group: paths
4671 public void moveTo (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
4672 nvg__appendCommands(ctx, Command.MoveTo, x, y);
4675 /// Starts new sub-path with specified point as first point.
4676 /// Arguments: [x, y]*
4677 /// Group: paths
4678 public void moveTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4679 enum ArgC = 2;
4680 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [moveTo] call");
4681 if (args.length < ArgC) return;
4682 nvg__appendCommands(ctx, Command.MoveTo, args[$-2..$]);
4685 /// Adds line segment from the last point in the path to the specified point.
4686 /// Group: paths
4687 public void lineTo (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
4688 nvg__appendCommands(ctx, Command.LineTo, x, y);
4691 /// Adds line segment from the last point in the path to the specified point.
4692 /// Arguments: [x, y]*
4693 /// Group: paths
4694 public void lineTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4695 enum ArgC = 2;
4696 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [lineTo] call");
4697 if (args.length < ArgC) return;
4698 foreach (immutable idx; 0..args.length/ArgC) {
4699 nvg__appendCommands(ctx, Command.LineTo, args.ptr[idx*ArgC..idx*ArgC+ArgC]);
4703 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
4704 /// Group: paths
4705 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 {
4706 nvg__appendCommands(ctx, Command.BezierTo, c1x, c1y, c2x, c2y, x, y);
4709 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
4710 /// Arguments: [c1x, c1y, c2x, c2y, x, y]*
4711 /// Group: paths
4712 public void bezierTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4713 enum ArgC = 6;
4714 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [bezierTo] call");
4715 if (args.length < ArgC) return;
4716 foreach (immutable idx; 0..args.length/ArgC) {
4717 nvg__appendCommands(ctx, Command.BezierTo, args.ptr[idx*ArgC..idx*ArgC+ArgC]);
4721 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
4722 /// Group: paths
4723 public void quadTo (NVGContext ctx, in float cx, in float cy, in float x, in float y) nothrow @trusted @nogc {
4724 immutable float x0 = ctx.commandx;
4725 immutable float y0 = ctx.commandy;
4726 nvg__appendCommands(ctx,
4727 Command.BezierTo,
4728 x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
4729 x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
4730 x, y,
4734 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
4735 /// Arguments: [cx, cy, x, y]*
4736 /// Group: paths
4737 public void quadTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4738 enum ArgC = 4;
4739 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [quadTo] call");
4740 if (args.length < ArgC) return;
4741 const(float)* aptr = args.ptr;
4742 foreach (immutable idx; 0..args.length/ArgC) {
4743 immutable float x0 = ctx.commandx;
4744 immutable float y0 = ctx.commandy;
4745 immutable float cx = *aptr++;
4746 immutable float cy = *aptr++;
4747 immutable float x = *aptr++;
4748 immutable float y = *aptr++;
4749 nvg__appendCommands(ctx,
4750 Command.BezierTo,
4751 x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
4752 x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
4753 x, y,
4758 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
4759 /// Group: paths
4760 public void arcTo (NVGContext ctx, in float x1, in float y1, in float x2, in float y2, in float radius) nothrow @trusted @nogc {
4761 if (ctx.ncommands == 0) return;
4763 immutable float x0 = ctx.commandx;
4764 immutable float y0 = ctx.commandy;
4766 // handle degenerate cases
4767 if (nvg__ptEquals(x0, y0, x1, y1, ctx.distTol) ||
4768 nvg__ptEquals(x1, y1, x2, y2, ctx.distTol) ||
4769 nvg__distPtSeg(x1, y1, x0, y0, x2, y2) < ctx.distTol*ctx.distTol ||
4770 radius < ctx.distTol)
4772 ctx.lineTo(x1, y1);
4773 return;
4776 // calculate tangential circle to lines (x0, y0)-(x1, y1) and (x1, y1)-(x2, y2)
4777 float dx0 = x0-x1;
4778 float dy0 = y0-y1;
4779 float dx1 = x2-x1;
4780 float dy1 = y2-y1;
4781 nvg__normalize(&dx0, &dy0);
4782 nvg__normalize(&dx1, &dy1);
4783 immutable float a = nvg__acosf(dx0*dx1+dy0*dy1);
4784 immutable float d = radius/nvg__tanf(a/2.0f);
4786 //printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
4788 if (d > 10000.0f) {
4789 ctx.lineTo(x1, y1);
4790 return;
4793 float cx = void, cy = void, a0 = void, a1 = void;
4794 NVGWinding dir;
4795 if (nvg__cross(dx0, dy0, dx1, dy1) > 0.0f) {
4796 cx = x1+dx0*d+dy0*radius;
4797 cy = y1+dy0*d+-dx0*radius;
4798 a0 = nvg__atan2f(dx0, -dy0);
4799 a1 = nvg__atan2f(-dx1, dy1);
4800 dir = NVGWinding.CW;
4801 //printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
4802 } else {
4803 cx = x1+dx0*d+-dy0*radius;
4804 cy = y1+dy0*d+dx0*radius;
4805 a0 = nvg__atan2f(-dx0, dy0);
4806 a1 = nvg__atan2f(dx1, -dy1);
4807 dir = NVGWinding.CCW;
4808 //printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
4811 ctx.arc(dir, cx, cy, radius, a0, a1); // first is line
4815 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
4816 /// Arguments: [x1, y1, x2, y2, radius]*
4817 /// Group: paths
4818 public void arcTo (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4819 enum ArgC = 5;
4820 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [arcTo] call");
4821 if (args.length < ArgC) return;
4822 if (ctx.ncommands == 0) return;
4823 const(float)* aptr = args.ptr;
4824 foreach (immutable idx; 0..args.length/ArgC) {
4825 immutable float x0 = ctx.commandx;
4826 immutable float y0 = ctx.commandy;
4827 immutable float x1 = *aptr++;
4828 immutable float y1 = *aptr++;
4829 immutable float x2 = *aptr++;
4830 immutable float y2 = *aptr++;
4831 immutable float radius = *aptr++;
4832 ctx.arcTo(x1, y1, x2, y2, radius);
4836 /// Closes current sub-path with a line segment.
4837 /// Group: paths
4838 public void closePath (NVGContext ctx) nothrow @trusted @nogc {
4839 nvg__appendCommands(ctx, Command.Close);
4842 /// Sets the current sub-path winding, see NVGWinding and NVGSolidity.
4843 /// Group: paths
4844 public void pathWinding (NVGContext ctx, NVGWinding dir) nothrow @trusted @nogc {
4845 nvg__appendCommands(ctx, Command.Winding, cast(float)dir);
4848 /// Ditto.
4849 public void pathWinding (NVGContext ctx, NVGSolidity dir) nothrow @trusted @nogc {
4850 nvg__appendCommands(ctx, Command.Winding, cast(float)dir);
4853 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
4854 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
4855 * Angles are specified in radians.
4857 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
4859 * Group: paths
4861 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 {
4862 static assert(mode == "original" || mode == "move" || mode == "line");
4864 float[3+5*7+100] vals = void;
4865 //int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
4866 static if (mode == "original") {
4867 immutable int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
4868 } else static if (mode == "move") {
4869 enum move = Command.MoveTo;
4870 } else static if (mode == "line") {
4871 enum move = Command.LineTo;
4872 } else {
4873 static assert(0, "wtf?!");
4876 // Clamp angles
4877 float da = a1-a0;
4878 if (dir == NVGWinding.CW) {
4879 if (nvg__absf(da) >= NVG_PI*2) {
4880 da = NVG_PI*2;
4881 } else {
4882 while (da < 0.0f) da += NVG_PI*2;
4884 } else {
4885 if (nvg__absf(da) >= NVG_PI*2) {
4886 da = -NVG_PI*2;
4887 } else {
4888 while (da > 0.0f) da -= NVG_PI*2;
4892 // Split arc into max 90 degree segments.
4893 immutable int ndivs = nvg__max(1, nvg__min(cast(int)(nvg__absf(da)/(NVG_PI*0.5f)+0.5f), 5));
4894 immutable float hda = (da/cast(float)ndivs)/2.0f;
4895 float kappa = nvg__absf(4.0f/3.0f*(1.0f-nvg__cosf(hda))/nvg__sinf(hda));
4897 if (dir == NVGWinding.CCW) kappa = -kappa;
4899 int nvals = 0;
4900 float px = 0, py = 0, ptanx = 0, ptany = 0;
4901 foreach (int i; 0..ndivs+1) {
4902 immutable float a = a0+da*(i/cast(float)ndivs);
4903 immutable float dx = nvg__cosf(a);
4904 immutable float dy = nvg__sinf(a);
4905 immutable float x = cx+dx*r;
4906 immutable float y = cy+dy*r;
4907 immutable float tanx = -dy*r*kappa;
4908 immutable float tany = dx*r*kappa;
4910 if (i == 0) {
4911 if (vals.length-nvals < 3) {
4912 // flush
4913 nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
4914 nvals = 0;
4916 vals.ptr[nvals++] = cast(float)move;
4917 vals.ptr[nvals++] = x;
4918 vals.ptr[nvals++] = y;
4919 } else {
4920 if (vals.length-nvals < 7) {
4921 // flush
4922 nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
4923 nvals = 0;
4925 vals.ptr[nvals++] = Command.BezierTo;
4926 vals.ptr[nvals++] = px+ptanx;
4927 vals.ptr[nvals++] = py+ptany;
4928 vals.ptr[nvals++] = x-tanx;
4929 vals.ptr[nvals++] = y-tany;
4930 vals.ptr[nvals++] = x;
4931 vals.ptr[nvals++] = y;
4933 px = x;
4934 py = y;
4935 ptanx = tanx;
4936 ptany = tany;
4939 nvg__appendCommands!false(ctx, Command.MoveTo, vals.ptr[0..nvals]); // ignore command
4943 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
4944 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
4945 * Angles are specified in radians.
4947 * Arguments: [cx, cy, r, a0, a1]*
4949 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
4951 * Group: paths
4953 public void arc(string mode="original") (NVGContext ctx, NVGWinding dir, in float[] args) nothrow @trusted @nogc {
4954 static assert(mode == "original" || mode == "move" || mode == "line");
4955 enum ArgC = 5;
4956 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [arc] call");
4957 if (args.length < ArgC) return;
4958 const(float)* aptr = args.ptr;
4959 foreach (immutable idx; 0..args.length/ArgC) {
4960 immutable cx = *aptr++;
4961 immutable cy = *aptr++;
4962 immutable r = *aptr++;
4963 immutable a0 = *aptr++;
4964 immutable a1 = *aptr++;
4965 ctx.arc!mode(dir, cx, cy, r, a0, a1);
4969 /// Creates new rectangle shaped sub-path.
4970 /// Group: paths
4971 public void rect (NVGContext ctx, in float x, in float y, in float w, in float h) nothrow @trusted @nogc {
4972 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
4973 Command.MoveTo, x, y,
4974 Command.LineTo, x, y+h,
4975 Command.LineTo, x+w, y+h,
4976 Command.LineTo, x+w, y,
4977 Command.Close,
4981 /// Creates new rectangle shaped sub-path.
4982 /// Arguments: [x, y, w, h]*
4983 /// Group: paths
4984 public void rect (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
4985 enum ArgC = 4;
4986 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [rect] call");
4987 if (args.length < ArgC) return;
4988 const(float)* aptr = args.ptr;
4989 foreach (immutable idx; 0..args.length/ArgC) {
4990 immutable x = *aptr++;
4991 immutable y = *aptr++;
4992 immutable w = *aptr++;
4993 immutable h = *aptr++;
4994 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
4995 Command.MoveTo, x, y,
4996 Command.LineTo, x, y+h,
4997 Command.LineTo, x+w, y+h,
4998 Command.LineTo, x+w, y,
4999 Command.Close,
5004 /// Creates new rounded rectangle shaped sub-path.
5005 /// Group: paths
5006 public void roundedRect (NVGContext ctx, in float x, in float y, in float w, in float h, in float radius) nothrow @trusted @nogc {
5007 ctx.roundedRectVarying(x, y, w, h, radius, radius, radius, radius);
5010 /// Creates new rounded rectangle shaped sub-path.
5011 /// Arguments: [x, y, w, h, radius]*
5012 /// Group: paths
5013 public void roundedRect (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5014 enum ArgC = 5;
5015 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRect] call");
5016 if (args.length < ArgC) return;
5017 const(float)* aptr = args.ptr;
5018 foreach (immutable idx; 0..args.length/ArgC) {
5019 immutable x = *aptr++;
5020 immutable y = *aptr++;
5021 immutable w = *aptr++;
5022 immutable h = *aptr++;
5023 immutable r = *aptr++;
5024 ctx.roundedRectVarying(x, y, w, h, r, r, r, r);
5028 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5029 /// Group: paths
5030 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 {
5031 if (rw < 0.1f || rh < 0.1f) {
5032 rect(ctx, x, y, w, h);
5033 } else {
5034 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5035 Command.MoveTo, x+rw, y,
5036 Command.LineTo, x+w-rw, y,
5037 Command.BezierTo, x+w-rw*(1-NVG_KAPPA90), y, x+w, y+rh*(1-NVG_KAPPA90), x+w, y+rh,
5038 Command.LineTo, x+w, y+h-rh,
5039 Command.BezierTo, x+w, y+h-rh*(1-NVG_KAPPA90), x+w-rw*(1-NVG_KAPPA90), y+h, x+w-rw, y+h,
5040 Command.LineTo, x+rw, y+h,
5041 Command.BezierTo, x+rw*(1-NVG_KAPPA90), y+h, x, y+h-rh*(1-NVG_KAPPA90), x, y+h-rh,
5042 Command.LineTo, x, y+rh,
5043 Command.BezierTo, x, y+rh*(1-NVG_KAPPA90), x+rw*(1-NVG_KAPPA90), y, x+rw, y,
5044 Command.Close,
5049 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5050 /// Arguments: [x, y, w, h, rw, rh]*
5051 /// Group: paths
5052 public void roundedRectEllipse (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5053 enum ArgC = 6;
5054 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRectEllipse] call");
5055 if (args.length < ArgC) return;
5056 const(float)* aptr = args.ptr;
5057 foreach (immutable idx; 0..args.length/ArgC) {
5058 immutable x = *aptr++;
5059 immutable y = *aptr++;
5060 immutable w = *aptr++;
5061 immutable h = *aptr++;
5062 immutable rw = *aptr++;
5063 immutable rh = *aptr++;
5064 if (rw < 0.1f || rh < 0.1f) {
5065 rect(ctx, x, y, w, h);
5066 } else {
5067 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5068 Command.MoveTo, x+rw, y,
5069 Command.LineTo, x+w-rw, y,
5070 Command.BezierTo, x+w-rw*(1-NVG_KAPPA90), y, x+w, y+rh*(1-NVG_KAPPA90), x+w, y+rh,
5071 Command.LineTo, x+w, y+h-rh,
5072 Command.BezierTo, x+w, y+h-rh*(1-NVG_KAPPA90), x+w-rw*(1-NVG_KAPPA90), y+h, x+w-rw, y+h,
5073 Command.LineTo, x+rw, y+h,
5074 Command.BezierTo, x+rw*(1-NVG_KAPPA90), y+h, x, y+h-rh*(1-NVG_KAPPA90), x, y+h-rh,
5075 Command.LineTo, x, y+rh,
5076 Command.BezierTo, x, y+rh*(1-NVG_KAPPA90), x+rw*(1-NVG_KAPPA90), y, x+rw, y,
5077 Command.Close,
5083 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5084 /// Group: paths
5085 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 {
5086 if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
5087 ctx.rect(x, y, w, h);
5088 } else {
5089 immutable float halfw = nvg__absf(w)*0.5f;
5090 immutable float halfh = nvg__absf(h)*0.5f;
5091 immutable float rxBL = nvg__min(radBottomLeft, halfw)*nvg__sign(w), ryBL = nvg__min(radBottomLeft, halfh)*nvg__sign(h);
5092 immutable float rxBR = nvg__min(radBottomRight, halfw)*nvg__sign(w), ryBR = nvg__min(radBottomRight, halfh)*nvg__sign(h);
5093 immutable float rxTR = nvg__min(radTopRight, halfw)*nvg__sign(w), ryTR = nvg__min(radTopRight, halfh)*nvg__sign(h);
5094 immutable float rxTL = nvg__min(radTopLeft, halfw)*nvg__sign(w), ryTL = nvg__min(radTopLeft, halfh)*nvg__sign(h);
5095 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5096 Command.MoveTo, x, y+ryTL,
5097 Command.LineTo, x, y+h-ryBL,
5098 Command.BezierTo, x, y+h-ryBL*(1-NVG_KAPPA90), x+rxBL*(1-NVG_KAPPA90), y+h, x+rxBL, y+h,
5099 Command.LineTo, x+w-rxBR, y+h,
5100 Command.BezierTo, x+w-rxBR*(1-NVG_KAPPA90), y+h, x+w, y+h-ryBR*(1-NVG_KAPPA90), x+w, y+h-ryBR,
5101 Command.LineTo, x+w, y+ryTR,
5102 Command.BezierTo, x+w, y+ryTR*(1-NVG_KAPPA90), x+w-rxTR*(1-NVG_KAPPA90), y, x+w-rxTR, y,
5103 Command.LineTo, x+rxTL, y,
5104 Command.BezierTo, x+rxTL*(1-NVG_KAPPA90), y, x, y+ryTL*(1-NVG_KAPPA90), x, y+ryTL,
5105 Command.Close,
5110 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5111 /// Arguments: [x, y, w, h, radTopLeft, radTopRight, radBottomRight, radBottomLeft]*
5112 /// Group: paths
5113 public void roundedRectVarying (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5114 enum ArgC = 8;
5115 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [roundedRectVarying] call");
5116 if (args.length < ArgC) return;
5117 const(float)* aptr = args.ptr;
5118 foreach (immutable idx; 0..args.length/ArgC) {
5119 immutable x = *aptr++;
5120 immutable y = *aptr++;
5121 immutable w = *aptr++;
5122 immutable h = *aptr++;
5123 immutable radTopLeft = *aptr++;
5124 immutable radTopRight = *aptr++;
5125 immutable radBottomRight = *aptr++;
5126 immutable radBottomLeft = *aptr++;
5127 if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
5128 ctx.rect(x, y, w, h);
5129 } else {
5130 immutable float halfw = nvg__absf(w)*0.5f;
5131 immutable float halfh = nvg__absf(h)*0.5f;
5132 immutable float rxBL = nvg__min(radBottomLeft, halfw)*nvg__sign(w), ryBL = nvg__min(radBottomLeft, halfh)*nvg__sign(h);
5133 immutable float rxBR = nvg__min(radBottomRight, halfw)*nvg__sign(w), ryBR = nvg__min(radBottomRight, halfh)*nvg__sign(h);
5134 immutable float rxTR = nvg__min(radTopRight, halfw)*nvg__sign(w), ryTR = nvg__min(radTopRight, halfh)*nvg__sign(h);
5135 immutable float rxTL = nvg__min(radTopLeft, halfw)*nvg__sign(w), ryTL = nvg__min(radTopLeft, halfh)*nvg__sign(h);
5136 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5137 Command.MoveTo, x, y+ryTL,
5138 Command.LineTo, x, y+h-ryBL,
5139 Command.BezierTo, x, y+h-ryBL*(1-NVG_KAPPA90), x+rxBL*(1-NVG_KAPPA90), y+h, x+rxBL, y+h,
5140 Command.LineTo, x+w-rxBR, y+h,
5141 Command.BezierTo, x+w-rxBR*(1-NVG_KAPPA90), y+h, x+w, y+h-ryBR*(1-NVG_KAPPA90), x+w, y+h-ryBR,
5142 Command.LineTo, x+w, y+ryTR,
5143 Command.BezierTo, x+w, y+ryTR*(1-NVG_KAPPA90), x+w-rxTR*(1-NVG_KAPPA90), y, x+w-rxTR, y,
5144 Command.LineTo, x+rxTL, y,
5145 Command.BezierTo, x+rxTL*(1-NVG_KAPPA90), y, x, y+ryTL*(1-NVG_KAPPA90), x, y+ryTL,
5146 Command.Close,
5152 /// Creates new ellipse shaped sub-path.
5153 /// Group: paths
5154 public void ellipse (NVGContext ctx, in float cx, in float cy, in float rx, in float ry) nothrow @trusted @nogc {
5155 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5156 Command.MoveTo, cx-rx, cy,
5157 Command.BezierTo, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
5158 Command.BezierTo, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
5159 Command.BezierTo, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
5160 Command.BezierTo, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
5161 Command.Close,
5165 /// Creates new ellipse shaped sub-path.
5166 /// Arguments: [cx, cy, rx, ry]*
5167 /// Group: paths
5168 public void ellipse (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5169 enum ArgC = 4;
5170 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [ellipse] call");
5171 if (args.length < ArgC) return;
5172 const(float)* aptr = args.ptr;
5173 foreach (immutable idx; 0..args.length/ArgC) {
5174 immutable cx = *aptr++;
5175 immutable cy = *aptr++;
5176 immutable rx = *aptr++;
5177 immutable ry = *aptr++;
5178 nvg__appendCommands!false(ctx, Command.MoveTo, // ignore command
5179 Command.MoveTo, cx-rx, cy,
5180 Command.BezierTo, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
5181 Command.BezierTo, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
5182 Command.BezierTo, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
5183 Command.BezierTo, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
5184 Command.Close,
5189 /// Creates new circle shaped sub-path.
5190 /// Group: paths
5191 public void circle (NVGContext ctx, in float cx, in float cy, in float r) nothrow @trusted @nogc {
5192 ctx.ellipse(cx, cy, r, r);
5195 /// Creates new circle shaped sub-path.
5196 /// Arguments: [cx, cy, r]*
5197 /// Group: paths
5198 public void circle (NVGContext ctx, in float[] args) nothrow @trusted @nogc {
5199 enum ArgC = 3;
5200 if (args.length%ArgC != 0) assert(0, "NanoVega: invalid [circle] call");
5201 if (args.length < ArgC) return;
5202 const(float)* aptr = args.ptr;
5203 foreach (immutable idx; 0..args.length/ArgC) {
5204 immutable cx = *aptr++;
5205 immutable cy = *aptr++;
5206 immutable r = *aptr++;
5207 ctx.ellipse(cx, cy, r, r);
5211 // Debug function to dump cached path data.
5212 debug public void debugDumpPathCache (NVGContext ctx) nothrow @trusted @nogc {
5213 import core.stdc.stdio : printf;
5214 const(NVGpath)* path;
5215 printf("Dumping %d cached paths\n", ctx.cache.npaths);
5216 for (int i = 0; i < ctx.cache.npaths; ++i) {
5217 path = &ctx.cache.paths[i];
5218 printf("-Path %d\n", i);
5219 if (path.nfill) {
5220 printf("-fill: %d\n", path.nfill);
5221 for (int j = 0; j < path.nfill; ++j) printf("%f\t%f\n", path.fill[j].x, path.fill[j].y);
5223 if (path.nstroke) {
5224 printf("-stroke: %d\n", path.nstroke);
5225 for (int j = 0; j < path.nstroke; ++j) printf("%f\t%f\n", path.stroke[j].x, path.stroke[j].y);
5230 // Flatten path, prepare it for fill operation.
5231 void nvg__prepareFill (NVGContext ctx) nothrow @trusted @nogc {
5232 NVGpathCache* cache = ctx.cache;
5233 NVGstate* state = nvg__getState(ctx);
5235 nvg__flattenPaths(ctx);
5237 if (ctx.params.edgeAntiAlias && state.shapeAntiAlias) {
5238 nvg__expandFill(ctx, ctx.fringeWidth, NVGLineCap.Miter, 2.4f);
5239 } else {
5240 nvg__expandFill(ctx, 0.0f, NVGLineCap.Miter, 2.4f);
5243 cache.evenOddMode = state.evenOddMode;
5244 cache.fringeWidth = ctx.fringeWidth;
5245 cache.fillReady = true;
5246 cache.strokeReady = false;
5247 cache.clipmode = NVGClipMode.None;
5250 // Flatten path, prepare it for stroke operation.
5251 void nvg__prepareStroke (NVGContext ctx) nothrow @trusted @nogc {
5252 NVGstate* state = nvg__getState(ctx);
5253 NVGpathCache* cache = ctx.cache;
5255 nvg__flattenPaths(ctx);
5257 immutable float scale = nvg__getAverageScale(state.xform);
5258 float strokeWidth = nvg__clamp(state.strokeWidth*scale, 0.0f, 200.0f);
5260 if (strokeWidth < ctx.fringeWidth) {
5261 // If the stroke width is less than pixel size, use alpha to emulate coverage.
5262 // Since coverage is area, scale by alpha*alpha.
5263 immutable float alpha = nvg__clamp(strokeWidth/ctx.fringeWidth, 0.0f, 1.0f);
5264 cache.strokeAlphaMul = alpha*alpha;
5265 strokeWidth = ctx.fringeWidth;
5266 } else {
5267 cache.strokeAlphaMul = 1.0f;
5269 cache.strokeWidth = strokeWidth;
5271 if (ctx.params.edgeAntiAlias && state.shapeAntiAlias) {
5272 nvg__expandStroke(ctx, strokeWidth*0.5f+ctx.fringeWidth*0.5f, state.lineCap, state.lineJoin, state.miterLimit);
5273 } else {
5274 nvg__expandStroke(ctx, strokeWidth*0.5f, state.lineCap, state.lineJoin, state.miterLimit);
5277 cache.fringeWidth = ctx.fringeWidth;
5278 cache.fillReady = false;
5279 cache.strokeReady = true;
5280 cache.clipmode = NVGClipMode.None;
5283 /// Fills the current path with current fill style.
5284 /// Group: paths
5285 public void fill (NVGContext ctx) nothrow @trusted @nogc {
5286 NVGstate* state = nvg__getState(ctx);
5288 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Fill|(NVGPickKind.Fill<<16))) == NVGPickKind.Fill) {
5289 ctx.pathPickRegistered |= NVGPickKind.Fill<<16;
5290 ctx.currFillHitId = ctx.pathPickId;
5293 nvg__prepareFill(ctx);
5295 // apply global alpha
5296 NVGPaint fillPaint = state.fill;
5297 fillPaint.innerColor.a *= state.alpha;
5298 fillPaint.outerColor.a *= state.alpha;
5300 ctx.appendCurrentPathToCache(ctx.recset, state.fill);
5302 if (ctx.recblockdraw) return;
5304 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);
5306 // count triangles
5307 foreach (int i; 0..ctx.cache.npaths) {
5308 NVGpath* path = &ctx.cache.paths[i];
5309 ctx.fillTriCount += path.nfill-2;
5310 ctx.fillTriCount += path.nstroke-2;
5311 ctx.drawCallCount += 2;
5315 /// Fills the current path with current stroke style.
5316 /// Group: paths
5317 public void stroke (NVGContext ctx) nothrow @trusted @nogc {
5318 NVGstate* state = nvg__getState(ctx);
5320 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Stroke|(NVGPickKind.Stroke<<16))) == NVGPickKind.Stroke) {
5321 ctx.pathPickRegistered |= NVGPickKind.Stroke<<16;
5322 ctx.currStrokeHitId = ctx.pathPickId;
5325 nvg__prepareStroke(ctx);
5327 NVGpathCache* cache = ctx.cache;
5329 NVGPaint strokePaint = state.stroke;
5330 strokePaint.innerColor.a *= cache.strokeAlphaMul;
5331 strokePaint.outerColor.a *= cache.strokeAlphaMul;
5333 // apply global alpha
5334 strokePaint.innerColor.a *= state.alpha;
5335 strokePaint.outerColor.a *= state.alpha;
5337 ctx.appendCurrentPathToCache(ctx.recset, state.stroke);
5339 if (ctx.recblockdraw) return;
5341 ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, NVGClipMode.None, &strokePaint, &state.scissor, ctx.fringeWidth, cache.strokeWidth, ctx.cache.paths, ctx.cache.npaths);
5343 // count triangles
5344 foreach (int i; 0..ctx.cache.npaths) {
5345 NVGpath* path = &ctx.cache.paths[i];
5346 ctx.strokeTriCount += path.nstroke-2;
5347 ++ctx.drawCallCount;
5351 /// Sets current path as clipping region.
5352 /// Group: clipping
5353 public void clip (NVGContext ctx, NVGClipMode aclipmode=NVGClipMode.Union) nothrow @trusted @nogc {
5354 NVGstate* state = nvg__getState(ctx);
5356 if (aclipmode == NVGClipMode.None) return;
5357 if (ctx.recblockdraw) return; //???
5359 if (aclipmode == NVGClipMode.Replace) ctx.params.renderResetClip(ctx.params.userPtr);
5362 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Fill|(NVGPickKind.Fill<<16))) == NVGPickKind.Fill) {
5363 ctx.pathPickRegistered |= NVGPickKind.Fill<<16;
5364 ctx.currFillHitId = ctx.pathPickId;
5368 nvg__prepareFill(ctx);
5370 // apply global alpha
5371 NVGPaint fillPaint = state.fill;
5372 fillPaint.innerColor.a *= state.alpha;
5373 fillPaint.outerColor.a *= state.alpha;
5375 //ctx.appendCurrentPathToCache(ctx.recset, state.fill);
5377 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);
5379 // count triangles
5380 foreach (int i; 0..ctx.cache.npaths) {
5381 NVGpath* path = &ctx.cache.paths[i];
5382 ctx.fillTriCount += path.nfill-2;
5383 ctx.fillTriCount += path.nstroke-2;
5384 ctx.drawCallCount += 2;
5388 /// Sets current path as clipping region.
5389 /// Group: clipping
5390 public alias clipFill = clip;
5392 /// Sets current path' stroke as clipping region.
5393 /// Group: clipping
5394 public void clipStroke (NVGContext ctx, NVGClipMode aclipmode=NVGClipMode.Union) nothrow @trusted @nogc {
5395 NVGstate* state = nvg__getState(ctx);
5397 if (aclipmode == NVGClipMode.None) return;
5398 if (ctx.recblockdraw) return; //???
5400 if (aclipmode == NVGClipMode.Replace) ctx.params.renderResetClip(ctx.params.userPtr);
5403 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Stroke|(NVGPickKind.Stroke<<16))) == NVGPickKind.Stroke) {
5404 ctx.pathPickRegistered |= NVGPickKind.Stroke<<16;
5405 ctx.currStrokeHitId = ctx.pathPickId;
5409 nvg__prepareStroke(ctx);
5411 NVGpathCache* cache = ctx.cache;
5413 NVGPaint strokePaint = state.stroke;
5414 strokePaint.innerColor.a *= cache.strokeAlphaMul;
5415 strokePaint.outerColor.a *= cache.strokeAlphaMul;
5417 // apply global alpha
5418 strokePaint.innerColor.a *= state.alpha;
5419 strokePaint.outerColor.a *= state.alpha;
5421 //ctx.appendCurrentPathToCache(ctx.recset, state.stroke);
5423 ctx.params.renderStroke(ctx.params.userPtr, state.compositeOperation, aclipmode, &strokePaint, &state.scissor, ctx.fringeWidth, cache.strokeWidth, ctx.cache.paths, ctx.cache.npaths);
5425 // count triangles
5426 foreach (int i; 0..ctx.cache.npaths) {
5427 NVGpath* path = &ctx.cache.paths[i];
5428 ctx.strokeTriCount += path.nstroke-2;
5429 ++ctx.drawCallCount;
5434 // ////////////////////////////////////////////////////////////////////////// //
5435 // Picking API
5437 // most of the code is by Michael Wynne <mike@mikesspace.net>
5438 // https://github.com/memononen/nanovg/pull/230
5439 // https://github.com/MikeWW/nanovg
5441 /// Pick type query. Used in [hitTest] and [hitTestAll].
5442 /// Group: picking_api
5443 public enum NVGPickKind : ubyte {
5444 Fill = 0x01, ///
5445 Stroke = 0x02, ///
5446 All = 0x03, ///
5449 /// Marks the fill of the current path as pickable with the specified id.
5450 /// Note that you can create and mark path without rasterizing it.
5451 /// Group: picking_api
5452 public void currFillHitId (NVGContext ctx, int id) nothrow @trusted @nogc {
5453 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5454 NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], id, /*forStroke:*/false);
5455 nvg__pickSceneInsert(ps, pp);
5458 public alias currFillPickId = currFillHitId; /// Ditto.
5460 /// Marks the stroke of the current path as pickable with the specified id.
5461 /// Note that you can create and mark path without rasterizing it.
5462 /// Group: picking_api
5463 public void currStrokeHitId (NVGContext ctx, int id) nothrow @trusted @nogc {
5464 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5465 NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], id, /*forStroke:*/true);
5466 nvg__pickSceneInsert(ps, pp);
5469 public alias currStrokePickId = currStrokeHitId; /// Ditto.
5471 // Marks the saved path set (fill) as pickable with the specified id.
5472 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
5473 // Group: picking_api
5475 public void pathSetFillHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
5476 if (svp is null) return;
5477 if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
5478 foreach (ref cp; svp.caches[0..svp.ncaches]) {
5479 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5480 NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/false);
5481 nvg__pickSceneInsert(ps, pp);
5486 // Marks the saved path set (stroke) as pickable with the specified id.
5487 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
5488 // Group: picking_api
5490 public void pathSetStrokeHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
5491 if (svp is null) return;
5492 if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
5493 foreach (ref cp; svp.caches[0..svp.ncaches]) {
5494 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5495 NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/true);
5496 nvg__pickSceneInsert(ps, pp);
5501 private template IsGoodHitTestDG(DG) {
5502 enum IsGoodHitTestDG =
5503 __traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); }) ||
5504 __traits(compiles, (){ DG dg; dg(cast(int)42, cast(int)666); });
5507 private template IsGoodHitTestInternalDG(DG) {
5508 enum IsGoodHitTestInternalDG =
5509 __traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); }) ||
5510 __traits(compiles, (){ DG dg; NVGpickPath* pp; dg(pp); });
5513 /// Call delegate [dg] for each path under the specified position (in no particular order).
5514 /// Returns the id of the path for which delegate [dg] returned true or [NVGNoPick].
5515 /// dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
5516 /// Group: picking_api
5517 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) {
5518 if (ctx.pickScene is null || ctx.pickScene.npaths == 0 || (kind&NVGPickKind.All) == 0) return -1;
5520 NVGpickScene* ps = ctx.pickScene;
5521 int levelwidth = 1<<(ps.nlevels-1);
5522 int cellx = nvg__clamp(cast(int)(x/ps.xdim), 0, levelwidth);
5523 int celly = nvg__clamp(cast(int)(y/ps.ydim), 0, levelwidth);
5524 int npicked = 0;
5526 // if we are interested only in most-toplevel path, there is no reason to check pathes with worser order.
5527 // but we cannot just get out on the first path found, 'cause we are using quad tree to speed up bounds
5528 // checking, so path walking order is not guaranteed.
5529 static if (bestOrder) {
5530 int lastBestOrder = int.min;
5533 //{ import core.stdc.stdio; printf("npaths=%d\n", ps.npaths); }
5534 for (int lvl = ps.nlevels-1; lvl >= 0; --lvl) {
5535 for (NVGpickPath* pp = ps.levels[lvl][celly*levelwidth+cellx]; pp !is null; pp = pp.next) {
5536 //{ 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); }
5537 static if (bestOrder) {
5538 // reject earlier paths
5539 if (pp.order <= lastBestOrder) continue; // not interesting
5541 immutable uint kpx = kind&pp.flags&3;
5542 if (kpx == 0) continue; // not interesting
5543 if (!nvg__pickPathTestBounds(ctx, ps, pp, x, y)) continue; // not interesting
5544 //{ import core.stdc.stdio; printf("in bounds!\n"); }
5545 int hit = 0;
5546 if (kpx&NVGPickKind.Stroke) hit = nvg__pickPathStroke(ps, pp, x, y);
5547 if (!hit && (kpx&NVGPickKind.Fill)) hit = nvg__pickPath(ps, pp, x, y);
5548 if (!hit) continue;
5549 //{ import core.stdc.stdio; printf(" HIT!\n"); }
5550 static if (bestOrder) lastBestOrder = pp.order;
5551 static if (IsGoodHitTestDG!DG) {
5552 static if (__traits(compiles, (){ DG dg; bool res = dg(cast(int)42, cast(int)666); })) {
5553 if (dg(pp.id, cast(int)pp.order)) return pp.id;
5554 } else {
5555 dg(pp.id, cast(int)pp.order);
5557 } else {
5558 static if (__traits(compiles, (){ DG dg; NVGpickPath* pp; bool res = dg(pp); })) {
5559 if (dg(pp)) return pp.id;
5560 } else {
5561 dg(pp);
5565 cellx >>= 1;
5566 celly >>= 1;
5567 levelwidth >>= 1;
5570 return -1;
5573 /// Fills ids with a list of the top most hit ids (from bottom to top) under the specified position.
5574 /// Returns the slice of [ids].
5575 /// Group: picking_api
5576 public int[] hitTestAll (NVGContext ctx, in float x, in float y, NVGPickKind kind, int[] ids) nothrow @trusted @nogc {
5577 if (ctx.pickScene is null || ids.length == 0) return ids[0..0];
5579 int npicked = 0;
5580 NVGpickScene* ps = ctx.pickScene;
5582 ctx.hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) nothrow @trusted @nogc {
5583 if (npicked == ps.cpicked) {
5584 int cpicked = ps.cpicked+ps.cpicked;
5585 NVGpickPath** picked = cast(NVGpickPath**)realloc(ps.picked, (NVGpickPath*).sizeof*ps.cpicked);
5586 if (picked is null) return true; // abort
5587 ps.cpicked = cpicked;
5588 ps.picked = picked;
5590 ps.picked[npicked] = pp;
5591 ++npicked;
5592 return false; // go on
5595 qsort(ps.picked, npicked, (NVGpickPath*).sizeof, &nvg__comparePaths);
5597 assert(npicked >= 0);
5598 if (npicked > ids.length) npicked = cast(int)ids.length;
5599 foreach (immutable nidx, ref int did; ids[0..npicked]) did = ps.picked[nidx].id;
5601 return ids[0..npicked];
5604 /// Returns the id of the pickable shape containing x,y or [NVGNoPick] if no shape was found.
5605 /// Group: picking_api
5606 public int hitTest (NVGContext ctx, in float x, in float y, NVGPickKind kind=NVGPickKind.All) nothrow @trusted @nogc {
5607 if (ctx.pickScene is null) return -1;
5609 int bestOrder = int.min;
5610 int bestID = -1;
5612 ctx.hitTestDG!true(x, y, kind, delegate (NVGpickPath* pp) {
5613 if (pp.order > bestOrder) {
5614 bestOrder = pp.order;
5615 bestID = pp.id;
5619 return bestID;
5622 /// Returns `true` if the path with the given id contains x,y.
5623 /// Group: picking_api
5624 public bool hitTestForId (NVGContext ctx, in int id, in float x, in float y, NVGPickKind kind=NVGPickKind.All) nothrow @trusted @nogc {
5625 if (ctx.pickScene is null || id == NVGNoPick) return false;
5627 bool res = false;
5629 ctx.hitTestDG!false(x, y, kind, delegate (NVGpickPath* pp) {
5630 if (pp.id == id) {
5631 res = true;
5632 return true; // stop
5634 return false; // continue
5637 return res;
5640 /// Returns `true` if the given point is within the fill of the currently defined path.
5641 /// This operation can be done before rasterizing the current path.
5642 /// Group: picking_api
5643 public bool hitTestCurrFill (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
5644 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5645 int oldnpoints = ps.npoints;
5646 int oldnsegments = ps.nsegments;
5647 NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], 1, /*forStroke:*/false);
5648 if (pp is null) return false; // oops
5649 scope(exit) {
5650 nvg__freePickPath(ps, pp);
5651 ps.npoints = oldnpoints;
5652 ps.nsegments = oldnsegments;
5654 return (nvg__pointInBounds(x, y, pp.bounds) ? nvg__pickPath(ps, pp, x, y) : false);
5657 alias isPointInPath = hitTestCurrFill; /// Ditto.
5659 /// Returns `true` if the given point is within the stroke of the currently defined path.
5660 /// This operation can be done before rasterizing the current path.
5661 /// Group: picking_api
5662 public bool hitTestCurrStroke (NVGContext ctx, in float x, in float y) nothrow @trusted @nogc {
5663 NVGpickScene* ps = nvg__pickSceneGet(ctx);
5664 int oldnpoints = ps.npoints;
5665 int oldnsegments = ps.nsegments;
5666 NVGpickPath* pp = nvg__pickPathCreate(ctx, ctx.commands[0..ctx.ncommands], 1, /*forStroke:*/true);
5667 if (pp is null) return false; // oops
5668 scope(exit) {
5669 nvg__freePickPath(ps, pp);
5670 ps.npoints = oldnpoints;
5671 ps.nsegments = oldnsegments;
5673 return (nvg__pointInBounds(x, y, pp.bounds) ? nvg__pickPathStroke(ps, pp, x, y) : false);
5677 nothrow @trusted @nogc {
5678 extern(C) {
5679 private alias _compare_fp_t = int function (const void*, const void*) nothrow @nogc;
5680 private extern(C) void qsort (scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc;
5682 extern(C) int nvg__comparePaths (const void* a, const void* b) {
5683 return (*cast(const(NVGpickPath)**)b).order-(*cast(const(NVGpickPath)**)a).order;
5687 enum NVGPickEPS = 0.0001f;
5689 // Segment flags
5690 enum NVGSegmentFlags {
5691 Corner = 1,
5692 Bevel = 2,
5693 InnerBevel = 4,
5694 Cap = 8,
5695 Endcap = 16,
5698 // Path flags
5699 enum NVGPathFlags : ushort {
5700 Fill = NVGPickKind.Fill,
5701 Stroke = NVGPickKind.Stroke,
5702 Scissor = 0x80,
5705 struct NVGsegment {
5706 int firstPoint; // Index into NVGpickScene.points
5707 short type; // NVG_LINETO or NVG_BEZIERTO
5708 short flags; // Flags relate to the corner between the prev segment and this one.
5709 float[4] bounds;
5710 float[2] startDir; // Direction at t == 0
5711 float[2] endDir; // Direction at t == 1
5712 float[2] miterDir; // Direction of miter of corner between the prev segment and this one.
5715 struct NVGpickSubPath {
5716 short winding; // TODO: Merge to flag field
5717 bool closed; // TODO: Merge to flag field
5719 int firstSegment; // Index into NVGpickScene.segments
5720 int nsegments;
5722 float[4] bounds;
5724 NVGpickSubPath* next;
5727 struct NVGpickPath {
5728 int id;
5729 short flags;
5730 short order;
5731 float strokeWidth;
5732 float miterLimit;
5733 short lineCap;
5734 short lineJoin;
5735 bool evenOddMode;
5737 float[4] bounds;
5738 int scissor; // Indexes into ps->points and defines scissor rect as XVec, YVec and Center
5740 NVGpickSubPath* subPaths;
5741 NVGpickPath* next;
5742 NVGpickPath* cellnext;
5745 struct NVGpickScene {
5746 int npaths;
5748 NVGpickPath* paths; // Linked list of paths
5749 NVGpickPath* lastPath; // The last path in the paths linked list (the first path added)
5750 NVGpickPath* freePaths; // Linked list of free paths
5752 NVGpickSubPath* freeSubPaths; // Linked list of free sub paths
5754 int width;
5755 int height;
5757 // Points for all path sub paths.
5758 float* points;
5759 int npoints;
5760 int cpoints;
5762 // Segments for all path sub paths
5763 NVGsegment* segments;
5764 int nsegments;
5765 int csegments;
5767 // Implicit quadtree
5768 float xdim; // Width / (1 << nlevels)
5769 float ydim; // Height / (1 << nlevels)
5770 int ncells; // Total number of cells in all levels
5771 int nlevels;
5772 NVGpickPath*** levels; // Index: [Level][LevelY * LevelW + LevelX] Value: Linked list of paths
5774 // Temp storage for picking
5775 int cpicked;
5776 NVGpickPath** picked;
5780 // bounds utilities
5781 void nvg__initBounds (ref float[4] bounds) {
5782 bounds.ptr[0] = bounds.ptr[1] = 1e6f;
5783 bounds.ptr[2] = bounds.ptr[3] = -1e6f;
5786 void nvg__expandBounds (ref float[4] bounds, const(float)* points, int npoints) {
5787 npoints *= 2;
5788 for (int i = 0; i < npoints; i += 2) {
5789 bounds.ptr[0] = nvg__min(bounds.ptr[0], points[i]);
5790 bounds.ptr[1] = nvg__min(bounds.ptr[1], points[i+1]);
5791 bounds.ptr[2] = nvg__max(bounds.ptr[2], points[i]);
5792 bounds.ptr[3] = nvg__max(bounds.ptr[3], points[i+1]);
5796 void nvg__unionBounds (ref float[4] bounds, in ref float[4] boundsB) {
5797 bounds.ptr[0] = nvg__min(bounds.ptr[0], boundsB.ptr[0]);
5798 bounds.ptr[1] = nvg__min(bounds.ptr[1], boundsB.ptr[1]);
5799 bounds.ptr[2] = nvg__max(bounds.ptr[2], boundsB.ptr[2]);
5800 bounds.ptr[3] = nvg__max(bounds.ptr[3], boundsB.ptr[3]);
5803 void nvg__intersectBounds (ref float[4] bounds, in ref float[4] boundsB) {
5804 bounds.ptr[0] = nvg__max(boundsB.ptr[0], bounds.ptr[0]);
5805 bounds.ptr[1] = nvg__max(boundsB.ptr[1], bounds.ptr[1]);
5806 bounds.ptr[2] = nvg__min(boundsB.ptr[2], bounds.ptr[2]);
5807 bounds.ptr[3] = nvg__min(boundsB.ptr[3], bounds.ptr[3]);
5809 bounds.ptr[2] = nvg__max(bounds.ptr[0], bounds.ptr[2]);
5810 bounds.ptr[3] = nvg__max(bounds.ptr[1], bounds.ptr[3]);
5813 bool nvg__pointInBounds (in float x, in float y, in ref float[4] bounds) {
5814 pragma(inline, true);
5815 return (x >= bounds.ptr[0] && x <= bounds.ptr[2] && y >= bounds.ptr[1] && y <= bounds.ptr[3]);
5818 // building paths & sub paths
5819 int nvg__pickSceneAddPoints (NVGpickScene* ps, const(float)* xy, int n) {
5820 import core.stdc.string : memcpy;
5821 if (ps.npoints+n > ps.cpoints) {
5822 import core.stdc.stdlib : realloc;
5823 int cpoints = ps.npoints+n+(ps.cpoints<<1);
5824 float* points = cast(float*)realloc(ps.points, float.sizeof*2*cpoints);
5825 if (points is null) assert(0, "NanoVega: out of memory");
5826 ps.points = points;
5827 ps.cpoints = cpoints;
5829 int i = ps.npoints;
5830 if (xy !is null) memcpy(&ps.points[i*2], xy, float.sizeof*2*n);
5831 ps.npoints += n;
5832 return i;
5835 void nvg__pickSubPathAddSegment (NVGpickScene* ps, NVGpickSubPath* psp, int firstPoint, int type, short flags) {
5836 NVGsegment* seg = null;
5837 if (ps.nsegments == ps.csegments) {
5838 int csegments = 1+ps.csegments+(ps.csegments<<1);
5839 NVGsegment* segments = cast(NVGsegment*)realloc(ps.segments, NVGsegment.sizeof*csegments);
5840 if (segments is null) assert(0, "NanoVega: out of memory");
5841 ps.segments = segments;
5842 ps.csegments = csegments;
5845 if (psp.firstSegment == -1) psp.firstSegment = ps.nsegments;
5847 seg = &ps.segments[ps.nsegments];
5848 ++ps.nsegments;
5849 seg.firstPoint = firstPoint;
5850 seg.type = cast(short)type;
5851 seg.flags = flags;
5852 ++psp.nsegments;
5854 nvg__segmentDir(ps, psp, seg, 0, seg.startDir);
5855 nvg__segmentDir(ps, psp, seg, 1, seg.endDir);
5858 void nvg__segmentDir (NVGpickScene* ps, NVGpickSubPath* psp, NVGsegment* seg, float t, ref float[2] d) {
5859 const(float)* points = &ps.points[seg.firstPoint*2];
5860 immutable float x0 = points[0*2+0], x1 = points[1*2+0];
5861 immutable float y0 = points[0*2+1], y1 = points[1*2+1];
5862 switch (seg.type) {
5863 case Command.LineTo:
5864 d.ptr[0] = x1-x0;
5865 d.ptr[1] = y1-y0;
5866 nvg__normalize(&d.ptr[0], &d.ptr[1]);
5867 break;
5868 case Command.BezierTo:
5869 immutable float x2 = points[2*2+0];
5870 immutable float y2 = points[2*2+1];
5871 immutable float x3 = points[3*2+0];
5872 immutable float y3 = points[3*2+1];
5874 immutable float omt = 1.0f-t;
5875 immutable float omt2 = omt*omt;
5876 immutable float t2 = t*t;
5878 d.ptr[0] =
5879 3.0f*omt2*(x1-x0)+
5880 6.0f*omt*t*(x2-x1)+
5881 3.0f*t2*(x3-x2);
5883 d.ptr[1] =
5884 3.0f*omt2*(y1-y0)+
5885 6.0f*omt*t*(y2-y1)+
5886 3.0f*t2*(y3-y2);
5888 nvg__normalize(&d.ptr[0], &d.ptr[1]);
5889 break;
5890 default:
5891 break;
5895 void nvg__pickSubPathAddFillSupports (NVGpickScene* ps, NVGpickSubPath* psp) {
5896 if (psp.firstSegment == -1) return;
5897 NVGsegment* segments = &ps.segments[psp.firstSegment];
5898 for (int s = 0; s < psp.nsegments; ++s) {
5899 NVGsegment* seg = &segments[s];
5900 const(float)* points = &ps.points[seg.firstPoint*2];
5901 if (seg.type == Command.LineTo) {
5902 nvg__initBounds(seg.bounds);
5903 nvg__expandBounds(seg.bounds, points, 2);
5904 } else {
5905 nvg__bezierBounds(points, seg.bounds);
5910 void nvg__pickSubPathAddStrokeSupports (NVGpickScene* ps, NVGpickSubPath* psp, float strokeWidth, int lineCap, int lineJoin, float miterLimit) {
5911 if (psp.firstSegment == -1) return;
5912 immutable bool closed = psp.closed;
5913 const(float)* points = ps.points;
5914 NVGsegment* seg = null;
5915 NVGsegment* segments = &ps.segments[psp.firstSegment];
5916 int nsegments = psp.nsegments;
5917 NVGsegment* prevseg = (closed ? &segments[psp.nsegments-1] : null);
5919 int ns = 0; // nsupports
5920 float[32] supportingPoints;
5921 int firstPoint, lastPoint;
5923 if (!closed) {
5924 segments[0].flags |= NVGSegmentFlags.Cap;
5925 segments[nsegments-1].flags |= NVGSegmentFlags.Endcap;
5928 for (int s = 0; s < nsegments; ++s) {
5929 seg = &segments[s];
5930 nvg__initBounds(seg.bounds);
5932 firstPoint = seg.firstPoint*2;
5933 lastPoint = firstPoint+(seg.type == Command.LineTo ? 2 : 6);
5935 ns = 0;
5937 // First two supporting points are either side of the start point
5938 supportingPoints.ptr[ns++] = points[firstPoint]-seg.startDir.ptr[1]*strokeWidth;
5939 supportingPoints.ptr[ns++] = points[firstPoint+1]+seg.startDir.ptr[0]*strokeWidth;
5941 supportingPoints.ptr[ns++] = points[firstPoint]+seg.startDir.ptr[1]*strokeWidth;
5942 supportingPoints.ptr[ns++] = points[firstPoint+1]-seg.startDir.ptr[0]*strokeWidth;
5944 // Second two supporting points are either side of the end point
5945 supportingPoints.ptr[ns++] = points[lastPoint]-seg.endDir.ptr[1]*strokeWidth;
5946 supportingPoints.ptr[ns++] = points[lastPoint+1]+seg.endDir.ptr[0]*strokeWidth;
5948 supportingPoints.ptr[ns++] = points[lastPoint]+seg.endDir.ptr[1]*strokeWidth;
5949 supportingPoints.ptr[ns++] = points[lastPoint+1]-seg.endDir.ptr[0]*strokeWidth;
5951 if ((seg.flags&NVGSegmentFlags.Corner) && prevseg !is null) {
5952 seg.miterDir.ptr[0] = 0.5f*(-prevseg.endDir.ptr[1]-seg.startDir.ptr[1]);
5953 seg.miterDir.ptr[1] = 0.5f*(prevseg.endDir.ptr[0]+seg.startDir.ptr[0]);
5955 immutable float M2 = seg.miterDir.ptr[0]*seg.miterDir.ptr[0]+seg.miterDir.ptr[1]*seg.miterDir.ptr[1];
5957 if (M2 > 0.000001f) {
5958 float scale = 1.0f/M2;
5959 if (scale > 600.0f) scale = 600.0f;
5960 seg.miterDir.ptr[0] *= scale;
5961 seg.miterDir.ptr[1] *= scale;
5964 //NVG_PICK_DEBUG_VECTOR_SCALE(&points[firstPoint], seg.miterDir, 10);
5966 // Add an additional support at the corner on the other line
5967 supportingPoints.ptr[ns++] = points[firstPoint]-prevseg.endDir.ptr[1]*strokeWidth;
5968 supportingPoints.ptr[ns++] = points[firstPoint+1]+prevseg.endDir.ptr[0]*strokeWidth;
5970 if (lineJoin == NVGLineCap.Miter || lineJoin == NVGLineCap.Bevel) {
5971 // Set a corner as beveled if the join type is bevel or mitered and
5972 // miterLimit is hit.
5973 if (lineJoin == NVGLineCap.Bevel || (M2*miterLimit*miterLimit) < 1.0f) {
5974 seg.flags |= NVGSegmentFlags.Bevel;
5975 } else {
5976 // Corner is mitered - add miter point as a support
5977 supportingPoints.ptr[ns++] = points[firstPoint]+seg.miterDir.ptr[0]*strokeWidth;
5978 supportingPoints.ptr[ns++] = points[firstPoint+1]+seg.miterDir.ptr[1]*strokeWidth;
5980 } else if (lineJoin == NVGLineCap.Round) {
5981 // ... and at the midpoint of the corner arc
5982 float[2] vertexN = [ -seg.startDir.ptr[0]+prevseg.endDir.ptr[0], -seg.startDir.ptr[1]+prevseg.endDir.ptr[1] ];
5983 nvg__normalize(&vertexN[0], &vertexN[1]);
5985 supportingPoints.ptr[ns++] = points[firstPoint]+vertexN[0]*strokeWidth;
5986 supportingPoints.ptr[ns++] = points[firstPoint+1]+vertexN[1]*strokeWidth;
5990 if (seg.flags&NVGSegmentFlags.Cap) {
5991 switch (lineCap) {
5992 case NVGLineCap.Butt:
5993 // supports for butt already added
5994 break;
5995 case NVGLineCap.Square:
5996 // square cap supports are just the original two supports moved out along the direction
5997 supportingPoints.ptr[ns++] = supportingPoints.ptr[0]-seg.startDir.ptr[0]*strokeWidth;
5998 supportingPoints.ptr[ns++] = supportingPoints.ptr[1]-seg.startDir.ptr[1]*strokeWidth;
5999 supportingPoints.ptr[ns++] = supportingPoints.ptr[2]-seg.startDir.ptr[0]*strokeWidth;
6000 supportingPoints.ptr[ns++] = supportingPoints.ptr[3]-seg.startDir.ptr[1]*strokeWidth;
6001 break;
6002 case NVGLineCap.Round:
6003 // add one additional support for the round cap along the dir
6004 supportingPoints.ptr[ns++] = points[firstPoint]-seg.startDir.ptr[0]*strokeWidth;
6005 supportingPoints.ptr[ns++] = points[firstPoint+1]-seg.startDir.ptr[1]*strokeWidth;
6006 break;
6007 default:
6008 break;
6012 if (seg.flags&NVGSegmentFlags.Endcap) {
6013 // end supporting points, either side of line
6014 int end = 4;
6015 switch(lineCap) {
6016 case NVGLineCap.Butt:
6017 // supports for butt already added
6018 break;
6019 case NVGLineCap.Square:
6020 // square cap supports are just the original two supports moved out along the direction
6021 supportingPoints.ptr[ns++] = supportingPoints.ptr[end+0]+seg.endDir.ptr[0]*strokeWidth;
6022 supportingPoints.ptr[ns++] = supportingPoints.ptr[end+1]+seg.endDir.ptr[1]*strokeWidth;
6023 supportingPoints.ptr[ns++] = supportingPoints.ptr[end+2]+seg.endDir.ptr[0]*strokeWidth;
6024 supportingPoints.ptr[ns++] = supportingPoints.ptr[end+3]+seg.endDir.ptr[1]*strokeWidth;
6025 break;
6026 case NVGLineCap.Round:
6027 // add one additional support for the round cap along the dir
6028 supportingPoints.ptr[ns++] = points[lastPoint]+seg.endDir.ptr[0]*strokeWidth;
6029 supportingPoints.ptr[ns++] = points[lastPoint+1]+seg.endDir.ptr[1]*strokeWidth;
6030 break;
6031 default:
6032 break;
6036 nvg__expandBounds(seg.bounds, supportingPoints.ptr, ns/2);
6038 prevseg = seg;
6042 NVGpickPath* nvg__pickPathCreate (NVGContext context, const(float)[] acommands, int id, bool forStroke) {
6043 NVGpickScene* ps = nvg__pickSceneGet(context);
6044 if (ps is null) return null;
6046 int i = 0;
6048 int ncommands = cast(int)acommands.length;
6049 const(float)* commands = acommands.ptr;
6051 NVGpickPath* pp = null;
6052 NVGpickSubPath* psp = null;
6053 float[2] start = void;
6054 int firstPoint;
6056 //bool hasHoles = false;
6057 NVGpickSubPath* prev = null;
6059 float[8] points = void;
6060 float[2] inflections = void;
6061 int ninflections = 0;
6063 NVGstate* state = nvg__getState(context);
6064 float[4] totalBounds = void;
6065 NVGsegment* segments = null;
6066 const(NVGsegment)* seg = null;
6067 NVGpickSubPath *curpsp;
6069 pp = nvg__allocPickPath(ps);
6070 if (pp is null) return null;
6072 pp.id = id;
6074 bool hasPoints = false;
6076 void closeIt () {
6077 if (psp is null || !hasPoints) return;
6078 if (ps.points[(ps.npoints-1)*2] != start.ptr[0] || ps.points[(ps.npoints-1)*2+1] != start.ptr[1]) {
6079 firstPoint = nvg__pickSceneAddPoints(ps, start.ptr, 1);
6080 nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, Command.LineTo, NVGSegmentFlags.Corner);
6082 psp.closed = true;
6085 while (i < ncommands) {
6086 int cmd = cast(int)commands[i++];
6087 switch (cmd) {
6088 case Command.MoveTo: // one coordinate pair
6089 const(float)* tfxy = commands+i;
6090 i += 2;
6092 // new starting point
6093 start.ptr[0..2] = tfxy[0..2];
6095 // start a new path for each sub path to handle sub paths that intersect other sub paths
6096 prev = psp;
6097 psp = nvg__allocPickSubPath(ps);
6098 if (psp is null) { psp = prev; break; }
6099 psp.firstSegment = -1;
6100 psp.winding = NVGSolidity.Solid;
6101 psp.next = prev;
6103 nvg__pickSceneAddPoints(ps, tfxy, 1);
6104 hasPoints = true;
6105 break;
6106 case Command.LineTo: // one coordinate pair
6107 const(float)* tfxy = commands+i;
6108 i += 2;
6109 firstPoint = nvg__pickSceneAddPoints(ps, tfxy, 1);
6110 nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, NVGSegmentFlags.Corner);
6111 hasPoints = true;
6112 break;
6113 case Command.BezierTo: // three coordinate pairs
6114 const(float)* tfxy = commands+i;
6115 i += 3*2;
6117 // Split the curve at it's dx==0 or dy==0 inflection points.
6118 // Thus:
6119 // A horizontal line only ever interects the curves once.
6120 // and
6121 // Finding the closest point on any curve converges more reliably.
6123 // NOTE: We could just split on dy==0 here.
6125 memcpy(&points.ptr[0], &ps.points[(ps.npoints-1)*2], float.sizeof*2);
6126 memcpy(&points.ptr[2], tfxy, float.sizeof*2*3);
6128 ninflections = 0;
6129 nvg__bezierInflections(points.ptr, 1, &ninflections, inflections.ptr);
6130 nvg__bezierInflections(points.ptr, 0, &ninflections, inflections.ptr);
6132 if (ninflections) {
6133 float previnfl = 0;
6134 float[8] pointsA = void, pointsB = void;
6136 nvg__smallsort(inflections.ptr, ninflections);
6138 for (int infl = 0; infl < ninflections; ++infl) {
6139 if (nvg__absf(inflections.ptr[infl]-previnfl) < NVGPickEPS) continue;
6141 immutable float t = (inflections.ptr[infl]-previnfl)*(1.0f/(1.0f-previnfl));
6143 previnfl = inflections.ptr[infl];
6145 nvg__splitBezier(points.ptr, t, pointsA.ptr, pointsB.ptr);
6147 firstPoint = nvg__pickSceneAddPoints(ps, &pointsA.ptr[2], 3);
6148 nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, (infl == 0) ? NVGSegmentFlags.Corner : 0);
6150 memcpy(points.ptr, pointsB.ptr, float.sizeof*8);
6153 firstPoint = nvg__pickSceneAddPoints(ps, &pointsB.ptr[2], 3);
6154 nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, 0);
6155 } else {
6156 firstPoint = nvg__pickSceneAddPoints(ps, tfxy, 3);
6157 nvg__pickSubPathAddSegment(ps, psp, firstPoint-1, cmd, NVGSegmentFlags.Corner);
6159 hasPoints = true;
6160 break;
6161 case Command.Close:
6162 closeIt();
6163 break;
6164 case Command.Winding:
6165 psp.winding = cast(short)cast(int)commands[i];
6166 //if (psp.winding == NVGSolidity.Hole) hasHoles = true;
6167 i += 1;
6168 break;
6169 default:
6170 break;
6174 // force-close filled pathes
6175 if (psp !is null && !forStroke && hasPoints && !psp.closed) closeIt();
6177 pp.flags = (forStroke ? NVGPathFlags.Stroke : NVGPathFlags.Fill);
6178 pp.subPaths = psp;
6179 pp.strokeWidth = state.strokeWidth*0.5f;
6180 pp.miterLimit = state.miterLimit;
6181 pp.lineCap = cast(short)state.lineCap;
6182 pp.lineJoin = cast(short)state.lineJoin;
6183 pp.evenOddMode = nvg__getState(context).evenOddMode;
6185 nvg__initBounds(totalBounds);
6187 for (curpsp = psp; curpsp; curpsp = curpsp.next) {
6188 if (forStroke) {
6189 nvg__pickSubPathAddStrokeSupports(ps, curpsp, pp.strokeWidth, pp.lineCap, pp.lineJoin, pp.miterLimit);
6190 } else {
6191 nvg__pickSubPathAddFillSupports(ps, curpsp);
6194 if (curpsp.firstSegment == -1) continue;
6195 segments = &ps.segments[curpsp.firstSegment];
6196 nvg__initBounds(curpsp.bounds);
6197 for (int s = 0; s < curpsp.nsegments; ++s) {
6198 seg = &segments[s];
6199 //NVG_PICK_DEBUG_BOUNDS(seg.bounds);
6200 nvg__unionBounds(curpsp.bounds, seg.bounds);
6203 nvg__unionBounds(totalBounds, curpsp.bounds);
6206 // Store the scissor rect if present.
6207 if (state.scissor.extent.ptr[0] != -1.0f) {
6208 // Use points storage to store the scissor data
6209 pp.scissor = nvg__pickSceneAddPoints(ps, null, 4);
6210 float* scissor = &ps.points[pp.scissor*2];
6212 //memcpy(scissor, state.scissor.xform.ptr, 6*float.sizeof);
6213 scissor[0..6] = state.scissor.xform.mat[];
6214 memcpy(scissor+6, state.scissor.extent.ptr, 2*float.sizeof);
6216 pp.flags |= NVGPathFlags.Scissor;
6219 memcpy(pp.bounds.ptr, totalBounds.ptr, float.sizeof*4);
6221 return pp;
6225 // Struct management
6226 NVGpickPath* nvg__allocPickPath (NVGpickScene* ps) {
6227 NVGpickPath* pp = ps.freePaths;
6228 if (pp !is null) {
6229 ps.freePaths = pp.next;
6230 } else {
6231 pp = cast(NVGpickPath*)malloc(NVGpickPath.sizeof);
6233 memset(pp, 0, NVGpickPath.sizeof);
6234 return pp;
6237 // Put a pick path and any sub paths (back) to the free lists.
6238 void nvg__freePickPath (NVGpickScene* ps, NVGpickPath* pp) {
6239 // Add all sub paths to the sub path free list.
6240 // Finds the end of the path sub paths, links that to the current
6241 // sub path free list head and replaces the head ptr with the
6242 // head path sub path entry.
6243 NVGpickSubPath* psp = null;
6244 for (psp = pp.subPaths; psp !is null && psp.next !is null; psp = psp.next) {}
6246 if (psp) {
6247 psp.next = ps.freeSubPaths;
6248 ps.freeSubPaths = pp.subPaths;
6250 pp.subPaths = null;
6252 // Add the path to the path freelist
6253 pp.next = ps.freePaths;
6254 ps.freePaths = pp;
6255 if (pp.next is null) ps.lastPath = pp;
6258 NVGpickSubPath* nvg__allocPickSubPath (NVGpickScene* ps) {
6259 NVGpickSubPath* psp = ps.freeSubPaths;
6260 if (psp !is null) {
6261 ps.freeSubPaths = psp.next;
6262 } else {
6263 psp = cast(NVGpickSubPath*)malloc(NVGpickSubPath.sizeof);
6264 if (psp is null) return null;
6266 memset(psp, 0, NVGpickSubPath.sizeof);
6267 return psp;
6270 void nvg__returnPickSubPath (NVGpickScene* ps, NVGpickSubPath* psp) {
6271 psp.next = ps.freeSubPaths;
6272 ps.freeSubPaths = psp;
6275 NVGpickScene* nvg__allocPickScene () {
6276 NVGpickScene* ps = cast(NVGpickScene*)malloc(NVGpickScene.sizeof);
6277 if (ps is null) return null;
6278 memset(ps, 0, NVGpickScene.sizeof);
6279 ps.nlevels = 5;
6280 return ps;
6283 void nvg__deletePickScene (NVGpickScene* ps) {
6284 NVGpickPath* pp;
6285 NVGpickSubPath* psp;
6287 // Add all paths (and thus sub paths) to the free list(s).
6288 while (ps.paths !is null) {
6289 pp = ps.paths.next;
6290 nvg__freePickPath(ps, ps.paths);
6291 ps.paths = pp;
6294 // Delete all paths
6295 while (ps.freePaths !is null) {
6296 pp = ps.freePaths;
6297 ps.freePaths = pp.next;
6298 while (pp.subPaths !is null) {
6299 psp = pp.subPaths;
6300 pp.subPaths = psp.next;
6301 free(psp);
6303 free(pp);
6306 // Delete all sub paths
6307 while (ps.freeSubPaths !is null) {
6308 psp = ps.freeSubPaths.next;
6309 free(ps.freeSubPaths);
6310 ps.freeSubPaths = psp;
6313 ps.npoints = 0;
6314 ps.nsegments = 0;
6316 if (ps.levels !is null) {
6317 free(ps.levels[0]);
6318 free(ps.levels);
6321 if (ps.picked !is null) free(ps.picked);
6322 if (ps.points !is null) free(ps.points);
6323 if (ps.segments !is null) free(ps.segments);
6325 free(ps);
6328 NVGpickScene* nvg__pickSceneGet (NVGContext ctx) {
6329 if (ctx.pickScene is null) ctx.pickScene = nvg__allocPickScene();
6330 return ctx.pickScene;
6334 // Applies Casteljau's algorithm to a cubic bezier for a given parameter t
6335 // points is 4 points (8 floats)
6336 // lvl1 is 3 points (6 floats)
6337 // lvl2 is 2 points (4 floats)
6338 // lvl3 is 1 point (2 floats)
6339 void nvg__casteljau (const(float)* points, float t, float* lvl1, float* lvl2, float* lvl3) {
6340 enum x0 = 0*2+0; enum x1 = 1*2+0; enum x2 = 2*2+0; enum x3 = 3*2+0;
6341 enum y0 = 0*2+1; enum y1 = 1*2+1; enum y2 = 2*2+1; enum y3 = 3*2+1;
6343 // Level 1
6344 lvl1[x0] = (points[x1]-points[x0])*t+points[x0];
6345 lvl1[y0] = (points[y1]-points[y0])*t+points[y0];
6347 lvl1[x1] = (points[x2]-points[x1])*t+points[x1];
6348 lvl1[y1] = (points[y2]-points[y1])*t+points[y1];
6350 lvl1[x2] = (points[x3]-points[x2])*t+points[x2];
6351 lvl1[y2] = (points[y3]-points[y2])*t+points[y2];
6353 // Level 2
6354 lvl2[x0] = (lvl1[x1]-lvl1[x0])*t+lvl1[x0];
6355 lvl2[y0] = (lvl1[y1]-lvl1[y0])*t+lvl1[y0];
6357 lvl2[x1] = (lvl1[x2]-lvl1[x1])*t+lvl1[x1];
6358 lvl2[y1] = (lvl1[y2]-lvl1[y1])*t+lvl1[y1];
6360 // Level 3
6361 lvl3[x0] = (lvl2[x1]-lvl2[x0])*t+lvl2[x0];
6362 lvl3[y0] = (lvl2[y1]-lvl2[y0])*t+lvl2[y0];
6365 // Calculates a point on a bezier at point t.
6366 void nvg__bezierEval (const(float)* points, float t, ref float[2] tpoint) {
6367 immutable float omt = 1-t;
6368 immutable float omt3 = omt*omt*omt;
6369 immutable float omt2 = omt*omt;
6370 immutable float t3 = t*t*t;
6371 immutable float t2 = t*t;
6373 tpoint.ptr[0] =
6374 points[0]*omt3+
6375 points[2]*3.0f*omt2*t+
6376 points[4]*3.0f*omt*t2+
6377 points[6]*t3;
6379 tpoint.ptr[1] =
6380 points[1]*omt3+
6381 points[3]*3.0f*omt2*t+
6382 points[5]*3.0f*omt*t2+
6383 points[7]*t3;
6386 // Splits a cubic bezier curve into two parts at point t.
6387 void nvg__splitBezier (const(float)* points, float t, float* pointsA, float* pointsB) {
6388 enum x0 = 0*2+0; enum x1 = 1*2+0; enum x2 = 2*2+0; enum x3 = 3*2+0;
6389 enum y0 = 0*2+1; enum y1 = 1*2+1; enum y2 = 2*2+1; enum y3 = 3*2+1;
6391 float[6] lvl1 = void;
6392 float[4] lvl2 = void;
6393 float[2] lvl3 = void;
6395 nvg__casteljau(points, t, lvl1.ptr, lvl2.ptr, lvl3.ptr);
6397 // First half
6398 pointsA[x0] = points[x0];
6399 pointsA[y0] = points[y0];
6401 pointsA[x1] = lvl1.ptr[x0];
6402 pointsA[y1] = lvl1.ptr[y0];
6404 pointsA[x2] = lvl2.ptr[x0];
6405 pointsA[y2] = lvl2.ptr[y0];
6407 pointsA[x3] = lvl3.ptr[x0];
6408 pointsA[y3] = lvl3.ptr[y0];
6410 // Second half
6411 pointsB[x0] = lvl3.ptr[x0];
6412 pointsB[y0] = lvl3.ptr[y0];
6414 pointsB[x1] = lvl2.ptr[x1];
6415 pointsB[y1] = lvl2.ptr[y1];
6417 pointsB[x2] = lvl1.ptr[x2];
6418 pointsB[y2] = lvl1.ptr[y2];
6420 pointsB[x3] = points[x3];
6421 pointsB[y3] = points[y3];
6424 // Calculates the inflection points in coordinate coord (X = 0, Y = 1) of a cubic bezier.
6425 // Appends any found inflection points to the array inflections and increments *ninflections.
6426 // So finds the parameters where dx/dt or dy/dt is 0
6427 void nvg__bezierInflections (const(float)* points, int coord, int* ninflections, float* inflections) {
6428 immutable float v0 = points[0*2+coord], v1 = points[1*2+coord], v2 = points[2*2+coord], v3 = points[3*2+coord];
6429 float[2] t = void;
6430 int nvalid = *ninflections;
6432 immutable float a = 3.0f*( -v0+3.0f*v1-3.0f*v2+v3 );
6433 immutable float b = 6.0f*( v0-2.0f*v1+v2 );
6434 immutable float c = 3.0f*( v1-v0 );
6436 float d = b*b-4.0f*a*c;
6437 if (nvg__absf(d-0.0f) < NVGPickEPS) {
6438 // Zero or one root
6439 t.ptr[0] = -b/2.0f*a;
6440 if (t.ptr[0] > NVGPickEPS && t.ptr[0] < (1.0f-NVGPickEPS)) {
6441 inflections[nvalid] = t.ptr[0];
6442 ++nvalid;
6444 } else if (d > NVGPickEPS) {
6445 // zero, one or two roots
6446 d = nvg__sqrtf(d);
6448 t.ptr[0] = (-b+d)/(2.0f*a);
6449 t.ptr[1] = (-b-d)/(2.0f*a);
6451 for (int i = 0; i < 2; ++i) {
6452 if (t.ptr[i] > NVGPickEPS && t.ptr[i] < (1.0f-NVGPickEPS)) {
6453 inflections[nvalid] = t.ptr[i];
6454 ++nvalid;
6457 } else {
6458 // zero roots
6461 *ninflections = nvalid;
6464 // Sort a small number of floats in ascending order (0 < n < 6)
6465 void nvg__smallsort (float* values, int n) {
6466 bool bSwapped = true;
6467 for (int j = 0; j < n-1 && bSwapped; ++j) {
6468 bSwapped = false;
6469 for (int i = 0; i < n-1; ++i) {
6470 if (values[i] > values[i+1]) {
6471 auto tmp = values[i];
6472 values[i] = values[i+1];
6473 values[i+1] = tmp;
6479 // Calculates the bounding rect of a given cubic bezier curve.
6480 void nvg__bezierBounds (const(float)* points, ref float[4] bounds) {
6481 float[4] inflections = void;
6482 int ninflections = 0;
6483 float[2] tpoint = void;
6485 nvg__initBounds(bounds);
6487 // Include start and end points in bounds
6488 nvg__expandBounds(bounds, &points[0], 1);
6489 nvg__expandBounds(bounds, &points[6], 1);
6491 // Calculate dx==0 and dy==0 inflection points and add then to the bounds
6493 nvg__bezierInflections(points, 0, &ninflections, inflections.ptr);
6494 nvg__bezierInflections(points, 1, &ninflections, inflections.ptr);
6496 for (int i = 0; i < ninflections; ++i) {
6497 nvg__bezierEval(points, inflections[i], tpoint);
6498 nvg__expandBounds(bounds, tpoint.ptr, 1);
6502 // Checks to see if a line originating from x,y along the +ve x axis
6503 // intersects the given line (points[0],points[1]) -> (points[2], points[3]).
6504 // Returns `true` on intersection.
6505 // Horizontal lines are never hit.
6506 bool nvg__intersectLine (const(float)* points, float x, float y) {
6507 immutable float x1 = points[0];
6508 immutable float y1 = points[1];
6509 immutable float x2 = points[2];
6510 immutable float y2 = points[3];
6511 immutable float d = y2-y1;
6512 if (d > NVGPickEPS || d < -NVGPickEPS) {
6513 immutable float s = (x2-x1)/d;
6514 immutable float lineX = x1+(y-y1)*s;
6515 return (lineX > x);
6516 } else {
6517 return false;
6521 // Checks to see if a line originating from x,y along the +ve x axis intersects the given bezier.
6522 // It is assumed that the line originates from within the bounding box of
6523 // the bezier and that the curve has no dy=0 inflection points.
6524 // Returns the number of intersections found (which is either 1 or 0).
6525 int nvg__intersectBezier (const(float)* points, float x, float y) {
6526 immutable float x0 = points[0*2+0], x1 = points[1*2+0], x2 = points[2*2+0], x3 = points[3*2+0];
6527 immutable float y0 = points[0*2+1], y1 = points[1*2+1], y2 = points[2*2+1], y3 = points[3*2+1];
6529 if (y0 == y1 && y1 == y2 && y2 == y3) return 0;
6531 // Initial t guess
6532 float t = void;
6533 if (y3 != y0) t = (y-y0)/(y3-y0);
6534 else if (x3 != x0) t = (x-x0)/(x3-x0);
6535 else t = 0.5f;
6537 // A few Newton iterations
6538 for (int iter = 0; iter < 6; ++iter) {
6539 immutable float omt = 1-t;
6540 immutable float omt2 = omt*omt;
6541 immutable float t2 = t*t;
6542 immutable float omt3 = omt2*omt;
6543 immutable float t3 = t2*t;
6545 immutable float ty = y0*omt3 +
6546 y1*3.0f*omt2*t +
6547 y2*3.0f*omt*t2 +
6548 y3*t3;
6550 // Newton iteration
6551 immutable float dty = 3.0f*omt2*(y1-y0) +
6552 6.0f*omt*t*(y2-y1) +
6553 3.0f*t2*(y3-y2);
6555 // dty will never == 0 since:
6556 // Either omt, omt2 are zero OR t2 is zero
6557 // y0 != y1 != y2 != y3 (checked above)
6558 t = t-(ty-y)/dty;
6562 immutable float omt = 1-t;
6563 immutable float omt2 = omt*omt;
6564 immutable float t2 = t*t;
6565 immutable float omt3 = omt2*omt;
6566 immutable float t3 = t2*t;
6568 immutable float tx =
6569 x0*omt3+
6570 x1*3.0f*omt2*t+
6571 x2*3.0f*omt*t2+
6572 x3*t3;
6574 return (tx > x ? 1 : 0);
6578 // Finds the closest point on a line to a given point
6579 void nvg__closestLine (const(float)* points, float x, float y, float* closest, float* ot) {
6580 immutable float x1 = points[0];
6581 immutable float y1 = points[1];
6582 immutable float x2 = points[2];
6583 immutable float y2 = points[3];
6584 immutable float pqx = x2-x1;
6585 immutable float pqz = y2-y1;
6586 immutable float dx = x-x1;
6587 immutable float dz = y-y1;
6588 immutable float d = pqx*pqx+pqz*pqz;
6589 float t = pqx*dx+pqz*dz;
6590 if (d > 0) t /= d;
6591 if (t < 0) t = 0; else if (t > 1) t = 1;
6592 closest[0] = x1+t*pqx;
6593 closest[1] = y1+t*pqz;
6594 *ot = t;
6597 // Finds the closest point on a curve for a given point (x,y).
6598 // Assumes that the curve has no dx==0 or dy==0 inflection points.
6599 void nvg__closestBezier (const(float)* points, float x, float y, float* closest, float *ot) {
6600 immutable float x0 = points[0*2+0], x1 = points[1*2+0], x2 = points[2*2+0], x3 = points[3*2+0];
6601 immutable float y0 = points[0*2+1], y1 = points[1*2+1], y2 = points[2*2+1], y3 = points[3*2+1];
6603 // This assumes that the curve has no dy=0 inflection points.
6605 // Initial t guess
6606 float t = 0.5f;
6608 // A few Newton iterations
6609 for (int iter = 0; iter < 6; ++iter) {
6610 immutable float omt = 1-t;
6611 immutable float omt2 = omt*omt;
6612 immutable float t2 = t*t;
6613 immutable float omt3 = omt2*omt;
6614 immutable float t3 = t2*t;
6616 immutable float ty =
6617 y0*omt3+
6618 y1*3.0f*omt2*t+
6619 y2*3.0f*omt*t2+
6620 y3*t3;
6622 immutable float tx =
6623 x0*omt3+
6624 x1*3.0f*omt2*t+
6625 x2*3.0f*omt*t2+
6626 x3*t3;
6628 // Newton iteration
6629 immutable float dty =
6630 3.0f*omt2*(y1-y0)+
6631 6.0f*omt*t*(y2-y1)+
6632 3.0f*t2*(y3-y2);
6634 immutable float ddty =
6635 6.0f*omt*(y2-2.0f*y1+y0)+
6636 6.0f*t*(y3-2.0f*y2+y1);
6638 immutable float dtx =
6639 3.0f*omt2*(x1-x0)+
6640 6.0f*omt*t*(x2-x1)+
6641 3.0f*t2*(x3-x2);
6643 immutable float ddtx =
6644 6.0f*omt*(x2-2.0f*x1+x0)+
6645 6.0f*t*(x3-2.0f*x2+x1);
6647 immutable float errorx = tx-x;
6648 immutable float errory = ty-y;
6650 immutable float n = errorx*dtx+errory*dty;
6651 if (n == 0) break;
6653 immutable float d = dtx*dtx+dty*dty+errorx*ddtx+errory*ddty;
6654 if (d != 0) t = t-n/d; else break;
6657 t = nvg__max(0, nvg__min(1.0, t));
6658 *ot = t;
6660 immutable float omt = 1-t;
6661 immutable float omt2 = omt*omt;
6662 immutable float t2 = t*t;
6663 immutable float omt3 = omt2*omt;
6664 immutable float t3 = t2*t;
6666 immutable float ty =
6667 y0*omt3+
6668 y1*3.0f*omt2*t+
6669 y2*3.0f*omt*t2+
6670 y3*t3;
6672 immutable float tx =
6673 x0*omt3+
6674 x1*3.0f*omt2*t+
6675 x2*3.0f*omt*t2+
6676 x3*t3;
6678 closest[0] = tx;
6679 closest[1] = ty;
6683 // Returns:
6684 // 1 If (x,y) is contained by the stroke of the path
6685 // 0 If (x,y) is not contained by the path.
6686 int nvg__pickSubPathStroke (const NVGpickScene* ps, const NVGpickSubPath* psp, float x, float y, float strokeWidth, int lineCap, int lineJoin) {
6687 if (!nvg__pointInBounds(x, y, psp.bounds)) return 0;
6688 if (psp.firstSegment == -1) return 0;
6690 float[2] closest = void;
6691 float[2] d = void;
6692 float t = void;
6694 // trace a line from x,y out along the positive x axis and count the number of intersections
6695 int nsegments = psp.nsegments;
6696 const(NVGsegment)* seg = ps.segments+psp.firstSegment;
6697 const(NVGsegment)* prevseg = (psp.closed ? &ps.segments[psp.firstSegment+nsegments-1] : null);
6698 immutable float strokeWidthSqd = strokeWidth*strokeWidth;
6700 for (int s = 0; s < nsegments; ++s, prevseg = seg, ++seg) {
6701 if (nvg__pointInBounds(x, y, seg.bounds)) {
6702 // Line potentially hits stroke.
6703 switch (seg.type) {
6704 case Command.LineTo:
6705 nvg__closestLine(&ps.points[seg.firstPoint*2], x, y, closest.ptr, &t);
6706 break;
6707 case Command.BezierTo:
6708 nvg__closestBezier(&ps.points[seg.firstPoint*2], x, y, closest.ptr, &t);
6709 break;
6710 default:
6711 continue;
6714 d.ptr[0] = x-closest.ptr[0];
6715 d.ptr[1] = y-closest.ptr[1];
6717 if ((t >= NVGPickEPS && t <= 1.0f-NVGPickEPS) ||
6718 (seg.flags&(NVGSegmentFlags.Corner|NVGSegmentFlags.Cap|NVGSegmentFlags.Endcap)) == 0 ||
6719 (lineJoin == NVGLineCap.Round))
6721 // Closest point is in the middle of the line/curve, at a rounded join/cap
6722 // or at a smooth join
6723 immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
6724 if (distSqd < strokeWidthSqd) return 1;
6725 } else if ((t > 1.0f-NVGPickEPS && (seg.flags&NVGSegmentFlags.Endcap)) ||
6726 (t < NVGPickEPS && (seg.flags&NVGSegmentFlags.Cap))) {
6727 switch (lineCap) {
6728 case NVGLineCap.Butt:
6729 immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
6730 immutable float dirD = (t < NVGPickEPS ?
6731 -(d.ptr[0]*seg.startDir.ptr[0]+d.ptr[1]*seg.startDir.ptr[1]) :
6732 d.ptr[0]*seg.endDir.ptr[0]+d.ptr[1]*seg.endDir.ptr[1]);
6733 if (dirD < -NVGPickEPS && distSqd < strokeWidthSqd) return 1;
6734 break;
6735 case NVGLineCap.Square:
6736 if (nvg__absf(d.ptr[0]) < strokeWidth && nvg__absf(d.ptr[1]) < strokeWidth) return 1;
6737 break;
6738 case NVGLineCap.Round:
6739 immutable float distSqd = d.ptr[0]*d.ptr[0]+d.ptr[1]*d.ptr[1];
6740 if (distSqd < strokeWidthSqd) return 1;
6741 break;
6742 default:
6743 break;
6745 } else if (seg.flags&NVGSegmentFlags.Corner) {
6746 // Closest point is at a corner
6747 const(NVGsegment)* seg0, seg1;
6749 if (t < NVGPickEPS) {
6750 seg0 = prevseg;
6751 seg1 = seg;
6752 } else {
6753 seg0 = seg;
6754 seg1 = (s == nsegments-1 ? &ps.segments[psp.firstSegment] : seg+1);
6757 if (!(seg1.flags&NVGSegmentFlags.Bevel)) {
6758 immutable float prevNDist = -seg0.endDir.ptr[1]*d.ptr[0]+seg0.endDir.ptr[0]*d.ptr[1];
6759 immutable float curNDist = seg1.startDir.ptr[1]*d.ptr[0]-seg1.startDir.ptr[0]*d.ptr[1];
6760 if (nvg__absf(prevNDist) < strokeWidth && nvg__absf(curNDist) < strokeWidth) return 1;
6761 } else {
6762 d.ptr[0] -= -seg1.startDir.ptr[1]*strokeWidth;
6763 d.ptr[1] -= +seg1.startDir.ptr[0]*strokeWidth;
6764 if (seg1.miterDir.ptr[0]*d.ptr[0]+seg1.miterDir.ptr[1]*d.ptr[1] < 0) return 1;
6770 return 0;
6773 // Returns:
6774 // 1 If (x,y) is contained by the path and the path is solid.
6775 // -1 If (x,y) is contained by the path and the path is a hole.
6776 // 0 If (x,y) is not contained by the path.
6777 int nvg__pickSubPath (const NVGpickScene* ps, const NVGpickSubPath* psp, float x, float y, bool evenOddMode) {
6778 if (!nvg__pointInBounds(x, y, psp.bounds)) return 0;
6779 if (psp.firstSegment == -1) return 0;
6781 const(NVGsegment)* seg = &ps.segments[psp.firstSegment];
6782 int nsegments = psp.nsegments;
6783 int nintersections = 0;
6785 // trace a line from x,y out along the positive x axis and count the number of intersections
6786 for (int s = 0; s < nsegments; ++s, ++seg) {
6787 if ((seg.bounds.ptr[1]-NVGPickEPS) < y &&
6788 (seg.bounds.ptr[3]-NVGPickEPS) > y &&
6789 seg.bounds.ptr[2] > x)
6791 // Line hits the box.
6792 switch (seg.type) {
6793 case Command.LineTo:
6794 if (seg.bounds.ptr[0] > x) {
6795 // line originates outside the box
6796 ++nintersections;
6797 } else {
6798 // line originates inside the box
6799 nintersections += nvg__intersectLine(&ps.points[seg.firstPoint*2], x, y);
6801 break;
6802 case Command.BezierTo:
6803 if (seg.bounds.ptr[0] > x) {
6804 // line originates outside the box
6805 ++nintersections;
6806 } else {
6807 // line originates inside the box
6808 nintersections += nvg__intersectBezier(&ps.points[seg.firstPoint*2], x, y);
6810 break;
6811 default:
6812 break;
6817 if (evenOddMode) {
6818 return nintersections;
6819 } else {
6820 return (nintersections&1 ? (psp.winding == NVGSolidity.Solid ? 1 : -1) : 0);
6824 bool nvg__pickPath (const(NVGpickScene)* ps, const(NVGpickPath)* pp, float x, float y) {
6825 int pickCount = 0;
6826 const(NVGpickSubPath)* psp = pp.subPaths;
6827 while (psp !is null) {
6828 pickCount += nvg__pickSubPath(ps, psp, x, y, pp.evenOddMode);
6829 psp = psp.next;
6831 return ((pp.evenOddMode ? pickCount&1 : pickCount) != 0);
6834 bool nvg__pickPathStroke (const(NVGpickScene)* ps, const(NVGpickPath)* pp, float x, float y) {
6835 const(NVGpickSubPath)* psp = pp.subPaths;
6836 while (psp !is null) {
6837 if (nvg__pickSubPathStroke(ps, psp, x, y, pp.strokeWidth, pp.lineCap, pp.lineJoin)) return true;
6838 psp = psp.next;
6840 return false;
6843 bool nvg__pickPathTestBounds (NVGContext ctx, const NVGpickScene* ps, const NVGpickPath* pp, float x, float y) {
6844 if (nvg__pointInBounds(x, y, pp.bounds)) {
6845 //{ import core.stdc.stdio; printf(" (0): in bounds!\n"); }
6846 if (pp.flags&NVGPathFlags.Scissor) {
6847 const(float)* scissor = &ps.points[pp.scissor*2];
6848 // untransform scissor translation
6849 float stx = void, sty = void;
6850 ctx.gpuUntransformPoint(&stx, &sty, scissor[4], scissor[5]);
6851 immutable float rx = x-stx;
6852 immutable float ry = y-sty;
6853 //{ 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]); }
6854 if (nvg__absf((scissor[0]*rx)+(scissor[1]*ry)) > scissor[6] ||
6855 nvg__absf((scissor[2]*rx)+(scissor[3]*ry)) > scissor[7])
6857 //{ import core.stdc.stdio; printf(" (1): scissor reject!\n"); }
6858 return false;
6861 return true;
6863 return false;
6866 int nvg__countBitsUsed (uint v) pure {
6867 pragma(inline, true);
6868 import core.bitop : bsr;
6869 return (v != 0 ? bsr(v)+1 : 0);
6872 void nvg__pickSceneInsert (NVGpickScene* ps, NVGpickPath* pp) {
6873 if (ps is null || pp is null) return;
6875 int[4] cellbounds;
6876 int base = ps.nlevels-1;
6877 int level;
6878 int levelwidth;
6879 int levelshift;
6880 int levelx;
6881 int levely;
6882 NVGpickPath** cell = null;
6884 // Bit tricks for inserting into an implicit quadtree.
6886 // Calc bounds of path in cells at the lowest level
6887 cellbounds.ptr[0] = cast(int)(pp.bounds.ptr[0]/ps.xdim);
6888 cellbounds.ptr[1] = cast(int)(pp.bounds.ptr[1]/ps.ydim);
6889 cellbounds.ptr[2] = cast(int)(pp.bounds.ptr[2]/ps.xdim);
6890 cellbounds.ptr[3] = cast(int)(pp.bounds.ptr[3]/ps.ydim);
6892 // Find which bits differ between the min/max x/y coords
6893 cellbounds.ptr[0] ^= cellbounds.ptr[2];
6894 cellbounds.ptr[1] ^= cellbounds.ptr[3];
6896 // Use the number of bits used (countBitsUsed(x) == sizeof(int) * 8 - clz(x);
6897 // to calculate the level to insert at (the level at which the bounds fit in a single cell)
6898 level = nvg__min(base-nvg__countBitsUsed(cellbounds.ptr[0]), base-nvg__countBitsUsed(cellbounds.ptr[1]));
6899 if (level < 0) level = 0;
6900 //{ 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]); }
6901 //level = 0;
6903 // Find the correct cell in the chosen level, clamping to the edges.
6904 levelwidth = 1<<level;
6905 levelshift = (ps.nlevels-level)-1;
6906 levelx = nvg__clamp(cellbounds.ptr[2]>>levelshift, 0, levelwidth-1);
6907 levely = nvg__clamp(cellbounds.ptr[3]>>levelshift, 0, levelwidth-1);
6909 // Insert the path into the linked list at that cell.
6910 cell = &ps.levels[level][levely*levelwidth+levelx];
6912 pp.cellnext = *cell;
6913 *cell = pp;
6915 if (ps.paths is null) ps.lastPath = pp;
6916 pp.next = ps.paths;
6917 ps.paths = pp;
6919 // Store the order (depth) of the path for picking ops.
6920 pp.order = cast(short)ps.npaths;
6921 ++ps.npaths;
6924 void nvg__pickBeginFrame (NVGContext ctx, int width, int height) {
6925 NVGpickScene* ps = nvg__pickSceneGet(ctx);
6927 //NVG_PICK_DEBUG_NEWFRAME();
6929 // Return all paths & sub paths from last frame to the free list
6930 while (ps.paths !is null) {
6931 NVGpickPath* pp = ps.paths.next;
6932 nvg__freePickPath(ps, ps.paths);
6933 ps.paths = pp;
6936 ps.paths = null;
6937 ps.npaths = 0;
6939 // Store the screen metrics for the quadtree
6940 ps.width = width;
6941 ps.height = height;
6943 immutable float lowestSubDiv = cast(float)(1<<(ps.nlevels-1));
6944 ps.xdim = cast(float)width/lowestSubDiv;
6945 ps.ydim = cast(float)height/lowestSubDiv;
6947 // Allocate the quadtree if required.
6948 if (ps.levels is null) {
6949 int ncells = 1;
6951 ps.levels = cast(NVGpickPath***)malloc((NVGpickPath**).sizeof*ps.nlevels);
6952 for (int l = 0; l < ps.nlevels; ++l) {
6953 int leveldim = 1<<l;
6954 ncells += leveldim*leveldim;
6957 ps.levels[0] = cast(NVGpickPath**)malloc((NVGpickPath*).sizeof*ncells);
6959 int cell = 1;
6960 for (int l = 1; l < ps.nlevels; ++l) {
6961 ps.levels[l] = &ps.levels[0][cell];
6962 int leveldim = 1<<l;
6963 cell += leveldim*leveldim;
6966 ps.ncells = ncells;
6968 memset(ps.levels[0], 0, ps.ncells*(NVGpickPath*).sizeof);
6970 // Allocate temporary storage for nvgHitTestAll results if required.
6971 if (ps.picked is null) {
6972 ps.cpicked = 16;
6973 ps.picked = cast(NVGpickPath**)malloc((NVGpickPath*).sizeof*ps.cpicked);
6976 ps.npoints = 0;
6977 ps.nsegments = 0;
6979 } // nothrow @trusted @nogc
6982 // ////////////////////////////////////////////////////////////////////////// //
6983 // Text
6985 /** Creates font by loading it from the disk from specified file name.
6986 * Returns handle to the font or FONS_INVALID (aka -1) on error.
6987 * Use "fontname:noaa" as [name] to turn off antialiasing (if font driver supports that).
6989 * On POSIX systems it is possible to use fontconfig font names too.
6990 * `:noaa` in font path is still allowed, but it must be the last option.
6992 * Group: text_api
6994 public int createFont (NVGContext ctx, const(char)[] name, const(char)[] path) nothrow @trusted {
6995 return fonsAddFont(ctx.fs, name, path, ctx.params.fontAA);
6998 /** Creates font by loading it from the specified memory chunk.
6999 * Returns handle to the font or FONS_INVALID (aka -1) on error.
7000 * Won't free data on error.
7002 * Group: text_api
7004 public int createFontMem (NVGContext ctx, const(char)[] name, ubyte* data, int ndata, bool freeData) nothrow @trusted @nogc {
7005 return fonsAddFontMem(ctx.fs, name, data, ndata, freeData, ctx.params.fontAA);
7008 /// Add fonts from another context.
7009 /// This is more effective than reloading fonts, 'cause font data will be shared.
7010 /// Group: text_api
7011 public void addFontsFrom (NVGContext ctx, NVGContext source) nothrow @trusted @nogc {
7012 if (ctx is null || source is null) return;
7013 ctx.fs.fonsAddStashFonts(source.fs);
7016 /// Finds a loaded font of specified name, and returns handle to it, or FONS_INVALID (aka -1) if the font is not found.
7017 /// Group: text_api
7018 public int findFont (NVGContext ctx, const(char)[] name) nothrow @trusted @nogc {
7019 pragma(inline, true);
7020 return (name.length == 0 ? FONS_INVALID : fonsGetFontByName(ctx.fs, name));
7023 /// Sets the font size of current text style.
7024 /// Group: text_api
7025 public void fontSize (NVGContext ctx, float size) nothrow @trusted @nogc {
7026 pragma(inline, true);
7027 nvg__getState(ctx).fontSize = size;
7030 /// Gets the font size of current text style.
7031 /// Group: text_api
7032 public float fontSize (NVGContext ctx) nothrow @trusted @nogc {
7033 pragma(inline, true);
7034 return nvg__getState(ctx).fontSize;
7037 /// Sets the blur of current text style.
7038 /// Group: text_api
7039 public void fontBlur (NVGContext ctx, float blur) nothrow @trusted @nogc {
7040 pragma(inline, true);
7041 nvg__getState(ctx).fontBlur = blur;
7044 /// Gets the blur of current text style.
7045 /// Group: text_api
7046 public float fontBlur (NVGContext ctx) nothrow @trusted @nogc {
7047 pragma(inline, true);
7048 return nvg__getState(ctx).fontBlur;
7051 /// Sets the letter spacing of current text style.
7052 /// Group: text_api
7053 public void textLetterSpacing (NVGContext ctx, float spacing) nothrow @trusted @nogc {
7054 pragma(inline, true);
7055 nvg__getState(ctx).letterSpacing = spacing;
7058 /// Gets the letter spacing of current text style.
7059 /// Group: text_api
7060 public float textLetterSpacing (NVGContext ctx) nothrow @trusted @nogc {
7061 pragma(inline, true);
7062 return nvg__getState(ctx).letterSpacing;
7065 /// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
7066 /// Group: text_api
7067 public void textLineHeight (NVGContext ctx, float lineHeight) nothrow @trusted @nogc {
7068 pragma(inline, true);
7069 nvg__getState(ctx).lineHeight = lineHeight;
7072 /// Gets the proportional line height of current text style. The line height is specified as multiple of font size.
7073 /// Group: text_api
7074 public float textLineHeight (NVGContext ctx) nothrow @trusted @nogc {
7075 pragma(inline, true);
7076 return nvg__getState(ctx).lineHeight;
7079 /// Sets the text align of current text style, see [NVGTextAlign] for options.
7080 /// Group: text_api
7081 public void textAlign (NVGContext ctx, NVGTextAlign talign) nothrow @trusted @nogc {
7082 pragma(inline, true);
7083 nvg__getState(ctx).textAlign = talign;
7086 /// Ditto.
7087 public void textAlign (NVGContext ctx, NVGTextAlign.H h) nothrow @trusted @nogc {
7088 pragma(inline, true);
7089 nvg__getState(ctx).textAlign.horizontal = h;
7092 /// Ditto.
7093 public void textAlign (NVGContext ctx, NVGTextAlign.V v) nothrow @trusted @nogc {
7094 pragma(inline, true);
7095 nvg__getState(ctx).textAlign.vertical = v;
7098 /// Ditto.
7099 public void textAlign (NVGContext ctx, NVGTextAlign.H h, NVGTextAlign.V v) nothrow @trusted @nogc {
7100 pragma(inline, true);
7101 nvg__getState(ctx).textAlign.reset(h, v);
7104 /// Ditto.
7105 public void textAlign (NVGContext ctx, NVGTextAlign.V v, NVGTextAlign.H h) nothrow @trusted @nogc {
7106 pragma(inline, true);
7107 nvg__getState(ctx).textAlign.reset(h, v);
7110 /// Gets the text align of current text style, see [NVGTextAlign] for options.
7111 /// Group: text_api
7112 public NVGTextAlign textAlign (NVGContext ctx) nothrow @trusted @nogc {
7113 pragma(inline, true);
7114 return nvg__getState(ctx).textAlign;
7117 /// Sets the font face based on specified id of current text style.
7118 /// Group: text_api
7119 public void fontFaceId (NVGContext ctx, int font) nothrow @trusted @nogc {
7120 pragma(inline, true);
7121 nvg__getState(ctx).fontId = font;
7124 /// Gets the font face based on specified id of current text style.
7125 /// Group: text_api
7126 public int fontFaceId (NVGContext ctx) nothrow @trusted @nogc {
7127 pragma(inline, true);
7128 return nvg__getState(ctx).fontId;
7131 /** Sets the font face based on specified name of current text style.
7133 * The underlying implementation is using O(1) data structure to lookup
7134 * font names, so you probably should use this function instead of [fontFaceId]
7135 * to make your code more robust and less error-prone.
7137 * Group: text_api
7139 public void fontFace (NVGContext ctx, const(char)[] font) nothrow @trusted @nogc {
7140 pragma(inline, true);
7141 nvg__getState(ctx).fontId = fonsGetFontByName(ctx.fs, font);
7144 static if (is(typeof(&fons__nvg__toPath))) {
7145 public enum NanoVegaHasCharToPath = true; ///
7146 } else {
7147 public enum NanoVegaHasCharToPath = false; ///
7150 /// Adds glyph outlines to the current path. Vertical 0 is baseline.
7151 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
7152 /// Returns `false` if there is no such glyph, or current font is not scalable.
7153 /// Group: text_api
7154 public bool charToPath (NVGContext ctx, dchar dch, float[] bounds=null) nothrow @trusted @nogc {
7155 NVGstate* state = nvg__getState(ctx);
7156 fonsSetFont(ctx.fs, state.fontId);
7157 return fonsToPath(ctx.fs, ctx, dch, bounds);
7160 static if (is(typeof(&fons__nvg__bounds))) {
7161 public enum NanoVegaHasCharPathBounds = true; ///
7162 } else {
7163 public enum NanoVegaHasCharPathBounds = false; ///
7166 /// Returns bounds of the glyph outlines. Vertical 0 is baseline.
7167 /// The glyph is not scaled in any way.
7168 /// Returns `false` if there is no such glyph, or current font is not scalable.
7169 /// Group: text_api
7170 public bool charPathBounds (NVGContext ctx, dchar dch, float[] bounds) nothrow @trusted @nogc {
7171 NVGstate* state = nvg__getState(ctx);
7172 fonsSetFont(ctx.fs, state.fontId);
7173 return fonsPathBounds(ctx.fs, dch, bounds);
7176 /** [charOutline] will return malloced [NVGGlyphOutline].
7178 some usage samples:
7181 float[4] bounds = void;
7183 nvg.scale(0.5, 0.5);
7184 nvg.translate(500, 800);
7185 nvg.evenOddFill;
7187 nvg.newPath();
7188 nvg.charToPath('&', bounds[]);
7189 conwriteln(bounds[]);
7190 nvg.fillPaint(nvg.linearGradient(0, 0, 600, 600, NVGColor("#f70"), NVGColor("#ff0")));
7191 nvg.strokeColor(NVGColor("#0f0"));
7192 nvg.strokeWidth = 3;
7193 nvg.fill();
7194 nvg.stroke();
7195 // glyph bounds
7196 nvg.newPath();
7197 nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7198 nvg.strokeColor(NVGColor("#00f"));
7199 nvg.stroke();
7201 nvg.newPath();
7202 nvg.charToPath('g', bounds[]);
7203 conwriteln(bounds[]);
7204 nvg.fill();
7205 nvg.strokeColor(NVGColor("#0f0"));
7206 nvg.stroke();
7207 // glyph bounds
7208 nvg.newPath();
7209 nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7210 nvg.strokeColor(NVGColor("#00f"));
7211 nvg.stroke();
7213 nvg.newPath();
7214 nvg.moveTo(0, 0);
7215 nvg.lineTo(600, 0);
7216 nvg.strokeColor(NVGColor("#0ff"));
7217 nvg.stroke();
7219 if (auto ol = nvg.charOutline('Q')) {
7220 scope(exit) ol.kill();
7221 nvg.newPath();
7222 conwriteln("==== length: ", ol.length, " ====");
7223 foreach (const ref cmd; ol.commands) {
7224 //conwriteln(" ", cmd.code, ": ", cmd.args[]);
7225 assert(cmd.valid);
7226 final switch (cmd.code) {
7227 case cmd.Kind.MoveTo: nvg.moveTo(cmd.args[0], cmd.args[1]); break;
7228 case cmd.Kind.LineTo: nvg.lineTo(cmd.args[0], cmd.args[1]); break;
7229 case cmd.Kind.QuadTo: nvg.quadTo(cmd.args[0], cmd.args[1], cmd.args[2], cmd.args[3]); break;
7230 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;
7233 nvg.strokeColor(NVGColor("#f00"));
7234 nvg.stroke();
7238 Group: text_api
7240 public struct NVGGlyphOutline {
7241 public:
7242 /// commands
7243 static struct Command {
7244 enum Kind : ubyte {
7245 MoveTo, ///
7246 LineTo, ///
7247 QuadTo, ///
7248 BezierTo, ///
7250 Kind code; ///
7251 const(float)[] args; ///
7252 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (code >= 0 && code <= 3 && args.length >= 2); } ///
7254 /// perform NanoVega command with stored data.
7255 void perform (NVGContext ctx) const nothrow @trusted @nogc {
7256 if (ctx is null) return;
7257 switch (code) {
7258 case Kind.MoveTo: if (args.length > 1) ctx.moveTo(args.ptr[0..2]); break;
7259 case Kind.LineTo: if (args.length > 1) ctx.lineTo(args.ptr[0..2]); break;
7260 case Kind.QuadTo: if (args.length > 3) ctx.quadTo(args.ptr[0..4]); break;
7261 case Kind.BezierTo: if (args.length > 5) ctx.bezierTo(args.ptr[0..6]); break;
7262 default: break;
7266 /// perform NanoVega command with stored data, transforming points with [xform] transformation matrix.
7267 void perform() (NVGContext ctx, in auto ref NVGMatrix xform) const nothrow @trusted @nogc {
7268 if (ctx is null || !valid) return;
7269 float[6] pts = void;
7270 pts[0..args.length] = args[];
7271 foreach (immutable pidx; 0..args.length/2) xform.point(pts.ptr[pidx*2+0], pts.ptr[pidx*2+1]);
7272 switch (code) {
7273 case Kind.MoveTo: if (args.length > 1) ctx.moveTo(pts.ptr[0..2]); break;
7274 case Kind.LineTo: if (args.length > 1) ctx.lineTo(pts.ptr[0..2]); break;
7275 case Kind.QuadTo: if (args.length > 3) ctx.quadTo(pts.ptr[0..4]); break;
7276 case Kind.BezierTo: if (args.length > 5) ctx.bezierTo(pts.ptr[0..6]); break;
7277 default: break;
7282 @disable this (this); // no copies
7284 private:
7285 ubyte* data;
7286 uint used;
7287 uint size;
7288 uint ccount; // number of commands
7290 private:
7291 void clear () nothrow @trusted @nogc {
7292 import core.stdc.stdlib : free;
7293 if (data !is null) { free(data); data = null; }
7294 used = size = ccount = 0;
7295 bounds[] = 0;
7298 public:
7299 float[4] bounds = 0;
7301 @property int length () const pure { pragma(inline, true); return ccount; }
7303 public:
7304 /// Returns forward range with all glyph commands.
7305 /// $(WARNING returned rande should not outlive parent struct!)
7306 auto commands () nothrow @trusted @nogc {
7307 static struct Range {
7308 private nothrow @trusted @nogc:
7309 const(ubyte)* data;
7310 uint cleft; // number of commands left
7311 public:
7312 @property bool empty () const pure { pragma(inline, true); return (cleft == 0); }
7313 @property int length () const pure { pragma(inline, true); return cleft; }
7314 @property Range save () const pure { pragma(inline, true); Range res = this; return res; }
7315 @property Command front () const {
7316 Command res = void;
7317 if (cleft > 0) {
7318 res.code = cast(Command.Kind)data[0];
7319 switch (res.code) {
7320 case Command.Kind.MoveTo:
7321 case Command.Kind.LineTo:
7322 res.args = (cast(const(float*))(data+1))[0..1*2];
7323 break;
7324 case Command.Kind.QuadTo:
7325 res.args = (cast(const(float*))(data+1))[0..2*2];
7326 break;
7327 case Command.Kind.BezierTo:
7328 res.args = (cast(const(float*))(data+1))[0..3*2];
7329 break;
7330 default:
7331 res.code = cast(Command.Kind)255;
7332 res.args = null;
7333 break;
7335 } else {
7336 res.code = cast(Command.Kind)255;
7337 res.args = null;
7339 return res;
7341 void popFront () {
7342 if (cleft == 0) return;
7343 if (--cleft == 0) return; // don't waste time skipping last command
7344 switch (data[0]) {
7345 case Command.Kind.MoveTo:
7346 case Command.Kind.LineTo:
7347 data += 1+1*2*cast(uint)float.sizeof;
7348 break;
7349 case Command.Kind.QuadTo:
7350 data += 1+2*2*cast(uint)float.sizeof;
7351 break;
7352 case Command.Kind.BezierTo:
7353 data += 1+3*2*cast(uint)float.sizeof;
7354 break;
7355 default:
7356 cleft = 0;
7357 break;
7361 return Range(data, ccount);
7365 /// Destroy glyph outiline and free allocated memory.
7366 /// Group: text_api
7367 public void kill (ref NVGGlyphOutline* ol) nothrow @trusted @nogc {
7368 if (ol !is null) {
7369 import core.stdc.stdlib : free;
7370 ol.clear();
7371 free(ol);
7372 ol = null;
7376 static if (is(typeof(&fons__nvg__toOutline))) {
7377 public enum NanoVegaHasCharOutline = true; ///
7378 } else {
7379 public enum NanoVegaHasCharOutline = false; ///
7382 /// Returns glyph outlines as array of commands. Vertical 0 is baseline.
7383 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
7384 /// Returns `null` if there is no such glyph, or current font is not scalable.
7385 /// Group: text_api
7386 public NVGGlyphOutline* charOutline (NVGContext ctx, dchar dch) nothrow @trusted @nogc {
7387 import core.stdc.stdlib : malloc;
7388 import core.stdc.string : memcpy;
7389 NVGstate* state = nvg__getState(ctx);
7390 fonsSetFont(ctx.fs, state.fontId);
7391 NVGGlyphOutline oline;
7392 if (!fonsToOutline(ctx.fs, dch, &oline)) { oline.clear(); return null; }
7393 auto res = cast(NVGGlyphOutline*)malloc(NVGGlyphOutline.sizeof);
7394 if (res is null) { oline.clear(); return null; }
7395 memcpy(res, &oline, oline.sizeof);
7396 return res;
7400 float nvg__quantize (float a, float d) pure nothrow @safe @nogc {
7401 pragma(inline, true);
7402 return (cast(int)(a/d+0.5f))*d;
7405 float nvg__getFontScale (NVGstate* state) nothrow @safe @nogc {
7406 pragma(inline, true);
7407 return nvg__min(nvg__quantize(nvg__getAverageScale(state.xform), 0.01f), 4.0f);
7410 void nvg__flushTextTexture (NVGContext ctx) nothrow @trusted @nogc {
7411 int[4] dirty = void;
7412 if (fonsValidateTexture(ctx.fs, dirty.ptr)) {
7413 auto fontImage = &ctx.fontImages[ctx.fontImageIdx];
7414 // Update texture
7415 if (fontImage.valid) {
7416 int iw, ih;
7417 const(ubyte)* data = fonsGetTextureData(ctx.fs, &iw, &ih);
7418 int x = dirty[0];
7419 int y = dirty[1];
7420 int w = dirty[2]-dirty[0];
7421 int h = dirty[3]-dirty[1];
7422 ctx.params.renderUpdateTexture(ctx.params.userPtr, fontImage.id, x, y, w, h, data);
7427 bool nvg__allocTextAtlas (NVGContext ctx) nothrow @trusted @nogc {
7428 int iw, ih;
7429 nvg__flushTextTexture(ctx);
7430 if (ctx.fontImageIdx >= NVG_MAX_FONTIMAGES-1) return false;
7431 // if next fontImage already have a texture
7432 if (ctx.fontImages[ctx.fontImageIdx+1].valid) {
7433 ctx.imageSize(ctx.fontImages[ctx.fontImageIdx+1], iw, ih);
7434 } else {
7435 // calculate the new font image size and create it
7436 ctx.imageSize(ctx.fontImages[ctx.fontImageIdx], iw, ih);
7437 if (iw > ih) ih *= 2; else iw *= 2;
7438 if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) iw = ih = NVG_MAX_FONTIMAGE_SIZE;
7439 ctx.fontImages[ctx.fontImageIdx+1].id = ctx.params.renderCreateTexture(ctx.params.userPtr, NVGtexture.Alpha, iw, ih, (ctx.params.fontAA ? 0 : NVGImageFlag.NoFiltering), null);
7440 if (ctx.fontImages[ctx.fontImageIdx+1].id > 0) {
7441 ctx.fontImages[ctx.fontImageIdx+1].ctx = ctx;
7442 ctx.nvg__imageIncRef(ctx.fontImages[ctx.fontImageIdx+1].id);
7445 ++ctx.fontImageIdx;
7446 fonsResetAtlas(ctx.fs, iw, ih);
7447 return true;
7450 void nvg__renderText (NVGContext ctx, NVGvertex* verts, int nverts) nothrow @trusted @nogc {
7451 NVGstate* state = nvg__getState(ctx);
7452 NVGPaint paint = state.fill;
7454 // Render triangles.
7455 paint.image = ctx.fontImages[ctx.fontImageIdx];
7457 // Apply global alpha
7458 paint.innerColor.a *= state.alpha;
7459 paint.outerColor.a *= state.alpha;
7461 ctx.params.renderTriangles(ctx.params.userPtr, state.compositeOperation, NVGClipMode.None, &paint, &state.scissor, verts, nverts);
7463 ++ctx.drawCallCount;
7464 ctx.textTriCount += nverts/3;
7467 /// Draws text string at specified location. Returns next x position.
7468 /// Group: text_api
7469 public float text(T) (NVGContext ctx, float x, float y, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
7470 NVGstate* state = nvg__getState(ctx);
7471 FONStextIter!T iter, prevIter;
7472 FONSquad q;
7473 NVGvertex* verts;
7474 float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
7475 float invscale = 1.0f/scale;
7476 int cverts = 0;
7477 int nverts = 0;
7479 if (state.fontId == FONS_INVALID) return x;
7480 if (str.length == 0) return x;
7482 fonsSetSize(ctx.fs, state.fontSize*scale);
7483 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
7484 fonsSetBlur(ctx.fs, state.fontBlur*scale);
7485 fonsSetAlign(ctx.fs, state.textAlign);
7486 fonsSetFont(ctx.fs, state.fontId);
7488 cverts = nvg__max(2, cast(int)(str.length))*6; // conservative estimate
7489 verts = nvg__allocTempVerts(ctx, cverts);
7490 if (verts is null) return x;
7492 fonsTextIterInit(ctx.fs, &iter, x*scale, y*scale, str, FONS_GLYPH_BITMAP_REQUIRED);
7493 prevIter = iter;
7494 while (fonsTextIterNext(ctx.fs, &iter, &q)) {
7495 float[4*2] c = void;
7496 if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
7497 if (nverts != 0) {
7498 // TODO: add back-end bit to do this just once per frame
7499 nvg__flushTextTexture(ctx);
7500 nvg__renderText(ctx, verts, nverts);
7501 nverts = 0;
7503 if (!nvg__allocTextAtlas(ctx)) break; // no memory :(
7504 iter = prevIter;
7505 fonsTextIterNext(ctx.fs, &iter, &q); // try again
7506 if (iter.prevGlyphIndex < 0) {
7507 // still can not find glyph, try replacement
7508 iter = prevIter;
7509 if (!fonsTextIterGetDummyChar(ctx.fs, &iter, &q)) break;
7512 prevIter = iter;
7513 // Transform corners.
7514 state.xform.point(&c[0], &c[1], q.x0*invscale, q.y0*invscale);
7515 state.xform.point(&c[2], &c[3], q.x1*invscale, q.y0*invscale);
7516 state.xform.point(&c[4], &c[5], q.x1*invscale, q.y1*invscale);
7517 state.xform.point(&c[6], &c[7], q.x0*invscale, q.y1*invscale);
7518 // Create triangles
7519 if (nverts+6 <= cverts) {
7520 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); ++nverts;
7521 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); ++nverts;
7522 nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); ++nverts;
7523 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); ++nverts;
7524 nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); ++nverts;
7525 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); ++nverts;
7529 // TODO: add back-end bit to do this just once per frame
7530 if (nverts > 0) {
7531 nvg__flushTextTexture(ctx);
7532 nvg__renderText(ctx, verts, nverts);
7535 return iter.nextx/scale;
7538 /** Draws multi-line text string at specified location wrapped at the specified width.
7539 * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
7540 * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
7542 * Group: text_api
7544 public void textBox(T) (NVGContext ctx, float x, float y, float breakRowWidth, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
7545 NVGstate* state = nvg__getState(ctx);
7546 if (state.fontId == FONS_INVALID) return;
7548 NVGTextRow!T[2] rows;
7549 auto oldAlign = state.textAlign;
7550 scope(exit) state.textAlign = oldAlign;
7551 auto halign = state.textAlign.horizontal;
7552 float lineh = 0;
7554 ctx.textMetrics(null, null, &lineh);
7555 state.textAlign.horizontal = NVGTextAlign.H.Left;
7556 for (;;) {
7557 auto rres = ctx.textBreakLines(str, breakRowWidth, rows[]);
7558 //{ import core.stdc.stdio : printf; printf("slen=%u; rlen=%u; bw=%f\n", cast(uint)str.length, cast(uint)rres.length, cast(double)breakRowWidth); }
7559 if (rres.length == 0) break;
7560 foreach (ref row; rres) {
7561 final switch (halign) {
7562 case NVGTextAlign.H.Left: ctx.text(x, y, row.row); break;
7563 case NVGTextAlign.H.Center: ctx.text(x+breakRowWidth*0.5f-row.width*0.5f, y, row.row); break;
7564 case NVGTextAlign.H.Right: ctx.text(x+breakRowWidth-row.width, y, row.row); break;
7566 y += lineh*state.lineHeight;
7568 str = rres[$-1].rest;
7572 private template isGoodPositionDelegate(DG) {
7573 private DG dg;
7574 static if (is(typeof({ NVGGlyphPosition pos; bool res = dg(pos); })) ||
7575 is(typeof({ NVGGlyphPosition pos; dg(pos); })))
7576 enum isGoodPositionDelegate = true;
7577 else
7578 enum isGoodPositionDelegate = false;
7581 /** Calculates the glyph x positions of the specified text.
7582 * Measured values are returned in local coordinate space.
7584 * Group: text_api
7586 public NVGGlyphPosition[] textGlyphPositions(T) (NVGContext ctx, float x, float y, const(T)[] str, NVGGlyphPosition[] positions) nothrow @trusted @nogc
7587 if (isAnyCharType!T)
7589 if (str.length == 0 || positions.length == 0) return positions[0..0];
7590 usize posnum;
7591 auto len = ctx.textGlyphPositions(x, y, str, (in ref NVGGlyphPosition pos) {
7592 positions.ptr[posnum++] = pos;
7593 return (posnum < positions.length);
7595 return positions[0..len];
7598 /// Ditto.
7599 public int textGlyphPositions(T, DG) (NVGContext ctx, float x, float y, const(T)[] str, scope DG dg)
7600 if (isAnyCharType!T && isGoodPositionDelegate!DG)
7602 import std.traits : ReturnType;
7603 static if (is(ReturnType!dg == void)) enum RetBool = false; else enum RetBool = true;
7605 NVGstate* state = nvg__getState(ctx);
7606 float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
7607 float invscale = 1.0f/scale;
7608 FONStextIter!T iter, prevIter;
7609 FONSquad q;
7610 int npos = 0;
7612 if (str.length == 0) return 0;
7614 fonsSetSize(ctx.fs, state.fontSize*scale);
7615 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
7616 fonsSetBlur(ctx.fs, state.fontBlur*scale);
7617 fonsSetAlign(ctx.fs, state.textAlign);
7618 fonsSetFont(ctx.fs, state.fontId);
7620 fonsTextIterInit(ctx.fs, &iter, x*scale, y*scale, str, FONS_GLYPH_BITMAP_OPTIONAL);
7621 prevIter = iter;
7622 while (fonsTextIterNext(ctx.fs, &iter, &q)) {
7623 if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
7624 if (!nvg__allocTextAtlas(ctx)) break; // no memory
7625 iter = prevIter;
7626 fonsTextIterNext(ctx.fs, &iter, &q); // try again
7627 if (iter.prevGlyphIndex < 0) {
7628 // still can not find glyph, try replacement
7629 iter = prevIter;
7630 if (!fonsTextIterGetDummyChar(ctx.fs, &iter, &q)) break;
7633 prevIter = iter;
7634 NVGGlyphPosition position = void; //WARNING!
7635 position.strpos = cast(usize)(iter.string-str.ptr);
7636 position.x = iter.x*invscale;
7637 position.minx = nvg__min(iter.x, q.x0)*invscale;
7638 position.maxx = nvg__max(iter.nextx, q.x1)*invscale;
7639 ++npos;
7640 static if (RetBool) { if (!dg(position)) return npos; } else dg(position);
7643 return npos;
7646 private template isGoodRowDelegate(CT, DG) {
7647 private DG dg;
7648 static if (is(typeof({ NVGTextRow!CT row; bool res = dg(row); })) ||
7649 is(typeof({ NVGTextRow!CT row; dg(row); })))
7650 enum isGoodRowDelegate = true;
7651 else
7652 enum isGoodRowDelegate = false;
7655 /** Breaks the specified text into lines.
7656 * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
7657 * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
7659 * Group: text_api
7661 public NVGTextRow!T[] textBreakLines(T) (NVGContext ctx, const(T)[] str, float breakRowWidth, NVGTextRow!T[] rows) nothrow @trusted @nogc
7662 if (isAnyCharType!T)
7664 if (rows.length == 0) return rows;
7665 if (rows.length > int.max-1) rows = rows[0..int.max-1];
7666 int nrow = 0;
7667 auto count = ctx.textBreakLines(str, breakRowWidth, (in ref NVGTextRow!T row) {
7668 rows[nrow++] = row;
7669 return (nrow < rows.length);
7671 return rows[0..count];
7674 /// Ditto.
7675 public int textBreakLines(T, DG) (NVGContext ctx, const(T)[] str, float breakRowWidth, scope DG dg)
7676 if (isAnyCharType!T && isGoodRowDelegate!(T, DG))
7678 import std.traits : ReturnType;
7679 static if (is(ReturnType!dg == void)) enum RetBool = false; else enum RetBool = true;
7681 enum NVGcodepointType : int {
7682 Space,
7683 NewLine,
7684 Char,
7687 NVGstate* state = nvg__getState(ctx);
7688 float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
7689 float invscale = 1.0f/scale;
7690 FONStextIter!T iter, prevIter;
7691 FONSquad q;
7692 int nrows = 0;
7693 float rowStartX = 0;
7694 float rowWidth = 0;
7695 float rowMinX = 0;
7696 float rowMaxX = 0;
7697 int rowStart = 0;
7698 int rowEnd = 0;
7699 int wordStart = 0;
7700 float wordStartX = 0;
7701 float wordMinX = 0;
7702 int breakEnd = 0;
7703 float breakWidth = 0;
7704 float breakMaxX = 0;
7705 int type = NVGcodepointType.Space, ptype = NVGcodepointType.Space;
7706 uint pcodepoint = 0;
7708 if (state.fontId == FONS_INVALID) return 0;
7709 if (str.length == 0 || dg is null) return 0;
7711 fonsSetSize(ctx.fs, state.fontSize*scale);
7712 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
7713 fonsSetBlur(ctx.fs, state.fontBlur*scale);
7714 fonsSetAlign(ctx.fs, state.textAlign);
7715 fonsSetFont(ctx.fs, state.fontId);
7717 breakRowWidth *= scale;
7719 enum Phase {
7720 Normal, // searching for breaking point
7721 SkipBlanks, // skip leading blanks
7723 Phase phase = Phase.SkipBlanks; // don't skip blanks on first line
7725 fonsTextIterInit(ctx.fs, &iter, 0, 0, str, FONS_GLYPH_BITMAP_OPTIONAL);
7726 prevIter = iter;
7727 while (fonsTextIterNext(ctx.fs, &iter, &q)) {
7728 if (iter.prevGlyphIndex < 0) { // can not retrieve glyph?
7729 if (!nvg__allocTextAtlas(ctx)) break; // no memory
7730 iter = prevIter;
7731 fonsTextIterNext(ctx.fs, &iter, &q); // try again
7732 if (iter.prevGlyphIndex < 0) {
7733 // still can not find glyph, try replacement
7734 iter = prevIter;
7735 if (!fonsTextIterGetDummyChar(ctx.fs, &iter, &q)) break;
7738 prevIter = iter;
7739 switch (iter.codepoint) {
7740 case 9: // \t
7741 case 11: // \v
7742 case 12: // \f
7743 case 32: // space
7744 case 0x00a0: // NBSP
7745 type = NVGcodepointType.Space;
7746 break;
7747 case 10: // \n
7748 type = (pcodepoint == 13 ? NVGcodepointType.Space : NVGcodepointType.NewLine);
7749 break;
7750 case 13: // \r
7751 type = (pcodepoint == 10 ? NVGcodepointType.Space : NVGcodepointType.NewLine);
7752 break;
7753 case 0x0085: // NEL
7754 case 0x2028: // Line Separator
7755 case 0x2029: // Paragraph Separator
7756 type = NVGcodepointType.NewLine;
7757 break;
7758 default:
7759 type = NVGcodepointType.Char;
7760 break;
7762 if (phase == Phase.SkipBlanks) {
7763 // fix row start
7764 rowStart = cast(int)(iter.string-str.ptr);
7765 rowEnd = rowStart;
7766 rowStartX = iter.x;
7767 rowWidth = iter.nextx-rowStartX; // q.x1-rowStartX;
7768 rowMinX = q.x0-rowStartX;
7769 rowMaxX = q.x1-rowStartX;
7770 wordStart = rowStart;
7771 wordStartX = iter.x;
7772 wordMinX = q.x0-rowStartX;
7773 breakEnd = rowStart;
7774 breakWidth = 0.0;
7775 breakMaxX = 0.0;
7776 if (type == NVGcodepointType.Space) continue;
7777 phase = Phase.Normal;
7780 if (type == NVGcodepointType.NewLine) {
7781 // always handle new lines
7782 NVGTextRow!T row;
7783 row.string = str;
7784 row.start = rowStart;
7785 row.end = rowEnd;
7786 row.width = rowWidth*invscale;
7787 row.minx = rowMinX*invscale;
7788 row.maxx = rowMaxX*invscale;
7789 ++nrows;
7790 static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
7791 phase = Phase.SkipBlanks;
7792 } else {
7793 float nextWidth = iter.nextx-rowStartX;
7794 // track last non-white space character
7795 if (type == NVGcodepointType.Char) {
7796 rowEnd = cast(int)(iter.nextp-str.ptr);
7797 rowWidth = iter.nextx-rowStartX;
7798 rowMaxX = q.x1-rowStartX;
7800 // track last end of a word
7801 if (ptype == NVGcodepointType.Char && type == NVGcodepointType.Space) {
7802 breakEnd = cast(int)(iter.string-str.ptr);
7803 breakWidth = rowWidth;
7804 breakMaxX = rowMaxX;
7806 // track last beginning of a word
7807 if (ptype == NVGcodepointType.Space && type == NVGcodepointType.Char) {
7808 wordStart = cast(int)(iter.string-str.ptr);
7809 wordStartX = iter.x;
7810 wordMinX = q.x0-rowStartX;
7812 // break to new line when a character is beyond break width
7813 if (type == NVGcodepointType.Char && nextWidth > breakRowWidth) {
7814 // the run length is too long, need to break to new line
7815 NVGTextRow!T row;
7816 row.string = str;
7817 if (breakEnd == rowStart) {
7818 // the current word is longer than the row length, just break it from here
7819 row.start = rowStart;
7820 row.end = cast(int)(iter.string-str.ptr);
7821 row.width = rowWidth*invscale;
7822 row.minx = rowMinX*invscale;
7823 row.maxx = rowMaxX*invscale;
7824 ++nrows;
7825 static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
7826 rowStartX = iter.x;
7827 rowStart = cast(int)(iter.string-str.ptr);
7828 rowEnd = cast(int)(iter.nextp-str.ptr);
7829 rowWidth = iter.nextx-rowStartX;
7830 rowMinX = q.x0-rowStartX;
7831 rowMaxX = q.x1-rowStartX;
7832 wordStart = rowStart;
7833 wordStartX = iter.x;
7834 wordMinX = q.x0-rowStartX;
7835 } else {
7836 // break the line from the end of the last word, and start new line from the beginning of the new
7837 //{ import core.stdc.stdio : printf; printf("rowStart=%u; rowEnd=%u; breakEnd=%u; len=%u\n", rowStart, rowEnd, breakEnd, cast(uint)str.length); }
7838 row.start = rowStart;
7839 row.end = breakEnd;
7840 row.width = breakWidth*invscale;
7841 row.minx = rowMinX*invscale;
7842 row.maxx = breakMaxX*invscale;
7843 ++nrows;
7844 static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
7845 rowStartX = wordStartX;
7846 rowStart = wordStart;
7847 rowEnd = cast(int)(iter.nextp-str.ptr);
7848 rowWidth = iter.nextx-rowStartX;
7849 rowMinX = wordMinX;
7850 rowMaxX = q.x1-rowStartX;
7851 // no change to the word start
7853 // set null break point
7854 breakEnd = rowStart;
7855 breakWidth = 0.0;
7856 breakMaxX = 0.0;
7860 pcodepoint = iter.codepoint;
7861 ptype = type;
7864 // break the line from the end of the last word, and start new line from the beginning of the new
7865 if (phase != Phase.SkipBlanks && rowStart < str.length) {
7866 //{ import core.stdc.stdio : printf; printf(" rowStart=%u; len=%u\n", rowStart, cast(uint)str.length); }
7867 NVGTextRow!T row;
7868 row.string = str;
7869 row.start = rowStart;
7870 row.end = cast(int)str.length;
7871 row.width = rowWidth*invscale;
7872 row.minx = rowMinX*invscale;
7873 row.maxx = rowMaxX*invscale;
7874 ++nrows;
7875 static if (RetBool) { if (!dg(row)) return nrows; } else dg(row);
7878 return nrows;
7881 /** Returns iterator which you can use to calculate text bounds and advancement.
7882 * This is usable when you need to do some text layouting with wrapping, to avoid
7883 * guesswork ("will advancement for this space stay the same?"), and Schlemiel's
7884 * algorithm. Note that you can copy the returned struct to save iterator state.
7886 * You can check if iterator is valid with [valid] property, put new chars with
7887 * [put] method, get current advance with [advance] property, and current
7888 * bounds with `getBounds(ref float[4] bounds)` method.
7890 * $(WARNING Don't change font parameters while iterating! Or use [restoreFont] method.)
7892 * Group: text_api
7894 public struct TextBoundsIterator {
7895 private:
7896 NVGContext ctx;
7897 FonsTextBoundsIterator fsiter; // fontstash iterator
7898 float scale, invscale, xscaled, yscaled;
7899 // font settings
7900 float fsSize, fsSpacing, fsBlur;
7901 int fsFontId;
7902 NVGTextAlign fsAlign;
7904 public:
7905 this (NVGContext actx, float ax, float ay) nothrow @trusted @nogc { reset(actx, ax, ay); }
7907 void reset (NVGContext actx, float ax, float ay) nothrow @trusted @nogc {
7908 fsiter = fsiter.init;
7909 this = this.init;
7910 if (actx is null) return;
7911 NVGstate* state = nvg__getState(actx);
7912 if (state is null) return;
7913 if (state.fontId == FONS_INVALID) { ctx = null; return; }
7915 ctx = actx;
7916 scale = nvg__getFontScale(state)*ctx.devicePxRatio;
7917 invscale = 1.0f/scale;
7919 fsSize = state.fontSize*scale;
7920 fsSpacing = state.letterSpacing*scale;
7921 fsBlur = state.fontBlur*scale;
7922 fsAlign = state.textAlign;
7923 fsFontId = state.fontId;
7924 restoreFont();
7926 xscaled = ax*scale;
7927 yscaled = ay*scale;
7928 fsiter.reset(ctx.fs, xscaled, yscaled);
7931 /// Restart iteration. Will not restore font.
7932 void restart () nothrow @trusted @nogc {
7933 if (ctx !is null) fsiter.reset(ctx.fs, xscaled, yscaled);
7936 /// Restore font settings for the context.
7937 void restoreFont () nothrow @trusted @nogc {
7938 if (ctx !is null) {
7939 fonsSetSize(ctx.fs, fsSize);
7940 fonsSetSpacing(ctx.fs, fsSpacing);
7941 fonsSetBlur(ctx.fs, fsBlur);
7942 fonsSetAlign(ctx.fs, fsAlign);
7943 fonsSetFont(ctx.fs, fsFontId);
7947 /// Is this iterator valid?
7948 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (ctx !is null); }
7950 /// Add chars.
7951 void put(T) (const(T)[] str...) nothrow @trusted @nogc if (isAnyCharType!T) { pragma(inline, true); if (ctx !is null) fsiter.put(str[]); }
7953 /// Returns current advance
7954 @property float advance () const pure nothrow @safe @nogc { pragma(inline, true); return (ctx !is null ? fsiter.advance*invscale : 0); }
7956 /// Returns current text bounds.
7957 void getBounds (ref float[4] bounds) nothrow @trusted @nogc {
7958 if (ctx !is null) {
7959 fsiter.getBounds(bounds);
7960 fonsLineBounds(ctx.fs, yscaled, &bounds[1], &bounds[3]);
7961 bounds[0] *= invscale;
7962 bounds[1] *= invscale;
7963 bounds[2] *= invscale;
7964 bounds[3] *= invscale;
7965 } else {
7966 bounds[] = 0;
7970 /// Returns current horizontal text bounds.
7971 void getHBounds (out float xmin, out float xmax) nothrow @trusted @nogc {
7972 if (ctx !is null) {
7973 fsiter.getHBounds(xmin, xmax);
7974 xmin *= invscale;
7975 xmax *= invscale;
7979 /// Returns current vertical text bounds.
7980 void getVBounds (out float ymin, out float ymax) nothrow @trusted @nogc {
7981 if (ctx !is null) {
7982 //fsiter.getVBounds(ymin, ymax);
7983 fonsLineBounds(ctx.fs, yscaled, &ymin, &ymax);
7984 ymin *= invscale;
7985 ymax *= invscale;
7990 /// Returns font line height (without line spacing), measured in local coordinate space.
7991 /// Group: text_api
7992 public float textFontHeight (NVGContext ctx) nothrow @trusted @nogc {
7993 float res = void;
7994 ctx.textMetrics(null, null, &res);
7995 return res;
7998 /// Returns font ascender (positive), measured in local coordinate space.
7999 /// Group: text_api
8000 public float textFontAscender (NVGContext ctx) nothrow @trusted @nogc {
8001 float res = void;
8002 ctx.textMetrics(&res, null, null);
8003 return res;
8006 /// Returns font descender (negative), measured in local coordinate space.
8007 /// Group: text_api
8008 public float textFontDescender (NVGContext ctx) nothrow @trusted @nogc {
8009 float res = void;
8010 ctx.textMetrics(null, &res, null);
8011 return res;
8014 /** Measures the specified text string. Returns horizontal and vertical sizes of the measured text.
8015 * Measured values are returned in local coordinate space.
8017 * Group: text_api
8019 public void textExtents(T) (NVGContext ctx, const(T)[] str, float *w, float *h) nothrow @trusted @nogc if (isAnyCharType!T) {
8020 float[4] bnd = void;
8021 ctx.textBounds(0, 0, str, bnd[]);
8022 if (!fonsGetFontAA(ctx.fs, nvg__getState(ctx).fontId)) {
8023 if (w !is null) *w = nvg__lrintf(bnd.ptr[2]-bnd.ptr[0]);
8024 if (h !is null) *h = nvg__lrintf(bnd.ptr[3]-bnd.ptr[1]);
8025 } else {
8026 if (w !is null) *w = bnd.ptr[2]-bnd.ptr[0];
8027 if (h !is null) *h = bnd.ptr[3]-bnd.ptr[1];
8031 /** Measures the specified text string. Returns horizontal size of the measured text.
8032 * Measured values are returned in local coordinate space.
8034 * Group: text_api
8036 public float textWidth(T) (NVGContext ctx, const(T)[] str) nothrow @trusted @nogc if (isAnyCharType!T) {
8037 float w = void;
8038 ctx.textExtents(str, &w, null);
8039 return w;
8042 /** Measures the specified text string. Parameter bounds should be a float[4],
8043 * if the bounding box of the text should be returned. The bounds value are [xmin, ymin, xmax, ymax]
8044 * Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
8045 * Measured values are returned in local coordinate space.
8047 * Group: text_api
8049 public float textBounds(T) (NVGContext ctx, float x, float y, const(T)[] str, float[] bounds) nothrow @trusted @nogc
8050 if (isAnyCharType!T)
8052 NVGstate* state = nvg__getState(ctx);
8053 float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8054 float invscale = 1.0f/scale;
8055 float width;
8057 if (state.fontId == FONS_INVALID) {
8058 bounds[] = 0;
8059 return 0;
8062 fonsSetSize(ctx.fs, state.fontSize*scale);
8063 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
8064 fonsSetBlur(ctx.fs, state.fontBlur*scale);
8065 fonsSetAlign(ctx.fs, state.textAlign);
8066 fonsSetFont(ctx.fs, state.fontId);
8068 float[4] b = void;
8069 width = fonsTextBounds(ctx.fs, x*scale, y*scale, str, b[]);
8070 if (bounds.length) {
8071 // use line bounds for height
8072 fonsLineBounds(ctx.fs, y*scale, b.ptr+1, b.ptr+3);
8073 if (bounds.length > 0) bounds.ptr[0] = b.ptr[0]*invscale;
8074 if (bounds.length > 1) bounds.ptr[1] = b.ptr[1]*invscale;
8075 if (bounds.length > 2) bounds.ptr[2] = b.ptr[2]*invscale;
8076 if (bounds.length > 3) bounds.ptr[3] = b.ptr[3]*invscale;
8078 return width*invscale;
8081 /// Ditto.
8082 public void textBoxBounds(T) (NVGContext ctx, float x, float y, float breakRowWidth, const(T)[] str, float[] bounds) if (isAnyCharType!T) {
8083 NVGstate* state = nvg__getState(ctx);
8084 NVGTextRow!T[2] rows;
8085 float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8086 float invscale = 1.0f/scale;
8087 float lineh = 0, rminy = 0, rmaxy = 0;
8088 float minx, miny, maxx, maxy;
8090 if (state.fontId == FONS_INVALID) {
8091 bounds[] = 0;
8092 return;
8095 auto oldAlign = state.textAlign;
8096 scope(exit) state.textAlign = oldAlign;
8097 auto halign = state.textAlign.horizontal;
8099 ctx.textMetrics(null, null, &lineh);
8100 state.textAlign.horizontal = NVGTextAlign.H.Left;
8102 minx = maxx = x;
8103 miny = maxy = y;
8105 fonsSetSize(ctx.fs, state.fontSize*scale);
8106 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
8107 fonsSetBlur(ctx.fs, state.fontBlur*scale);
8108 fonsSetAlign(ctx.fs, state.textAlign);
8109 fonsSetFont(ctx.fs, state.fontId);
8110 fonsLineBounds(ctx.fs, 0, &rminy, &rmaxy);
8111 rminy *= invscale;
8112 rmaxy *= invscale;
8114 for (;;) {
8115 auto rres = ctx.textBreakLines(str, breakRowWidth, rows[]);
8116 if (rres.length == 0) break;
8117 foreach (ref row; rres) {
8118 float rminx, rmaxx, dx = 0;
8119 // horizontal bounds
8120 final switch (halign) {
8121 case NVGTextAlign.H.Left: dx = 0; break;
8122 case NVGTextAlign.H.Center: dx = breakRowWidth*0.5f-row.width*0.5f; break;
8123 case NVGTextAlign.H.Right: dx = breakRowWidth-row.width; break;
8125 rminx = x+row.minx+dx;
8126 rmaxx = x+row.maxx+dx;
8127 minx = nvg__min(minx, rminx);
8128 maxx = nvg__max(maxx, rmaxx);
8129 // vertical bounds
8130 miny = nvg__min(miny, y+rminy);
8131 maxy = nvg__max(maxy, y+rmaxy);
8132 y += lineh*state.lineHeight;
8134 str = rres[$-1].rest;
8137 if (bounds.length) {
8138 if (bounds.length > 0) bounds.ptr[0] = minx;
8139 if (bounds.length > 1) bounds.ptr[1] = miny;
8140 if (bounds.length > 2) bounds.ptr[2] = maxx;
8141 if (bounds.length > 3) bounds.ptr[3] = maxy;
8145 /// Returns the vertical metrics based on the current text style. Measured values are returned in local coordinate space.
8146 /// Group: text_api
8147 public void textMetrics (NVGContext ctx, float* ascender, float* descender, float* lineh) nothrow @trusted @nogc {
8148 NVGstate* state = nvg__getState(ctx);
8150 if (state.fontId == FONS_INVALID) {
8151 if (ascender !is null) *ascender *= 0;
8152 if (descender !is null) *descender *= 0;
8153 if (lineh !is null) *lineh *= 0;
8154 return;
8157 immutable float scale = nvg__getFontScale(state)*ctx.devicePxRatio;
8158 immutable float invscale = 1.0f/scale;
8160 fonsSetSize(ctx.fs, state.fontSize*scale);
8161 fonsSetSpacing(ctx.fs, state.letterSpacing*scale);
8162 fonsSetBlur(ctx.fs, state.fontBlur*scale);
8163 fonsSetAlign(ctx.fs, state.textAlign);
8164 fonsSetFont(ctx.fs, state.fontId);
8166 fonsVertMetrics(ctx.fs, ascender, descender, lineh);
8167 if (ascender !is null) *ascender *= invscale;
8168 if (descender !is null) *descender *= invscale;
8169 if (lineh !is null) *lineh *= invscale;
8173 // ////////////////////////////////////////////////////////////////////////// //
8174 // fontstash
8175 // ////////////////////////////////////////////////////////////////////////// //
8176 import core.stdc.stdlib : malloc, realloc, free;
8177 import core.stdc.string : memset, memcpy, strncpy, strcmp, strlen;
8178 import core.stdc.stdio : FILE, fopen, fclose, fseek, ftell, fread, SEEK_END, SEEK_SET;
8180 public:
8181 // welcome to version hell!
8182 version(nanovg_force_detect) {} else version(nanovg_use_freetype) { version = nanovg_use_freetype_ii; }
8183 version(nanovg_ignore_iv_stb_ttf) enum nanovg_ignore_iv_stb_ttf = true; else enum nanovg_ignore_iv_stb_ttf = false;
8184 //version(nanovg_ignore_mono);
8186 version (nanovg_builtin_freetype_bindings) {
8187 version(Posix) {
8188 private enum NanoVegaForceFreeType = true;
8189 } else {
8190 private enum NanoVegaForceFreeType = false;
8192 } else {
8193 version(Posix) {
8194 private enum NanoVegaForceFreeType = true;
8195 } else {
8196 private enum NanoVegaForceFreeType = false;
8200 version(nanovg_use_freetype_ii) {
8201 enum NanoVegaIsUsingSTBTTF = false;
8202 //pragma(msg, "iv.freetype: forced");
8203 } else {
8204 static if (NanoVegaForceFreeType) {
8205 enum NanoVegaIsUsingSTBTTF = false;
8206 } else {
8207 static if (!nanovg_ignore_iv_stb_ttf && __traits(compiles, { import iv.stb.ttf; })) {
8208 import iv.stb.ttf;
8209 enum NanoVegaIsUsingSTBTTF = true;
8210 //pragma(msg, "iv.stb.ttf");
8211 } else static if (__traits(compiles, { import arsd.ttf; })) {
8212 import arsd.ttf;
8213 enum NanoVegaIsUsingSTBTTF = true;
8214 //pragma(msg, "arsd.ttf");
8215 } else static if (__traits(compiles, { import stb_truetype; })) {
8216 import stb_truetype;
8217 enum NanoVegaIsUsingSTBTTF = true;
8218 //pragma(msg, "stb_truetype");
8219 } else static if (__traits(compiles, { import iv.freetype; })) {
8220 version (nanovg_builtin_freetype_bindings) {
8221 enum NanoVegaIsUsingSTBTTF = false;
8222 version = nanovg_builtin_freetype_bindings;
8223 } else {
8224 import iv.freetype;
8225 enum NanoVegaIsUsingSTBTTF = false;
8227 //pragma(msg, "iv.freetype");
8228 } else {
8229 static assert(0, "no stb_ttf/iv.freetype found!");
8234 //version = nanovg_kill_font_blur;
8237 // ////////////////////////////////////////////////////////////////////////// //
8238 //version = nanovg_ft_mono;
8240 enum FONS_INVALID = -1;
8242 alias FONSflags = int;
8243 enum /*FONSflags*/ {
8244 FONS_ZERO_TOPLEFT = 1<<0,
8245 FONS_ZERO_BOTTOMLEFT = 1<<1,
8249 alias FONSalign = int;
8250 enum /*FONSalign*/ {
8251 // Horizontal align
8252 FONS_ALIGN_LEFT = 1<<0, // Default
8253 FONS_ALIGN_CENTER = 1<<1,
8254 FONS_ALIGN_RIGHT = 1<<2,
8255 // Vertical align
8256 FONS_ALIGN_TOP = 1<<3,
8257 FONS_ALIGN_MIDDLE = 1<<4,
8258 FONS_ALIGN_BOTTOM = 1<<5,
8259 FONS_ALIGN_BASELINE = 1<<6, // Default
8263 alias FONSglyphBitmap = int;
8264 enum /*FONSglyphBitmap*/ {
8265 FONS_GLYPH_BITMAP_OPTIONAL = 1,
8266 FONS_GLYPH_BITMAP_REQUIRED = 2,
8269 alias FONSerrorCode = int;
8270 enum /*FONSerrorCode*/ {
8271 // Font atlas is full.
8272 FONS_ATLAS_FULL = 1,
8273 // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
8274 FONS_SCRATCH_FULL = 2,
8275 // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
8276 FONS_STATES_OVERFLOW = 3,
8277 // Trying to pop too many states fonsPopState().
8278 FONS_STATES_UNDERFLOW = 4,
8281 struct FONSparams {
8282 int width, height;
8283 ubyte flags;
8284 void* userPtr;
8285 bool function (void* uptr, int width, int height) nothrow @trusted @nogc renderCreate;
8286 int function (void* uptr, int width, int height) nothrow @trusted @nogc renderResize;
8287 void function (void* uptr, int* rect, const(ubyte)* data) nothrow @trusted @nogc renderUpdate;
8288 debug(nanovega) {
8289 void function (void* uptr, const(float)* verts, const(float)* tcoords, const(uint)* colors, int nverts) nothrow @trusted @nogc renderDraw;
8291 void function (void* uptr) nothrow @trusted @nogc renderDelete;
8294 struct FONSquad {
8295 float x0=0, y0=0, s0=0, t0=0;
8296 float x1=0, y1=0, s1=0, t1=0;
8299 struct FONStextIter(CT) if (isAnyCharType!CT) {
8300 alias CharType = CT;
8301 float x=0, y=0, nextx=0, nexty=0, scale=0, spacing=0;
8302 uint codepoint;
8303 short isize, iblur;
8304 FONSfont* font;
8305 int prevGlyphIndex;
8306 const(CT)* s; // string
8307 const(CT)* n; // next
8308 const(CT)* e; // end
8309 FONSglyphBitmap bitmapOption;
8310 static if (is(CT == char)) {
8311 uint utf8state;
8313 ~this () nothrow @trusted @nogc { pragma(inline, true); static if (is(CT == char)) utf8state = 0; s = n = e = null; }
8314 @property const(CT)* string () const pure nothrow @nogc { pragma(inline, true); return s; }
8315 @property const(CT)* nextp () const pure nothrow @nogc { pragma(inline, true); return n; }
8316 @property const(CT)* endp () const pure nothrow @nogc { pragma(inline, true); return e; }
8320 // ////////////////////////////////////////////////////////////////////////// //
8321 //static if (!HasAST) version = nanovg_use_freetype_ii_x;
8323 /*version(nanovg_use_freetype_ii_x)*/ static if (!NanoVegaIsUsingSTBTTF) {
8324 version(nanovg_builtin_freetype_bindings) {
8325 pragma(lib, "freetype");
8326 private extern(C) nothrow @trusted @nogc {
8327 private import core.stdc.config : c_long, c_ulong;
8328 alias FT_Pos = c_long;
8329 // config/ftconfig.h
8330 alias FT_Int16 = short;
8331 alias FT_UInt16 = ushort;
8332 alias FT_Int32 = int;
8333 alias FT_UInt32 = uint;
8334 alias FT_Fast = int;
8335 alias FT_UFast = uint;
8336 alias FT_Int64 = long;
8337 alias FT_Uint64 = ulong;
8338 // fttypes.h
8339 alias FT_Bool = ubyte;
8340 alias FT_FWord = short;
8341 alias FT_UFWord = ushort;
8342 alias FT_Char = char;
8343 alias FT_Byte = ubyte;
8344 alias FT_Bytes = FT_Byte*;
8345 alias FT_Tag = FT_UInt32;
8346 alias FT_String = char;
8347 alias FT_Short = short;
8348 alias FT_UShort = ushort;
8349 alias FT_Int = int;
8350 alias FT_UInt = uint;
8351 alias FT_Long = c_long;
8352 alias FT_ULong = c_ulong;
8353 alias FT_F2Dot14 = short;
8354 alias FT_F26Dot6 = c_long;
8355 alias FT_Fixed = c_long;
8356 alias FT_Error = int;
8357 alias FT_Pointer = void*;
8358 alias FT_Offset = usize;
8359 alias FT_PtrDist = ptrdiff_t;
8361 struct FT_UnitVector {
8362 FT_F2Dot14 x;
8363 FT_F2Dot14 y;
8366 struct FT_Matrix {
8367 FT_Fixed xx, xy;
8368 FT_Fixed yx, yy;
8371 struct FT_Data {
8372 const(FT_Byte)* pointer;
8373 FT_Int length;
8375 alias FT_Face = FT_FaceRec*;
8376 struct FT_FaceRec {
8377 FT_Long num_faces;
8378 FT_Long face_index;
8379 FT_Long face_flags;
8380 FT_Long style_flags;
8381 FT_Long num_glyphs;
8382 FT_String* family_name;
8383 FT_String* style_name;
8384 FT_Int num_fixed_sizes;
8385 FT_Bitmap_Size* available_sizes;
8386 FT_Int num_charmaps;
8387 FT_CharMap* charmaps;
8388 FT_Generic generic;
8389 FT_BBox bbox;
8390 FT_UShort units_per_EM;
8391 FT_Short ascender;
8392 FT_Short descender;
8393 FT_Short height;
8394 FT_Short max_advance_width;
8395 FT_Short max_advance_height;
8396 FT_Short underline_position;
8397 FT_Short underline_thickness;
8398 FT_GlyphSlot glyph;
8399 FT_Size size;
8400 FT_CharMap charmap;
8401 FT_Driver driver;
8402 FT_Memory memory;
8403 FT_Stream stream;
8404 FT_ListRec sizes_list;
8405 FT_Generic autohint;
8406 void* extensions;
8407 FT_Face_Internal internal;
8409 struct FT_Bitmap_Size {
8410 FT_Short height;
8411 FT_Short width;
8412 FT_Pos size;
8413 FT_Pos x_ppem;
8414 FT_Pos y_ppem;
8416 alias FT_CharMap = FT_CharMapRec*;
8417 struct FT_CharMapRec {
8418 FT_Face face;
8419 FT_Encoding encoding;
8420 FT_UShort platform_id;
8421 FT_UShort encoding_id;
8423 extern(C) nothrow @nogc { alias FT_Generic_Finalizer = void function (void* object); }
8424 struct FT_Generic {
8425 void* data;
8426 FT_Generic_Finalizer finalizer;
8428 struct FT_Vector {
8429 FT_Pos x;
8430 FT_Pos y;
8432 struct FT_BBox {
8433 FT_Pos xMin, yMin;
8434 FT_Pos xMax, yMax;
8436 alias FT_Pixel_Mode = int;
8437 enum {
8438 FT_PIXEL_MODE_NONE = 0,
8439 FT_PIXEL_MODE_MONO,
8440 FT_PIXEL_MODE_GRAY,
8441 FT_PIXEL_MODE_GRAY2,
8442 FT_PIXEL_MODE_GRAY4,
8443 FT_PIXEL_MODE_LCD,
8444 FT_PIXEL_MODE_LCD_V,
8445 FT_PIXEL_MODE_MAX
8447 struct FT_Bitmap {
8448 uint rows;
8449 uint width;
8450 int pitch;
8451 ubyte* buffer;
8452 ushort num_grays;
8453 ubyte pixel_mode;
8454 ubyte palette_mode;
8455 void* palette;
8457 struct FT_Outline {
8458 short n_contours;
8459 short n_points;
8460 FT_Vector* points;
8461 byte* tags;
8462 short* contours;
8463 int flags;
8465 alias FT_GlyphSlot = FT_GlyphSlotRec*;
8466 struct FT_GlyphSlotRec {
8467 FT_Library library;
8468 FT_Face face;
8469 FT_GlyphSlot next;
8470 FT_UInt reserved;
8471 FT_Generic generic;
8472 FT_Glyph_Metrics metrics;
8473 FT_Fixed linearHoriAdvance;
8474 FT_Fixed linearVertAdvance;
8475 FT_Vector advance;
8476 FT_Glyph_Format format;
8477 FT_Bitmap bitmap;
8478 FT_Int bitmap_left;
8479 FT_Int bitmap_top;
8480 FT_Outline outline;
8481 FT_UInt num_subglyphs;
8482 FT_SubGlyph subglyphs;
8483 void* control_data;
8484 c_long control_len;
8485 FT_Pos lsb_delta;
8486 FT_Pos rsb_delta;
8487 void* other;
8488 FT_Slot_Internal internal;
8490 alias FT_Size = FT_SizeRec*;
8491 struct FT_SizeRec {
8492 FT_Face face;
8493 FT_Generic generic;
8494 FT_Size_Metrics metrics;
8495 FT_Size_Internal internal;
8497 alias FT_Encoding = FT_Tag;
8498 alias FT_Face_Internal = void*;
8499 alias FT_Driver = void*;
8500 alias FT_Memory = void*;
8501 alias FT_Stream = void*;
8502 alias FT_Library = void*;
8503 alias FT_SubGlyph = void*;
8504 alias FT_Slot_Internal = void*;
8505 alias FT_Size_Internal = void*;
8506 alias FT_ListNode = FT_ListNodeRec*;
8507 alias FT_List = FT_ListRec*;
8508 struct FT_ListNodeRec {
8509 FT_ListNode prev;
8510 FT_ListNode next;
8511 void* data;
8513 struct FT_ListRec {
8514 FT_ListNode head;
8515 FT_ListNode tail;
8517 struct FT_Glyph_Metrics {
8518 FT_Pos width;
8519 FT_Pos height;
8520 FT_Pos horiBearingX;
8521 FT_Pos horiBearingY;
8522 FT_Pos horiAdvance;
8523 FT_Pos vertBearingX;
8524 FT_Pos vertBearingY;
8525 FT_Pos vertAdvance;
8527 alias FT_Glyph_Format = FT_Tag;
8528 FT_Tag FT_MAKE_TAG (char x1, char x2, char x3, char x4) pure nothrow @safe @nogc {
8529 pragma(inline, true);
8530 return cast(FT_UInt32)((x1<<24)|(x2<<16)|(x3<<8)|x4);
8532 enum : FT_Tag {
8533 FT_GLYPH_FORMAT_NONE = 0,
8534 FT_GLYPH_FORMAT_COMPOSITE = FT_MAKE_TAG('c','o','m','p'),
8535 FT_GLYPH_FORMAT_BITMAP = FT_MAKE_TAG('b','i','t','s'),
8536 FT_GLYPH_FORMAT_OUTLINE = FT_MAKE_TAG('o','u','t','l'),
8537 FT_GLYPH_FORMAT_PLOTTER = FT_MAKE_TAG('p','l','o','t'),
8539 struct FT_Size_Metrics {
8540 FT_UShort x_ppem;
8541 FT_UShort y_ppem;
8543 FT_Fixed x_scale;
8544 FT_Fixed y_scale;
8546 FT_Pos ascender;
8547 FT_Pos descender;
8548 FT_Pos height;
8549 FT_Pos max_advance;
8551 enum FT_LOAD_DEFAULT = 0x0U;
8552 enum FT_LOAD_NO_SCALE = 1U<<0;
8553 enum FT_LOAD_NO_HINTING = 1U<<1;
8554 enum FT_LOAD_RENDER = 1U<<2;
8555 enum FT_LOAD_NO_BITMAP = 1U<<3;
8556 enum FT_LOAD_VERTICAL_LAYOUT = 1U<<4;
8557 enum FT_LOAD_FORCE_AUTOHINT = 1U<<5;
8558 enum FT_LOAD_CROP_BITMAP = 1U<<6;
8559 enum FT_LOAD_PEDANTIC = 1U<<7;
8560 enum FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1U<<9;
8561 enum FT_LOAD_NO_RECURSE = 1U<<10;
8562 enum FT_LOAD_IGNORE_TRANSFORM = 1U<<11;
8563 enum FT_LOAD_MONOCHROME = 1U<<12;
8564 enum FT_LOAD_LINEAR_DESIGN = 1U<<13;
8565 enum FT_LOAD_NO_AUTOHINT = 1U<<15;
8566 enum FT_LOAD_COLOR = 1U<<20;
8567 enum FT_LOAD_COMPUTE_METRICS = 1U<<21;
8568 enum FT_FACE_FLAG_KERNING = 1U<<6;
8569 alias FT_Kerning_Mode = int;
8570 enum /*FT_Kerning_Mode*/ {
8571 FT_KERNING_DEFAULT = 0,
8572 FT_KERNING_UNFITTED,
8573 FT_KERNING_UNSCALED
8575 extern(C) nothrow @nogc {
8576 alias FT_Outline_MoveToFunc = int function (const(FT_Vector)*, void*);
8577 alias FT_Outline_LineToFunc = int function (const(FT_Vector)*, void*);
8578 alias FT_Outline_ConicToFunc = int function (const(FT_Vector)*, const(FT_Vector)*, void*);
8579 alias FT_Outline_CubicToFunc = int function (const(FT_Vector)*, const(FT_Vector)*, const(FT_Vector)*, void*);
8581 struct FT_Outline_Funcs {
8582 FT_Outline_MoveToFunc move_to;
8583 FT_Outline_LineToFunc line_to;
8584 FT_Outline_ConicToFunc conic_to;
8585 FT_Outline_CubicToFunc cubic_to;
8586 int shift;
8587 FT_Pos delta;
8590 FT_Error FT_Init_FreeType (FT_Library*);
8591 FT_Error FT_New_Memory_Face (FT_Library, const(FT_Byte)*, FT_Long, FT_Long, FT_Face*);
8592 FT_UInt FT_Get_Char_Index (FT_Face, FT_ULong);
8593 FT_Error FT_Set_Pixel_Sizes (FT_Face, FT_UInt, FT_UInt);
8594 FT_Error FT_Load_Glyph (FT_Face, FT_UInt, FT_Int32);
8595 FT_Error FT_Get_Advance (FT_Face, FT_UInt, FT_Int32, FT_Fixed*);
8596 FT_Error FT_Get_Kerning (FT_Face, FT_UInt, FT_UInt, FT_UInt, FT_Vector*);
8597 void FT_Outline_Get_CBox (const(FT_Outline)*, FT_BBox*);
8598 FT_Error FT_Outline_Decompose (FT_Outline*, const(FT_Outline_Funcs)*, void*);
8600 } else {
8601 import iv.freetype;
8604 struct FONSttFontImpl {
8605 FT_Face font;
8606 bool mono; // no aa?
8609 __gshared FT_Library ftLibrary;
8611 int fons__tt_init (FONScontext* context) nothrow @trusted @nogc {
8612 FT_Error ftError;
8613 //FONS_NOTUSED(context);
8614 ftError = FT_Init_FreeType(&ftLibrary);
8615 return (ftError == 0);
8618 void fons__tt_setMono (FONScontext* context, FONSttFontImpl* font, bool v) nothrow @trusted @nogc {
8619 font.mono = v;
8622 bool fons__tt_getMono (FONScontext* context, FONSttFontImpl* font) nothrow @trusted @nogc {
8623 return font.mono;
8626 int fons__tt_loadFont (FONScontext* context, FONSttFontImpl* font, ubyte* data, int dataSize) nothrow @trusted @nogc {
8627 FT_Error ftError;
8628 //font.font.userdata = stash;
8629 ftError = FT_New_Memory_Face(ftLibrary, cast(const(FT_Byte)*)data, dataSize, 0, &font.font);
8630 return ftError == 0;
8633 void fons__tt_getFontVMetrics (FONSttFontImpl* font, int* ascent, int* descent, int* lineGap) nothrow @trusted @nogc {
8634 *ascent = font.font.ascender;
8635 *descent = font.font.descender;
8636 *lineGap = font.font.height-(*ascent - *descent);
8639 float fons__tt_getPixelHeightScale (FONSttFontImpl* font, float size) nothrow @trusted @nogc {
8640 return size/(font.font.ascender-font.font.descender);
8643 int fons__tt_getGlyphIndex (FONSttFontImpl* font, int codepoint) nothrow @trusted @nogc {
8644 return FT_Get_Char_Index(font.font, codepoint);
8647 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 {
8648 FT_Error ftError;
8649 FT_GlyphSlot ftGlyph;
8650 //version(nanovg_ignore_mono) enum exflags = 0;
8651 //else version(nanovg_ft_mono) enum exflags = FT_LOAD_MONOCHROME; else enum exflags = 0;
8652 uint exflags = (font.mono ? FT_LOAD_MONOCHROME : 0);
8653 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)));
8654 if (ftError) return 0;
8655 ftError = FT_Load_Glyph(font.font, glyph, FT_LOAD_RENDER|/*FT_LOAD_NO_AUTOHINT|*/exflags);
8656 if (ftError) return 0;
8657 ftError = FT_Get_Advance(font.font, glyph, FT_LOAD_NO_SCALE|/*FT_LOAD_NO_AUTOHINT|*/exflags, cast(FT_Fixed*)advance);
8658 if (ftError) return 0;
8659 ftGlyph = font.font.glyph;
8660 *lsb = cast(int)ftGlyph.metrics.horiBearingX;
8661 *x0 = ftGlyph.bitmap_left;
8662 *x1 = *x0+ftGlyph.bitmap.width;
8663 *y0 = -ftGlyph.bitmap_top;
8664 *y1 = *y0+ftGlyph.bitmap.rows;
8665 return 1;
8668 void fons__tt_renderGlyphBitmap (FONSttFontImpl* font, ubyte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) nothrow @trusted @nogc {
8669 FT_GlyphSlot ftGlyph = font.font.glyph;
8670 //FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap
8671 //version(nanovg_ignore_mono) enum RenderAA = true;
8672 //else version(nanovg_ft_mono) enum RenderAA = false;
8673 //else enum RenderAA = true;
8674 if (font.mono) {
8675 auto src = ftGlyph.bitmap.buffer;
8676 auto dst = output;
8677 auto spt = ftGlyph.bitmap.pitch;
8678 if (spt < 0) spt = -spt;
8679 foreach (int y; 0..ftGlyph.bitmap.rows) {
8680 ubyte count = 0, b = 0;
8681 auto s = src;
8682 auto d = dst;
8683 foreach (int x; 0..ftGlyph.bitmap.width) {
8684 if (count-- == 0) { count = 7; b = *s++; } else b <<= 1;
8685 *d++ = (b&0x80 ? 255 : 0);
8687 src += spt;
8688 dst += outStride;
8690 } else {
8691 auto src = ftGlyph.bitmap.buffer;
8692 auto dst = output;
8693 auto spt = ftGlyph.bitmap.pitch;
8694 if (spt < 0) spt = -spt;
8695 foreach (int y; 0..ftGlyph.bitmap.rows) {
8696 import core.stdc.string : memcpy;
8697 //dst[0..ftGlyph.bitmap.width] = src[0..ftGlyph.bitmap.width];
8698 memcpy(dst, src, ftGlyph.bitmap.width);
8699 src += spt;
8700 dst += outStride;
8705 float fons__tt_getGlyphKernAdvance (FONSttFontImpl* font, float size, int glyph1, int glyph2) nothrow @trusted @nogc {
8706 FT_Vector ftKerning;
8707 version(none) {
8708 // fitted kerning
8709 FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
8710 //{ import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d\n", glyph1, glyph2, ftKerning.x, ftKerning.y); }
8711 return cast(int)ftKerning.x; // round up and convert to integer
8712 } else {
8713 // unfitted kerning
8714 //FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_UNFITTED, &ftKerning);
8715 if (glyph1 <= 0 || glyph2 <= 0 || (font.font.face_flags&FT_FACE_FLAG_KERNING) == 0) return 0;
8716 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;
8717 if (FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning)) return 0;
8718 version(none) {
8719 if (ftKerning.x) {
8720 //{ import core.stdc.stdio : printf; printf("has kerning: %u\n", cast(uint)(font.font.face_flags&FT_FACE_FLAG_KERNING)); }
8721 { import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d (size=%g)\n", glyph1, glyph2, ftKerning.x, ftKerning.y, cast(double)size); }
8724 version(none) {
8725 FT_Vector kk;
8726 if (FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_UNSCALED, &kk)) assert(0, "wtf?!");
8727 auto kadvfrac = FT_MulFix(kk.x, font.font.size.metrics.x_scale); // 1/64 of pixel
8728 //return cast(int)((kadvfrac/*+(kadvfrac < 0 ? -32 : 32)*/)>>6);
8729 //assert(ftKerning.x == kadvfrac);
8730 if (ftKerning.x || kadvfrac) {
8731 { 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); }
8733 //return cast(int)(kadvfrac+(kadvfrac < 0 ? -31 : 32)>>6); // round up and convert to integer
8734 return kadvfrac/64.0f;
8736 //return cast(int)(ftKerning.x+(ftKerning.x < 0 ? -31 : 32)>>6); // round up and convert to integer
8737 return ftKerning.x/64.0f;
8741 extern(C) nothrow @trusted @nogc {
8742 static struct OutlinerData {
8743 @disable this (this);
8744 NVGContext vg;
8745 NVGGlyphOutline* ol;
8746 FT_BBox outlineBBox;
8747 nothrow @trusted @nogc:
8748 T transx(T) (T v) const pure { pragma(inline, true); return v; }
8749 T transy(T) (T v) const pure { pragma(inline, true); return -v; }
8750 void putBytes (const(void)[] b) {
8751 assert(b.length <= 512);
8752 if (b.length == 0) return;
8753 if (ol.used+cast(uint)b.length > ol.size) {
8754 import core.stdc.stdlib : realloc;
8755 uint newsz = (ol.size == 0 ? 2048 : ol.size < 32768 ? ol.size*2 : ol.size+8192);
8756 assert(ol.used+cast(uint)b.length <= newsz);
8757 auto nd = cast(ubyte*)realloc(ol.data, newsz);
8758 if (nd is null) assert(0, "FONS: out of memory");
8759 ol.size = newsz;
8760 ol.data = nd;
8762 import core.stdc.string : memcpy;
8763 memcpy(ol.data+ol.used, b.ptr, b.length);
8764 ol.used += cast(uint)b.length;
8766 void newCommand (ubyte cmd) { pragma(inline, true); ++ol.ccount; putBytes((&cmd)[0..1]); }
8767 void putArg (float f) { putBytes((&f)[0..1]); }
8770 int fons__nvg__moveto_cb (const(FT_Vector)* to, void* user) {
8771 auto odata = cast(OutlinerData*)user;
8772 if (odata.vg !is null) odata.vg.moveTo(odata.transx(to.x), odata.transy(to.y));
8773 if (odata.ol !is null) {
8774 odata.newCommand(odata.ol.Command.Kind.MoveTo);
8775 odata.putArg(odata.transx(to.x));
8776 odata.putArg(odata.transy(to.y));
8778 return 0;
8781 int fons__nvg__lineto_cb (const(FT_Vector)* to, void* user) {
8782 auto odata = cast(OutlinerData*)user;
8783 if (odata.vg !is null) odata.vg.lineTo(odata.transx(to.x), odata.transy(to.y));
8784 if (odata.ol !is null) {
8785 odata.newCommand(odata.ol.Command.Kind.LineTo);
8786 odata.putArg(odata.transx(to.x));
8787 odata.putArg(odata.transy(to.y));
8789 return 0;
8792 int fons__nvg__quadto_cb (const(FT_Vector)* c1, const(FT_Vector)* to, void* user) {
8793 auto odata = cast(OutlinerData*)user;
8794 if (odata.vg !is null) odata.vg.quadTo(odata.transx(c1.x), odata.transy(c1.y), odata.transx(to.x), odata.transy(to.y));
8795 if (odata.ol !is null) {
8796 odata.newCommand(odata.ol.Command.Kind.QuadTo);
8797 odata.putArg(odata.transx(c1.x));
8798 odata.putArg(odata.transy(c1.y));
8799 odata.putArg(odata.transx(to.x));
8800 odata.putArg(odata.transy(to.y));
8802 return 0;
8805 int fons__nvg__cubicto_cb (const(FT_Vector)* c1, const(FT_Vector)* c2, const(FT_Vector)* to, void* user) {
8806 auto odata = cast(OutlinerData*)user;
8807 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));
8808 if (odata.ol !is null) {
8809 odata.newCommand(odata.ol.Command.Kind.BezierTo);
8810 odata.putArg(odata.transx(c1.x));
8811 odata.putArg(odata.transy(c1.y));
8812 odata.putArg(odata.transx(c2.x));
8813 odata.putArg(odata.transy(c2.y));
8814 odata.putArg(odata.transx(to.x));
8815 odata.putArg(odata.transy(to.y));
8817 return 0;
8821 bool fons__nvg__toPath (NVGContext vg, FONSttFontImpl* font, uint glyphidx, float[] bounds=null) nothrow @trusted @nogc {
8822 if (bounds.length > 4) bounds = bounds.ptr[0..4];
8824 FT_Outline_Funcs funcs;
8825 funcs.move_to = &fons__nvg__moveto_cb;
8826 funcs.line_to = &fons__nvg__lineto_cb;
8827 funcs.conic_to = &fons__nvg__quadto_cb;
8828 funcs.cubic_to = &fons__nvg__cubicto_cb;
8830 auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
8831 if (err) { bounds[] = 0; return false; }
8832 if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) { bounds[] = 0; return false; }
8834 FT_Outline outline = font.font.glyph.outline;
8836 OutlinerData odata;
8837 odata.vg = vg;
8838 FT_Outline_Get_CBox(&outline, &odata.outlineBBox);
8840 err = FT_Outline_Decompose(&outline, &funcs, &odata);
8841 if (err) { bounds[] = 0; return false; }
8842 if (bounds.length > 0) bounds.ptr[0] = odata.outlineBBox.xMin;
8843 if (bounds.length > 1) bounds.ptr[1] = -odata.outlineBBox.yMax;
8844 if (bounds.length > 2) bounds.ptr[2] = odata.outlineBBox.xMax;
8845 if (bounds.length > 3) bounds.ptr[3] = -odata.outlineBBox.yMin;
8846 return true;
8849 bool fons__nvg__toOutline (FONSttFontImpl* font, uint glyphidx, NVGGlyphOutline* ol) nothrow @trusted @nogc {
8850 FT_Outline_Funcs funcs;
8851 funcs.move_to = &fons__nvg__moveto_cb;
8852 funcs.line_to = &fons__nvg__lineto_cb;
8853 funcs.conic_to = &fons__nvg__quadto_cb;
8854 funcs.cubic_to = &fons__nvg__cubicto_cb;
8856 auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
8857 if (err) return false;
8858 if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) return false;
8860 FT_Outline outline = font.font.glyph.outline;
8862 OutlinerData odata;
8863 odata.ol = ol;
8864 FT_Outline_Get_CBox(&outline, &odata.outlineBBox);
8866 err = FT_Outline_Decompose(&outline, &funcs, &odata);
8867 if (err) return false;
8868 ol.bounds.ptr[0] = odata.outlineBBox.xMin;
8869 ol.bounds.ptr[1] = -odata.outlineBBox.yMax;
8870 ol.bounds.ptr[2] = odata.outlineBBox.xMax;
8871 ol.bounds.ptr[3] = -odata.outlineBBox.yMin;
8872 return true;
8875 bool fons__nvg__bounds (FONSttFontImpl* font, uint glyphidx, float[] bounds) nothrow @trusted @nogc {
8876 if (bounds.length > 4) bounds = bounds.ptr[0..4];
8878 auto err = FT_Load_Glyph(font.font, glyphidx, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE);
8879 if (err) return false;
8880 if (font.font.glyph.format != FT_GLYPH_FORMAT_OUTLINE) { bounds[] = 0; return false; }
8882 FT_Outline outline = font.font.glyph.outline;
8883 FT_BBox outlineBBox;
8884 FT_Outline_Get_CBox(&outline, &outlineBBox);
8885 if (bounds.length > 0) bounds.ptr[0] = outlineBBox.xMin;
8886 if (bounds.length > 1) bounds.ptr[1] = -outlineBBox.yMax;
8887 if (bounds.length > 2) bounds.ptr[2] = outlineBBox.xMax;
8888 if (bounds.length > 3) bounds.ptr[3] = -outlineBBox.yMin;
8889 return true;
8893 } else {
8894 // ////////////////////////////////////////////////////////////////////////// //
8895 // sorry
8896 import std.traits : isFunctionPointer, isDelegate;
8897 private auto assumeNoThrowNoGC(T) (scope T t) if (isFunctionPointer!T || isDelegate!T) {
8898 import std.traits;
8899 enum attrs = functionAttributes!T|FunctionAttribute.nogc|FunctionAttribute.nothrow_;
8900 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
8903 private auto forceNoThrowNoGC(T) (scope T t) if (isFunctionPointer!T || isDelegate!T) {
8904 try {
8905 return assumeNoThrowNoGC(t)();
8906 } catch (Exception e) {
8907 assert(0, "OOPS!");
8911 struct FONSttFontImpl {
8912 stbtt_fontinfo font;
8913 bool mono; // no aa?
8916 int fons__tt_init (FONScontext* context) nothrow @trusted @nogc {
8917 return 1;
8920 void fons__tt_setMono (FONScontext* context, FONSttFontImpl* font, bool v) nothrow @trusted @nogc {
8921 font.mono = v;
8924 bool fons__tt_getMono (FONScontext* context, FONSttFontImpl* font) nothrow @trusted @nogc {
8925 return font.mono;
8928 int fons__tt_loadFont (FONScontext* context, FONSttFontImpl* font, ubyte* data, int dataSize) nothrow @trusted @nogc {
8929 int stbError;
8930 font.font.userdata = context;
8931 forceNoThrowNoGC({ stbError = stbtt_InitFont(&font.font, data, 0); });
8932 return stbError;
8935 void fons__tt_getFontVMetrics (FONSttFontImpl* font, int* ascent, int* descent, int* lineGap) nothrow @trusted @nogc {
8936 forceNoThrowNoGC({ stbtt_GetFontVMetrics(&font.font, ascent, descent, lineGap); });
8939 float fons__tt_getPixelHeightScale (FONSttFontImpl* font, float size) nothrow @trusted @nogc {
8940 float res = void;
8941 forceNoThrowNoGC({ res = stbtt_ScaleForPixelHeight(&font.font, size); });
8942 return res;
8945 int fons__tt_getGlyphIndex (FONSttFontImpl* font, int codepoint) nothrow @trusted @nogc {
8946 int res;
8947 forceNoThrowNoGC({ res = stbtt_FindGlyphIndex(&font.font, codepoint); });
8948 return res;
8951 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 {
8952 forceNoThrowNoGC({ stbtt_GetGlyphHMetrics(&font.font, glyph, advance, lsb); });
8953 forceNoThrowNoGC({ stbtt_GetGlyphBitmapBox(&font.font, glyph, scale, scale, x0, y0, x1, y1); });
8954 return 1;
8957 void fons__tt_renderGlyphBitmap (FONSttFontImpl* font, ubyte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) nothrow @trusted @nogc {
8958 forceNoThrowNoGC({ stbtt_MakeGlyphBitmap(&font.font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); });
8961 float fons__tt_getGlyphKernAdvance (FONSttFontImpl* font, float size, int glyph1, int glyph2) nothrow @trusted @nogc {
8962 float res = void;
8963 forceNoThrowNoGC({ res = stbtt_GetGlyphKernAdvance(&font.font, glyph1, glyph2); });
8964 return res;
8967 } // version
8970 // ////////////////////////////////////////////////////////////////////////// //
8971 private:
8972 enum FONS_SCRATCH_BUF_SIZE = 64000;
8973 enum FONS_HASH_LUT_SIZE = 256;
8974 enum FONS_INIT_FONTS = 4;
8975 enum FONS_INIT_GLYPHS = 256;
8976 enum FONS_INIT_ATLAS_NODES = 256;
8977 enum FONS_VERTEX_COUNT = 1024;
8978 enum FONS_MAX_STATES = 20;
8979 enum FONS_MAX_FALLBACKS = 20;
8981 uint fons__hashint() (uint a) pure nothrow @safe @nogc {
8982 pragma(inline, true);
8983 a += ~(a<<15);
8984 a ^= (a>>10);
8985 a += (a<<3);
8986 a ^= (a>>6);
8987 a += ~(a<<11);
8988 a ^= (a>>16);
8989 return a;
8992 uint fons__djbhash (const(void)[] s) pure nothrow @safe @nogc {
8993 uint hash = 5381;
8994 foreach (ubyte b; cast(const(ubyte)[])s) {
8995 if (b >= 'A' && b <= 'Z') b += 32; // poor man's tolower
8996 hash = ((hash<<5)+hash)+b;
8998 return hash;
9001 private bool fons_strequci (const(char)[] s0, const(char)[] s1) nothrow @trusted @nogc {
9002 if (s0.length != s1.length) return false;
9003 const(char)* sp0 = s0.ptr;
9004 const(char)* sp1 = s1.ptr;
9005 foreach (immutable _; 0..s0.length) {
9006 char c0 = *sp0++;
9007 char c1 = *sp1++;
9008 if (c0 != c1) {
9009 if (c0 >= 'A' && c0 <= 'Z') c0 += 32; // poor man tolower
9010 if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man tolower
9011 if (c0 != c1) return false;
9014 return true;
9018 struct FONSglyph {
9019 uint codepoint;
9020 int index;
9021 int next;
9022 short size, blur;
9023 short x0, y0, x1, y1;
9024 short xadv, xoff, yoff;
9027 // refcounted
9028 struct FONSfontData {
9029 ubyte* data;
9030 int dataSize;
9031 bool freeData;
9032 int rc;
9034 @disable this (this); // no copies
9037 // won't set rc to 1
9038 FONSfontData* fons__createFontData (ubyte* adata, int asize, bool afree) nothrow @trusted @nogc {
9039 import core.stdc.stdlib : malloc;
9040 assert(adata !is null);
9041 assert(asize > 0);
9042 auto res = cast(FONSfontData*)malloc(FONSfontData.sizeof);
9043 if (res is null) assert(0, "FONS: out of memory");
9044 res.data = adata;
9045 res.dataSize = asize;
9046 res.freeData = afree;
9047 res.rc = 0;
9048 return res;
9051 void incref (FONSfontData* fd) pure nothrow @trusted @nogc {
9052 pragma(inline, true);
9053 if (fd !is null) ++fd.rc;
9056 void decref (ref FONSfontData* fd) nothrow @trusted @nogc {
9057 if (fd !is null) {
9058 if (--fd.rc == 0) {
9059 import core.stdc.stdlib : free;
9060 if (fd.freeData && fd.data !is null) {
9061 free(fd.data);
9062 fd.data = null;
9064 free(fd);
9065 fd = null;
9070 // as creating and destroying fonts is a rare operation, malloc some data
9071 struct FONSfont {
9072 FONSttFontImpl font;
9073 char* name; // malloced, strz, always lowercase
9074 uint namelen;
9075 uint namehash;
9076 char* path; // malloced, strz
9077 FONSfontData* fdata;
9078 float ascender;
9079 float descender;
9080 float lineh;
9081 FONSglyph* glyphs;
9082 int cglyphs;
9083 int nglyphs;
9084 int[FONS_HASH_LUT_SIZE] lut;
9085 int[FONS_MAX_FALLBACKS] fallbacks;
9086 int nfallbacks;
9088 // except glyphs
9089 void freeMemory () nothrow @trusted @nogc {
9090 import core.stdc.stdlib : free;
9091 if (name !is null) { free(name); name = null; }
9092 namelen = namehash = 0;
9093 if (path !is null) { free(path); path = null; }
9094 fdata.decref();
9097 // this also calcs name hash
9098 void setName (const(char)[] aname) nothrow @trusted @nogc {
9099 //{ import core.stdc.stdio; printf("setname: [%.*s]\n", cast(uint)aname.length, aname.ptr); }
9100 import core.stdc.stdlib : realloc;
9101 if (aname.length > int.max/32) assert(0, "FONS: invalid font name");
9102 namelen = cast(uint)aname.length;
9103 name = cast(char*)realloc(name, namelen+1);
9104 if (name is null) assert(0, "FONS: out of memory");
9105 if (aname.length) name[0..aname.length] = aname[];
9106 name[namelen] = 0;
9107 // lowercase it
9108 foreach (ref char ch; name[0..namelen]) if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
9109 namehash = fons__djbhash(name[0..namelen]);
9110 //{ import core.stdc.stdio; printf(" [%s] [%.*s] [0x%08x]\n", name, namelen, name, namehash); }
9113 void setPath (const(char)[] apath) nothrow @trusted @nogc {
9114 import core.stdc.stdlib : realloc;
9115 if (apath.length > int.max/32) assert(0, "FONS: invalid font path");
9116 path = cast(char*)realloc(path, apath.length+1);
9117 if (path is null) assert(0, "FONS: out of memory");
9118 if (apath.length) path[0..apath.length] = apath[];
9119 path[apath.length] = 0;
9122 // this won't check hash
9123 bool nameEqu (const(char)[] aname) nothrow @trusted @nogc {
9124 //{ import core.stdc.stdio; printf("nameEqu: aname=[%.*s]; namelen=%u; aslen=%u\n", cast(uint)aname.length, aname.ptr, namelen, cast(uint)aname.length); }
9125 if (namelen != aname.length) return false;
9126 const(char)* ns = name;
9127 // name part
9128 foreach (char ch; aname) {
9129 if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower
9130 if (ch != *ns++) return false;
9132 // done (length was checked earlier)
9133 return true;
9137 struct FONSstate {
9138 int font;
9139 NVGTextAlign talign;
9140 float size;
9141 uint color;
9142 float blur;
9143 float spacing;
9146 struct FONSatlasNode {
9147 short x, y, width;
9150 struct FONSatlas {
9151 int width, height;
9152 FONSatlasNode* nodes;
9153 int nnodes;
9154 int cnodes;
9157 public struct FONScontext {
9158 FONSparams params;
9159 float itw, ith;
9160 ubyte* texData;
9161 int[4] dirtyRect;
9162 FONSfont** fonts; // actually, a simple hash table; can't grow yet
9163 int cfonts; // allocated
9164 int nfonts; // used (so we can track hash table stats)
9165 int* hashidx; // [hsize] items; holds indicies in [fonts] array
9166 int hused, hsize;// used items and total items in [hashidx]
9167 FONSatlas* atlas;
9168 debug(nanovega) {
9169 float[FONS_VERTEX_COUNT*2] verts;
9170 float[FONS_VERTEX_COUNT*2] tcoords;
9171 uint[FONS_VERTEX_COUNT] colors;
9172 int nverts;
9174 ubyte* scratch;
9175 int nscratch;
9176 FONSstate[FONS_MAX_STATES] states;
9177 int nstates;
9178 void function (void* uptr, int error, int val) nothrow @trusted @nogc handleError;
9179 void* errorUptr;
9181 // simple linear probing; returns [FONS_INVALID] if not found
9182 int findNameInHash (const(char)[] name) nothrow @trusted @nogc {
9183 if (nfonts == 0) return FONS_INVALID;
9184 auto nhash = fons__djbhash(name);
9185 //{ import core.stdc.stdio; printf("findinhash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
9186 auto res = nhash%hsize;
9187 // hash will never be 100% full, so this loop is safe
9188 for (;;) {
9189 int idx = hashidx[res];
9190 if (idx == -1) break;
9191 auto font = fonts[idx];
9192 if (font is null) assert(0, "FONS internal error");
9193 if (font.namehash == nhash && font.nameEqu(name)) return idx;
9194 //{ import core.stdc.stdio; printf("findinhash chained: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
9195 res = (res+1)%hsize;
9197 return FONS_INVALID;
9200 // should be called $(B before) freeing `fonts[fidx]`
9201 private void removeIndexFromHash (int fidx) nothrow @trusted @nogc {
9202 if (fidx < 0 || fidx >= nfonts) assert(0, "FONS internal error");
9203 if (fonts[fidx] is null) assert(0, "FONS internal error");
9204 if (hused != nfonts) assert(0, "FONS internal error");
9205 auto nhash = fonts[fidx].namehash;
9206 auto res = nhash%hsize;
9207 // hash will never be 100% full, so this loop is safe
9208 for (;;) {
9209 int idx = hashidx[res];
9210 if (idx == -1) assert(0, "FONS INTERNAL ERROR");
9211 if (idx == fidx) {
9212 // i found her! copy rest here
9213 int nidx = (res+1)%hsize;
9214 for (;;) {
9215 if ((hashidx[res] = hashidx[nidx]) == -1) break; // so it will copy `-1` too
9216 res = nidx;
9217 nidx = (nidx+1)%hsize;
9219 return;
9221 res = (res+1)%hsize;
9225 // add font with the given index to hash
9226 // prerequisite: font should not exists in hash
9227 private void addIndexToHash (int idx) nothrow @trusted @nogc {
9228 if (idx < 0 || idx >= nfonts) assert(0, "FONS internal error");
9229 if (fonts[idx] is null) assert(0, "FONS internal error");
9230 import core.stdc.stdlib : realloc;
9231 auto nhash = fonts[idx].namehash;
9232 //{ import core.stdc.stdio; printf("addtohash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
9233 // allocate new hash table if there was none
9234 if (hsize == 0) {
9235 enum InitSize = 256;
9236 auto newlist = cast(int*)realloc(null, InitSize*hashidx[0].sizeof);
9237 if (newlist is null) assert(0, "FONS: out of memory");
9238 newlist[0..InitSize] = -1;
9239 hsize = InitSize;
9240 hused = 0;
9241 hashidx = newlist;
9243 int res = cast(int)(nhash%hsize);
9244 // need to rehash? we want our hash table 50% full at max
9245 if (hashidx[res] != -1 && hused >= hsize/2) {
9246 uint nsz = hsize*2;
9247 if (nsz > 1024*1024) assert(0, "FONS: out of memory for fonts");
9248 auto newlist = cast(int*)realloc(fonts, nsz*hashidx[0].sizeof);
9249 if (newlist is null) assert(0, "FONS: out of memory");
9250 newlist[0..nsz] = -1;
9251 hused = 0;
9252 // rehash
9253 foreach (immutable fidx, FONSfont* ff; fonts[0..nfonts]) {
9254 if (ff is null) continue;
9255 // find slot for this font (guaranteed to have one)
9256 uint newslot = ff.namehash%nsz;
9257 while (newlist[newslot] != -1) newslot = (newslot+1)%nsz;
9258 newlist[newslot] = cast(int)fidx;
9259 ++hused;
9261 hsize = nsz;
9262 hashidx = newlist;
9263 // we added everything, including [idx], so nothing more to do here
9264 } else {
9265 // find slot (guaranteed to have one)
9266 while (hashidx[res] != -1) res = (res+1)%hsize;
9267 // i found her!
9268 hashidx[res] = idx;
9269 ++hused;
9274 void* fons__tmpalloc (usize size, void* up) nothrow @trusted @nogc {
9275 ubyte* ptr;
9276 FONScontext* stash = cast(FONScontext*)up;
9277 // 16-byte align the returned pointer
9278 size = (size+0xf)&~0xf;
9279 if (stash.nscratch+cast(int)size > FONS_SCRATCH_BUF_SIZE) {
9280 if (stash.handleError) stash.handleError(stash.errorUptr, FONS_SCRATCH_FULL, stash.nscratch+cast(int)size);
9281 return null;
9283 ptr = stash.scratch+stash.nscratch;
9284 stash.nscratch += cast(int)size;
9285 return ptr;
9288 void fons__tmpfree (void* ptr, void* up) nothrow @trusted @nogc {
9289 // empty
9292 // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
9293 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
9295 enum FONS_UTF8_ACCEPT = 0;
9296 enum FONS_UTF8_REJECT = 12;
9298 static immutable ubyte[364] utf8d = [
9299 // The first part of the table maps bytes to character classes that
9300 // to reduce the size of the transition table and create bitmasks.
9301 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,
9302 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,
9303 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,
9304 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,
9305 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,
9306 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,
9307 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,
9308 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,
9310 // The second part is a transition table that maps a combination
9311 // of a state of the automaton and a character class to a state.
9312 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
9313 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12,
9314 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12,
9315 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12,
9316 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
9319 private enum DecUtfMixin(string state, string codep, string byte_) =
9321 uint type_ = utf8d.ptr[`~byte_~`];
9322 `~codep~` = (`~state~` != FONS_UTF8_ACCEPT ? (`~byte_~`&0x3fu)|(`~codep~`<<6) : (0xff>>type_)&`~byte_~`);
9323 if ((`~state~` = utf8d.ptr[256+`~state~`+type_]) == FONS_UTF8_REJECT) {
9324 `~state~` = FONS_UTF8_ACCEPT;
9325 `~codep~` = 0xFFFD;
9330 uint fons__decutf8 (uint* state, uint* codep, uint byte_) {
9331 pragma(inline, true);
9332 uint type = utf8d.ptr[byte_];
9333 *codep = (*state != FONS_UTF8_ACCEPT ? (byte_&0x3fu)|(*codep<<6) : (0xff>>type)&byte_);
9334 *state = utf8d.ptr[256 + *state+type];
9335 return *state;
9339 // Atlas based on Skyline Bin Packer by Jukka Jylänki
9340 void fons__deleteAtlas (FONSatlas* atlas) nothrow @trusted @nogc {
9341 if (atlas is null) return;
9342 if (atlas.nodes !is null) free(atlas.nodes);
9343 free(atlas);
9346 FONSatlas* fons__allocAtlas (int w, int h, int nnodes) nothrow @trusted @nogc {
9347 FONSatlas* atlas = null;
9349 // Allocate memory for the font stash.
9350 atlas = cast(FONSatlas*)malloc(FONSatlas.sizeof);
9351 if (atlas is null) goto error;
9352 memset(atlas, 0, FONSatlas.sizeof);
9354 atlas.width = w;
9355 atlas.height = h;
9357 // Allocate space for skyline nodes
9358 atlas.nodes = cast(FONSatlasNode*)malloc(FONSatlasNode.sizeof*nnodes);
9359 if (atlas.nodes is null) goto error;
9360 memset(atlas.nodes, 0, FONSatlasNode.sizeof*nnodes);
9361 atlas.nnodes = 0;
9362 atlas.cnodes = nnodes;
9364 // Init root node.
9365 atlas.nodes[0].x = 0;
9366 atlas.nodes[0].y = 0;
9367 atlas.nodes[0].width = cast(short)w;
9368 ++atlas.nnodes;
9370 return atlas;
9372 error:
9373 if (atlas !is null) fons__deleteAtlas(atlas);
9374 return null;
9377 bool fons__atlasInsertNode (FONSatlas* atlas, int idx, int x, int y, int w) nothrow @trusted @nogc {
9378 // Insert node
9379 if (atlas.nnodes+1 > atlas.cnodes) {
9380 atlas.cnodes = (atlas.cnodes == 0 ? 8 : atlas.cnodes*2);
9381 atlas.nodes = cast(FONSatlasNode*)realloc(atlas.nodes, FONSatlasNode.sizeof*atlas.cnodes);
9382 if (atlas.nodes is null) return false;
9384 for (int i = atlas.nnodes; i > idx; --i) atlas.nodes[i] = atlas.nodes[i-1];
9385 atlas.nodes[idx].x = cast(short)x;
9386 atlas.nodes[idx].y = cast(short)y;
9387 atlas.nodes[idx].width = cast(short)w;
9388 ++atlas.nnodes;
9389 return 1;
9392 void fons__atlasRemoveNode (FONSatlas* atlas, int idx) nothrow @trusted @nogc {
9393 if (atlas.nnodes == 0) return;
9394 for (int i = idx; i < atlas.nnodes-1; ++i) atlas.nodes[i] = atlas.nodes[i+1];
9395 --atlas.nnodes;
9398 void fons__atlasExpand (FONSatlas* atlas, int w, int h) nothrow @trusted @nogc {
9399 // Insert node for empty space
9400 if (w > atlas.width) fons__atlasInsertNode(atlas, atlas.nnodes, atlas.width, 0, w-atlas.width);
9401 atlas.width = w;
9402 atlas.height = h;
9405 void fons__atlasReset (FONSatlas* atlas, int w, int h) nothrow @trusted @nogc {
9406 atlas.width = w;
9407 atlas.height = h;
9408 atlas.nnodes = 0;
9409 // Init root node.
9410 atlas.nodes[0].x = 0;
9411 atlas.nodes[0].y = 0;
9412 atlas.nodes[0].width = cast(short)w;
9413 ++atlas.nnodes;
9416 bool fons__atlasAddSkylineLevel (FONSatlas* atlas, int idx, int x, int y, int w, int h) nothrow @trusted @nogc {
9417 // Insert new node
9418 if (!fons__atlasInsertNode(atlas, idx, x, y+h, w)) return false;
9420 // Delete skyline segments that fall under the shadow of the new segment
9421 for (int i = idx+1; i < atlas.nnodes; ++i) {
9422 if (atlas.nodes[i].x < atlas.nodes[i-1].x+atlas.nodes[i-1].width) {
9423 int shrink = atlas.nodes[i-1].x+atlas.nodes[i-1].width-atlas.nodes[i].x;
9424 atlas.nodes[i].x += cast(short)shrink;
9425 atlas.nodes[i].width -= cast(short)shrink;
9426 if (atlas.nodes[i].width <= 0) {
9427 fons__atlasRemoveNode(atlas, i);
9428 --i;
9429 } else {
9430 break;
9432 } else {
9433 break;
9437 // Merge same height skyline segments that are next to each other
9438 for (int i = 0; i < atlas.nnodes-1; ++i) {
9439 if (atlas.nodes[i].y == atlas.nodes[i+1].y) {
9440 atlas.nodes[i].width += atlas.nodes[i+1].width;
9441 fons__atlasRemoveNode(atlas, i+1);
9442 --i;
9446 return true;
9449 int fons__atlasRectFits (FONSatlas* atlas, int i, int w, int h) nothrow @trusted @nogc {
9450 // Checks if there is enough space at the location of skyline span 'i',
9451 // and return the max height of all skyline spans under that at that location,
9452 // (think tetris block being dropped at that position). Or -1 if no space found.
9453 int x = atlas.nodes[i].x;
9454 int y = atlas.nodes[i].y;
9455 int spaceLeft;
9456 if (x+w > atlas.width) return -1;
9457 spaceLeft = w;
9458 while (spaceLeft > 0) {
9459 if (i == atlas.nnodes) return -1;
9460 y = nvg__max(y, atlas.nodes[i].y);
9461 if (y+h > atlas.height) return -1;
9462 spaceLeft -= atlas.nodes[i].width;
9463 ++i;
9465 return y;
9468 bool fons__atlasAddRect (FONSatlas* atlas, int rw, int rh, int* rx, int* ry) nothrow @trusted @nogc {
9469 int besth = atlas.height, bestw = atlas.width, besti = -1;
9470 int bestx = -1, besty = -1;
9472 // Bottom left fit heuristic.
9473 for (int i = 0; i < atlas.nnodes; ++i) {
9474 int y = fons__atlasRectFits(atlas, i, rw, rh);
9475 if (y != -1) {
9476 if (y+rh < besth || (y+rh == besth && atlas.nodes[i].width < bestw)) {
9477 besti = i;
9478 bestw = atlas.nodes[i].width;
9479 besth = y+rh;
9480 bestx = atlas.nodes[i].x;
9481 besty = y;
9486 if (besti == -1) return false;
9488 // Perform the actual packing.
9489 if (!fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh)) return false;
9491 *rx = bestx;
9492 *ry = besty;
9494 return true;
9497 void fons__addWhiteRect (FONScontext* stash, int w, int h) nothrow @trusted @nogc {
9498 int gx, gy;
9499 ubyte* dst;
9501 if (!fons__atlasAddRect(stash.atlas, w, h, &gx, &gy)) return;
9503 // Rasterize
9504 dst = &stash.texData[gx+gy*stash.params.width];
9505 foreach (int y; 0..h) {
9506 foreach (int x; 0..w) {
9507 dst[x] = 0xff;
9509 dst += stash.params.width;
9512 stash.dirtyRect.ptr[0] = nvg__min(stash.dirtyRect.ptr[0], gx);
9513 stash.dirtyRect.ptr[1] = nvg__min(stash.dirtyRect.ptr[1], gy);
9514 stash.dirtyRect.ptr[2] = nvg__max(stash.dirtyRect.ptr[2], gx+w);
9515 stash.dirtyRect.ptr[3] = nvg__max(stash.dirtyRect.ptr[3], gy+h);
9518 public FONScontext* fonsCreateInternal (FONSparams* params) nothrow @trusted @nogc {
9519 FONScontext* stash = null;
9521 // Allocate memory for the font stash.
9522 stash = cast(FONScontext*)malloc(FONScontext.sizeof);
9523 if (stash is null) goto error;
9524 memset(stash, 0, FONScontext.sizeof);
9526 stash.params = *params;
9528 // Allocate scratch buffer.
9529 stash.scratch = cast(ubyte*)malloc(FONS_SCRATCH_BUF_SIZE);
9530 if (stash.scratch is null) goto error;
9532 // Initialize implementation library
9533 if (!fons__tt_init(stash)) goto error;
9535 if (stash.params.renderCreate !is null) {
9536 if (!stash.params.renderCreate(stash.params.userPtr, stash.params.width, stash.params.height)) goto error;
9539 stash.atlas = fons__allocAtlas(stash.params.width, stash.params.height, FONS_INIT_ATLAS_NODES);
9540 if (stash.atlas is null) goto error;
9542 // Don't allocate space for fonts: hash manager will do that for us later.
9543 //stash.cfonts = 0;
9544 //stash.nfonts = 0;
9546 // Create texture for the cache.
9547 stash.itw = 1.0f/stash.params.width;
9548 stash.ith = 1.0f/stash.params.height;
9549 stash.texData = cast(ubyte*)malloc(stash.params.width*stash.params.height);
9550 if (stash.texData is null) goto error;
9551 memset(stash.texData, 0, stash.params.width*stash.params.height);
9553 stash.dirtyRect.ptr[0] = stash.params.width;
9554 stash.dirtyRect.ptr[1] = stash.params.height;
9555 stash.dirtyRect.ptr[2] = 0;
9556 stash.dirtyRect.ptr[3] = 0;
9558 // Add white rect at 0, 0 for debug drawing.
9559 fons__addWhiteRect(stash, 2, 2);
9561 fonsPushState(stash);
9562 fonsClearState(stash);
9564 return stash;
9566 error:
9567 fonsDeleteInternal(stash);
9568 return null;
9571 FONSstate* fons__getState (FONScontext* stash) nothrow @trusted @nogc {
9572 pragma(inline, true);
9573 return &stash.states[stash.nstates-1];
9576 bool fonsAddFallbackFont (FONScontext* stash, int base, int fallback) nothrow @trusted @nogc {
9577 FONSfont* baseFont = stash.fonts[base];
9578 if (baseFont !is null && baseFont.nfallbacks < FONS_MAX_FALLBACKS) {
9579 baseFont.fallbacks.ptr[baseFont.nfallbacks++] = fallback;
9580 return true;
9582 return false;
9585 public void fonsSetSize (FONScontext* stash, float size) nothrow @trusted @nogc {
9586 pragma(inline, true);
9587 fons__getState(stash).size = size;
9590 public void fonsSetColor (FONScontext* stash, uint color) nothrow @trusted @nogc {
9591 pragma(inline, true);
9592 fons__getState(stash).color = color;
9595 public void fonsSetSpacing (FONScontext* stash, float spacing) nothrow @trusted @nogc {
9596 pragma(inline, true);
9597 fons__getState(stash).spacing = spacing;
9600 public void fonsSetBlur (FONScontext* stash, float blur) nothrow @trusted @nogc {
9601 pragma(inline, true);
9602 version(nanovg_kill_font_blur) blur = 0;
9603 fons__getState(stash).blur = blur;
9606 public void fonsSetAlign (FONScontext* stash, NVGTextAlign talign) nothrow @trusted @nogc {
9607 pragma(inline, true);
9608 fons__getState(stash).talign = talign;
9611 public void fonsSetFont (FONScontext* stash, int font) nothrow @trusted @nogc {
9612 pragma(inline, true);
9613 fons__getState(stash).font = font;
9616 // get AA for current font or for the specified font
9617 public bool fonsGetFontAA (FONScontext* stash, int font=-1) nothrow @trusted @nogc {
9618 FONSstate* state = fons__getState(stash);
9619 if (font < 0) font = state.font;
9620 if (font < 0 || font >= stash.nfonts) return false;
9621 FONSfont* f = stash.fonts[font];
9622 return (f !is null ? !f.font.mono : false);
9625 public void fonsPushState (FONScontext* stash) nothrow @trusted @nogc {
9626 if (stash.nstates >= FONS_MAX_STATES) {
9627 if (stash.handleError) stash.handleError(stash.errorUptr, FONS_STATES_OVERFLOW, 0);
9628 return;
9630 if (stash.nstates > 0) memcpy(&stash.states[stash.nstates], &stash.states[stash.nstates-1], FONSstate.sizeof);
9631 ++stash.nstates;
9634 public void fonsPopState (FONScontext* stash) nothrow @trusted @nogc {
9635 if (stash.nstates <= 1) {
9636 if (stash.handleError) stash.handleError(stash.errorUptr, FONS_STATES_UNDERFLOW, 0);
9637 return;
9639 --stash.nstates;
9642 public void fonsClearState (FONScontext* stash) nothrow @trusted @nogc {
9643 FONSstate* state = fons__getState(stash);
9644 state.size = 12.0f;
9645 state.color = 0xffffffff;
9646 state.font = 0;
9647 state.blur = 0;
9648 state.spacing = 0;
9649 state.talign.reset; //FONS_ALIGN_LEFT|FONS_ALIGN_BASELINE;
9652 void fons__freeFont (FONSfont* font) nothrow @trusted @nogc {
9653 if (font is null) return;
9654 if (font.glyphs) free(font.glyphs);
9655 font.freeMemory();
9656 free(font);
9659 // returns fid, not hash slot
9660 int fons__allocFontAt (FONScontext* stash, int atidx) nothrow @trusted @nogc {
9661 if (atidx >= 0 && atidx >= stash.nfonts) assert(0, "internal NanoVega fontstash error");
9663 if (atidx < 0) {
9664 if (stash.nfonts >= stash.cfonts) {
9665 import core.stdc.stdlib : realloc;
9666 import core.stdc.string : memset;
9667 assert(stash.nfonts == stash.cfonts);
9668 int newsz = stash.cfonts+64;
9669 if (newsz > 65535) assert(0, "FONS: too many fonts");
9670 auto newlist = cast(FONSfont**)realloc(stash.fonts, newsz*(FONSfont*).sizeof);
9671 if (newlist is null) assert(0, "FONS: out of memory");
9672 memset(newlist+stash.cfonts, 0, (newsz-stash.cfonts)*(FONSfont*).sizeof);
9673 stash.fonts = newlist;
9674 stash.cfonts = newsz;
9676 assert(stash.nfonts < stash.cfonts);
9679 FONSfont* font = cast(FONSfont*)malloc(FONSfont.sizeof);
9680 if (font is null) assert(0, "FONS: out of memory");
9681 memset(font, 0, FONSfont.sizeof);
9683 font.glyphs = cast(FONSglyph*)malloc(FONSglyph.sizeof*FONS_INIT_GLYPHS);
9684 if (font.glyphs is null) assert(0, "FONS: out of memory");
9685 font.cglyphs = FONS_INIT_GLYPHS;
9686 font.nglyphs = 0;
9688 if (atidx < 0) {
9689 stash.fonts[stash.nfonts] = font;
9690 return stash.nfonts++;
9691 } else {
9692 stash.fonts[atidx] = font;
9693 return atidx;
9697 private enum NoAlias = ":noaa";
9699 // defAA: antialias flag for fonts without ":noaa"
9700 public int fonsAddFont (FONScontext* stash, const(char)[] name, const(char)[] path, bool defAA) nothrow @trusted {
9701 if (path.length == 0 || name.length == 0 || fons_strequci(name, NoAlias)) return FONS_INVALID;
9702 if (path.length > 32768) return FONS_INVALID; // arbitrary limit
9704 // if font path ends with ":noaa", turn off antialiasing
9705 if (path.length >= NoAlias.length && fons_strequci(path[$-NoAlias.length..$], NoAlias)) {
9706 path = path[0..$-NoAlias.length];
9707 if (path.length == 0) return FONS_INVALID;
9708 defAA = false;
9711 // if font name ends with ":noaa", turn off antialiasing
9712 if (name.length > NoAlias.length && fons_strequci(name[$-NoAlias.length..$], NoAlias)) {
9713 name = name[0..$-NoAlias.length];
9714 defAA = false;
9717 // find a font with the given name
9718 int fidx = stash.findNameInHash(name);
9719 //{ import core.stdc.stdio; printf("loading font '%.*s' [%s] (fidx=%d)...\n", cast(uint)path.length, path.ptr, fontnamebuf.ptr, fidx); }
9721 int loadFontFile (const(char)[] path) {
9722 // check if existing font (if any) has the same path
9723 if (fidx >= 0) {
9724 import core.stdc.string : strlen;
9725 auto plen = (stash.fonts[fidx].path !is null ? strlen(stash.fonts[fidx].path) : 0);
9726 version(Posix) {
9727 //{ import core.stdc.stdio; printf("+++ font [%.*s] was loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)stash.fonts[fidx].path.length, stash.fonts[fidx].path.ptr); }
9728 if (plen == path.length && stash.fonts[fidx].path[0..plen] == path) {
9729 //{ import core.stdc.stdio; printf("*** font [%.*s] already loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)plen, path.ptr); }
9730 // i found her!
9731 return fidx;
9733 } else {
9734 if (plen == path.length && fons_strequci(stash.fonts[fidx].path[0..plen], path)) {
9735 // i found her!
9736 return fidx;
9740 version(Windows) {
9741 // special shitdows check: this will reject fontconfig font names (but still allow things like "c:myfont")
9742 foreach (immutable char ch; path[(path.length >= 2 && path[1] == ':' ? 2 : 0)..$]) if (ch == ':') return FONS_INVALID;
9744 // either no such font, or different path
9745 //{ import core.stdc.stdio; printf("trying font [%.*s] from file [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)path.length, path.ptr); }
9746 int xres = FONS_INVALID;
9747 try {
9748 import core.stdc.stdlib : free, malloc;
9749 static if (NanoVegaHasIVVFS) {
9750 auto fl = VFile(path);
9751 auto dataSize = fl.size;
9752 if (dataSize < 16 || dataSize > int.max/32) return FONS_INVALID;
9753 ubyte* data = cast(ubyte*)malloc(cast(uint)dataSize);
9754 if (data is null) assert(0, "out of memory in NanoVega fontstash");
9755 scope(failure) free(data); // oops
9756 fl.rawReadExact(data[0..cast(uint)dataSize]);
9757 fl.close();
9758 } else {
9759 import core.stdc.stdio : FILE, fopen, fclose, fread, ftell, fseek;
9760 import std.internal.cstring : tempCString;
9761 auto fl = fopen(path.tempCString, "rb");
9762 if (fl is null) return FONS_INVALID;
9763 scope(exit) fclose(fl);
9764 if (fseek(fl, 0, 2/*SEEK_END*/) != 0) return FONS_INVALID;
9765 auto dataSize = ftell(fl);
9766 if (fseek(fl, 0, 0/*SEEK_SET*/) != 0) return FONS_INVALID;
9767 if (dataSize < 16 || dataSize > int.max/32) return FONS_INVALID;
9768 ubyte* data = cast(ubyte*)malloc(cast(uint)dataSize);
9769 if (data is null) assert(0, "out of memory in NanoVega fontstash");
9770 scope(failure) free(data); // oops
9771 ubyte* dptr = data;
9772 auto left = cast(uint)dataSize;
9773 while (left > 0) {
9774 auto rd = fread(dptr, 1, left, fl);
9775 if (rd == 0) { free(data); return FONS_INVALID; } // unexpected EOF or reading error, it doesn't matter
9776 dptr += rd;
9777 left -= rd;
9780 scope(failure) free(data); // oops
9781 // create font data
9782 FONSfontData* fdata = fons__createFontData(data, cast(int)dataSize, true); // free data
9783 fdata.incref();
9784 xres = fonsAddFontWithData(stash, name, fdata, defAA);
9785 if (xres == FONS_INVALID) {
9786 fdata.decref(); // this will free [data] and [fdata]
9787 } else {
9788 // remember path
9789 stash.fonts[xres].setPath(path);
9791 } catch (Exception e) {
9792 // oops; sorry
9794 return xres;
9797 // first try direct path
9798 auto res = loadFontFile(path);
9799 // if loading failed, try fontconfig (if fontconfig is available)
9800 static if (NanoVegaHasFontConfig) {
9801 if (res == FONS_INVALID && fontconfigAvailable) {
9802 import std.internal.cstring : tempCString;
9803 FcPattern* pat = FcNameParse(path.tempCString);
9804 if (pat !is null) {
9805 scope(exit) FcPatternDestroy(pat);
9806 if (FcConfigSubstitute(null, pat, FcMatchPattern)) {
9807 FcDefaultSubstitute(pat);
9808 // find the font
9809 FcResult result;
9810 FcPattern* font = FcFontMatch(null, pat, &result);
9811 if (font !is null) {
9812 scope(exit) FcPatternDestroy(font);
9813 char* file = null;
9814 if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
9815 if (file !is null && file[0]) {
9816 import core.stdc.string : strlen;
9817 res = loadFontFile(file[0..strlen(file)]);
9825 return res;
9828 // This will not free data on error!
9829 public int fonsAddFontMem (FONScontext* stash, const(char)[] name, ubyte* data, int dataSize, bool freeData, bool defAA) nothrow @trusted @nogc {
9830 FONSfontData* fdata = fons__createFontData(data, dataSize, freeData);
9831 fdata.incref();
9832 auto res = fonsAddFontWithData(stash, name, fdata, defAA);
9833 if (res == FONS_INVALID) {
9834 // we promised to not free data on error
9835 fdata.freeData = false;
9836 fdata.decref(); // this will free [fdata]
9838 return res;
9841 // Add fonts from another font stash
9842 // This is more effective than reloading fonts, 'cause font data will be shared.
9843 public void fonsAddStashFonts (FONScontext* stash, FONScontext* source) nothrow @trusted @nogc {
9844 if (stash is null || source is null) return;
9845 foreach (FONSfont* font; source.fonts[0..source.nfonts]) {
9846 if (font !is null) {
9847 auto newidx = fonsAddCookedFont(stash, font);
9848 FONSfont* newfont = stash.fonts[newidx];
9849 assert(newfont !is null);
9850 assert(newfont.path is null);
9851 // copy path
9852 if (font.path !is null && font.path[0]) {
9853 import core.stdc.stdlib : malloc;
9854 import core.stdc.string : strcpy, strlen;
9855 newfont.path = cast(char*)malloc(strlen(font.path)+1);
9856 if (newfont.path is null) assert(0, "FONS: out of memory");
9857 strcpy(newfont.path, font.path);
9863 // used to add font from another fontstash
9864 int fonsAddCookedFont (FONScontext* stash, FONSfont* font) nothrow @trusted @nogc {
9865 if (font is null || font.fdata is null) return FONS_INVALID;
9866 font.fdata.incref();
9867 auto res = fonsAddFontWithData(stash, font.name[0..font.namelen], font.fdata, !font.font.mono);
9868 if (res == FONS_INVALID) font.fdata.decref(); // oops
9869 return res;
9872 // fdata refcount must be already increased; it won't be changed
9873 int fonsAddFontWithData (FONScontext* stash, const(char)[] name, FONSfontData* fdata, bool defAA) nothrow @trusted @nogc {
9874 int i, ascent, descent, fh, lineGap;
9876 if (name.length == 0 || fons_strequci(name, NoAlias)) return FONS_INVALID;
9877 if (name.length > 32767) return FONS_INVALID;
9878 if (fdata is null) return FONS_INVALID;
9880 // find a font with the given name
9881 int newidx;
9882 FONSfont* oldfont = null;
9883 int oldidx = stash.findNameInHash(name);
9884 if (oldidx != FONS_INVALID) {
9885 // replacement font
9886 oldfont = stash.fonts[oldidx];
9887 newidx = oldidx;
9888 } else {
9889 // new font, allocate new bucket
9890 newidx = -1;
9893 newidx = fons__allocFontAt(stash, newidx);
9894 FONSfont* font = stash.fonts[newidx];
9895 font.setName(name);
9896 font.lut.ptr[0..FONS_HASH_LUT_SIZE] = -1; // init hash lookup
9897 font.fdata = fdata; // set the font data (don't change reference count)
9898 fons__tt_setMono(stash, &font.font, !defAA);
9900 // init font
9901 stash.nscratch = 0;
9902 if (!fons__tt_loadFont(stash, &font.font, fdata.data, fdata.dataSize)) {
9903 // we promised to not free data on error, so just clear the data store (it will be freed by the caller)
9904 font.fdata = null;
9905 fons__freeFont(font);
9906 if (oldidx != FONS_INVALID) {
9907 assert(oldidx == newidx);
9908 stash.fonts[oldidx] = oldfont;
9909 } else {
9910 assert(newidx == stash.nfonts-1);
9911 stash.fonts[newidx] = null;
9912 --stash.nfonts;
9914 return FONS_INVALID;
9915 } else {
9916 // free old font data, if any
9917 if (oldfont) fons__freeFont(oldfont);
9920 // add font to name hash
9921 if (oldidx == FONS_INVALID) stash.addIndexToHash(newidx);
9923 // store normalized line height
9924 // the real line height is got by multiplying the lineh by font size
9925 fons__tt_getFontVMetrics(&font.font, &ascent, &descent, &lineGap);
9926 fh = ascent-descent;
9927 font.ascender = cast(float)ascent/cast(float)fh;
9928 font.descender = cast(float)descent/cast(float)fh;
9929 font.lineh = cast(float)(fh+lineGap)/cast(float)fh;
9931 //{ import core.stdc.stdio; printf("created font [%.*s] (idx=%d)...\n", cast(uint)name.length, name.ptr, idx); }
9932 return newidx;
9935 // returns `null` on invalid index
9936 // $(WARNING copy name, as name buffer can be invalidated by next fontstash API call!)
9937 public const(char)[] fonsGetNameByIndex (FONScontext* stash, int idx) nothrow @trusted @nogc {
9938 if (idx < 0 || idx >= stash.nfonts || stash.fonts[idx] is null) return null;
9939 return stash.fonts[idx].name[0..stash.fonts[idx].namelen];
9942 // allowSubstitutes: check AA variants if exact name wasn't found?
9943 // return [FONS_INVALID] if no font was found
9944 public int fonsGetFontByName (FONScontext* stash, const(char)[] name) nothrow @trusted @nogc {
9945 //{ import core.stdc.stdio; printf("fonsGetFontByName: [%.*s]\n", cast(uint)name.length, name.ptr); }
9946 // remove ":noaa" suffix
9947 if (name.length >= NoAlias.length && fons_strequci(name[$-NoAlias.length..$], NoAlias)) {
9948 name = name[0..$-NoAlias.length];
9950 if (name.length == 0) return FONS_INVALID;
9951 return stash.findNameInHash(name);
9954 FONSglyph* fons__allocGlyph (FONSfont* font) nothrow @trusted @nogc {
9955 if (font.nglyphs+1 > font.cglyphs) {
9956 font.cglyphs = (font.cglyphs == 0 ? 8 : font.cglyphs*2);
9957 font.glyphs = cast(FONSglyph*)realloc(font.glyphs, FONSglyph.sizeof*font.cglyphs);
9958 if (font.glyphs is null) return null;
9960 ++font.nglyphs;
9961 return &font.glyphs[font.nglyphs-1];
9964 // 0: ooops
9965 int fons__findGlyphForCP (FONScontext* stash, FONSfont *font, dchar dch, FONSfont** renderfont) nothrow @trusted @nogc {
9966 if (renderfont !is null) *renderfont = font;
9967 if (stash is null) return 0;
9968 if (font is null || font.fdata is null) return 0;
9969 auto g = fons__tt_getGlyphIndex(&font.font, cast(uint)dch);
9970 // try to find the glyph in fallback fonts
9971 if (g == 0) {
9972 foreach (immutable i; 0..font.nfallbacks) {
9973 FONSfont* fallbackFont = stash.fonts[font.fallbacks.ptr[i]];
9974 if (fallbackFont !is null) {
9975 int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont.font, cast(uint)dch);
9976 if (fallbackIndex != 0) {
9977 if (renderfont !is null) *renderfont = fallbackFont;
9978 return g;
9982 // no char, try to find replacement one
9983 if (dch != 0xFFFD) {
9984 g = fons__tt_getGlyphIndex(&font.font, 0xFFFD);
9985 if (g == 0) {
9986 foreach (immutable i; 0..font.nfallbacks) {
9987 FONSfont* fallbackFont = stash.fonts[font.fallbacks.ptr[i]];
9988 if (fallbackFont !is null) {
9989 int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont.font, 0xFFFD);
9990 if (fallbackIndex != 0) {
9991 if (renderfont !is null) *renderfont = fallbackFont;
9992 return g;
9999 return g;
10002 public bool fonsPathBounds (FONScontext* stash, dchar dch, float[] bounds) nothrow @trusted @nogc {
10003 if (bounds.length > 4) bounds = bounds.ptr[0..4];
10004 static if (is(typeof(&fons__nvg__bounds))) {
10005 if (stash is null) { bounds[] = 0; return false; }
10006 FONSstate* state = fons__getState(stash);
10007 if (state.font < 0 || state.font >= stash.nfonts) { bounds[] = 0; return false; }
10008 FONSfont* font;
10009 auto g = fons__findGlyphForCP(stash, stash.fonts[state.font], dch, &font);
10010 if (g == 0) { bounds[] = 0; return false; }
10011 assert(font !is null);
10012 return fons__nvg__bounds(&font.font, g, bounds);
10013 } else {
10014 bounds[] = 0;
10015 return false;
10019 public bool fonsToPath (FONScontext* stash, NVGContext vg, dchar dch, float[] bounds=null) nothrow @trusted @nogc {
10020 if (bounds.length > 4) bounds = bounds.ptr[0..4];
10021 static if (is(typeof(&fons__nvg__toPath))) {
10022 if (vg is null || stash is null) { bounds[] = 0; return false; }
10023 FONSstate* state = fons__getState(stash);
10024 if (state.font < 0 || state.font >= stash.nfonts) { bounds[] = 0; return false; }
10025 FONSfont* font;
10026 auto g = fons__findGlyphForCP(stash, stash.fonts[state.font], dch, &font);
10027 if (g == 0) { bounds[] = 0; return false; }
10028 assert(font !is null);
10029 return fons__nvg__toPath(vg, &font.font, g, bounds);
10030 } else {
10031 bounds[] = 0;
10032 return false;
10036 public bool fonsToOutline (FONScontext* stash, dchar dch, NVGGlyphOutline* ol) nothrow @trusted @nogc {
10037 if (stash is null || ol is null) return false;
10038 static if (is(typeof(&fons__nvg__toOutline))) {
10039 FONSstate* state = fons__getState(stash);
10040 if (state.font < 0 || state.font >= stash.nfonts) return false;
10041 FONSfont* font;
10042 auto g = fons__findGlyphForCP(stash, stash.fonts[state.font], dch, &font);
10043 if (g == 0) return false;
10044 assert(font !is null);
10045 return fons__nvg__toOutline(&font.font, g, ol);
10046 } else {
10047 return false;
10052 // Based on Exponential blur, Jani Huhtanen, 2006
10054 enum APREC = 16;
10055 enum ZPREC = 7;
10057 void fons__blurCols (ubyte* dst, int w, int h, int dstStride, int alpha) nothrow @trusted @nogc {
10058 foreach (int y; 0..h) {
10059 int z = 0; // force zero border
10060 foreach (int x; 1..w) {
10061 z += (alpha*((cast(int)(dst[x])<<ZPREC)-z))>>APREC;
10062 dst[x] = cast(ubyte)(z>>ZPREC);
10064 dst[w-1] = 0; // force zero border
10065 z = 0;
10066 for (int x = w-2; x >= 0; --x) {
10067 z += (alpha*((cast(int)(dst[x])<<ZPREC)-z))>>APREC;
10068 dst[x] = cast(ubyte)(z>>ZPREC);
10070 dst[0] = 0; // force zero border
10071 dst += dstStride;
10075 void fons__blurRows (ubyte* dst, int w, int h, int dstStride, int alpha) nothrow @trusted @nogc {
10076 foreach (int x; 0..w) {
10077 int z = 0; // force zero border
10078 for (int y = dstStride; y < h*dstStride; y += dstStride) {
10079 z += (alpha*((cast(int)(dst[y])<<ZPREC)-z))>>APREC;
10080 dst[y] = cast(ubyte)(z>>ZPREC);
10082 dst[(h-1)*dstStride] = 0; // force zero border
10083 z = 0;
10084 for (int y = (h-2)*dstStride; y >= 0; y -= dstStride) {
10085 z += (alpha*((cast(int)(dst[y])<<ZPREC)-z))>>APREC;
10086 dst[y] = cast(ubyte)(z>>ZPREC);
10088 dst[0] = 0; // force zero border
10089 ++dst;
10094 void fons__blur (FONScontext* stash, ubyte* dst, int w, int h, int dstStride, int blur) nothrow @trusted @nogc {
10095 import std.math : expf = exp;
10096 int alpha;
10097 float sigma;
10098 if (blur < 1) return;
10099 // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
10100 sigma = cast(float)blur*0.57735f; // 1/sqrt(3)
10101 alpha = cast(int)((1<<APREC)*(1.0f-expf(-2.3f/(sigma+1.0f))));
10102 fons__blurRows(dst, w, h, dstStride, alpha);
10103 fons__blurCols(dst, w, h, dstStride, alpha);
10104 fons__blurRows(dst, w, h, dstStride, alpha);
10105 fons__blurCols(dst, w, h, dstStride, alpha);
10106 //fons__blurrows(dst, w, h, dstStride, alpha);
10107 //fons__blurcols(dst, w, h, dstStride, alpha);
10110 FONSglyph* fons__getGlyph (FONScontext* stash, FONSfont* font, uint codepoint, short isize, short iblur, FONSglyphBitmap bitmapOption) nothrow @trusted @nogc {
10111 int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;
10112 float scale;
10113 FONSglyph* glyph = null;
10114 uint h;
10115 float size = isize/10.0f;
10116 int pad, added;
10117 ubyte* bdst;
10118 ubyte* dst;
10119 FONSfont* renderFont = font;
10121 version(nanovg_kill_font_blur) iblur = 0;
10123 if (isize < 2) return null;
10124 if (iblur > 20) iblur = 20;
10125 pad = iblur+2;
10127 // Reset allocator.
10128 stash.nscratch = 0;
10130 // Find code point and size.
10131 h = fons__hashint(codepoint)&(FONS_HASH_LUT_SIZE-1);
10132 i = font.lut.ptr[h];
10133 while (i != -1) {
10134 //if (font.glyphs[i].codepoint == codepoint && font.glyphs[i].size == isize && font.glyphs[i].blur == iblur) return &font.glyphs[i];
10135 if (font.glyphs[i].codepoint == codepoint && font.glyphs[i].size == isize && font.glyphs[i].blur == iblur) {
10136 glyph = &font.glyphs[i];
10137 // Negative coordinate indicates there is no bitmap data created.
10138 if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph.x0 >= 0 && glyph.y0 >= 0)) return glyph;
10139 // At this point, glyph exists but the bitmap data is not yet created.
10140 break;
10142 i = font.glyphs[i].next;
10145 // Create a new glyph or rasterize bitmap data for a cached glyph.
10146 //scale = fons__tt_getPixelHeightScale(&font.font, size);
10147 g = fons__findGlyphForCP(stash, font, cast(dchar)codepoint, &renderFont);
10148 // It is possible that we did not find a fallback glyph.
10149 // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
10151 scale = fons__tt_getPixelHeightScale(&renderFont.font, size);
10152 fons__tt_buildGlyphBitmap(&renderFont.font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
10153 gw = x1-x0+pad*2;
10154 gh = y1-y0+pad*2;
10156 // Determines the spot to draw glyph in the atlas.
10157 if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) {
10158 // Find free spot for the rect in the atlas.
10159 added = fons__atlasAddRect(stash.atlas, gw, gh, &gx, &gy);
10160 if (added == 0 && stash.handleError !is null) {
10161 // Atlas is full, let the user to resize the atlas (or not), and try again.
10162 stash.handleError(stash.errorUptr, FONS_ATLAS_FULL, 0);
10163 added = fons__atlasAddRect(stash.atlas, gw, gh, &gx, &gy);
10165 if (added == 0) return null;
10166 } else {
10167 // Negative coordinate indicates there is no bitmap data created.
10168 gx = -1;
10169 gy = -1;
10172 // Init glyph.
10173 if (glyph is null) {
10174 glyph = fons__allocGlyph(font);
10175 glyph.codepoint = codepoint;
10176 glyph.size = isize;
10177 glyph.blur = iblur;
10178 glyph.next = 0;
10180 // Insert char to hash lookup.
10181 glyph.next = font.lut.ptr[h];
10182 font.lut.ptr[h] = font.nglyphs-1;
10184 glyph.index = g;
10185 glyph.x0 = cast(short)gx;
10186 glyph.y0 = cast(short)gy;
10187 glyph.x1 = cast(short)(glyph.x0+gw);
10188 glyph.y1 = cast(short)(glyph.y0+gh);
10189 glyph.xadv = cast(short)(scale*advance*10.0f);
10190 glyph.xoff = cast(short)(x0-pad);
10191 glyph.yoff = cast(short)(y0-pad);
10193 if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) return glyph;
10195 // Rasterize
10196 dst = &stash.texData[(glyph.x0+pad)+(glyph.y0+pad)*stash.params.width];
10197 fons__tt_renderGlyphBitmap(&font.font, dst, gw-pad*2, gh-pad*2, stash.params.width, scale, scale, g);
10199 // Make sure there is one pixel empty border.
10200 dst = &stash.texData[glyph.x0+glyph.y0*stash.params.width];
10201 for (y = 0; y < gh; y++) {
10202 dst[y*stash.params.width] = 0;
10203 dst[gw-1+y*stash.params.width] = 0;
10205 for (x = 0; x < gw; x++) {
10206 dst[x] = 0;
10207 dst[x+(gh-1)*stash.params.width] = 0;
10210 // Debug code to color the glyph background
10211 version(none) {
10212 foreach (immutable yy; 0..gh) {
10213 foreach (immutable xx; 0..gw) {
10214 int a = cast(int)dst[xx+yy*stash.params.width]+42;
10215 if (a > 255) a = 255;
10216 dst[xx+yy*stash.params.width] = cast(ubyte)a;
10221 // Blur
10222 if (iblur > 0) {
10223 stash.nscratch = 0;
10224 bdst = &stash.texData[glyph.x0+glyph.y0*stash.params.width];
10225 fons__blur(stash, bdst, gw, gh, stash.params.width, iblur);
10228 stash.dirtyRect.ptr[0] = nvg__min(stash.dirtyRect.ptr[0], glyph.x0);
10229 stash.dirtyRect.ptr[1] = nvg__min(stash.dirtyRect.ptr[1], glyph.y0);
10230 stash.dirtyRect.ptr[2] = nvg__max(stash.dirtyRect.ptr[2], glyph.x1);
10231 stash.dirtyRect.ptr[3] = nvg__max(stash.dirtyRect.ptr[3], glyph.y1);
10233 return glyph;
10236 void fons__getQuad (FONScontext* stash, FONSfont* font, int prevGlyphIndex, FONSglyph* glyph, float size, float scale, float spacing, float* x, float* y, FONSquad* q) nothrow @trusted @nogc {
10237 if (prevGlyphIndex >= 0) {
10238 immutable float adv = fons__tt_getGlyphKernAdvance(&font.font, size, prevGlyphIndex, glyph.index)/**scale*/; //k8: do we really need scale here?
10239 //if (adv != 0) { import core.stdc.stdio; printf("adv=%g (scale=%g; spacing=%g)\n", cast(double)adv, cast(double)scale, cast(double)spacing); }
10240 *x += cast(int)(adv+spacing /*+0.5f*/); //k8: for me, it looks better this way (with non-aa fonts)
10243 // Each glyph has 2px border to allow good interpolation,
10244 // one pixel to prevent leaking, and one to allow good interpolation for rendering.
10245 // Inset the texture region by one pixel for correct interpolation.
10246 immutable float xoff = cast(short)(glyph.xoff+1);
10247 immutable float yoff = cast(short)(glyph.yoff+1);
10248 immutable float x0 = cast(float)(glyph.x0+1);
10249 immutable float y0 = cast(float)(glyph.y0+1);
10250 immutable float x1 = cast(float)(glyph.x1-1);
10251 immutable float y1 = cast(float)(glyph.y1-1);
10253 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10254 immutable float rx = cast(float)cast(int)(*x+xoff);
10255 immutable float ry = cast(float)cast(int)(*y+yoff);
10257 q.x0 = rx;
10258 q.y0 = ry;
10259 q.x1 = rx+x1-x0;
10260 q.y1 = ry+y1-y0;
10262 q.s0 = x0*stash.itw;
10263 q.t0 = y0*stash.ith;
10264 q.s1 = x1*stash.itw;
10265 q.t1 = y1*stash.ith;
10266 } else {
10267 immutable float rx = cast(float)cast(int)(*x+xoff);
10268 immutable float ry = cast(float)cast(int)(*y-yoff);
10270 q.x0 = rx;
10271 q.y0 = ry;
10272 q.x1 = rx+x1-x0;
10273 q.y1 = ry-y1+y0;
10275 q.s0 = x0*stash.itw;
10276 q.t0 = y0*stash.ith;
10277 q.s1 = x1*stash.itw;
10278 q.t1 = y1*stash.ith;
10281 *x += cast(int)(glyph.xadv/10.0f+0.5f);
10284 void fons__flush (FONScontext* stash) nothrow @trusted @nogc {
10285 // Flush texture
10286 if (stash.dirtyRect.ptr[0] < stash.dirtyRect.ptr[2] && stash.dirtyRect.ptr[1] < stash.dirtyRect.ptr[3]) {
10287 if (stash.params.renderUpdate !is null) stash.params.renderUpdate(stash.params.userPtr, stash.dirtyRect.ptr, stash.texData);
10288 // Reset dirty rect
10289 stash.dirtyRect.ptr[0] = stash.params.width;
10290 stash.dirtyRect.ptr[1] = stash.params.height;
10291 stash.dirtyRect.ptr[2] = 0;
10292 stash.dirtyRect.ptr[3] = 0;
10295 debug(nanovega) {
10296 // Flush triangles
10297 if (stash.nverts > 0) {
10298 if (stash.params.renderDraw !is null) stash.params.renderDraw(stash.params.userPtr, stash.verts.ptr, stash.tcoords.ptr, stash.colors.ptr, stash.nverts);
10299 stash.nverts = 0;
10304 debug(nanovega) void fons__vertex (FONScontext* stash, float x, float y, float s, float t, uint c) nothrow @trusted @nogc {
10305 stash.verts.ptr[stash.nverts*2+0] = x;
10306 stash.verts.ptr[stash.nverts*2+1] = y;
10307 stash.tcoords.ptr[stash.nverts*2+0] = s;
10308 stash.tcoords.ptr[stash.nverts*2+1] = t;
10309 stash.colors.ptr[stash.nverts] = c;
10310 ++stash.nverts;
10313 float fons__getVertAlign (FONScontext* stash, FONSfont* font, NVGTextAlign talign, short isize) nothrow @trusted @nogc {
10314 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10315 final switch (talign.vertical) {
10316 case NVGTextAlign.V.Top: return font.ascender*cast(float)isize/10.0f;
10317 case NVGTextAlign.V.Middle: return (font.ascender+font.descender)/2.0f*cast(float)isize/10.0f;
10318 case NVGTextAlign.V.Baseline: return 0.0f;
10319 case NVGTextAlign.V.Bottom: return font.descender*cast(float)isize/10.0f;
10321 } else {
10322 final switch (talign.vertical) {
10323 case NVGTextAlign.V.Top: return -font.ascender*cast(float)isize/10.0f;
10324 case NVGTextAlign.V.Middle: return -(font.ascender+font.descender)/2.0f*cast(float)isize/10.0f;
10325 case NVGTextAlign.V.Baseline: return 0.0f;
10326 case NVGTextAlign.V.Bottom: return -font.descender*cast(float)isize/10.0f;
10329 assert(0);
10332 public bool fonsTextIterInit(T) (FONScontext* stash, FONStextIter!T* iter, float x, float y, const(T)[] str, FONSglyphBitmap bitmapOption) if (isAnyCharType!T) {
10333 if (stash is null || iter is null) return false;
10335 FONSstate* state = fons__getState(stash);
10336 float width;
10338 memset(iter, 0, (*iter).sizeof);
10340 if (stash is null) return false;
10341 if (state.font < 0 || state.font >= stash.nfonts) return false;
10342 iter.font = stash.fonts[state.font];
10343 if (iter.font is null || iter.font.fdata is null) return false;
10345 iter.isize = cast(short)(state.size*10.0f);
10346 iter.iblur = cast(short)state.blur;
10347 iter.scale = fons__tt_getPixelHeightScale(&iter.font.font, cast(float)iter.isize/10.0f);
10349 // Align horizontally
10350 if (state.talign.left) {
10351 // empty
10352 } else if (state.talign.right) {
10353 width = fonsTextBounds(stash, x, y, str, null);
10354 x -= width;
10355 } else if (state.talign.center) {
10356 width = fonsTextBounds(stash, x, y, str, null);
10357 x -= width*0.5f;
10359 // Align vertically.
10360 y += fons__getVertAlign(stash, iter.font, state.talign, iter.isize);
10362 iter.x = iter.nextx = x;
10363 iter.y = iter.nexty = y;
10364 iter.spacing = state.spacing;
10365 if (str.ptr is null) {
10366 static if (is(T == char)) str = "";
10367 else static if (is(T == wchar)) str = ""w;
10368 else static if (is(T == dchar)) str = ""d;
10369 else static assert(0, "wtf?!");
10371 iter.s = str.ptr;
10372 iter.n = str.ptr;
10373 iter.e = str.ptr+str.length;
10374 iter.codepoint = 0;
10375 iter.prevGlyphIndex = -1;
10376 iter.bitmapOption = bitmapOption;
10378 return true;
10381 public bool fonsTextIterGetDummyChar(FT) (FONScontext* stash, FT* iter, FONSquad* quad) nothrow @trusted @nogc if (is(FT : FONStextIter!CT, CT)) {
10382 if (stash is null || iter is null) return false;
10383 // Get glyph and quad
10384 iter.x = iter.nextx;
10385 iter.y = iter.nexty;
10386 FONSglyph* glyph = fons__getGlyph(stash, iter.font, 0xFFFD, iter.isize, iter.iblur, iter.bitmapOption);
10387 if (glyph !is null) {
10388 fons__getQuad(stash, iter.font, iter.prevGlyphIndex, glyph, iter.isize/10.0f, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad);
10389 iter.prevGlyphIndex = glyph.index;
10390 return true;
10391 } else {
10392 iter.prevGlyphIndex = -1;
10393 return false;
10397 public bool fonsTextIterNext(FT) (FONScontext* stash, FT* iter, FONSquad* quad) nothrow @trusted @nogc if (is(FT : FONStextIter!CT, CT)) {
10398 if (stash is null || iter is null) return false;
10399 FONSglyph* glyph = null;
10400 static if (is(FT.CharType == char)) {
10401 const(char)* str = iter.n;
10402 iter.s = iter.n;
10403 if (str is iter.e) return false;
10404 const(char)* e = iter.e;
10405 for (; str !is e; ++str) {
10406 /*if (fons__decutf8(&iter.utf8state, &iter.codepoint, *cast(const(ubyte)*)str)) continue;*/
10407 mixin(DecUtfMixin!("iter.utf8state", "iter.codepoint", "*cast(const(ubyte)*)str"));
10408 if (iter.utf8state) continue;
10409 ++str; // 'cause we'll break anyway
10410 // get glyph and quad
10411 iter.x = iter.nextx;
10412 iter.y = iter.nexty;
10413 glyph = fons__getGlyph(stash, iter.font, iter.codepoint, iter.isize, iter.iblur, iter.bitmapOption);
10414 if (glyph !is null) {
10415 fons__getQuad(stash, iter.font, iter.prevGlyphIndex, glyph, iter.isize/10.0f, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad);
10416 iter.prevGlyphIndex = glyph.index;
10417 } else {
10418 iter.prevGlyphIndex = -1;
10420 break;
10422 iter.n = str;
10423 } else {
10424 const(FT.CharType)* str = iter.n;
10425 iter.s = iter.n;
10426 if (str is iter.e) return false;
10427 iter.codepoint = cast(uint)(*str++);
10428 if (iter.codepoint > dchar.max) iter.codepoint = 0xFFFD;
10429 // Get glyph and quad
10430 iter.x = iter.nextx;
10431 iter.y = iter.nexty;
10432 glyph = fons__getGlyph(stash, iter.font, iter.codepoint, iter.isize, iter.iblur, iter.bitmapOption);
10433 if (glyph !is null) {
10434 fons__getQuad(stash, iter.font, iter.prevGlyphIndex, glyph, iter.isize/10.0f, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad);
10435 iter.prevGlyphIndex = glyph.index;
10436 } else {
10437 iter.prevGlyphIndex = -1;
10439 iter.n = str;
10441 return true;
10444 debug(nanovega) public void fonsDrawDebug (FONScontext* stash, float x, float y) nothrow @trusted @nogc {
10445 int i;
10446 int w = stash.params.width;
10447 int h = stash.params.height;
10448 float u = (w == 0 ? 0 : 1.0f/w);
10449 float v = (h == 0 ? 0 : 1.0f/h);
10451 if (stash.nverts+6+6 > FONS_VERTEX_COUNT) fons__flush(stash);
10453 // Draw background
10454 fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
10455 fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);
10456 fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff);
10458 fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
10459 fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff);
10460 fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);
10462 // Draw texture
10463 fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);
10464 fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);
10465 fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff);
10467 fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);
10468 fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff);
10469 fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);
10471 // Drawbug draw atlas
10472 for (i = 0; i < stash.atlas.nnodes; i++) {
10473 FONSatlasNode* n = &stash.atlas.nodes[i];
10475 if (stash.nverts+6 > FONS_VERTEX_COUNT)
10476 fons__flush(stash);
10478 fons__vertex(stash, x+n.x+0, y+n.y+0, u, v, 0xc00000ff);
10479 fons__vertex(stash, x+n.x+n.width, y+n.y+1, u, v, 0xc00000ff);
10480 fons__vertex(stash, x+n.x+n.width, y+n.y+0, u, v, 0xc00000ff);
10482 fons__vertex(stash, x+n.x+0, y+n.y+0, u, v, 0xc00000ff);
10483 fons__vertex(stash, x+n.x+0, y+n.y+1, u, v, 0xc00000ff);
10484 fons__vertex(stash, x+n.x+n.width, y+n.y+1, u, v, 0xc00000ff);
10487 fons__flush(stash);
10490 public struct FonsTextBoundsIterator {
10491 private:
10492 FONScontext* stash;
10493 FONSstate* state;
10494 uint codepoint;
10495 uint utf8state = 0;
10496 FONSquad q;
10497 FONSglyph* glyph = null;
10498 int prevGlyphIndex = -1;
10499 short isize, iblur;
10500 float scale;
10501 FONSfont* font;
10502 float startx, x, y;
10503 float minx, miny, maxx, maxy;
10505 public:
10506 this (FONScontext* astash, float ax, float ay) nothrow @trusted @nogc { reset(astash, ax, ay); }
10508 void reset (FONScontext* astash, float ax, float ay) nothrow @trusted @nogc {
10509 this = this.init;
10510 if (astash is null) return;
10511 stash = astash;
10512 state = fons__getState(stash);
10513 if (state is null) { stash = null; return; } // alas
10515 x = ax;
10516 y = ay;
10518 isize = cast(short)(state.size*10.0f);
10519 iblur = cast(short)state.blur;
10521 if (state.font < 0 || state.font >= stash.nfonts) { stash = null; return; }
10522 font = stash.fonts[state.font];
10523 if (font is null || font.fdata is null) { stash = null; return; }
10525 scale = fons__tt_getPixelHeightScale(&font.font, cast(float)isize/10.0f);
10527 // align vertically
10528 y += fons__getVertAlign(stash, font, state.talign, isize);
10530 minx = maxx = x;
10531 miny = maxy = y;
10532 startx = x;
10533 //assert(prevGlyphIndex == -1);
10536 public:
10537 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (state !is null); }
10539 void put(T) (const(T)[] str...) nothrow @trusted @nogc if (isAnyCharType!T) {
10540 enum DoCodePointMixin = q{
10541 glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
10542 if (glyph !is null) {
10543 fons__getQuad(stash, font, prevGlyphIndex, glyph, isize/10.0f, scale, state.spacing, &x, &y, &q);
10544 if (q.x0 < minx) minx = q.x0;
10545 if (q.x1 > maxx) maxx = q.x1;
10546 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10547 if (q.y0 < miny) miny = q.y0;
10548 if (q.y1 > maxy) maxy = q.y1;
10549 } else {
10550 if (q.y1 < miny) miny = q.y1;
10551 if (q.y0 > maxy) maxy = q.y0;
10553 prevGlyphIndex = glyph.index;
10554 } else {
10555 prevGlyphIndex = -1;
10559 if (state is null) return; // alas
10560 static if (is(T == char)) {
10561 foreach (char ch; str) {
10562 mixin(DecUtfMixin!("utf8state", "codepoint", "cast(ubyte)ch"));
10563 if (utf8state) continue; // full char is not collected yet
10564 mixin(DoCodePointMixin);
10566 } else {
10567 if (str.length == 0) return;
10568 if (utf8state) {
10569 utf8state = 0;
10570 codepoint = 0xFFFD;
10571 mixin(DoCodePointMixin);
10573 foreach (T dch; str) {
10574 static if (is(T == dchar)) {
10575 if (dch > dchar.max) dch = 0xFFFD;
10577 codepoint = cast(uint)dch;
10578 mixin(DoCodePointMixin);
10583 // return current advance
10584 @property float advance () const pure nothrow @safe @nogc { pragma(inline, true); return (state !is null ? x-startx : 0); }
10586 void getBounds (ref float[4] bounds) const pure nothrow @safe @nogc {
10587 if (state is null) { bounds[] = 0; return; }
10588 float lminx = minx, lmaxx = maxx;
10589 // align horizontally
10590 if (state.talign.left) {
10591 // empty
10592 } else if (state.talign.right) {
10593 float ca = advance;
10594 lminx -= ca;
10595 lmaxx -= ca;
10596 } else if (state.talign.center) {
10597 float ca = advance*0.5f;
10598 lminx -= ca;
10599 lmaxx -= ca;
10601 bounds[0] = lminx;
10602 bounds[1] = miny;
10603 bounds[2] = lmaxx;
10604 bounds[3] = maxy;
10607 // Returns current horizontal text bounds.
10608 void getHBounds (out float xmin, out float xmax) nothrow @trusted @nogc {
10609 if (state !is null) {
10610 float lminx = minx, lmaxx = maxx;
10611 // align horizontally
10612 if (state.talign.left) {
10613 // empty
10614 } else if (state.talign.right) {
10615 float ca = advance;
10616 lminx -= ca;
10617 lmaxx -= ca;
10618 } else if (state.talign.center) {
10619 float ca = advance*0.5f;
10620 lminx -= ca;
10621 lmaxx -= ca;
10623 xmin = lminx;
10624 xmax = lmaxx;
10628 // Returns current vertical text bounds.
10629 void getVBounds (out float ymin, out float ymax) nothrow @trusted @nogc {
10630 if (state !is null) {
10631 ymin = miny;
10632 ymax = maxy;
10637 public float fonsTextBounds(T) (FONScontext* stash, float x, float y, const(T)[] str, float[] bounds) nothrow @trusted @nogc
10638 if (isAnyCharType!T)
10640 FONSstate* state = fons__getState(stash);
10641 uint codepoint;
10642 uint utf8state = 0;
10643 FONSquad q;
10644 FONSglyph* glyph = null;
10645 int prevGlyphIndex = -1;
10646 short isize = cast(short)(state.size*10.0f);
10647 short iblur = cast(short)state.blur;
10648 float scale;
10649 FONSfont* font;
10650 float startx, advance;
10651 float minx, miny, maxx, maxy;
10653 if (stash is null) return 0;
10654 if (state.font < 0 || state.font >= stash.nfonts) return 0;
10655 font = stash.fonts[state.font];
10656 if (font is null || font.fdata is null) return 0;
10658 scale = fons__tt_getPixelHeightScale(&font.font, cast(float)isize/10.0f);
10660 // Align vertically.
10661 y += fons__getVertAlign(stash, font, state.talign, isize);
10663 minx = maxx = x;
10664 miny = maxy = y;
10665 startx = x;
10667 static if (is(T == char)) {
10668 foreach (char ch; str) {
10669 //if (fons__decutf8(&utf8state, &codepoint, *cast(const(ubyte)*)str)) continue;
10670 mixin(DecUtfMixin!("utf8state", "codepoint", "(cast(ubyte)ch)"));
10671 if (utf8state) continue;
10672 glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
10673 if (glyph !is null) {
10674 fons__getQuad(stash, font, prevGlyphIndex, glyph, isize/10.0f, scale, state.spacing, &x, &y, &q);
10675 if (q.x0 < minx) minx = q.x0;
10676 if (q.x1 > maxx) maxx = q.x1;
10677 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10678 if (q.y0 < miny) miny = q.y0;
10679 if (q.y1 > maxy) maxy = q.y1;
10680 } else {
10681 if (q.y1 < miny) miny = q.y1;
10682 if (q.y0 > maxy) maxy = q.y0;
10684 prevGlyphIndex = glyph.index;
10685 } else {
10686 prevGlyphIndex = -1;
10689 } else {
10690 foreach (T ch; str) {
10691 static if (is(T == dchar)) {
10692 if (ch > dchar.max) ch = 0xFFFD;
10694 codepoint = cast(uint)ch;
10695 glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
10696 if (glyph !is null) {
10697 fons__getQuad(stash, font, prevGlyphIndex, glyph, isize/10.0f, scale, state.spacing, &x, &y, &q);
10698 if (q.x0 < minx) minx = q.x0;
10699 if (q.x1 > maxx) maxx = q.x1;
10700 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10701 if (q.y0 < miny) miny = q.y0;
10702 if (q.y1 > maxy) maxy = q.y1;
10703 } else {
10704 if (q.y1 < miny) miny = q.y1;
10705 if (q.y0 > maxy) maxy = q.y0;
10707 prevGlyphIndex = glyph.index;
10708 } else {
10709 prevGlyphIndex = -1;
10714 advance = x-startx;
10716 // Align horizontally
10717 if (state.talign.left) {
10718 // empty
10719 } else if (state.talign.right) {
10720 minx -= advance;
10721 maxx -= advance;
10722 } else if (state.talign.center) {
10723 minx -= advance*0.5f;
10724 maxx -= advance*0.5f;
10727 if (bounds.length) {
10728 if (bounds.length > 0) bounds.ptr[0] = minx;
10729 if (bounds.length > 1) bounds.ptr[1] = miny;
10730 if (bounds.length > 2) bounds.ptr[2] = maxx;
10731 if (bounds.length > 3) bounds.ptr[3] = maxy;
10734 return advance;
10737 public void fonsVertMetrics (FONScontext* stash, float* ascender, float* descender, float* lineh) nothrow @trusted @nogc {
10738 FONSfont* font;
10739 FONSstate* state = fons__getState(stash);
10740 short isize;
10742 if (stash is null) return;
10743 if (state.font < 0 || state.font >= stash.nfonts) return;
10744 font = stash.fonts[state.font];
10745 isize = cast(short)(state.size*10.0f);
10746 if (font is null || font.fdata is null) return;
10748 if (ascender) *ascender = font.ascender*isize/10.0f;
10749 if (descender) *descender = font.descender*isize/10.0f;
10750 if (lineh) *lineh = font.lineh*isize/10.0f;
10753 public void fonsLineBounds (FONScontext* stash, float y, float* minyp, float* maxyp) nothrow @trusted @nogc {
10754 FONSfont* font;
10755 FONSstate* state = fons__getState(stash);
10756 short isize;
10758 if (minyp !is null) *minyp = 0;
10759 if (maxyp !is null) *maxyp = 0;
10761 if (stash is null) return;
10762 if (state.font < 0 || state.font >= stash.nfonts) return;
10763 font = stash.fonts[state.font];
10764 isize = cast(short)(state.size*10.0f);
10765 if (font is null || font.fdata is null) return;
10767 y += fons__getVertAlign(stash, font, state.talign, isize);
10769 if (stash.params.flags&FONS_ZERO_TOPLEFT) {
10770 immutable float miny = y-font.ascender*cast(float)isize/10.0f;
10771 immutable float maxy = miny+font.lineh*isize/10.0f;
10772 if (minyp !is null) *minyp = miny;
10773 if (maxyp !is null) *maxyp = maxy;
10774 } else {
10775 immutable float maxy = y+font.descender*cast(float)isize/10.0f;
10776 immutable float miny = maxy-font.lineh*isize/10.0f;
10777 if (minyp !is null) *minyp = miny;
10778 if (maxyp !is null) *maxyp = maxy;
10782 public const(ubyte)* fonsGetTextureData (FONScontext* stash, int* width, int* height) nothrow @trusted @nogc {
10783 if (width !is null) *width = stash.params.width;
10784 if (height !is null) *height = stash.params.height;
10785 return stash.texData;
10788 public int fonsValidateTexture (FONScontext* stash, int* dirty) nothrow @trusted @nogc {
10789 if (stash.dirtyRect.ptr[0] < stash.dirtyRect.ptr[2] && stash.dirtyRect.ptr[1] < stash.dirtyRect.ptr[3]) {
10790 dirty[0] = stash.dirtyRect.ptr[0];
10791 dirty[1] = stash.dirtyRect.ptr[1];
10792 dirty[2] = stash.dirtyRect.ptr[2];
10793 dirty[3] = stash.dirtyRect.ptr[3];
10794 // Reset dirty rect
10795 stash.dirtyRect.ptr[0] = stash.params.width;
10796 stash.dirtyRect.ptr[1] = stash.params.height;
10797 stash.dirtyRect.ptr[2] = 0;
10798 stash.dirtyRect.ptr[3] = 0;
10799 return 1;
10801 return 0;
10804 public void fonsDeleteInternal (FONScontext* stash) nothrow @trusted @nogc {
10805 if (stash is null) return;
10807 if (stash.params.renderDelete !is null) stash.params.renderDelete(stash.params.userPtr);
10809 foreach (int i; 0..stash.nfonts) fons__freeFont(stash.fonts[i]);
10811 if (stash.atlas) fons__deleteAtlas(stash.atlas);
10812 if (stash.fonts) free(stash.fonts);
10813 if (stash.texData) free(stash.texData);
10814 if (stash.scratch) free(stash.scratch);
10815 if (stash.hashidx) free(stash.hashidx);
10816 free(stash);
10819 public void fonsSetErrorCallback (FONScontext* stash, void function (void* uptr, int error, int val) nothrow @trusted @nogc callback, void* uptr) nothrow @trusted @nogc {
10820 if (stash is null) return;
10821 stash.handleError = callback;
10822 stash.errorUptr = uptr;
10825 public void fonsGetAtlasSize (FONScontext* stash, int* width, int* height) nothrow @trusted @nogc {
10826 if (stash is null) return;
10827 *width = stash.params.width;
10828 *height = stash.params.height;
10831 public int fonsExpandAtlas (FONScontext* stash, int width, int height) nothrow @trusted @nogc {
10832 int i, maxy = 0;
10833 ubyte* data = null;
10834 if (stash is null) return 0;
10836 width = nvg__max(width, stash.params.width);
10837 height = nvg__max(height, stash.params.height);
10839 if (width == stash.params.width && height == stash.params.height) return 1;
10841 // Flush pending glyphs.
10842 fons__flush(stash);
10844 // Create new texture
10845 if (stash.params.renderResize !is null) {
10846 if (stash.params.renderResize(stash.params.userPtr, width, height) == 0) return 0;
10848 // Copy old texture data over.
10849 data = cast(ubyte*)malloc(width*height);
10850 if (data is null) return 0;
10851 for (i = 0; i < stash.params.height; i++) {
10852 ubyte* dst = &data[i*width];
10853 ubyte* src = &stash.texData[i*stash.params.width];
10854 memcpy(dst, src, stash.params.width);
10855 if (width > stash.params.width)
10856 memset(dst+stash.params.width, 0, width-stash.params.width);
10858 if (height > stash.params.height) memset(&data[stash.params.height*width], 0, (height-stash.params.height)*width);
10860 free(stash.texData);
10861 stash.texData = data;
10863 // Increase atlas size
10864 fons__atlasExpand(stash.atlas, width, height);
10866 // Add existing data as dirty.
10867 for (i = 0; i < stash.atlas.nnodes; i++) maxy = nvg__max(maxy, stash.atlas.nodes[i].y);
10868 stash.dirtyRect.ptr[0] = 0;
10869 stash.dirtyRect.ptr[1] = 0;
10870 stash.dirtyRect.ptr[2] = stash.params.width;
10871 stash.dirtyRect.ptr[3] = maxy;
10873 stash.params.width = width;
10874 stash.params.height = height;
10875 stash.itw = 1.0f/stash.params.width;
10876 stash.ith = 1.0f/stash.params.height;
10878 return 1;
10881 public bool fonsResetAtlas (FONScontext* stash, int width, int height) nothrow @trusted @nogc {
10882 if (stash is null) return false;
10884 // Flush pending glyphs.
10885 fons__flush(stash);
10887 // Create new texture
10888 if (stash.params.renderResize !is null) {
10889 if (stash.params.renderResize(stash.params.userPtr, width, height) == 0) return false;
10892 // Reset atlas
10893 fons__atlasReset(stash.atlas, width, height);
10895 // Clear texture data.
10896 stash.texData = cast(ubyte*)realloc(stash.texData, width*height);
10897 if (stash.texData is null) return 0;
10898 memset(stash.texData, 0, width*height);
10900 // Reset dirty rect
10901 stash.dirtyRect.ptr[0] = width;
10902 stash.dirtyRect.ptr[1] = height;
10903 stash.dirtyRect.ptr[2] = 0;
10904 stash.dirtyRect.ptr[3] = 0;
10906 // Reset cached glyphs
10907 foreach (FONSfont* font; stash.fonts[0..stash.nfonts]) {
10908 if (font !is null) {
10909 font.nglyphs = 0;
10910 font.lut.ptr[0..FONS_HASH_LUT_SIZE] = -1;
10914 stash.params.width = width;
10915 stash.params.height = height;
10916 stash.itw = 1.0f/stash.params.width;
10917 stash.ith = 1.0f/stash.params.height;
10919 // Add white rect at 0, 0 for debug drawing.
10920 fons__addWhiteRect(stash, 2, 2);
10922 return true;
10926 // ////////////////////////////////////////////////////////////////////////// //
10927 // backgl
10928 // ////////////////////////////////////////////////////////////////////////// //
10929 import core.stdc.stdlib : malloc, realloc, free;
10930 import core.stdc.string : memcpy, memset;
10932 //import arsd.simpledisplay;
10933 version(nanovg_builtin_opengl_bindings) { import arsd.simpledisplay; } else { import iv.glbinds; }
10935 private:
10936 // sdpy is missing that yet
10937 static if (!is(typeof(GL_STENCIL_BUFFER_BIT))) enum uint GL_STENCIL_BUFFER_BIT = 0x00000400;
10940 // OpenGL API missing from simpledisplay
10941 private extern(System) nothrow @nogc {
10942 alias GLvoid = void;
10943 alias GLboolean = ubyte;
10944 alias GLuint = uint;
10945 alias GLenum = uint;
10946 alias GLchar = char;
10947 alias GLsizei = int;
10948 alias GLfloat = float;
10949 alias GLsizeiptr = ptrdiff_t;
10951 enum uint GL_STENCIL_BUFFER_BIT = 0x00000400;
10953 enum uint GL_INVALID_ENUM = 0x0500;
10955 enum uint GL_ZERO = 0;
10956 enum uint GL_ONE = 1;
10958 enum uint GL_FLOAT = 0x1406;
10960 enum uint GL_STREAM_DRAW = 0x88E0;
10962 enum uint GL_CCW = 0x0901;
10964 enum uint GL_STENCIL_TEST = 0x0B90;
10965 enum uint GL_SCISSOR_TEST = 0x0C11;
10967 enum uint GL_EQUAL = 0x0202;
10968 enum uint GL_NOTEQUAL = 0x0205;
10970 enum uint GL_ALWAYS = 0x0207;
10971 enum uint GL_KEEP = 0x1E00;
10973 enum uint GL_INCR = 0x1E02;
10975 enum uint GL_INCR_WRAP = 0x8507;
10976 enum uint GL_DECR_WRAP = 0x8508;
10978 enum uint GL_CULL_FACE = 0x0B44;
10979 enum uint GL_BACK = 0x0405;
10981 enum uint GL_FRAGMENT_SHADER = 0x8B30;
10982 enum uint GL_VERTEX_SHADER = 0x8B31;
10984 enum uint GL_COMPILE_STATUS = 0x8B81;
10985 enum uint GL_LINK_STATUS = 0x8B82;
10987 enum uint GL_UNPACK_ALIGNMENT = 0x0CF5;
10988 enum uint GL_UNPACK_ROW_LENGTH = 0x0CF2;
10989 enum uint GL_UNPACK_SKIP_PIXELS = 0x0CF4;
10990 enum uint GL_UNPACK_SKIP_ROWS = 0x0CF3;
10992 enum uint GL_GENERATE_MIPMAP = 0x8191;
10993 enum uint GL_LINEAR_MIPMAP_LINEAR = 0x2703;
10995 enum uint GL_RED = 0x1903;
10997 enum uint GL_TEXTURE0 = 0x84C0U;
10998 enum uint GL_TEXTURE1 = 0x84C1U;
11000 enum uint GL_ARRAY_BUFFER = 0x8892;
11002 enum uint GL_SRC_COLOR = 0x0300;
11003 enum uint GL_ONE_MINUS_SRC_COLOR = 0x0301;
11004 enum uint GL_SRC_ALPHA = 0x0302;
11005 enum uint GL_ONE_MINUS_SRC_ALPHA = 0x0303;
11006 enum uint GL_DST_ALPHA = 0x0304;
11007 enum uint GL_ONE_MINUS_DST_ALPHA = 0x0305;
11008 enum uint GL_DST_COLOR = 0x0306;
11009 enum uint GL_ONE_MINUS_DST_COLOR = 0x0307;
11010 enum uint GL_SRC_ALPHA_SATURATE = 0x0308;
11012 enum uint GL_INVERT = 0x150AU;
11014 enum uint GL_DEPTH_STENCIL = 0x84F9U;
11015 enum uint GL_UNSIGNED_INT_24_8 = 0x84FAU;
11017 enum uint GL_FRAMEBUFFER = 0x8D40U;
11018 enum uint GL_COLOR_ATTACHMENT0 = 0x8CE0U;
11019 enum uint GL_DEPTH_STENCIL_ATTACHMENT = 0x821AU;
11021 enum uint GL_FRAMEBUFFER_COMPLETE = 0x8CD5U;
11022 enum uint GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6U;
11023 enum uint GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7U;
11024 enum uint GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9U;
11025 enum uint GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDDU;
11027 enum uint GL_COLOR_LOGIC_OP = 0x0BF2U;
11028 enum uint GL_CLEAR = 0x1500U;
11029 enum uint GL_COPY = 0x1503U;
11030 enum uint GL_XOR = 0x1506U;
11033 version(Windows) {
11034 private void* kglLoad (const(char)* name) {
11035 void* res = glGetProcAddress(name);
11036 if (res is null) {
11037 import core.sys.windows.windef, core.sys.windows.winbase;
11038 static HINSTANCE dll = null;
11039 if (dll is null) {
11040 dll = LoadLibraryA("opengl32.dll");
11041 if (dll is null) return null; // <32, but idc
11042 return GetProcAddress(dll, name);
11046 } else {
11047 alias kglLoad = glGetProcAddress;
11051 alias glbfn_glStencilMask = void function(GLuint);
11052 __gshared glbfn_glStencilMask glStencilMask_NVGLZ; alias glStencilMask = glStencilMask_NVGLZ;
11053 alias glbfn_glStencilFunc = void function(GLenum, GLint, GLuint);
11054 __gshared glbfn_glStencilFunc glStencilFunc_NVGLZ; alias glStencilFunc = glStencilFunc_NVGLZ;
11055 alias glbfn_glGetShaderInfoLog = void function(GLuint, GLsizei, GLsizei*, GLchar*);
11056 __gshared glbfn_glGetShaderInfoLog glGetShaderInfoLog_NVGLZ; alias glGetShaderInfoLog = glGetShaderInfoLog_NVGLZ;
11057 alias glbfn_glGetProgramInfoLog = void function(GLuint, GLsizei, GLsizei*, GLchar*);
11058 __gshared glbfn_glGetProgramInfoLog glGetProgramInfoLog_NVGLZ; alias glGetProgramInfoLog = glGetProgramInfoLog_NVGLZ;
11059 alias glbfn_glCreateProgram = GLuint function();
11060 __gshared glbfn_glCreateProgram glCreateProgram_NVGLZ; alias glCreateProgram = glCreateProgram_NVGLZ;
11061 alias glbfn_glCreateShader = GLuint function(GLenum);
11062 __gshared glbfn_glCreateShader glCreateShader_NVGLZ; alias glCreateShader = glCreateShader_NVGLZ;
11063 alias glbfn_glShaderSource = void function(GLuint, GLsizei, const(GLchar*)*, const(GLint)*);
11064 __gshared glbfn_glShaderSource glShaderSource_NVGLZ; alias glShaderSource = glShaderSource_NVGLZ;
11065 alias glbfn_glCompileShader = void function(GLuint);
11066 __gshared glbfn_glCompileShader glCompileShader_NVGLZ; alias glCompileShader = glCompileShader_NVGLZ;
11067 alias glbfn_glGetShaderiv = void function(GLuint, GLenum, GLint*);
11068 __gshared glbfn_glGetShaderiv glGetShaderiv_NVGLZ; alias glGetShaderiv = glGetShaderiv_NVGLZ;
11069 alias glbfn_glAttachShader = void function(GLuint, GLuint);
11070 __gshared glbfn_glAttachShader glAttachShader_NVGLZ; alias glAttachShader = glAttachShader_NVGLZ;
11071 alias glbfn_glBindAttribLocation = void function(GLuint, GLuint, const(GLchar)*);
11072 __gshared glbfn_glBindAttribLocation glBindAttribLocation_NVGLZ; alias glBindAttribLocation = glBindAttribLocation_NVGLZ;
11073 alias glbfn_glLinkProgram = void function(GLuint);
11074 __gshared glbfn_glLinkProgram glLinkProgram_NVGLZ; alias glLinkProgram = glLinkProgram_NVGLZ;
11075 alias glbfn_glGetProgramiv = void function(GLuint, GLenum, GLint*);
11076 __gshared glbfn_glGetProgramiv glGetProgramiv_NVGLZ; alias glGetProgramiv = glGetProgramiv_NVGLZ;
11077 alias glbfn_glDeleteProgram = void function(GLuint);
11078 __gshared glbfn_glDeleteProgram glDeleteProgram_NVGLZ; alias glDeleteProgram = glDeleteProgram_NVGLZ;
11079 alias glbfn_glDeleteShader = void function(GLuint);
11080 __gshared glbfn_glDeleteShader glDeleteShader_NVGLZ; alias glDeleteShader = glDeleteShader_NVGLZ;
11081 alias glbfn_glGetUniformLocation = GLint function(GLuint, const(GLchar)*);
11082 __gshared glbfn_glGetUniformLocation glGetUniformLocation_NVGLZ; alias glGetUniformLocation = glGetUniformLocation_NVGLZ;
11083 alias glbfn_glGenBuffers = void function(GLsizei, GLuint*);
11084 __gshared glbfn_glGenBuffers glGenBuffers_NVGLZ; alias glGenBuffers = glGenBuffers_NVGLZ;
11085 alias glbfn_glPixelStorei = void function(GLenum, GLint);
11086 __gshared glbfn_glPixelStorei glPixelStorei_NVGLZ; alias glPixelStorei = glPixelStorei_NVGLZ;
11087 alias glbfn_glUniform4fv = void function(GLint, GLsizei, const(GLfloat)*);
11088 __gshared glbfn_glUniform4fv glUniform4fv_NVGLZ; alias glUniform4fv = glUniform4fv_NVGLZ;
11089 alias glbfn_glColorMask = void function(GLboolean, GLboolean, GLboolean, GLboolean);
11090 __gshared glbfn_glColorMask glColorMask_NVGLZ; alias glColorMask = glColorMask_NVGLZ;
11091 alias glbfn_glStencilOpSeparate = void function(GLenum, GLenum, GLenum, GLenum);
11092 __gshared glbfn_glStencilOpSeparate glStencilOpSeparate_NVGLZ; alias glStencilOpSeparate = glStencilOpSeparate_NVGLZ;
11093 alias glbfn_glDrawArrays = void function(GLenum, GLint, GLsizei);
11094 __gshared glbfn_glDrawArrays glDrawArrays_NVGLZ; alias glDrawArrays = glDrawArrays_NVGLZ;
11095 alias glbfn_glStencilOp = void function(GLenum, GLenum, GLenum);
11096 __gshared glbfn_glStencilOp glStencilOp_NVGLZ; alias glStencilOp = glStencilOp_NVGLZ;
11097 alias glbfn_glUseProgram = void function(GLuint);
11098 __gshared glbfn_glUseProgram glUseProgram_NVGLZ; alias glUseProgram = glUseProgram_NVGLZ;
11099 alias glbfn_glCullFace = void function(GLenum);
11100 __gshared glbfn_glCullFace glCullFace_NVGLZ; alias glCullFace = glCullFace_NVGLZ;
11101 alias glbfn_glFrontFace = void function(GLenum);
11102 __gshared glbfn_glFrontFace glFrontFace_NVGLZ; alias glFrontFace = glFrontFace_NVGLZ;
11103 alias glbfn_glActiveTexture = void function(GLenum);
11104 __gshared glbfn_glActiveTexture glActiveTexture_NVGLZ; alias glActiveTexture = glActiveTexture_NVGLZ;
11105 alias glbfn_glBindBuffer = void function(GLenum, GLuint);
11106 __gshared glbfn_glBindBuffer glBindBuffer_NVGLZ; alias glBindBuffer = glBindBuffer_NVGLZ;
11107 alias glbfn_glBufferData = void function(GLenum, GLsizeiptr, const(void)*, GLenum);
11108 __gshared glbfn_glBufferData glBufferData_NVGLZ; alias glBufferData = glBufferData_NVGLZ;
11109 alias glbfn_glEnableVertexAttribArray = void function(GLuint);
11110 __gshared glbfn_glEnableVertexAttribArray glEnableVertexAttribArray_NVGLZ; alias glEnableVertexAttribArray = glEnableVertexAttribArray_NVGLZ;
11111 alias glbfn_glVertexAttribPointer = void function(GLuint, GLint, GLenum, GLboolean, GLsizei, const(void)*);
11112 __gshared glbfn_glVertexAttribPointer glVertexAttribPointer_NVGLZ; alias glVertexAttribPointer = glVertexAttribPointer_NVGLZ;
11113 alias glbfn_glUniform1i = void function(GLint, GLint);
11114 __gshared glbfn_glUniform1i glUniform1i_NVGLZ; alias glUniform1i = glUniform1i_NVGLZ;
11115 alias glbfn_glUniform2fv = void function(GLint, GLsizei, const(GLfloat)*);
11116 __gshared glbfn_glUniform2fv glUniform2fv_NVGLZ; alias glUniform2fv = glUniform2fv_NVGLZ;
11117 alias glbfn_glDisableVertexAttribArray = void function(GLuint);
11118 __gshared glbfn_glDisableVertexAttribArray glDisableVertexAttribArray_NVGLZ; alias glDisableVertexAttribArray = glDisableVertexAttribArray_NVGLZ;
11119 alias glbfn_glDeleteBuffers = void function(GLsizei, const(GLuint)*);
11120 __gshared glbfn_glDeleteBuffers glDeleteBuffers_NVGLZ; alias glDeleteBuffers = glDeleteBuffers_NVGLZ;
11121 alias glbfn_glBlendFuncSeparate = void function(GLenum, GLenum, GLenum, GLenum);
11122 __gshared glbfn_glBlendFuncSeparate glBlendFuncSeparate_NVGLZ; alias glBlendFuncSeparate = glBlendFuncSeparate_NVGLZ;
11124 alias glbfn_glLogicOp = void function (GLenum opcode);
11125 __gshared glbfn_glLogicOp glLogicOp_NVGLZ; alias glLogicOp = glLogicOp_NVGLZ;
11126 alias glbfn_glFramebufferTexture2D = void function (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
11127 __gshared glbfn_glFramebufferTexture2D glFramebufferTexture2D_NVGLZ; alias glFramebufferTexture2D = glFramebufferTexture2D_NVGLZ;
11128 alias glbfn_glDeleteFramebuffers = void function (GLsizei n, const(GLuint)* framebuffers);
11129 __gshared glbfn_glDeleteFramebuffers glDeleteFramebuffers_NVGLZ; alias glDeleteFramebuffers = glDeleteFramebuffers_NVGLZ;
11130 alias glbfn_glGenFramebuffers = void function (GLsizei n, GLuint* framebuffers);
11131 __gshared glbfn_glGenFramebuffers glGenFramebuffers_NVGLZ; alias glGenFramebuffers = glGenFramebuffers_NVGLZ;
11132 alias glbfn_glCheckFramebufferStatus = GLenum function (GLenum target);
11133 __gshared glbfn_glCheckFramebufferStatus glCheckFramebufferStatus_NVGLZ; alias glCheckFramebufferStatus = glCheckFramebufferStatus_NVGLZ;
11134 alias glbfn_glBindFramebuffer = void function (GLenum target, GLuint framebuffer);
11135 __gshared glbfn_glBindFramebuffer glBindFramebuffer_NVGLZ; alias glBindFramebuffer = glBindFramebuffer_NVGLZ;
11137 private void nanovgInitOpenGL () {
11138 __gshared bool initialized = false;
11139 if (initialized) return;
11140 glStencilMask_NVGLZ = cast(glbfn_glStencilMask)glbindGetProcAddress(`glStencilMask`);
11141 if (glStencilMask_NVGLZ is null) assert(0, `OpenGL function 'glStencilMask' not found!`);
11142 glStencilFunc_NVGLZ = cast(glbfn_glStencilFunc)glbindGetProcAddress(`glStencilFunc`);
11143 if (glStencilFunc_NVGLZ is null) assert(0, `OpenGL function 'glStencilFunc' not found!`);
11144 glGetShaderInfoLog_NVGLZ = cast(glbfn_glGetShaderInfoLog)glbindGetProcAddress(`glGetShaderInfoLog`);
11145 if (glGetShaderInfoLog_NVGLZ is null) assert(0, `OpenGL function 'glGetShaderInfoLog' not found!`);
11146 glGetProgramInfoLog_NVGLZ = cast(glbfn_glGetProgramInfoLog)glbindGetProcAddress(`glGetProgramInfoLog`);
11147 if (glGetProgramInfoLog_NVGLZ is null) assert(0, `OpenGL function 'glGetProgramInfoLog' not found!`);
11148 glCreateProgram_NVGLZ = cast(glbfn_glCreateProgram)glbindGetProcAddress(`glCreateProgram`);
11149 if (glCreateProgram_NVGLZ is null) assert(0, `OpenGL function 'glCreateProgram' not found!`);
11150 glCreateShader_NVGLZ = cast(glbfn_glCreateShader)glbindGetProcAddress(`glCreateShader`);
11151 if (glCreateShader_NVGLZ is null) assert(0, `OpenGL function 'glCreateShader' not found!`);
11152 glShaderSource_NVGLZ = cast(glbfn_glShaderSource)glbindGetProcAddress(`glShaderSource`);
11153 if (glShaderSource_NVGLZ is null) assert(0, `OpenGL function 'glShaderSource' not found!`);
11154 glCompileShader_NVGLZ = cast(glbfn_glCompileShader)glbindGetProcAddress(`glCompileShader`);
11155 if (glCompileShader_NVGLZ is null) assert(0, `OpenGL function 'glCompileShader' not found!`);
11156 glGetShaderiv_NVGLZ = cast(glbfn_glGetShaderiv)glbindGetProcAddress(`glGetShaderiv`);
11157 if (glGetShaderiv_NVGLZ is null) assert(0, `OpenGL function 'glGetShaderiv' not found!`);
11158 glAttachShader_NVGLZ = cast(glbfn_glAttachShader)glbindGetProcAddress(`glAttachShader`);
11159 if (glAttachShader_NVGLZ is null) assert(0, `OpenGL function 'glAttachShader' not found!`);
11160 glBindAttribLocation_NVGLZ = cast(glbfn_glBindAttribLocation)glbindGetProcAddress(`glBindAttribLocation`);
11161 if (glBindAttribLocation_NVGLZ is null) assert(0, `OpenGL function 'glBindAttribLocation' not found!`);
11162 glLinkProgram_NVGLZ = cast(glbfn_glLinkProgram)glbindGetProcAddress(`glLinkProgram`);
11163 if (glLinkProgram_NVGLZ is null) assert(0, `OpenGL function 'glLinkProgram' not found!`);
11164 glGetProgramiv_NVGLZ = cast(glbfn_glGetProgramiv)glbindGetProcAddress(`glGetProgramiv`);
11165 if (glGetProgramiv_NVGLZ is null) assert(0, `OpenGL function 'glGetProgramiv' not found!`);
11166 glDeleteProgram_NVGLZ = cast(glbfn_glDeleteProgram)glbindGetProcAddress(`glDeleteProgram`);
11167 if (glDeleteProgram_NVGLZ is null) assert(0, `OpenGL function 'glDeleteProgram' not found!`);
11168 glDeleteShader_NVGLZ = cast(glbfn_glDeleteShader)glbindGetProcAddress(`glDeleteShader`);
11169 if (glDeleteShader_NVGLZ is null) assert(0, `OpenGL function 'glDeleteShader' not found!`);
11170 glGetUniformLocation_NVGLZ = cast(glbfn_glGetUniformLocation)glbindGetProcAddress(`glGetUniformLocation`);
11171 if (glGetUniformLocation_NVGLZ is null) assert(0, `OpenGL function 'glGetUniformLocation' not found!`);
11172 glGenBuffers_NVGLZ = cast(glbfn_glGenBuffers)glbindGetProcAddress(`glGenBuffers`);
11173 if (glGenBuffers_NVGLZ is null) assert(0, `OpenGL function 'glGenBuffers' not found!`);
11174 glPixelStorei_NVGLZ = cast(glbfn_glPixelStorei)glbindGetProcAddress(`glPixelStorei`);
11175 if (glPixelStorei_NVGLZ is null) assert(0, `OpenGL function 'glPixelStorei' not found!`);
11176 glUniform4fv_NVGLZ = cast(glbfn_glUniform4fv)glbindGetProcAddress(`glUniform4fv`);
11177 if (glUniform4fv_NVGLZ is null) assert(0, `OpenGL function 'glUniform4fv' not found!`);
11178 glColorMask_NVGLZ = cast(glbfn_glColorMask)glbindGetProcAddress(`glColorMask`);
11179 if (glColorMask_NVGLZ is null) assert(0, `OpenGL function 'glColorMask' not found!`);
11180 glStencilOpSeparate_NVGLZ = cast(glbfn_glStencilOpSeparate)glbindGetProcAddress(`glStencilOpSeparate`);
11181 if (glStencilOpSeparate_NVGLZ is null) assert(0, `OpenGL function 'glStencilOpSeparate' not found!`);
11182 glDrawArrays_NVGLZ = cast(glbfn_glDrawArrays)glbindGetProcAddress(`glDrawArrays`);
11183 if (glDrawArrays_NVGLZ is null) assert(0, `OpenGL function 'glDrawArrays' not found!`);
11184 glStencilOp_NVGLZ = cast(glbfn_glStencilOp)glbindGetProcAddress(`glStencilOp`);
11185 if (glStencilOp_NVGLZ is null) assert(0, `OpenGL function 'glStencilOp' not found!`);
11186 glUseProgram_NVGLZ = cast(glbfn_glUseProgram)glbindGetProcAddress(`glUseProgram`);
11187 if (glUseProgram_NVGLZ is null) assert(0, `OpenGL function 'glUseProgram' not found!`);
11188 glCullFace_NVGLZ = cast(glbfn_glCullFace)glbindGetProcAddress(`glCullFace`);
11189 if (glCullFace_NVGLZ is null) assert(0, `OpenGL function 'glCullFace' not found!`);
11190 glFrontFace_NVGLZ = cast(glbfn_glFrontFace)glbindGetProcAddress(`glFrontFace`);
11191 if (glFrontFace_NVGLZ is null) assert(0, `OpenGL function 'glFrontFace' not found!`);
11192 glActiveTexture_NVGLZ = cast(glbfn_glActiveTexture)glbindGetProcAddress(`glActiveTexture`);
11193 if (glActiveTexture_NVGLZ is null) assert(0, `OpenGL function 'glActiveTexture' not found!`);
11194 glBindBuffer_NVGLZ = cast(glbfn_glBindBuffer)glbindGetProcAddress(`glBindBuffer`);
11195 if (glBindBuffer_NVGLZ is null) assert(0, `OpenGL function 'glBindBuffer' not found!`);
11196 glBufferData_NVGLZ = cast(glbfn_glBufferData)glbindGetProcAddress(`glBufferData`);
11197 if (glBufferData_NVGLZ is null) assert(0, `OpenGL function 'glBufferData' not found!`);
11198 glEnableVertexAttribArray_NVGLZ = cast(glbfn_glEnableVertexAttribArray)glbindGetProcAddress(`glEnableVertexAttribArray`);
11199 if (glEnableVertexAttribArray_NVGLZ is null) assert(0, `OpenGL function 'glEnableVertexAttribArray' not found!`);
11200 glVertexAttribPointer_NVGLZ = cast(glbfn_glVertexAttribPointer)glbindGetProcAddress(`glVertexAttribPointer`);
11201 if (glVertexAttribPointer_NVGLZ is null) assert(0, `OpenGL function 'glVertexAttribPointer' not found!`);
11202 glUniform1i_NVGLZ = cast(glbfn_glUniform1i)glbindGetProcAddress(`glUniform1i`);
11203 if (glUniform1i_NVGLZ is null) assert(0, `OpenGL function 'glUniform1i' not found!`);
11204 glUniform2fv_NVGLZ = cast(glbfn_glUniform2fv)glbindGetProcAddress(`glUniform2fv`);
11205 if (glUniform2fv_NVGLZ is null) assert(0, `OpenGL function 'glUniform2fv' not found!`);
11206 glDisableVertexAttribArray_NVGLZ = cast(glbfn_glDisableVertexAttribArray)glbindGetProcAddress(`glDisableVertexAttribArray`);
11207 if (glDisableVertexAttribArray_NVGLZ is null) assert(0, `OpenGL function 'glDisableVertexAttribArray' not found!`);
11208 glDeleteBuffers_NVGLZ = cast(glbfn_glDeleteBuffers)glbindGetProcAddress(`glDeleteBuffers`);
11209 if (glDeleteBuffers_NVGLZ is null) assert(0, `OpenGL function 'glDeleteBuffers' not found!`);
11210 glBlendFuncSeparate_NVGLZ = cast(glbfn_glBlendFuncSeparate)glbindGetProcAddress(`glBlendFuncSeparate`);
11211 if (glBlendFuncSeparate_NVGLZ is null) assert(0, `OpenGL function 'glBlendFuncSeparate' not found!`);
11213 glLogicOp_NVGLZ = cast(glbfn_glLogicOp)glbindGetProcAddress(`glLogicOp`);
11214 if (glLogicOp_NVGLZ is null) assert(0, `OpenGL function 'glLogicOp' not found!`);
11215 glFramebufferTexture2D_NVGLZ = cast(glbfn_glFramebufferTexture2D)glbindGetProcAddress(`glFramebufferTexture2D`);
11216 if (glFramebufferTexture2D_NVGLZ is null) assert(0, `OpenGL function 'glFramebufferTexture2D' not found!`);
11217 glDeleteFramebuffers_NVGLZ = cast(glbfn_glDeleteFramebuffers)glbindGetProcAddress(`glDeleteFramebuffers`);
11218 if (glDeleteFramebuffers_NVGLZ is null) assert(0, `OpenGL function 'glDeleteFramebuffers' not found!`);
11219 glGenFramebuffers_NVGLZ = cast(glbfn_glGenFramebuffers)glbindGetProcAddress(`glGenFramebuffers`);
11220 if (glGenFramebuffers_NVGLZ is null) assert(0, `OpenGL function 'glGenFramebuffers' not found!`);
11221 glCheckFramebufferStatus_NVGLZ = cast(glbfn_glCheckFramebufferStatus)glbindGetProcAddress(`glCheckFramebufferStatus`);
11222 if (glCheckFramebufferStatus_NVGLZ is null) assert(0, `OpenGL function 'glCheckFramebufferStatus' not found!`);
11223 glBindFramebuffer_NVGLZ = cast(glbfn_glBindFramebuffer)glbindGetProcAddress(`glBindFramebuffer`);
11224 if (glBindFramebuffer_NVGLZ is null) assert(0, `OpenGL function 'glBindFramebuffer' not found!`);
11226 initialized = true;
11231 /// Context creation flags.
11232 /// Group: context_management
11233 public enum NVGContextFlag : int {
11234 /// Nothing special, i.e. empty flag.
11235 None = 0,
11236 /// Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA).
11237 Antialias = 1U<<0,
11238 /** Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little
11239 * slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. */
11240 StencilStrokes = 1U<<1,
11241 /// Flag indicating that additional debug checks are done.
11242 Debug = 1U<<2,
11243 /// Filter (antialias) fonts
11244 FontAA = 1U<<7,
11245 /// Don't filter (antialias) fonts
11246 FontNoAA = 1U<<8,
11247 /// You can use this as a substitute for default flags, for cases like this: `nvgCreateContext(NVGContextFlag.Default, NVGContextFlag.Debug);`.
11248 Default = 1U<<31,
11251 public enum NANOVG_GL_USE_STATE_FILTER = true;
11253 /// These are additional flags on top of [NVGImageFlag].
11254 /// Group: images
11255 public enum NVGImageFlagsGL : int {
11256 NoDelete = 1<<16, // Do not delete GL texture handle.
11260 /// Returns flags for glClear().
11261 /// Group: context_management
11262 public uint glNVGClearFlags () pure nothrow @safe @nogc {
11263 pragma(inline, true);
11264 return (GL_COLOR_BUFFER_BIT|/*GL_DEPTH_BUFFER_BIT|*/GL_STENCIL_BUFFER_BIT);
11268 // ////////////////////////////////////////////////////////////////////////// //
11269 private:
11271 version = nanovega_shared_stencil;
11272 //version = nanovega_debug_clipping;
11274 enum GLNVGuniformLoc {
11275 ViewSize,
11276 Tex,
11277 Frag,
11278 TMat,
11279 TTr,
11280 ClipTex,
11283 alias GLNVGshaderType = int;
11284 enum /*GLNVGshaderType*/ {
11285 NSVG_SHADER_FILLCOLOR,
11286 NSVG_SHADER_FILLGRAD,
11287 NSVG_SHADER_FILLIMG,
11288 NSVG_SHADER_SIMPLE, // also used for clipfill
11289 NSVG_SHADER_IMG,
11292 struct GLNVGshader {
11293 GLuint prog;
11294 GLuint frag;
11295 GLuint vert;
11296 GLint[GLNVGuniformLoc.max+1] loc;
11299 struct GLNVGtexture {
11300 int id;
11301 GLuint tex;
11302 int width, height;
11303 NVGtexture type;
11304 int flags;
11305 int rc;
11306 int nextfree;
11309 struct GLNVGblend {
11310 bool simple;
11311 GLenum srcRGB;
11312 GLenum dstRGB;
11313 GLenum srcAlpha;
11314 GLenum dstAlpha;
11317 alias GLNVGcallType = int;
11318 enum /*GLNVGcallType*/ {
11319 GLNVG_NONE = 0,
11320 GLNVG_FILL,
11321 GLNVG_CONVEXFILL,
11322 GLNVG_STROKE,
11323 GLNVG_TRIANGLES,
11324 GLNVG_AFFINE, // change affine transformation matrix
11325 GLNVG_PUSHCLIP,
11326 GLNVG_POPCLIP,
11327 GLNVG_RESETCLIP,
11328 GLNVG_CLIP_DDUMP_ON,
11329 GLNVG_CLIP_DDUMP_OFF,
11332 struct GLNVGcall {
11333 int type;
11334 int evenOdd; // for fill
11335 int image;
11336 int pathOffset;
11337 int pathCount;
11338 int triangleOffset;
11339 int triangleCount;
11340 int uniformOffset;
11341 NVGMatrix affine;
11342 GLNVGblend blendFunc;
11343 NVGClipMode clipmode;
11346 struct GLNVGpath {
11347 int fillOffset;
11348 int fillCount;
11349 int strokeOffset;
11350 int strokeCount;
11353 align(1) struct GLNVGfragUniforms {
11354 align(1):
11355 enum UNIFORM_ARRAY_SIZE = 12;
11356 // note: after modifying layout or size of uniform array,
11357 // don't forget to also update the fragment shader source!
11358 align(1) union {
11359 align(1):
11360 align(1) struct {
11361 align(1):
11362 float[12] scissorMat; // matrices are actually 3 vec4s
11363 float[12] paintMat;
11364 NVGColor innerCol;
11365 NVGColor outerCol;
11366 float[2] scissorExt;
11367 float[2] scissorScale;
11368 float[2] extent;
11369 float radius;
11370 float feather;
11371 float strokeMult;
11372 float strokeThr;
11373 float texType;
11374 float type;
11375 float doclip;
11376 float unused1, unused2, unused3;
11378 float[4][UNIFORM_ARRAY_SIZE] uniformArray;
11382 enum GLMaskState {
11383 DontMask = -1,
11384 Uninitialized = 0,
11385 Initialized = 1,
11386 JustCleared = 2,
11389 struct GLNVGcontext {
11390 GLNVGshader shader;
11391 GLNVGtexture* textures;
11392 float[2] view;
11393 int freetexid; // -1: none
11394 int ntextures;
11395 int ctextures;
11396 GLuint vertBuf;
11397 int fragSize;
11398 int flags;
11399 // FBOs for masks
11400 GLuint[NVG_MAX_STATES] fbo;
11401 GLuint[2][NVG_MAX_STATES] fboTex; // FBO textures: [0] is color, [1] is stencil
11402 int fboWidth, fboHeight;
11403 GLMaskState[NVG_MAX_STATES] maskStack;
11404 int msp; // mask stack pointer; starts from `0`; points to next free item; see below for logic description
11405 int lastClipFBO; // -666: cache invalidated; -1: don't mask
11406 int lastClipUniOfs;
11407 bool doClipUnion; // specal mode
11408 GLNVGshader shaderFillFBO;
11409 GLNVGshader shaderCopyFBO;
11411 // Per frame buffers
11412 GLNVGcall* calls;
11413 int ccalls;
11414 int ncalls;
11415 GLNVGpath* paths;
11416 int cpaths;
11417 int npaths;
11418 NVGvertex* verts;
11419 int cverts;
11420 int nverts;
11421 ubyte* uniforms;
11422 int cuniforms;
11423 int nuniforms;
11424 NVGMatrix lastAffine;
11426 // cached state
11427 static if (NANOVG_GL_USE_STATE_FILTER) {
11428 GLuint boundTexture;
11429 GLuint stencilMask;
11430 GLenum stencilFunc;
11431 GLint stencilFuncRef;
11432 GLuint stencilFuncMask;
11433 GLNVGblend blendFunc;
11437 int glnvg__maxi() (int a, int b) { pragma(inline, true); return (a > b ? a : b); }
11439 void glnvg__bindTexture (GLNVGcontext* gl, GLuint tex) nothrow @trusted @nogc {
11440 static if (NANOVG_GL_USE_STATE_FILTER) {
11441 if (gl.boundTexture != tex) {
11442 gl.boundTexture = tex;
11443 glBindTexture(GL_TEXTURE_2D, tex);
11445 } else {
11446 glBindTexture(GL_TEXTURE_2D, tex);
11450 void glnvg__stencilMask (GLNVGcontext* gl, GLuint mask) nothrow @trusted @nogc {
11451 static if (NANOVG_GL_USE_STATE_FILTER) {
11452 if (gl.stencilMask != mask) {
11453 gl.stencilMask = mask;
11454 glStencilMask(mask);
11456 } else {
11457 glStencilMask(mask);
11461 void glnvg__stencilFunc (GLNVGcontext* gl, GLenum func, GLint ref_, GLuint mask) nothrow @trusted @nogc {
11462 static if (NANOVG_GL_USE_STATE_FILTER) {
11463 if (gl.stencilFunc != func || gl.stencilFuncRef != ref_ || gl.stencilFuncMask != mask) {
11464 gl.stencilFunc = func;
11465 gl.stencilFuncRef = ref_;
11466 gl.stencilFuncMask = mask;
11467 glStencilFunc(func, ref_, mask);
11469 } else {
11470 glStencilFunc(func, ref_, mask);
11474 // texture id is never zero
11475 GLNVGtexture* glnvg__allocTexture (GLNVGcontext* gl) nothrow @trusted @nogc {
11476 GLNVGtexture* tex = null;
11478 int tid = gl.freetexid;
11479 if (tid == -1) {
11480 if (gl.ntextures >= gl.ctextures) {
11481 assert(gl.ntextures == gl.ctextures);
11482 int ctextures = (gl.ctextures == 0 ? 16 : glnvg__maxi(tid+1, 4)+gl.ctextures/2); // 1.5x overallocate
11483 GLNVGtexture* textures = cast(GLNVGtexture*)realloc(gl.textures, GLNVGtexture.sizeof*ctextures);
11484 if (textures is null) return null;
11485 memset(&textures[gl.ctextures], 0, (ctextures-gl.ctextures)*GLNVGtexture.sizeof);
11486 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("allocated more textures (n=%d; c=%d; nc=%d)\n", gl.ntextures, gl.ctextures, ctextures); }}
11487 gl.textures = textures;
11488 gl.ctextures = ctextures;
11490 tid = gl.ntextures++;
11491 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf(" got next free texture id %d, ntextures=%d\n", tid+1, gl.ntextures); }}
11492 } else {
11493 gl.freetexid = gl.textures[tid].nextfree;
11495 assert(tid <= gl.ntextures);
11497 assert(gl.textures[tid].id == 0);
11498 tex = &gl.textures[tid];
11499 memset(tex, 0, (*tex).sizeof);
11500 tex.id = tid+1;
11501 tex.rc = 1;
11502 tex.nextfree = -1;
11504 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("allocated texture with id %d (%d)\n", tex.id, tid+1); }}
11506 return tex;
11509 GLNVGtexture* glnvg__findTexture (GLNVGcontext* gl, int id) nothrow @trusted @nogc {
11510 if (id <= 0 || id > gl.ntextures) return null;
11511 if (gl.textures[id-1].id == 0) return null; // free one
11512 assert(gl.textures[id-1].id == id);
11513 return &gl.textures[id-1];
11516 bool glnvg__deleteTexture (GLNVGcontext* gl, ref int id) nothrow @trusted @nogc {
11517 if (id <= 0 || id > gl.ntextures) return false;
11518 auto tx = &gl.textures[id-1];
11519 if (tx.id == 0) { id = 0; return false; } // free one
11520 assert(tx.id == id);
11521 assert(tx.tex != 0);
11522 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("decrefing texture with id %d (%d)\n", tx.id, id); }}
11523 if (--tx.rc == 0) {
11524 if ((tx.flags&NVGImageFlagsGL.NoDelete) == 0) glDeleteTextures(1, &tx.tex);
11525 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("deleted texture with id %d (%d); glid=%u\n", tx.id, id, tx.tex); }}
11526 memset(tx, 0, (*tx).sizeof);
11527 //{ import core.stdc.stdio; printf("deleting texture with id %d\n", id); }
11528 tx.nextfree = gl.freetexid;
11529 gl.freetexid = id-1;
11531 id = 0;
11532 return true;
11535 void glnvg__dumpShaderError (GLuint shader, const(char)* name, const(char)* type) nothrow @trusted @nogc {
11536 import core.stdc.stdio : fprintf, stderr;
11537 GLchar[512+1] str = 0;
11538 GLsizei len = 0;
11539 glGetShaderInfoLog(shader, 512, &len, str.ptr);
11540 if (len > 512) len = 512;
11541 str[len] = '\0';
11542 fprintf(stderr, "Shader %s/%s error:\n%s\n", name, type, str.ptr);
11545 void glnvg__dumpProgramError (GLuint prog, const(char)* name) nothrow @trusted @nogc {
11546 import core.stdc.stdio : fprintf, stderr;
11547 GLchar[512+1] str = 0;
11548 GLsizei len = 0;
11549 glGetProgramInfoLog(prog, 512, &len, str.ptr);
11550 if (len > 512) len = 512;
11551 str[len] = '\0';
11552 fprintf(stderr, "Program %s error:\n%s\n", name, str.ptr);
11555 void glnvg__resetError(bool force=false) (GLNVGcontext* gl) nothrow @trusted @nogc {
11556 static if (!force) {
11557 if ((gl.flags&NVGContextFlag.Debug) == 0) return;
11559 glGetError();
11562 void glnvg__checkError(bool force=false) (GLNVGcontext* gl, const(char)* str) nothrow @trusted @nogc {
11563 GLenum err;
11564 static if (!force) {
11565 if ((gl.flags&NVGContextFlag.Debug) == 0) return;
11567 err = glGetError();
11568 if (err != GL_NO_ERROR) {
11569 import core.stdc.stdio : fprintf, stderr;
11570 fprintf(stderr, "Error %08x after %s\n", err, str);
11571 return;
11575 bool glnvg__createShader (GLNVGshader* shader, const(char)* name, const(char)* header, const(char)* opts, const(char)* vshader, const(char)* fshader) nothrow @trusted @nogc {
11576 GLint status;
11577 GLuint prog, vert, frag;
11578 const(char)*[3] str;
11580 memset(shader, 0, (*shader).sizeof);
11582 prog = glCreateProgram();
11583 vert = glCreateShader(GL_VERTEX_SHADER);
11584 frag = glCreateShader(GL_FRAGMENT_SHADER);
11585 str[0] = header;
11586 str[1] = (opts !is null ? opts : "");
11587 str[2] = vshader;
11588 glShaderSource(vert, 3, cast(const(char)**)str.ptr, null);
11590 glCompileShader(vert);
11591 glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
11592 if (status != GL_TRUE) {
11593 glnvg__dumpShaderError(vert, name, "vert");
11594 return false;
11597 str[0] = header;
11598 str[1] = (opts !is null ? opts : "");
11599 str[2] = fshader;
11600 glShaderSource(frag, 3, cast(const(char)**)str.ptr, null);
11602 glCompileShader(frag);
11603 glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
11604 if (status != GL_TRUE) {
11605 glnvg__dumpShaderError(frag, name, "frag");
11606 return false;
11609 glAttachShader(prog, vert);
11610 glAttachShader(prog, frag);
11612 glBindAttribLocation(prog, 0, "vertex");
11613 glBindAttribLocation(prog, 1, "tcoord");
11615 glLinkProgram(prog);
11616 glGetProgramiv(prog, GL_LINK_STATUS, &status);
11617 if (status != GL_TRUE) {
11618 glnvg__dumpProgramError(prog, name);
11619 return false;
11622 shader.prog = prog;
11623 shader.vert = vert;
11624 shader.frag = frag;
11626 return true;
11629 void glnvg__deleteShader (GLNVGshader* shader) nothrow @trusted @nogc {
11630 if (shader.prog != 0) glDeleteProgram(shader.prog);
11631 if (shader.vert != 0) glDeleteShader(shader.vert);
11632 if (shader.frag != 0) glDeleteShader(shader.frag);
11635 void glnvg__getUniforms (GLNVGshader* shader) nothrow @trusted @nogc {
11636 shader.loc[GLNVGuniformLoc.ViewSize] = glGetUniformLocation(shader.prog, "viewSize");
11637 shader.loc[GLNVGuniformLoc.Tex] = glGetUniformLocation(shader.prog, "tex");
11638 shader.loc[GLNVGuniformLoc.Frag] = glGetUniformLocation(shader.prog, "frag");
11639 shader.loc[GLNVGuniformLoc.TMat] = glGetUniformLocation(shader.prog, "tmat");
11640 shader.loc[GLNVGuniformLoc.TTr] = glGetUniformLocation(shader.prog, "ttr");
11641 shader.loc[GLNVGuniformLoc.ClipTex] = glGetUniformLocation(shader.prog, "clipTex");
11644 void glnvg__killFBOs (GLNVGcontext* gl) nothrow @trusted @nogc {
11645 foreach (immutable fidx, ref GLuint fbo; gl.fbo[]) {
11646 if (fbo != 0) {
11647 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
11648 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
11649 foreach (ref GLuint tid; gl.fboTex.ptr[fidx][]) if (tid != 0) { glDeleteTextures(1, &tid); tid = 0; }
11650 glDeleteFramebuffers(1, &fbo);
11651 fbo = 0;
11654 gl.fboWidth = gl.fboHeight = 0;
11657 // returns `true` is new FBO was created
11658 // will not unbind buffer, if it was created
11659 bool glnvg__allocFBO (GLNVGcontext* gl, int fidx, bool doclear=true) nothrow @trusted @nogc {
11660 assert(fidx >= 0 && fidx < gl.fbo.length);
11661 assert(gl.fboWidth > 0);
11662 assert(gl.fboHeight > 0);
11664 if (gl.fbo.ptr[fidx] != 0) return false; // nothing to do, this FBO is already initialized
11666 glnvg__resetError(gl);
11668 // allocate FBO object
11669 GLuint fbo = 0;
11670 glGenFramebuffers(1, &fbo);
11671 if (fbo == 0) assert(0, "NanoVega: cannot create FBO");
11672 glnvg__checkError(gl, "glnvg__allocFBO: glGenFramebuffers");
11673 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
11674 //scope(exit) glBindFramebuffer(GL_FRAMEBUFFER, 0);
11676 // attach 2D texture to this FBO
11677 GLuint tidColor = 0;
11678 glGenTextures(1, &tidColor);
11679 if (tidColor == 0) assert(0, "NanoVega: cannot create RGBA texture for FBO");
11680 glBindTexture(GL_TEXTURE_2D, tidColor);
11681 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
11682 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
11683 glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_S");
11684 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
11685 glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_T");
11686 //FIXME: linear or nearest?
11687 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
11688 glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MIN_FILTER");
11689 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
11690 glnvg__checkError(gl, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MAG_FILTER");
11691 // empty texture
11692 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
11693 // create texture with only one color channel
11694 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, gl.fboWidth, gl.fboHeight, 0, GL_RED, GL_UNSIGNED_BYTE, null);
11695 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
11696 glnvg__checkError(gl, "glnvg__allocFBO: glTexImage2D (color)");
11697 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tidColor, 0);
11698 glnvg__checkError(gl, "glnvg__allocFBO: glFramebufferTexture2D (color)");
11700 // attach stencil texture to this FBO
11701 GLuint tidStencil = 0;
11702 version(nanovega_shared_stencil) {
11703 if (gl.fboTex.ptr[0].ptr[0] == 0) {
11704 glGenTextures(1, &tidStencil);
11705 if (tidStencil == 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
11706 gl.fboTex.ptr[0].ptr[0] = tidStencil;
11707 } else {
11708 tidStencil = gl.fboTex.ptr[0].ptr[0];
11710 if (fidx != 0) gl.fboTex.ptr[fidx].ptr[1] = 0; // stencil texture is shared among FBOs
11711 } else {
11712 glGenTextures(1, &tidStencil);
11713 if (tidStencil == 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
11714 gl.fboTex.ptr[0].ptr[0] = tidStencil;
11716 glBindTexture(GL_TEXTURE_2D, tidStencil);
11717 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, gl.fboWidth, gl.fboHeight, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
11718 glnvg__checkError(gl, "glnvg__allocFBO: glTexImage2D (stencil)");
11719 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tidStencil, 0);
11720 glnvg__checkError(gl, "glnvg__allocFBO: glFramebufferTexture2D (stencil)");
11723 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
11724 if (status != GL_FRAMEBUFFER_COMPLETE) {
11725 version(all) {
11726 import core.stdc.stdio;
11727 if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) printf("fucked attachement\n");
11728 if (status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) printf("fucked dimensions\n");
11729 if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) printf("missing attachement\n");
11730 if (status == GL_FRAMEBUFFER_UNSUPPORTED) printf("unsupported\n");
11732 assert(0, "NanoVega: framebuffer creation failed");
11736 // clear 'em all
11737 if (doclear) {
11738 glClearColor(0, 0, 0, 0);
11739 glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
11742 // save texture ids
11743 gl.fbo.ptr[fidx] = fbo;
11744 gl.fboTex.ptr[fidx].ptr[0] = tidColor;
11745 version(nanovega_shared_stencil) {} else {
11746 gl.fboTex.ptr[fidx].ptr[1] = tidStencil;
11749 static if (NANOVG_GL_USE_STATE_FILTER) glBindTexture(GL_TEXTURE_2D, gl.boundTexture);
11751 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): created with index %d\n", gl.msp-1, fidx); }
11753 return true;
11756 // will not unbind buffer
11757 void glnvg__clearFBO (GLNVGcontext* gl, int fidx) nothrow @trusted @nogc {
11758 assert(fidx >= 0 && fidx < gl.fbo.length);
11759 assert(gl.fboWidth > 0);
11760 assert(gl.fboHeight > 0);
11761 assert(gl.fbo.ptr[fidx] != 0);
11762 glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[fidx]);
11763 glClearColor(0, 0, 0, 0);
11764 glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
11765 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): cleared with index %d\n", gl.msp-1, fidx); }
11768 // will not unbind buffer
11769 void glnvg__copyFBOToFrom (GLNVGcontext* gl, int didx, int sidx) nothrow @trusted @nogc {
11770 import core.stdc.string : memset;
11771 assert(didx >= 0 && didx < gl.fbo.length);
11772 assert(sidx >= 0 && sidx < gl.fbo.length);
11773 assert(gl.fboWidth > 0);
11774 assert(gl.fboHeight > 0);
11775 assert(gl.fbo.ptr[didx] != 0);
11776 assert(gl.fbo.ptr[sidx] != 0);
11777 if (didx == sidx) return;
11780 glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[didx]);
11781 glClearColor(0, 0, 0, 0);
11782 glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
11783 return;
11786 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): copy FBO: %d -> %d\n", gl.msp-1, sidx, didx); }
11788 glUseProgram(gl.shaderCopyFBO.prog);
11790 glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[didx]);
11791 glDisable(GL_CULL_FACE);
11792 glDisable(GL_BLEND);
11793 glDisable(GL_SCISSOR_TEST);
11794 glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[sidx].ptr[0]);
11795 // copy texture by drawing full quad
11796 enum x = 0;
11797 enum y = 0;
11798 immutable int w = gl.fboWidth;
11799 immutable int h = gl.fboHeight;
11800 glBegin(GL_QUADS);
11801 glVertex2i(x, y); // top-left
11802 glVertex2i(w, y); // top-right
11803 glVertex2i(w, h); // bottom-right
11804 glVertex2i(x, h); // bottom-left
11805 glEnd();
11807 // restore state (but don't unbind FBO)
11808 static if (NANOVG_GL_USE_STATE_FILTER) glBindTexture(GL_TEXTURE_2D, gl.boundTexture);
11809 glEnable(GL_CULL_FACE);
11810 glEnable(GL_BLEND);
11811 glUseProgram(gl.shader.prog);
11814 void glnvg__resetFBOClipTextureCache (GLNVGcontext* gl) nothrow @trusted @nogc {
11815 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): texture cache invalidated (%d)\n", gl.msp-1, gl.lastClipFBO); }
11817 if (gl.lastClipFBO >= 0) {
11818 glActiveTexture(GL_TEXTURE1);
11819 glBindTexture(GL_TEXTURE_2D, 0);
11820 glActiveTexture(GL_TEXTURE0);
11823 gl.lastClipFBO = -666;
11826 void glnvg__setFBOClipTexture (GLNVGcontext* gl, GLNVGfragUniforms* frag) nothrow @trusted @nogc {
11827 //assert(gl.msp > 0 && gl.msp <= gl.maskStack.length);
11828 if (gl.lastClipFBO != -666) {
11829 // cached
11830 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): cached (%d)\n", gl.msp-1, gl.lastClipFBO); }
11831 frag.doclip = (gl.lastClipFBO >= 0 ? 1 : 0);
11832 return;
11835 // no cache
11836 int fboidx = -1;
11837 mainloop: foreach_reverse (immutable sp, GLMaskState mst; gl.maskStack.ptr[0..gl.msp]/*; reverse*/) {
11838 final switch (mst) {
11839 case GLMaskState.DontMask: fboidx = -1; break mainloop;
11840 case GLMaskState.Uninitialized: break;
11841 case GLMaskState.Initialized: fboidx = cast(int)sp; break mainloop;
11842 case GLMaskState.JustCleared: assert(0, "NanoVega: `glnvg__setFBOClipTexture()` internal error");
11846 if (fboidx < 0) {
11847 // don't mask
11848 gl.lastClipFBO = -1;
11849 frag.doclip = 0;
11850 } else {
11851 // do masking
11852 assert(gl.fbo.ptr[fboidx] != 0);
11853 gl.lastClipFBO = fboidx;
11854 frag.doclip = 1;
11857 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): new cache (new:%d)\n", gl.msp-1, gl.lastClipFBO); }
11859 if (gl.lastClipFBO >= 0) {
11860 assert(gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
11861 glActiveTexture(GL_TEXTURE1);
11862 glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
11863 glActiveTexture(GL_TEXTURE0);
11867 // returns index in `gl.fbo`, or -1 for "don't mask"
11868 int glnvg__generateFBOClipTexture (GLNVGcontext* gl) nothrow @trusted @nogc {
11869 assert(gl.msp > 0 && gl.msp <= gl.maskStack.length);
11870 // reset cache
11871 //glnvg__resetFBOClipTextureCache(gl);
11872 // we need initialized FBO, even for "don't mask" case
11873 // for this, look back in stack, and either copy initialized FBO,
11874 // or stop at first uninitialized one, and clear it
11875 if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.Initialized) {
11876 // shortcut
11877 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); }
11878 glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[gl.msp-1]);
11879 return gl.msp-1;
11881 foreach_reverse (immutable sp; 0..gl.msp/*; reverse*/) {
11882 final switch (gl.maskStack.ptr[sp]) {
11883 case GLMaskState.DontMask:
11884 // clear it
11885 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): generating new clean texture\n", gl.msp-1); }
11886 if (!glnvg__allocFBO(gl, gl.msp-1)) glnvg__clearFBO(gl, gl.msp-1);
11887 gl.maskStack.ptr[gl.msp-1] = GLMaskState.JustCleared;
11888 return gl.msp-1;
11889 case GLMaskState.Uninitialized: break; // do nothing
11890 case GLMaskState.Initialized:
11891 // i found her! copy to TOS
11892 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): copying texture from %d\n", gl.msp-1, cast(int)sp); }
11893 glnvg__allocFBO(gl, gl.msp-1, false);
11894 glnvg__copyFBOToFrom(gl, gl.msp-1, sp);
11895 gl.maskStack.ptr[gl.msp-1] = GLMaskState.Initialized;
11896 return gl.msp-1;
11897 case GLMaskState.JustCleared: assert(0, "NanoVega: `glnvg__generateFBOClipTexture()` internal error");
11900 // nothing was initialized, lol
11901 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf("FBO(%d): generating new clean texture (first one)\n", gl.msp-1); }
11902 if (!glnvg__allocFBO(gl, gl.msp-1)) glnvg__clearFBO(gl, gl.msp-1);
11903 gl.maskStack.ptr[gl.msp-1] = GLMaskState.JustCleared;
11904 return gl.msp-1;
11907 void glnvg__renderPushClip (void* uptr) nothrow @trusted @nogc {
11908 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
11909 GLNVGcall* call = glnvg__allocCall(gl);
11910 if (call is null) return;
11911 call.type = GLNVG_PUSHCLIP;
11914 void glnvg__renderPopClip (void* uptr) nothrow @trusted @nogc {
11915 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
11916 GLNVGcall* call = glnvg__allocCall(gl);
11917 if (call is null) return;
11918 call.type = GLNVG_POPCLIP;
11921 void glnvg__renderResetClip (void* uptr) nothrow @trusted @nogc {
11922 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
11923 GLNVGcall* call = glnvg__allocCall(gl);
11924 if (call is null) return;
11925 call.type = GLNVG_RESETCLIP;
11928 void glnvg__clipDebugDump (void* uptr, bool doit) nothrow @trusted @nogc {
11929 version(nanovega_debug_clipping) {
11930 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
11931 GLNVGcall* call = glnvg__allocCall(gl);
11932 call.type = (doit ? GLNVG_CLIP_DDUMP_ON : GLNVG_CLIP_DDUMP_OFF);
11936 bool glnvg__renderCreate (void* uptr) nothrow @trusted @nogc {
11937 import core.stdc.stdio : snprintf;
11939 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
11940 enum align_ = 4;
11942 char[64] shaderHeader = void;
11943 //enum shaderHeader = "#define UNIFORM_ARRAY_SIZE 12\n";
11944 snprintf(shaderHeader.ptr, shaderHeader.length, "#define UNIFORM_ARRAY_SIZE %u\n", cast(uint)GLNVGfragUniforms.UNIFORM_ARRAY_SIZE);
11946 enum fillVertShader = q{
11947 uniform vec2 viewSize;
11948 attribute vec2 vertex;
11949 attribute vec2 tcoord;
11950 varying vec2 ftcoord;
11951 varying vec2 fpos;
11952 uniform vec4 tmat; /* abcd of affine matrix: xyzw */
11953 uniform vec2 ttr; /* tx and ty of affine matrix */
11954 void main (void) {
11955 /* affine transformation */
11956 float nx = vertex.x*tmat.x+vertex.y*tmat.z+ttr.x;
11957 float ny = vertex.x*tmat.y+vertex.y*tmat.w+ttr.y;
11958 ftcoord = tcoord;
11959 fpos = vec2(nx, ny);
11960 gl_Position = vec4(2.0*nx/viewSize.x-1.0, 1.0-2.0*ny/viewSize.y, 0, 1);
11964 enum fillFragShader = q{
11965 uniform vec4 frag[UNIFORM_ARRAY_SIZE];
11966 uniform sampler2D tex;
11967 uniform sampler2D clipTex;
11968 varying vec2 ftcoord;
11969 varying vec2 fpos;
11970 #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)
11971 #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)
11972 #define innerCol frag[6]
11973 #define outerCol frag[7]
11974 #define scissorExt frag[8].xy
11975 #define scissorScale frag[8].zw
11976 #define extent frag[9].xy
11977 #define radius frag[9].z
11978 #define feather frag[9].w
11979 #define strokeMult frag[10].x
11980 #define strokeThr frag[10].y
11981 #define texType int(frag[10].z)
11982 #define type int(frag[10].w)
11983 #define doclip int(frag[11].x)
11985 float sdroundrect (in vec2 pt, in vec2 ext, in float rad) {
11986 vec2 ext2 = ext-vec2(rad, rad);
11987 vec2 d = abs(pt)-ext2;
11988 return min(max(d.x, d.y), 0.0)+length(max(d, 0.0))-rad;
11991 // Scissoring
11992 float scissorMask (in vec2 p) {
11993 vec2 sc = (abs((scissorMat*vec3(p, 1.0)).xy)-scissorExt);
11994 sc = vec2(0.5, 0.5)-sc*scissorScale;
11995 return clamp(sc.x, 0.0, 1.0)*clamp(sc.y, 0.0, 1.0);
11998 #ifdef EDGE_AA
11999 // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.
12000 float strokeMask () {
12001 return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult)*min(1.0, ftcoord.y);
12003 #endif
12005 void main (void) {
12006 // clipping
12007 if (doclip != 0) {
12008 vec4 clr = texelFetch(clipTex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);
12009 if (clr.r == 0.0) discard;
12011 float scissor = scissorMask(fpos);
12012 if (scissor <= 0.0) discard; //k8: is it really faster?
12013 #ifdef EDGE_AA
12014 float strokeAlpha = strokeMask();
12015 if (strokeAlpha < strokeThr) discard;
12016 #else
12017 float strokeAlpha = 1.0;
12018 #endif
12019 // rendering
12020 vec4 color;
12021 if (type == 0) { /* NSVG_SHADER_FILLCOLOR */
12022 color = innerCol;
12023 // Combine alpha
12024 color *= strokeAlpha*scissor;
12025 } else if (type == 1) { /* NSVG_SHADER_FILLGRAD */
12026 // Gradient
12027 // Calculate gradient color using box gradient
12028 vec2 pt = (paintMat*vec3(fpos, 1.0)).xy;
12029 float d = clamp((sdroundrect(pt, extent, radius)+feather*0.5)/feather, 0.0, 1.0);
12030 color = mix(innerCol, outerCol, d);
12031 // Combine alpha
12032 color *= strokeAlpha*scissor;
12033 } else if (type == 2) { /* NSVG_SHADER_FILLIMG */
12034 // Image
12035 // Calculate color from texture
12036 vec2 pt = (paintMat*vec3(fpos, 1.0)).xy/extent;
12037 color = texture2D(tex, pt);
12038 if (texType == 1) color = vec4(color.xyz*color.w, color.w);
12039 if (texType == 2) color = vec4(color.x);
12040 // Apply color tint and alpha
12041 color *= innerCol;
12042 // Combine alpha
12043 color *= strokeAlpha*scissor;
12044 } else if (type == 3) { /* NSVG_SHADER_SIMPLE */
12045 // Stencil fill
12046 color = vec4(1, 1, 1, 1);
12047 } else if (type == 4) { /* NSVG_SHADER_IMG */
12048 // Textured tris
12049 color = texture2D(tex, ftcoord);
12050 if (texType == 1) color = vec4(color.xyz*color.w, color.w);
12051 if (texType == 2) color = vec4(color.x);
12052 color *= scissor;
12053 color *= innerCol; // Apply color tint
12055 gl_FragColor = color;
12059 enum clipVertShaderFill = q{
12060 uniform vec2 viewSize;
12061 attribute vec2 vertex;
12062 uniform vec4 tmat; /* abcd of affine matrix: xyzw */
12063 uniform vec2 ttr; /* tx and ty of affine matrix */
12064 void main (void) {
12065 /* affine transformation */
12066 float nx = vertex.x*tmat.x+vertex.y*tmat.z+ttr.x;
12067 float ny = vertex.x*tmat.y+vertex.y*tmat.w+ttr.y;
12068 gl_Position = vec4(2.0*nx/viewSize.x-1.0, 1.0-2.0*ny/viewSize.y, 0, 1);
12072 enum clipFragShaderFill = q{
12073 void main (void) {
12074 gl_FragColor = vec4(1, 1, 1, 1);
12078 enum clipVertShaderCopy = q{
12079 uniform vec2 viewSize;
12080 attribute vec2 vertex;
12081 void main (void) {
12082 gl_Position = vec4(2.0*vertex.x/viewSize.x-1.0, 1.0-2.0*vertex.y/viewSize.y, 0, 1);
12086 enum clipFragShaderCopy = q{
12087 uniform sampler2D tex;
12088 void main (void) {
12089 gl_FragColor = texelFetch(tex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);
12093 glnvg__checkError(gl, "init");
12095 string defines = (gl.flags&NVGContextFlag.Antialias ? "#define EDGE_AA 1\n" : null);
12096 if (!glnvg__createShader(&gl.shader, "shader", shaderHeader.ptr, defines.ptr, fillVertShader, fillFragShader)) return false;
12097 if (!glnvg__createShader(&gl.shaderFillFBO, "shaderFillFBO", shaderHeader.ptr, defines.ptr, clipVertShaderFill, clipFragShaderFill)) return false;
12098 if (!glnvg__createShader(&gl.shaderCopyFBO, "shaderCopyFBO", shaderHeader.ptr, defines.ptr, clipVertShaderCopy, clipFragShaderCopy)) return false;
12100 glnvg__checkError(gl, "uniform locations");
12101 glnvg__getUniforms(&gl.shader);
12102 glnvg__getUniforms(&gl.shaderFillFBO);
12103 glnvg__getUniforms(&gl.shaderCopyFBO);
12105 // Create dynamic vertex array
12106 glGenBuffers(1, &gl.vertBuf);
12108 gl.fragSize = GLNVGfragUniforms.sizeof+align_-GLNVGfragUniforms.sizeof%align_;
12110 glnvg__checkError(gl, "create done");
12112 glFinish();
12114 return true;
12117 int glnvg__renderCreateTexture (void* uptr, NVGtexture type, int w, int h, int imageFlags, const(ubyte)* data) nothrow @trusted @nogc {
12118 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12119 GLNVGtexture* tex = glnvg__allocTexture(gl);
12121 if (tex is null) return 0;
12123 glGenTextures(1, &tex.tex);
12124 tex.width = w;
12125 tex.height = h;
12126 tex.type = type;
12127 tex.flags = imageFlags;
12128 glnvg__bindTexture(gl, tex.tex);
12130 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("created texture with id %d; glid=%u\n", tex.id, tex.tex); }}
12132 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
12133 glPixelStorei(GL_UNPACK_ROW_LENGTH, tex.width);
12134 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
12135 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
12137 // GL 1.4 and later has support for generating mipmaps using a tex parameter.
12138 if ((imageFlags&(NVGImageFlag.GenerateMipmaps|NVGImageFlag.NoFiltering)) == NVGImageFlag.GenerateMipmaps) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
12140 immutable ttype = (type == NVGtexture.RGBA ? GL_RGBA : GL_RED);
12141 glTexImage2D(GL_TEXTURE_2D, 0, ttype, w, h, 0, ttype, GL_UNSIGNED_BYTE, data);
12143 immutable tfmin =
12144 (imageFlags&NVGImageFlag.NoFiltering ? GL_NEAREST :
12145 imageFlags&NVGImageFlag.GenerateMipmaps ? GL_LINEAR_MIPMAP_LINEAR :
12146 GL_LINEAR);
12147 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tfmin);
12148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (imageFlags&NVGImageFlag.NoFiltering ? GL_NEAREST : GL_LINEAR));
12150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (imageFlags&NVGImageFlag.RepeatX ? GL_REPEAT : GL_CLAMP_TO_EDGE));
12151 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (imageFlags&NVGImageFlag.RepeatY ? GL_REPEAT : GL_CLAMP_TO_EDGE));
12153 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
12154 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
12155 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
12156 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
12158 glnvg__checkError(gl, "create tex");
12159 glnvg__bindTexture(gl, 0);
12161 return tex.id;
12164 bool glnvg__renderDeleteTexture (void* uptr, int image) nothrow @trusted @nogc {
12165 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12166 return glnvg__deleteTexture(gl, image);
12169 bool glnvg__renderTextureIncRef (void* uptr, int image) nothrow @trusted @nogc {
12170 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12171 GLNVGtexture* tex = glnvg__findTexture(gl, image);
12172 if (tex is null) {
12173 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("CANNOT incref texture with id %d\n", image); }}
12174 return false;
12176 ++tex.rc;
12177 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("texture #%d: incref; newref=%d\n", image, tex.rc); }}
12178 return true;
12181 bool glnvg__renderUpdateTexture (void* uptr, int image, int x, int y, int w, int h, const(ubyte)* data) nothrow @trusted @nogc {
12182 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12183 GLNVGtexture* tex = glnvg__findTexture(gl, image);
12185 if (tex is null) {
12186 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("CANNOT update texture with id %d\n", image); }}
12187 return false;
12190 version(nanovega_debug_textures) {{ import core.stdc.stdio; printf("updated texture with id %d; glid=%u\n", tex.id, image, tex.tex); }}
12192 glnvg__bindTexture(gl, tex.tex);
12194 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
12195 glPixelStorei(GL_UNPACK_ROW_LENGTH, tex.width);
12196 glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);
12197 glPixelStorei(GL_UNPACK_SKIP_ROWS, y);
12199 immutable ttype = (tex.type == NVGtexture.RGBA ? GL_RGBA : GL_RED);
12200 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, ttype, GL_UNSIGNED_BYTE, data);
12202 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
12203 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
12204 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
12205 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
12207 glnvg__bindTexture(gl, 0);
12209 return true;
12212 bool glnvg__renderGetTextureSize (void* uptr, int image, int* w, int* h) nothrow @trusted @nogc {
12213 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12214 GLNVGtexture* tex = glnvg__findTexture(gl, image);
12215 if (tex is null) {
12216 if (w !is null) *w = 0;
12217 if (h !is null) *h = 0;
12218 return false;
12219 } else {
12220 if (w !is null) *w = tex.width;
12221 if (h !is null) *h = tex.height;
12222 return true;
12226 void glnvg__xformToMat3x4 (float[] m3, const(float)[] t) nothrow @trusted @nogc {
12227 assert(t.length >= 6);
12228 assert(m3.length >= 12);
12229 m3.ptr[0] = t.ptr[0];
12230 m3.ptr[1] = t.ptr[1];
12231 m3.ptr[2] = 0.0f;
12232 m3.ptr[3] = 0.0f;
12233 m3.ptr[4] = t.ptr[2];
12234 m3.ptr[5] = t.ptr[3];
12235 m3.ptr[6] = 0.0f;
12236 m3.ptr[7] = 0.0f;
12237 m3.ptr[8] = t.ptr[4];
12238 m3.ptr[9] = t.ptr[5];
12239 m3.ptr[10] = 1.0f;
12240 m3.ptr[11] = 0.0f;
12243 NVGColor glnvg__premulColor() (in auto ref NVGColor c) nothrow @trusted @nogc {
12244 //pragma(inline, true);
12245 NVGColor res = void;
12246 res.r = c.r*c.a;
12247 res.g = c.g*c.a;
12248 res.b = c.b*c.a;
12249 res.a = c.a;
12250 return res;
12253 bool glnvg__convertPaint (GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGPaint* paint, NVGscissor* scissor, float width, float fringe, float strokeThr) nothrow @trusted @nogc {
12254 import core.stdc.math : sqrtf;
12255 GLNVGtexture* tex = null;
12256 NVGMatrix invxform = void;
12258 memset(frag, 0, (*frag).sizeof);
12260 frag.innerCol = glnvg__premulColor(paint.innerColor);
12261 frag.outerCol = glnvg__premulColor(paint.outerColor);
12263 if (scissor.extent.ptr[0] < -0.5f || scissor.extent.ptr[1] < -0.5f) {
12264 memset(frag.scissorMat.ptr, 0, frag.scissorMat.sizeof);
12265 frag.scissorExt.ptr[0] = 1.0f;
12266 frag.scissorExt.ptr[1] = 1.0f;
12267 frag.scissorScale.ptr[0] = 1.0f;
12268 frag.scissorScale.ptr[1] = 1.0f;
12269 } else {
12270 //nvgTransformInverse(invxform[], scissor.xform[]);
12271 invxform = scissor.xform.inverted;
12272 glnvg__xformToMat3x4(frag.scissorMat[], invxform.mat[]);
12273 frag.scissorExt.ptr[0] = scissor.extent.ptr[0];
12274 frag.scissorExt.ptr[1] = scissor.extent.ptr[1];
12275 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;
12276 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;
12279 memcpy(frag.extent.ptr, paint.extent.ptr, frag.extent.sizeof);
12280 frag.strokeMult = (width*0.5f+fringe*0.5f)/fringe;
12281 frag.strokeThr = strokeThr;
12283 if (paint.image.valid) {
12284 tex = glnvg__findTexture(gl, paint.image.id);
12285 if (tex is null) return false;
12286 if ((tex.flags&NVGImageFlag.FlipY) != 0) {
12288 NVGMatrix flipped;
12289 nvgTransformScale(flipped[], 1.0f, -1.0f);
12290 nvgTransformMultiply(flipped[], paint.xform[]);
12291 nvgTransformInverse(invxform[], flipped[]);
12294 NVGMatrix m1 = void, m2 = void;
12295 nvgTransformTranslate(m1[], 0.0f, frag.extent.ptr[1]*0.5f);
12296 nvgTransformMultiply(m1[], paint.xform[]);
12297 nvgTransformScale(m2[], 1.0f, -1.0f);
12298 nvgTransformMultiply(m2[], m1[]);
12299 nvgTransformTranslate(m1[], 0.0f, -frag.extent.ptr[1]*0.5f);
12300 nvgTransformMultiply(m1[], m2[]);
12301 nvgTransformInverse(invxform[], m1[]);
12303 NVGMatrix m1 = NVGMatrix.Translated(0.0f, frag.extent.ptr[1]*0.5f);
12304 m1.mul(paint.xform);
12305 NVGMatrix m2 = NVGMatrix.Scaled(1.0f, -1.0f);
12306 m2.mul(m1);
12307 m1 = NVGMatrix.Translated(0.0f, -frag.extent.ptr[1]*0.5f);
12308 m1.mul(m2);
12309 invxform = m1.inverted;
12310 } else {
12311 //nvgTransformInverse(invxform[], paint.xform[]);
12312 invxform = paint.xform.inverted;
12314 frag.type = NSVG_SHADER_FILLIMG;
12316 if (tex.type == NVGtexture.RGBA) {
12317 frag.texType = (tex.flags&NVGImageFlag.Premultiplied ? 0 : 1);
12318 } else {
12319 frag.texType = 2;
12321 //printf("frag.texType = %d\n", frag.texType);
12322 } else {
12323 frag.type = (paint.simpleColor ? NSVG_SHADER_FILLCOLOR : NSVG_SHADER_FILLGRAD);
12324 frag.radius = paint.radius;
12325 frag.feather = paint.feather;
12326 //nvgTransformInverse(invxform[], paint.xform[]);
12327 invxform = paint.xform.inverted;
12330 glnvg__xformToMat3x4(frag.paintMat[], invxform.mat[]);
12332 return true;
12335 void glnvg__setUniforms (GLNVGcontext* gl, int uniformOffset, int image) nothrow @trusted @nogc {
12336 GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
12337 glnvg__setFBOClipTexture(gl, frag);
12338 glUniform4fv(gl.shader.loc[GLNVGuniformLoc.Frag], frag.UNIFORM_ARRAY_SIZE, &(frag.uniformArray.ptr[0].ptr[0]));
12339 glnvg__checkError(gl, "glnvg__setUniforms");
12340 if (image != 0) {
12341 GLNVGtexture* tex = glnvg__findTexture(gl, image);
12342 glnvg__bindTexture(gl, (tex !is null ? tex.tex : 0));
12343 glnvg__checkError(gl, "tex paint tex");
12344 } else {
12345 glnvg__bindTexture(gl, 0);
12349 void glnvg__finishClip (GLNVGcontext* gl, NVGClipMode clipmode) nothrow @trusted @nogc {
12350 assert(clipmode != NVGClipMode.None);
12352 // fill FBO, clear stencil buffer
12353 //TODO: optimize with bounds?
12354 version(all) {
12355 //glnvg__resetAffine(gl);
12356 //glUseProgram(gl.shaderFillFBO.prog);
12357 glDisable(GL_CULL_FACE);
12358 glDisable(GL_BLEND);
12359 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
12360 glEnable(GL_STENCIL_TEST);
12361 if (gl.doClipUnion) {
12362 // for "and" we should clear everything that is NOT stencil-masked
12363 glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
12364 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
12365 } else {
12366 glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x00, 0xff);
12367 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
12369 glBegin(GL_QUADS);
12370 glVertex2i(0, 0);
12371 glVertex2i(0, gl.fboHeight);
12372 glVertex2i(gl.fboWidth, gl.fboHeight);
12373 glVertex2i(gl.fboWidth, 0);
12374 glEnd();
12375 //glnvg__restoreAffine(gl);
12378 glBindFramebuffer(GL_FRAMEBUFFER, 0);
12379 glDisable(GL_COLOR_LOGIC_OP);
12380 //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // done above
12381 glEnable(GL_BLEND);
12382 glDisable(GL_STENCIL_TEST);
12383 glEnable(GL_CULL_FACE);
12384 glUseProgram(gl.shader.prog);
12386 // set current FBO as used one
12387 assert(gl.msp > 0 && gl.fbo.ptr[gl.msp-1] > 0 && gl.fboTex.ptr[gl.msp-1].ptr[0] > 0);
12388 if (gl.lastClipFBO != gl.msp-1) {
12389 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); }
12390 gl.lastClipFBO = gl.msp-1;
12391 glActiveTexture(GL_TEXTURE1);
12392 glBindTexture(GL_TEXTURE_2D, gl.fboTex.ptr[gl.lastClipFBO].ptr[0]);
12393 glActiveTexture(GL_TEXTURE0);
12397 void glnvg__setClipUniforms (GLNVGcontext* gl, int uniformOffset, NVGClipMode clipmode) nothrow @trusted @nogc {
12398 assert(clipmode != NVGClipMode.None);
12399 GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
12400 // save uniform offset for `glnvg__finishClip()`
12401 gl.lastClipUniOfs = uniformOffset;
12402 // get FBO index, bind this FBO
12403 immutable int clipTexId = glnvg__generateFBOClipTexture(gl);
12404 assert(clipTexId >= 0);
12405 glUseProgram(gl.shaderFillFBO.prog);
12406 glnvg__checkError(gl, "use");
12407 glBindFramebuffer(GL_FRAMEBUFFER, gl.fbo.ptr[clipTexId]);
12408 // set logic op for clip
12409 gl.doClipUnion = false;
12410 if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.JustCleared) {
12411 // it is cleared to zero, we can just draw a path
12412 glDisable(GL_COLOR_LOGIC_OP);
12413 gl.maskStack.ptr[gl.msp-1] = GLMaskState.Initialized;
12414 } else {
12415 glEnable(GL_COLOR_LOGIC_OP);
12416 final switch (clipmode) {
12417 case NVGClipMode.None: assert(0, "wtf?!");
12418 case NVGClipMode.Union: glLogicOp(GL_CLEAR); gl.doClipUnion = true; break; // use `GL_CLEAR` to avoid adding another shader mode
12419 case NVGClipMode.Or: glLogicOp(GL_COPY); break; // GL_OR
12420 case NVGClipMode.Xor: glLogicOp(GL_XOR); break;
12421 case NVGClipMode.Sub: glLogicOp(GL_CLEAR); break;
12422 case NVGClipMode.Replace: glLogicOp(GL_COPY); break;
12425 // set affine matrix
12426 glUniform4fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.TMat], 1, gl.lastAffine.mat.ptr);
12427 glnvg__checkError(gl, "affine 0");
12428 glUniform2fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.TTr], 1, gl.lastAffine.mat.ptr+4);
12429 glnvg__checkError(gl, "affine 1");
12430 // setup common OpenGL parameters
12431 glDisable(GL_BLEND);
12432 glDisable(GL_CULL_FACE);
12433 glEnable(GL_STENCIL_TEST);
12434 glnvg__stencilMask(gl, 0xff);
12435 glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
12436 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
12437 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
12440 void glnvg__renderViewport (void* uptr, int width, int height) nothrow @trusted @nogc {
12441 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12442 gl.view.ptr[0] = cast(float)width;
12443 gl.view.ptr[1] = cast(float)height;
12444 // kill FBOs if we need to create new ones (flushing will recreate 'em if necessary)
12445 if (width != gl.fboWidth || height != gl.fboHeight) {
12446 glnvg__killFBOs(gl);
12447 gl.fboWidth = width;
12448 gl.fboHeight = height;
12450 gl.msp = 1;
12451 gl.maskStack.ptr[0] = GLMaskState.DontMask;
12454 void glnvg__fill (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
12455 GLNVGpath* paths = &gl.paths[call.pathOffset];
12456 int npaths = call.pathCount;
12458 if (call.clipmode == NVGClipMode.None) {
12459 // Draw shapes
12460 glEnable(GL_STENCIL_TEST);
12461 glnvg__stencilMask(gl, 0xffU);
12462 glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xffU);
12464 glnvg__setUniforms(gl, call.uniformOffset, 0);
12465 glnvg__checkError(gl, "fill simple");
12467 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
12468 if (call.evenOdd) {
12469 //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
12470 //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
12471 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
12472 } else {
12473 glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
12474 glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
12476 glDisable(GL_CULL_FACE);
12477 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
12478 glEnable(GL_CULL_FACE);
12480 // Draw anti-aliased pixels
12481 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
12482 glnvg__setUniforms(gl, call.uniformOffset+gl.fragSize, call.image);
12483 glnvg__checkError(gl, "fill fill");
12485 if (gl.flags&NVGContextFlag.Antialias) {
12486 glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xffU);
12487 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
12488 // Draw fringes
12489 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12492 // Draw fill
12493 glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xffU);
12494 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
12495 if (call.evenOdd) {
12496 glDisable(GL_CULL_FACE);
12497 glDrawArrays(GL_TRIANGLE_STRIP, call.triangleOffset, call.triangleCount);
12498 //foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
12499 glEnable(GL_CULL_FACE);
12500 } else {
12501 glDrawArrays(GL_TRIANGLE_STRIP, call.triangleOffset, call.triangleCount);
12504 glDisable(GL_STENCIL_TEST);
12505 } else {
12506 glnvg__setClipUniforms(gl, call.uniformOffset/*+gl.fragSize*/, call.clipmode); // this activates our FBO
12507 glnvg__checkError(gl, "fillclip simple");
12508 glnvg__stencilFunc(gl, GL_ALWAYS, 0x00, 0xffU);
12509 if (call.evenOdd) {
12510 //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
12511 //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
12512 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
12513 } else {
12514 glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
12515 glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
12517 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
12518 glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
12522 void glnvg__convexFill (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
12523 GLNVGpath* paths = &gl.paths[call.pathOffset];
12524 int npaths = call.pathCount;
12526 if (call.clipmode == NVGClipMode.None) {
12527 glnvg__setUniforms(gl, call.uniformOffset, call.image);
12528 glnvg__checkError(gl, "convex fill");
12529 if (call.evenOdd) glDisable(GL_CULL_FACE);
12530 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
12531 if (gl.flags&NVGContextFlag.Antialias) {
12532 // Draw fringes
12533 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12535 if (call.evenOdd) glEnable(GL_CULL_FACE);
12536 } else {
12537 glnvg__setClipUniforms(gl, call.uniformOffset, call.clipmode); // this activates our FBO
12538 glnvg__checkError(gl, "clip convex fill");
12539 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
12540 if (gl.flags&NVGContextFlag.Antialias) {
12541 // Draw fringes
12542 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12544 glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
12548 void glnvg__stroke (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
12549 GLNVGpath* paths = &gl.paths[call.pathOffset];
12550 int npaths = call.pathCount;
12552 if (call.clipmode == NVGClipMode.None) {
12553 if (gl.flags&NVGContextFlag.StencilStrokes) {
12554 glEnable(GL_STENCIL_TEST);
12555 glnvg__stencilMask(gl, 0xff);
12557 // Fill the stroke base without overlap
12558 glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff);
12559 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
12560 glnvg__setUniforms(gl, call.uniformOffset+gl.fragSize, call.image);
12561 glnvg__checkError(gl, "stroke fill 0");
12562 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12564 // Draw anti-aliased pixels.
12565 glnvg__setUniforms(gl, call.uniformOffset, call.image);
12566 glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);
12567 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
12568 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12570 // Clear stencil buffer.
12571 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
12572 glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
12573 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
12574 glnvg__checkError(gl, "stroke fill 1");
12575 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12576 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
12578 glDisable(GL_STENCIL_TEST);
12580 //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, strokeWidth, fringe, 1.0f-0.5f/255.0f);
12581 } else {
12582 glnvg__setUniforms(gl, call.uniformOffset, call.image);
12583 glnvg__checkError(gl, "stroke fill");
12584 // Draw Strokes
12585 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12587 } else {
12588 glnvg__setClipUniforms(gl, call.uniformOffset/*+gl.fragSize*/, call.clipmode);
12589 glnvg__checkError(gl, "stroke fill 0");
12590 foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
12591 glnvg__finishClip(gl, call.clipmode); // deactivate FBO, restore rendering state
12595 void glnvg__triangles (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
12596 if (call.clipmode == NVGClipMode.None) {
12597 glnvg__setUniforms(gl, call.uniformOffset, call.image);
12598 glnvg__checkError(gl, "triangles fill");
12599 glDrawArrays(GL_TRIANGLES, call.triangleOffset, call.triangleCount);
12600 } else {
12601 //TODO(?): use texture as mask?
12605 void glnvg__affine (GLNVGcontext* gl, GLNVGcall* call) nothrow @trusted @nogc {
12606 glUniform4fv(gl.shader.loc[GLNVGuniformLoc.TMat], 1, call.affine.mat.ptr);
12607 glnvg__checkError(gl, "affine");
12608 glUniform2fv(gl.shader.loc[GLNVGuniformLoc.TTr], 1, call.affine.mat.ptr+4);
12609 glnvg__checkError(gl, "affine");
12610 //glnvg__setUniforms(gl, call.uniformOffset, call.image);
12613 void glnvg__renderCancelInternal (GLNVGcontext* gl, bool clearTextures) nothrow @trusted @nogc {
12614 if (clearTextures) {
12615 foreach (ref GLNVGcall c; gl.calls[0..gl.ncalls]) if (c.image > 0) glnvg__deleteTexture(gl, c.image);
12617 gl.nverts = 0;
12618 gl.npaths = 0;
12619 gl.ncalls = 0;
12620 gl.nuniforms = 0;
12621 gl.msp = 1;
12622 gl.maskStack.ptr[0] = GLMaskState.DontMask;
12625 void glnvg__renderCancel (void* uptr) nothrow @trusted @nogc {
12626 glnvg__renderCancelInternal(cast(GLNVGcontext*)uptr, true);
12629 GLenum glnvg_convertBlendFuncFactor (NVGBlendFactor factor) pure nothrow @trusted @nogc {
12630 if (factor == NVGBlendFactor.Zero) return GL_ZERO;
12631 if (factor == NVGBlendFactor.One) return GL_ONE;
12632 if (factor == NVGBlendFactor.SrcColor) return GL_SRC_COLOR;
12633 if (factor == NVGBlendFactor.OneMinusSrcColor) return GL_ONE_MINUS_SRC_COLOR;
12634 if (factor == NVGBlendFactor.DstColor) return GL_DST_COLOR;
12635 if (factor == NVGBlendFactor.OneMinusDstColor) return GL_ONE_MINUS_DST_COLOR;
12636 if (factor == NVGBlendFactor.SrcAlpha) return GL_SRC_ALPHA;
12637 if (factor == NVGBlendFactor.OneMinusSrcAlpha) return GL_ONE_MINUS_SRC_ALPHA;
12638 if (factor == NVGBlendFactor.DstAlpha) return GL_DST_ALPHA;
12639 if (factor == NVGBlendFactor.OneMinusDstAlpha) return GL_ONE_MINUS_DST_ALPHA;
12640 if (factor == NVGBlendFactor.SrcAlphaSaturate) return GL_SRC_ALPHA_SATURATE;
12641 return GL_INVALID_ENUM;
12644 GLNVGblend glnvg__buildBlendFunc (NVGCompositeOperationState op) pure nothrow @trusted @nogc {
12645 GLNVGblend res;
12646 res.simple = op.simple;
12647 res.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB);
12648 res.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB);
12649 res.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha);
12650 res.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha);
12651 if (res.simple) {
12652 if (res.srcAlpha == GL_INVALID_ENUM || res.dstAlpha == GL_INVALID_ENUM) {
12653 res.srcRGB = res.srcAlpha = res.dstRGB = res.dstAlpha = GL_INVALID_ENUM;
12655 } else {
12656 if (res.srcRGB == GL_INVALID_ENUM || res.dstRGB == GL_INVALID_ENUM || res.srcAlpha == GL_INVALID_ENUM || res.dstAlpha == GL_INVALID_ENUM) {
12657 res.simple = true;
12658 res.srcRGB = res.srcAlpha = res.dstRGB = res.dstAlpha = GL_INVALID_ENUM;
12661 return res;
12664 void glnvg__blendCompositeOperation() (GLNVGcontext* gl, in auto ref GLNVGblend op) nothrow @trusted @nogc {
12665 //glBlendFuncSeparate(glnvg_convertBlendFuncFactor(op.srcRGB), glnvg_convertBlendFuncFactor(op.dstRGB), glnvg_convertBlendFuncFactor(op.srcAlpha), glnvg_convertBlendFuncFactor(op.dstAlpha));
12666 static if (NANOVG_GL_USE_STATE_FILTER) {
12667 if (gl.blendFunc.simple == op.simple) {
12668 if (op.simple) {
12669 if (gl.blendFunc.srcAlpha == op.srcAlpha && gl.blendFunc.dstAlpha == op.dstAlpha) return;
12670 } else {
12671 if (gl.blendFunc.srcRGB == op.srcRGB && gl.blendFunc.dstRGB == op.dstRGB && gl.blendFunc.srcAlpha == op.srcAlpha && gl.blendFunc.dstAlpha == op.dstAlpha) return;
12674 gl.blendFunc = op;
12676 if (op.simple) {
12677 if (op.srcAlpha == GL_INVALID_ENUM || op.dstAlpha == GL_INVALID_ENUM) {
12678 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
12679 } else {
12680 glBlendFunc(op.srcAlpha, op.dstAlpha);
12682 } else {
12683 if (op.srcRGB == GL_INVALID_ENUM || op.dstRGB == GL_INVALID_ENUM || op.srcAlpha == GL_INVALID_ENUM || op.dstAlpha == GL_INVALID_ENUM) {
12684 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
12685 } else {
12686 glBlendFuncSeparate(op.srcRGB, op.dstRGB, op.srcAlpha, op.dstAlpha);
12691 void glnvg__renderSetAffine (void* uptr, in ref NVGMatrix mat) nothrow @trusted @nogc {
12692 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12693 GLNVGcall* call;
12694 // if last operation was GLNVG_AFFINE, simply replace the matrix
12695 if (gl.ncalls > 0 && gl.calls[gl.ncalls-1].type == GLNVG_AFFINE) {
12696 call = &gl.calls[gl.ncalls-1];
12697 } else {
12698 call = glnvg__allocCall(gl);
12699 if (call is null) return;
12700 call.type = GLNVG_AFFINE;
12702 call.affine.mat.ptr[0..6] = mat.mat.ptr[0..6];
12705 version(nanovega_debug_clipping) public __gshared bool nanovegaClipDebugDump = false;
12707 void glnvg__renderFlush (void* uptr) nothrow @trusted @nogc {
12708 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12709 enum ShaderType { None, Fill, Clip }
12710 auto lastShader = ShaderType.None;
12711 if (gl.ncalls > 0) {
12712 gl.msp = 1;
12713 gl.maskStack.ptr[0] = GLMaskState.DontMask;
12715 // Setup require GL state.
12716 glUseProgram(gl.shader.prog);
12718 glActiveTexture(GL_TEXTURE1);
12719 glBindTexture(GL_TEXTURE_2D, 0);
12720 glActiveTexture(GL_TEXTURE0);
12721 glnvg__resetFBOClipTextureCache(gl);
12723 //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
12724 static if (NANOVG_GL_USE_STATE_FILTER) {
12725 gl.blendFunc.simple = true;
12726 gl.blendFunc.srcRGB = gl.blendFunc.dstRGB = gl.blendFunc.srcAlpha = gl.blendFunc.dstAlpha = GL_INVALID_ENUM;
12728 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // just in case
12729 glEnable(GL_CULL_FACE);
12730 glCullFace(GL_BACK);
12731 glFrontFace(GL_CCW);
12732 glEnable(GL_BLEND);
12733 glDisable(GL_DEPTH_TEST);
12734 glDisable(GL_SCISSOR_TEST);
12735 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
12736 glStencilMask(0xffffffff);
12737 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
12738 glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
12739 glActiveTexture(GL_TEXTURE0);
12740 glBindTexture(GL_TEXTURE_2D, 0);
12741 static if (NANOVG_GL_USE_STATE_FILTER) {
12742 gl.boundTexture = 0;
12743 gl.stencilMask = 0xffffffff;
12744 gl.stencilFunc = GL_ALWAYS;
12745 gl.stencilFuncRef = 0;
12746 gl.stencilFuncMask = 0xffffffff;
12748 glnvg__checkError(gl, "OpenGL setup");
12750 // Upload vertex data
12751 glBindBuffer(GL_ARRAY_BUFFER, gl.vertBuf);
12752 glBufferData(GL_ARRAY_BUFFER, gl.nverts*NVGvertex.sizeof, gl.verts, GL_STREAM_DRAW);
12753 glEnableVertexAttribArray(0);
12754 glEnableVertexAttribArray(1);
12755 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, NVGvertex.sizeof, cast(const(GLvoid)*)cast(usize)0);
12756 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, NVGvertex.sizeof, cast(const(GLvoid)*)(0+2*float.sizeof));
12757 glnvg__checkError(gl, "vertex data uploading");
12759 // Set view and texture just once per frame.
12760 glUniform1i(gl.shader.loc[GLNVGuniformLoc.Tex], 0);
12761 if (gl.shader.loc[GLNVGuniformLoc.ClipTex] != -1) {
12762 //{ import core.stdc.stdio; printf("%d\n", gl.shader.loc[GLNVGuniformLoc.ClipTex]); }
12763 glUniform1i(gl.shader.loc[GLNVGuniformLoc.ClipTex], 1);
12765 if (gl.shader.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shader.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
12766 glnvg__checkError(gl, "render shader setup");
12768 // Reset affine transformations.
12769 glUniform4fv(gl.shader.loc[GLNVGuniformLoc.TMat], 1, NVGMatrix.IdentityMat.ptr);
12770 glUniform2fv(gl.shader.loc[GLNVGuniformLoc.TTr], 1, NVGMatrix.IdentityMat.ptr+4);
12771 glnvg__checkError(gl, "affine setup");
12773 // set clip shaders params
12774 // fill
12775 glUseProgram(gl.shaderFillFBO.prog);
12776 glnvg__checkError(gl, "clip shaders setup (fill 0)");
12777 if (gl.shaderFillFBO.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shaderFillFBO.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
12778 glnvg__checkError(gl, "clip shaders setup (fill 1)");
12779 // copy
12780 glUseProgram(gl.shaderCopyFBO.prog);
12781 glnvg__checkError(gl, "clip shaders setup (copy 0)");
12782 if (gl.shaderCopyFBO.loc[GLNVGuniformLoc.ViewSize] != -1) glUniform2fv(gl.shaderCopyFBO.loc[GLNVGuniformLoc.ViewSize], 1, gl.view.ptr);
12783 glnvg__checkError(gl, "clip shaders setup (copy 1)");
12784 //glUniform1i(gl.shaderFillFBO.loc[GLNVGuniformLoc.Tex], 0);
12785 glUniform1i(gl.shaderCopyFBO.loc[GLNVGuniformLoc.Tex], 0);
12786 glnvg__checkError(gl, "clip shaders setup (copy 2)");
12787 // restore render shader
12788 glUseProgram(gl.shader.prog);
12790 //{ 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]); }
12792 gl.lastAffine.identity;
12794 foreach (int i; 0..gl.ncalls) {
12795 GLNVGcall* call = &gl.calls[i];
12796 switch (call.type) {
12797 case GLNVG_FILL: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__fill(gl, call); break;
12798 case GLNVG_CONVEXFILL: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__convexFill(gl, call); break;
12799 case GLNVG_STROKE: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__stroke(gl, call); break;
12800 case GLNVG_TRIANGLES: glnvg__blendCompositeOperation(gl, call.blendFunc); glnvg__triangles(gl, call); break;
12801 case GLNVG_AFFINE: gl.lastAffine = call.affine; glnvg__affine(gl, call); break;
12802 // clip region management
12803 case GLNVG_PUSHCLIP:
12804 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]); }
12805 if (gl.msp >= gl.maskStack.length) assert(0, "NanoVega: mask stack overflow in OpenGL backend");
12806 if (gl.maskStack.ptr[gl.msp-1] == GLMaskState.DontMask) {
12807 gl.maskStack.ptr[gl.msp++] = GLMaskState.DontMask;
12808 } else {
12809 gl.maskStack.ptr[gl.msp++] = GLMaskState.Uninitialized;
12811 // no need to reset FBO cache here, as nothing was changed
12812 break;
12813 case GLNVG_POPCLIP:
12814 if (gl.msp <= 1) assert(0, "NanoVega: mask stack underflow in OpenGL backend");
12815 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]); }
12816 --gl.msp;
12817 assert(gl.msp > 0);
12818 //{ import core.stdc.stdio; printf("popped; new msp is %d; state is %d\n", gl.msp, gl.maskStack.ptr[gl.msp]); }
12819 // check popped item
12820 final switch (gl.maskStack.ptr[gl.msp]) {
12821 case GLMaskState.DontMask:
12822 // if last FBO was "don't mask", reset cache if current is not "don't mask"
12823 if (gl.maskStack.ptr[gl.msp-1] != GLMaskState.DontMask) {
12824 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf(" +++ need to reset FBO cache\n"); }
12825 glnvg__resetFBOClipTextureCache(gl);
12827 break;
12828 case GLMaskState.Uninitialized:
12829 // if last FBO texture was uninitialized, it means that nothing was changed,
12830 // so we can keep using cached FBO
12831 break;
12832 case GLMaskState.Initialized:
12833 // if last FBO was initialized, it means that something was definitely changed
12834 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf(" +++ need to reset FBO cache\n"); }
12835 glnvg__resetFBOClipTextureCache(gl);
12836 break;
12837 case GLMaskState.JustCleared: assert(0, "NanoVega: internal FBO stack error");
12839 break;
12840 case GLNVG_RESETCLIP:
12841 // mark current mask as "don't mask"
12842 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]); }
12843 if (gl.msp > 0) {
12844 if (gl.maskStack.ptr[gl.msp-1] != GLMaskState.DontMask) {
12845 gl.maskStack.ptr[gl.msp-1] = GLMaskState.DontMask;
12846 version(nanovega_debug_clipping) if (nanovegaClipDebugDump) { import core.stdc.stdio; printf(" +++ need to reset FBO cache\n"); }
12847 glnvg__resetFBOClipTextureCache(gl);
12850 break;
12851 case GLNVG_CLIP_DDUMP_ON:
12852 version(nanovega_debug_clipping) nanovegaClipDebugDump = true;
12853 break;
12854 case GLNVG_CLIP_DDUMP_OFF:
12855 version(nanovega_debug_clipping) nanovegaClipDebugDump = false;
12856 break;
12857 case GLNVG_NONE: break;
12858 default:
12860 import core.stdc.stdio; stderr.fprintf("NanoVega FATAL: invalid command in OpenGL backend: %d\n", call.type);
12862 assert(0, "NanoVega: invalid command in OpenGL backend (fatal internal error)");
12864 // and free texture, why not
12865 glnvg__deleteTexture(gl, call.image);
12868 glDisableVertexAttribArray(0);
12869 glDisableVertexAttribArray(1);
12870 glDisable(GL_CULL_FACE);
12871 glBindBuffer(GL_ARRAY_BUFFER, 0);
12872 glUseProgram(0);
12873 glnvg__bindTexture(gl, 0);
12876 // this will do all necessary cleanup
12877 glnvg__renderCancelInternal(gl, false); // no need to clear textures
12880 int glnvg__maxVertCount (const(NVGpath)* paths, int npaths) nothrow @trusted @nogc {
12881 int count = 0;
12882 foreach (int i; 0..npaths) {
12883 count += paths[i].nfill;
12884 count += paths[i].nstroke;
12886 return count;
12889 GLNVGcall* glnvg__allocCall (GLNVGcontext* gl) nothrow @trusted @nogc {
12890 GLNVGcall* ret = null;
12891 if (gl.ncalls+1 > gl.ccalls) {
12892 GLNVGcall* calls;
12893 int ccalls = glnvg__maxi(gl.ncalls+1, 128)+gl.ccalls/2; // 1.5x Overallocate
12894 calls = cast(GLNVGcall*)realloc(gl.calls, GLNVGcall.sizeof*ccalls);
12895 if (calls is null) return null;
12896 gl.calls = calls;
12897 gl.ccalls = ccalls;
12899 ret = &gl.calls[gl.ncalls++];
12900 memset(ret, 0, GLNVGcall.sizeof);
12901 return ret;
12904 int glnvg__allocPaths (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
12905 int ret = 0;
12906 if (gl.npaths+n > gl.cpaths) {
12907 GLNVGpath* paths;
12908 int cpaths = glnvg__maxi(gl.npaths+n, 128)+gl.cpaths/2; // 1.5x Overallocate
12909 paths = cast(GLNVGpath*)realloc(gl.paths, GLNVGpath.sizeof*cpaths);
12910 if (paths is null) return -1;
12911 gl.paths = paths;
12912 gl.cpaths = cpaths;
12914 ret = gl.npaths;
12915 gl.npaths += n;
12916 return ret;
12919 int glnvg__allocVerts (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
12920 int ret = 0;
12921 if (gl.nverts+n > gl.cverts) {
12922 NVGvertex* verts;
12923 int cverts = glnvg__maxi(gl.nverts+n, 4096)+gl.cverts/2; // 1.5x Overallocate
12924 verts = cast(NVGvertex*)realloc(gl.verts, NVGvertex.sizeof*cverts);
12925 if (verts is null) return -1;
12926 gl.verts = verts;
12927 gl.cverts = cverts;
12929 ret = gl.nverts;
12930 gl.nverts += n;
12931 return ret;
12934 int glnvg__allocFragUniforms (GLNVGcontext* gl, int n) nothrow @trusted @nogc {
12935 int ret = 0, structSize = gl.fragSize;
12936 if (gl.nuniforms+n > gl.cuniforms) {
12937 ubyte* uniforms;
12938 int cuniforms = glnvg__maxi(gl.nuniforms+n, 128)+gl.cuniforms/2; // 1.5x Overallocate
12939 uniforms = cast(ubyte*)realloc(gl.uniforms, structSize*cuniforms);
12940 if (uniforms is null) return -1;
12941 gl.uniforms = uniforms;
12942 gl.cuniforms = cuniforms;
12944 ret = gl.nuniforms*structSize;
12945 gl.nuniforms += n;
12946 return ret;
12949 GLNVGfragUniforms* nvg__fragUniformPtr (GLNVGcontext* gl, int i) nothrow @trusted @nogc {
12950 return cast(GLNVGfragUniforms*)&gl.uniforms[i];
12953 void glnvg__vset (NVGvertex* vtx, float x, float y, float u, float v) nothrow @trusted @nogc {
12954 vtx.x = x;
12955 vtx.y = y;
12956 vtx.u = u;
12957 vtx.v = v;
12960 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 {
12961 if (npaths < 1) return;
12963 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
12964 GLNVGcall* call = glnvg__allocCall(gl);
12965 NVGvertex* quad;
12966 GLNVGfragUniforms* frag;
12967 int maxverts, offset;
12969 if (call is null) return;
12971 call.type = GLNVG_FILL;
12972 call.evenOdd = evenOdd;
12973 call.clipmode = clipmode;
12974 //if (clipmode != NVGClipMode.None) { import core.stdc.stdio; printf("CLIP!\n"); }
12975 call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
12976 call.triangleCount = 4;
12977 call.pathOffset = glnvg__allocPaths(gl, npaths);
12978 if (call.pathOffset == -1) goto error;
12979 call.pathCount = npaths;
12980 call.image = paint.image.id;
12981 if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
12983 if (npaths == 1 && paths[0].convex) {
12984 call.type = GLNVG_CONVEXFILL;
12985 call.triangleCount = 0; // Bounding box fill quad not needed for convex fill
12988 // Allocate vertices for all the paths.
12989 maxverts = glnvg__maxVertCount(paths, npaths)+call.triangleCount;
12990 offset = glnvg__allocVerts(gl, maxverts);
12991 if (offset == -1) goto error;
12993 foreach (int i; 0..npaths) {
12994 GLNVGpath* copy = &gl.paths[call.pathOffset+i];
12995 const(NVGpath)* path = &paths[i];
12996 memset(copy, 0, GLNVGpath.sizeof);
12997 if (path.nfill > 0) {
12998 copy.fillOffset = offset;
12999 copy.fillCount = path.nfill;
13000 memcpy(&gl.verts[offset], path.fill, NVGvertex.sizeof*path.nfill);
13001 offset += path.nfill;
13003 if (path.nstroke > 0) {
13004 copy.strokeOffset = offset;
13005 copy.strokeCount = path.nstroke;
13006 memcpy(&gl.verts[offset], path.stroke, NVGvertex.sizeof*path.nstroke);
13007 offset += path.nstroke;
13011 // Setup uniforms for draw calls
13012 if (call.type == GLNVG_FILL) {
13013 import core.stdc.string : memcpy;
13014 // Quad
13015 call.triangleOffset = offset;
13016 quad = &gl.verts[call.triangleOffset];
13017 glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f);
13018 glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f);
13019 glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f);
13020 glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f);
13021 // Get uniform
13022 call.uniformOffset = glnvg__allocFragUniforms(gl, 2);
13023 if (call.uniformOffset == -1) goto error;
13024 // Simple shader for stencil
13025 frag = nvg__fragUniformPtr(gl, call.uniformOffset);
13026 memset(frag, 0, (*frag).sizeof);
13027 glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, fringe, fringe, -1.0f);
13028 memcpy(nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), frag, (*frag).sizeof);
13029 frag.strokeThr = -1.0f;
13030 frag.type = NSVG_SHADER_SIMPLE;
13031 // Fill shader
13032 //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, fringe, fringe, -1.0f);
13033 } else {
13034 call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
13035 if (call.uniformOffset == -1) goto error;
13036 // Fill shader
13037 glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, fringe, fringe, -1.0f);
13040 return;
13042 error:
13043 // We get here if call alloc was ok, but something else is not.
13044 // Roll back the last call to prevent drawing it.
13045 if (gl.ncalls > 0) --gl.ncalls;
13048 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 {
13049 if (npaths < 1) return;
13051 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13052 GLNVGcall* call = glnvg__allocCall(gl);
13053 int maxverts, offset;
13055 if (call is null) return;
13057 call.type = GLNVG_STROKE;
13058 call.clipmode = clipmode;
13059 call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
13060 call.pathOffset = glnvg__allocPaths(gl, npaths);
13061 if (call.pathOffset == -1) goto error;
13062 call.pathCount = npaths;
13063 call.image = paint.image.id;
13064 if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
13066 // Allocate vertices for all the paths.
13067 maxverts = glnvg__maxVertCount(paths, npaths);
13068 offset = glnvg__allocVerts(gl, maxverts);
13069 if (offset == -1) goto error;
13071 foreach (int i; 0..npaths) {
13072 GLNVGpath* copy = &gl.paths[call.pathOffset+i];
13073 const(NVGpath)* path = &paths[i];
13074 memset(copy, 0, GLNVGpath.sizeof);
13075 if (path.nstroke) {
13076 copy.strokeOffset = offset;
13077 copy.strokeCount = path.nstroke;
13078 memcpy(&gl.verts[offset], path.stroke, NVGvertex.sizeof*path.nstroke);
13079 offset += path.nstroke;
13083 if (gl.flags&NVGContextFlag.StencilStrokes) {
13084 // Fill shader
13085 call.uniformOffset = glnvg__allocFragUniforms(gl, 2);
13086 if (call.uniformOffset == -1) goto error;
13087 glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
13088 glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, strokeWidth, fringe, 1.0f-0.5f/255.0f);
13089 } else {
13090 // Fill shader
13091 call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
13092 if (call.uniformOffset == -1) goto error;
13093 glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
13096 return;
13098 error:
13099 // We get here if call alloc was ok, but something else is not.
13100 // Roll back the last call to prevent drawing it.
13101 if (gl.ncalls > 0) --gl.ncalls;
13104 void glnvg__renderTriangles (void* uptr, NVGCompositeOperationState compositeOperation, NVGClipMode clipmode, NVGPaint* paint, NVGscissor* scissor, const(NVGvertex)* verts, int nverts) nothrow @trusted @nogc {
13105 if (nverts < 1) return;
13107 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13108 GLNVGcall* call = glnvg__allocCall(gl);
13109 GLNVGfragUniforms* frag;
13111 if (call is null) return;
13113 call.type = GLNVG_TRIANGLES;
13114 call.clipmode = clipmode;
13115 call.blendFunc = glnvg__buildBlendFunc(compositeOperation);
13116 call.image = paint.image.id;
13117 if (call.image > 0) glnvg__renderTextureIncRef(uptr, call.image);
13119 // Allocate vertices for all the paths.
13120 call.triangleOffset = glnvg__allocVerts(gl, nverts);
13121 if (call.triangleOffset == -1) goto error;
13122 call.triangleCount = nverts;
13124 memcpy(&gl.verts[call.triangleOffset], verts, NVGvertex.sizeof*nverts);
13126 // Fill shader
13127 call.uniformOffset = glnvg__allocFragUniforms(gl, 1);
13128 if (call.uniformOffset == -1) goto error;
13129 frag = nvg__fragUniformPtr(gl, call.uniformOffset);
13130 glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
13131 frag.type = NSVG_SHADER_IMG;
13133 return;
13135 error:
13136 // We get here if call alloc was ok, but something else is not.
13137 // Roll back the last call to prevent drawing it.
13138 if (gl.ncalls > 0) --gl.ncalls;
13141 void glnvg__renderDelete (void* uptr) nothrow @trusted @nogc {
13142 GLNVGcontext* gl = cast(GLNVGcontext*)uptr;
13143 if (gl is null) return;
13145 glnvg__killFBOs(gl);
13146 glnvg__deleteShader(&gl.shader);
13147 glnvg__deleteShader(&gl.shaderFillFBO);
13148 glnvg__deleteShader(&gl.shaderCopyFBO);
13150 if (gl.vertBuf != 0) glDeleteBuffers(1, &gl.vertBuf);
13152 foreach (ref GLNVGtexture tex; gl.textures[0..gl.ntextures]) {
13153 if (tex.id != 0 && (tex.flags&NVGImageFlagsGL.NoDelete) == 0) {
13154 assert(tex.tex != 0);
13155 glDeleteTextures(1, &tex.tex);
13158 free(gl.textures);
13160 free(gl.paths);
13161 free(gl.verts);
13162 free(gl.uniforms);
13163 free(gl.calls);
13165 free(gl);
13169 /** Creates NanoVega contexts for OpenGL2+.
13171 * Specify creation flags as additional arguments, like this:
13172 * `nvgCreateContext(NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes);`
13174 * If you won't specify any flags, defaults will be used:
13175 * `[NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes]`.
13177 * Group: context_management
13179 public NVGContext nvgCreateContext (const(NVGContextFlag)[] flagList...) nothrow @trusted @nogc {
13180 version(aliced) {
13181 enum DefaultFlags = NVGContextFlag.Antialias|NVGContextFlag.StencilStrokes|NVGContextFlag.FontNoAA;
13182 } else {
13183 enum DefaultFlags = NVGContextFlag.Antialias|NVGContextFlag.StencilStrokes;
13185 uint flags = 0;
13186 if (flagList.length != 0) {
13187 foreach (immutable flg; flagList) flags |= (flg != NVGContextFlag.Default ? flg : DefaultFlags);
13188 } else {
13189 flags = DefaultFlags;
13191 NVGparams params = void;
13192 NVGContext ctx = null;
13193 version(nanovg_builtin_opengl_bindings) nanovgInitOpenGL(); // why not?
13194 GLNVGcontext* gl = cast(GLNVGcontext*)malloc(GLNVGcontext.sizeof);
13195 if (gl is null) goto error;
13196 memset(gl, 0, GLNVGcontext.sizeof);
13198 memset(&params, 0, params.sizeof);
13199 params.renderCreate = &glnvg__renderCreate;
13200 params.renderCreateTexture = &glnvg__renderCreateTexture;
13201 params.renderTextureIncRef = &glnvg__renderTextureIncRef;
13202 params.renderDeleteTexture = &glnvg__renderDeleteTexture;
13203 params.renderUpdateTexture = &glnvg__renderUpdateTexture;
13204 params.renderGetTextureSize = &glnvg__renderGetTextureSize;
13205 params.renderViewport = &glnvg__renderViewport;
13206 params.renderCancel = &glnvg__renderCancel;
13207 params.renderFlush = &glnvg__renderFlush;
13208 params.renderPushClip = &glnvg__renderPushClip;
13209 params.renderPopClip = &glnvg__renderPopClip;
13210 params.renderResetClip = &glnvg__renderResetClip;
13211 params.renderFill = &glnvg__renderFill;
13212 params.renderStroke = &glnvg__renderStroke;
13213 params.renderTriangles = &glnvg__renderTriangles;
13214 params.renderSetAffine = &glnvg__renderSetAffine;
13215 params.renderDelete = &glnvg__renderDelete;
13216 params.userPtr = gl;
13217 params.edgeAntiAlias = (flags&NVGContextFlag.Antialias ? true : false);
13218 if (flags&(NVGContextFlag.FontAA|NVGContextFlag.FontNoAA)) {
13219 params.fontAA = (flags&NVGContextFlag.FontNoAA ? NVG_INVERT_FONT_AA : !NVG_INVERT_FONT_AA);
13220 } else {
13221 params.fontAA = NVG_INVERT_FONT_AA;
13224 gl.flags = flags;
13225 gl.freetexid = -1;
13227 ctx = createInternal(&params);
13228 if (ctx is null) goto error;
13230 return ctx;
13232 error:
13233 // 'gl' is freed by nvgDeleteInternal.
13234 if (ctx !is null) ctx.deleteInternal();
13235 return null;
13238 /// Create NanoVega OpenGL image from texture id.
13239 /// Group: images
13240 public int glCreateImageFromHandleGL2 (NVGContext ctx, GLuint textureId, int w, int h, int imageFlags) nothrow @trusted @nogc {
13241 GLNVGcontext* gl = cast(GLNVGcontext*)ctx.internalParams().userPtr;
13242 GLNVGtexture* tex = glnvg__allocTexture(gl);
13244 if (tex is null) return 0;
13246 tex.type = NVGtexture.RGBA;
13247 tex.tex = textureId;
13248 tex.flags = imageFlags;
13249 tex.width = w;
13250 tex.height = h;
13252 return tex.id;
13255 /// Returns OpenGL texture id for NanoVega image.
13256 /// Group: images
13257 public GLuint glImageHandleGL2 (NVGContext ctx, int image) nothrow @trusted @nogc {
13258 GLNVGcontext* gl = cast(GLNVGcontext*)ctx.internalParams().userPtr;
13259 GLNVGtexture* tex = glnvg__findTexture(gl, image);
13260 return tex.tex;
13264 // ////////////////////////////////////////////////////////////////////////// //
13265 private:
13267 static if (NanoVegaHasFontConfig) {
13268 version(nanovg_builtin_fontconfig_bindings) {
13269 pragma(lib, "fontconfig");
13271 private extern(C) nothrow @trusted @nogc {
13272 enum FC_FILE = "file"; /* String */
13273 alias FcBool = int;
13274 alias FcChar8 = char;
13275 struct FcConfig;
13276 struct FcPattern;
13277 alias FcMatchKind = int;
13278 enum : FcMatchKind {
13279 FcMatchPattern,
13280 FcMatchFont,
13281 FcMatchScan
13283 alias FcResult = int;
13284 enum : FcResult {
13285 FcResultMatch,
13286 FcResultNoMatch,
13287 FcResultTypeMismatch,
13288 FcResultNoId,
13289 FcResultOutOfMemory
13291 FcBool FcInit ();
13292 FcBool FcConfigSubstituteWithPat (FcConfig* config, FcPattern* p, FcPattern* p_pat, FcMatchKind kind);
13293 void FcDefaultSubstitute (FcPattern* pattern);
13294 FcBool FcConfigSubstitute (FcConfig* config, FcPattern* p, FcMatchKind kind);
13295 FcPattern* FcFontMatch (FcConfig* config, FcPattern* p, FcResult* result);
13296 FcPattern* FcNameParse (const(FcChar8)* name);
13297 void FcPatternDestroy (FcPattern* p);
13298 FcResult FcPatternGetString (const(FcPattern)* p, const(char)* object, int n, FcChar8** s);
13302 __gshared bool fontconfigAvailable = false;
13303 // initialize fontconfig
13304 shared static this () {
13305 if (FcInit()) {
13306 fontconfigAvailable = true;
13307 } else {
13308 import core.stdc.stdio : stderr, fprintf;
13309 stderr.fprintf("***NanoVega WARNING: cannot init fontconfig!\n");
13315 // ////////////////////////////////////////////////////////////////////////// //
13316 public enum BaphometDims = 512.0f; // baphomet icon is 512x512 ([0..511])
13318 private static immutable ubyte[7641] baphometPath = [
13319 0x01,0x04,0x06,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x08,0xa0,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,
13320 0x00,0x80,0xff,0x43,0xa2,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x08,0x00,0x80,0xff,
13321 0x43,0x7a,0x89,0xe5,0x42,0xa0,0x1d,0xc6,0x43,0x00,0x00,0x00,0x00,0x30,0x89,0x7f,0x43,0x00,0x00,0x00,
13322 0x00,0x08,0x7a,0x89,0xe5,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x89,0xe5,0x42,0x00,0x00,
13323 0x00,0x00,0x30,0x89,0x7f,0x43,0x08,0x00,0x00,0x00,0x00,0xa2,0x1d,0xc6,0x43,0x7a,0x89,0xe5,0x42,0x00,
13324 0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x09,0x06,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
13325 0x43,0x08,0x16,0x68,0xb3,0x43,0x72,0x87,0xdd,0x43,0x71,0x87,0xdd,0x43,0x17,0x68,0xb3,0x43,0x71,0x87,
13326 0xdd,0x43,0x30,0x89,0x7f,0x43,0x08,0x71,0x87,0xdd,0x43,0xd2,0x2f,0x18,0x43,0x16,0x68,0xb3,0x43,0x35,
13327 0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x35,0xe2,0x87,0x42,0x08,0xd1,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,
13328 0x35,0xe2,0x87,0x42,0xd2,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x08,0x35,0xe2,0x87,
13329 0x42,0x17,0x68,0xb3,0x43,0xd1,0x2f,0x18,0x43,0x72,0x87,0xdd,0x43,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
13330 0x43,0x09,0x06,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x07,0xa4,0x3f,0x7f,0x43,0x0b,0x86,0xdc,0x43,
13331 0x07,0x6c,0xb9,0xb2,0x43,0xe8,0xd1,0xca,0x42,0x07,0x6e,0x4d,0xa0,0x42,0xa9,0x10,0x9c,0x43,0x07,0xb7,
13332 0x40,0xd7,0x43,0xa9,0x10,0x9c,0x43,0x07,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x09,0x06,0x98,0x42,
13333 0x74,0x43,0xb1,0x8d,0x68,0x43,0x08,0xd7,0x24,0x79,0x43,0xba,0x83,0x6e,0x43,0xa9,0x16,0x7c,0x43,0x56,
13334 0xa1,0x76,0x43,0x74,0x2a,0x7d,0x43,0x44,0x73,0x80,0x43,0x08,0x55,0xd1,0x7e,0x43,0xe3,0xea,0x76,0x43,
13335 0xbc,0x18,0x81,0x43,0x7f,0xa8,0x6e,0x43,0x8f,0x0a,0x84,0x43,0x02,0xfc,0x68,0x43,0x09,0x06,0x92,0x29,
13336 0x8d,0x43,0x73,0xc3,0x67,0x43,0x08,0xa4,0xd9,0x8e,0x43,0xf2,0xa6,0x7a,0x43,0x8f,0x22,0x88,0x43,0x75,
13337 0x2a,0x7d,0x43,0x42,0x7f,0x82,0x43,0x08,0xc8,0x88,0x43,0x09,0x06,0xc1,0x79,0x74,0x43,0x50,0x64,0x89,
13338 0x43,0x08,0x68,0x2d,0x72,0x43,0xee,0x21,0x81,0x43,0xcd,0x97,0x55,0x43,0xe6,0xf1,0x7b,0x43,0x91,0xec,
13339 0x5d,0x43,0xa8,0xc7,0x6a,0x43,0x09,0x06,0xfa,0xa5,0x52,0x43,0x60,0x97,0x7c,0x43,0x08,0x19,0xff,0x50,
13340 0x43,0xe9,0x6e,0x8a,0x43,0xb0,0xbd,0x70,0x43,0x4c,0x51,0x82,0x43,0x04,0xeb,0x69,0x43,0x66,0x0f,0x8e,
13341 0x43,0x09,0x06,0x17,0xbf,0x71,0x43,0x2c,0x58,0x94,0x43,0x08,0x1c,0x96,0x6e,0x43,0x61,0x68,0x99,0x43,
13342 0x2d,0x3a,0x6e,0x43,0xc8,0x81,0x9e,0x43,0xb7,0x9b,0x72,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0x30,0xdb,
13343 0x82,0x43,0xdb,0xe9,0x93,0x43,0x08,0x11,0x82,0x84,0x43,0x61,0x68,0x99,0x43,0xe8,0x4a,0x84,0x43,0x8e,
13344 0xa6,0x9e,0x43,0x42,0x7f,0x82,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0xc4,0x02,0x85,0x43,0xd1,0x0b,0x92,
13345 0x43,0x08,0xd6,0xb2,0x86,0x43,0x34,0x1e,0x92,0x43,0x4f,0x58,0x87,0x43,0xa4,0xf1,0x92,0x43,0x03,0xd9,
13346 0x87,0x43,0x7b,0xc6,0x94,0x43,0x09,0x06,0x87,0x3e,0x64,0x43,0x31,0x3b,0x93,0x43,0x08,0x3b,0xbf,0x64,
13347 0x43,0x6f,0xf9,0x91,0x43,0x96,0x0b,0x67,0x43,0xc5,0x4a,0x91,0x43,0xcf,0xfe,0x6a,0x43,0x31,0x2f,0x91,
13348 0x43,0x09,0x06,0x16,0x74,0xb5,0x43,0x08,0xec,0x8e,0x43,0x08,0x1b,0x4b,0xb2,0x43,0xee,0x5d,0x8b,0x43,
13349 0x48,0x4d,0xad,0x43,0x12,0xa6,0x8a,0x43,0xf3,0xd7,0xa7,0x43,0x74,0xb8,0x8a,0x43,0x08,0x8c,0xb2,0xa0,
13350 0x43,0xcd,0xf8,0x8a,0x43,0x68,0x46,0x9b,0x43,0x79,0x8f,0x87,0x43,0x49,0xc9,0x96,0x43,0xe9,0x3e,0x82,
13351 0x43,0x08,0x60,0x5c,0x97,0x43,0xa1,0xde,0x8b,0x43,0x4e,0xa0,0x93,0x43,0x31,0x3b,0x93,0x43,0x9f,0xea,
13352 0x8d,0x43,0x27,0x8d,0x99,0x43,0x08,0x07,0xe0,0x8c,0x43,0x06,0x34,0x9b,0x43,0x38,0xe9,0x8c,0x43,0x46,
13353 0x0a,0x9e,0x43,0x3d,0xcc,0x8b,0x43,0xb2,0x06,0xa2,0x43,0x08,0xf1,0x40,0x8a,0x43,0xb0,0x12,0xa4,0x43,
13354 0x39,0xd1,0x88,0x43,0x76,0x43,0xa6,0x43,0xfa,0x06,0x88,0x43,0xa4,0x75,0xa9,0x43,0x08,0x19,0x6c,0x88,
13355 0x43,0x9f,0x9e,0xac,0x43,0x66,0xeb,0x87,0x43,0x44,0x76,0xb0,0x43,0x6b,0xce,0x86,0x43,0x3b,0xbc,0xb4,
13356 0x43,0x08,0xa9,0x8c,0x85,0x43,0x06,0xd0,0xb5,0x43,0xfa,0xee,0x83,0x43,0x74,0xa3,0xb6,0x43,0x3d,0x90,
13357 0x81,0x43,0x31,0xf6,0xb6,0x43,0x08,0x9d,0x61,0x7d,0x43,0xee,0x48,0xb7,0x43,0x3b,0x1f,0x75,0x43,0xcf,
13358 0xe3,0xb6,0x43,0xee,0x6f,0x6d,0x43,0x68,0xe2,0xb5,0x43,0x08,0xd4,0xed,0x6b,0x43,0x87,0x2f,0xb2,0x43,
13359 0x0e,0xc9,0x6b,0x43,0xa7,0x7c,0xae,0x43,0x98,0xfa,0x67,0x43,0xab,0x53,0xab,0x43,0x08,0x25,0x2c,0x64,
13360 0x43,0x33,0xa2,0xa8,0x43,0x40,0x96,0x61,0x43,0xc3,0xc2,0xa5,0x43,0x64,0xde,0x60,0x43,0xfa,0xa2,0xa2,
13361 0x43,0x08,0xb0,0x5d,0x60,0x43,0x06,0x4c,0x9f,0x43,0x9a,0xca,0x5f,0x43,0x38,0x3d,0x9b,0x43,0x3b,0x8f,
13362 0x5c,0x43,0x85,0xb0,0x98,0x43,0x08,0x42,0x36,0x51,0x43,0x3d,0xf0,0x91,0x43,0xcd,0x4f,0x49,0x43,0xdb,
13363 0xb9,0x8b,0x43,0xe0,0xdb,0x44,0x43,0x42,0x8b,0x84,0x43,0x08,0x7e,0xc9,0x44,0x43,0x8a,0x57,0x8d,0x43,
13364 0xbc,0x6c,0x0f,0x43,0x23,0x62,0x8e,0x43,0xf5,0x17,0x07,0x43,0xc5,0x3e,0x8f,0x43,0x09,0x06,0xe0,0xea,
13365 0x76,0x43,0xab,0xef,0xc5,0x43,0x08,0x12,0x00,0x79,0x43,0xab,0xcb,0xbf,0x43,0x79,0xb9,0x6d,0x43,0x7e,
13366 0x8d,0xba,0x43,0xee,0x6f,0x6d,0x43,0x98,0xeb,0xb5,0x43,0x08,0xe0,0x02,0x7b,0x43,0x5f,0x1c,0xb8,0x43,
13367 0x85,0x2c,0x82,0x43,0xe9,0x65,0xb8,0x43,0xd6,0xb2,0x86,0x43,0xc6,0x05,0xb5,0x43,0x08,0x03,0xcd,0x85,
13368 0x43,0x5a,0x39,0xb9,0x43,0xe4,0x4f,0x81,0x43,0xdb,0xd4,0xbf,0x43,0xdf,0x6c,0x82,0x43,0xbc,0x93,0xc5,
13369 0x43,0x09,0x06,0xf0,0xd0,0x22,0x43,0x5d,0x19,0x08,0x43,0x08,0xbc,0xab,0x49,0x43,0x4a,0x35,0x29,0x43,
13370 0xcb,0xf7,0x65,0x43,0xce,0x37,0x45,0x43,0x0e,0x99,0x63,0x43,0x67,0xc6,0x5c,0x43,0x09,0x06,0x05,0x94,
13371 0xab,0x43,0xc2,0x13,0x04,0x43,0x08,0x9f,0x26,0x98,0x43,0x11,0x42,0x25,0x43,0x97,0x00,0x8a,0x43,0x32,
13372 0x32,0x41,0x43,0xf5,0x2f,0x8b,0x43,0xc7,0xc0,0x58,0x43,0x09,0x06,0x8f,0x85,0x48,0x43,0xe0,0xa8,0x8c,
13373 0x43,0x08,0x55,0xaa,0x48,0x43,0xe0,0xa8,0x8c,0x43,0x6b,0x3d,0x49,0x43,0xc1,0x43,0x8c,0x43,0x31,0x62,
13374 0x49,0x43,0xc1,0x43,0x8c,0x43,0x08,0x2f,0xe3,0x2f,0x43,0xad,0xe7,0x98,0x43,0xff,0x0d,0x0d,0x43,0xad,
13375 0xf3,0x9a,0x43,0xf0,0xaf,0xcc,0x42,0x74,0x00,0x97,0x43,0x08,0xbb,0xa2,0xf7,0x42,0x93,0x4d,0x93,0x43,
13376 0x5e,0x19,0x08,0x43,0x5a,0x2a,0x87,0x43,0x23,0x6e,0x10,0x43,0x42,0x97,0x86,0x43,0x08,0xca,0xe8,0x33,
13377 0x43,0x1b,0x3c,0x80,0x43,0x80,0xe8,0x4d,0x43,0xda,0xf4,0x70,0x43,0xae,0x0e,0x4f,0x43,0x2b,0x1b,0x65,
13378 0x43,0x08,0x66,0x96,0x54,0x43,0xa3,0xe1,0x3b,0x43,0x4e,0xc4,0x19,0x43,0xa0,0x1a,0x16,0x43,0x10,0xe2,
13379 0x14,0x43,0x26,0x14,0xe0,0x42,0x08,0x5c,0x91,0x1c,0x43,0xcb,0x27,0xee,0x42,0xa9,0x40,0x24,0x43,0x71,
13380 0x3b,0xfc,0x42,0xf3,0xef,0x2b,0x43,0x8b,0x27,0x05,0x43,0x08,0xe2,0x4b,0x2c,0x43,0x48,0x86,0x07,0x43,
13381 0x79,0x62,0x2f,0x43,0x05,0xe5,0x09,0x43,0x55,0x32,0x34,0x43,0xa0,0xd2,0x09,0x43,0x08,0x74,0xa3,0x36,
13382 0x43,0x3a,0xd1,0x08,0x43,0x7e,0x81,0x38,0x43,0x09,0xd4,0x0a,0x43,0x0d,0xba,0x39,0x43,0xa0,0xea,0x0d,
13383 0x43,0x08,0x6f,0xe4,0x3d,0x43,0x43,0xc7,0x0e,0x43,0xd6,0xe5,0x3e,0x43,0xc4,0x4a,0x11,0x43,0x55,0x7a,
13384 0x40,0x43,0x59,0x72,0x13,0x43,0x08,0x55,0x92,0x44,0x43,0xbf,0x73,0x14,0x43,0x23,0x95,0x46,0x43,0xa5,
13385 0x09,0x17,0x43,0xe0,0xf3,0x48,0x43,0xfe,0x55,0x19,0x43,0x08,0xcd,0x4f,0x49,0x43,0xaa,0x10,0x1c,0x43,
13386 0x61,0x77,0x4b,0x43,0xfe,0x6d,0x1d,0x43,0x80,0xe8,0x4d,0x43,0x2b,0x94,0x1e,0x43,0x08,0x58,0xc9,0x51,
13387 0x43,0x41,0x27,0x1f,0x43,0x9b,0x82,0x53,0x43,0x35,0x72,0x20,0x43,0x53,0xf2,0x54,0x43,0x88,0xcf,0x21,
13388 0x43,0x08,0x7b,0x29,0x55,0x43,0xe8,0x0a,0x25,0x43,0xb2,0x2d,0x58,0x43,0xef,0xe8,0x26,0x43,0x9b,0xb2,
13389 0x5b,0x43,0xd0,0x8f,0x28,0x43,0x08,0x5f,0xef,0x5f,0x43,0xeb,0x11,0x2a,0x43,0xfd,0xdc,0x5f,0x43,0x6e,
13390 0x95,0x2c,0x43,0x3b,0xa7,0x60,0x43,0x2b,0xf4,0x2e,0x43,0x08,0x06,0xbb,0x61,0x43,0xfd,0xe5,0x31,0x43,
13391 0xe7,0x61,0x63,0x43,0xef,0x30,0x33,0x43,0x53,0x52,0x65,0x43,0xa3,0xb1,0x33,0x43,0x08,0x12,0xa0,0x68,
13392 0x43,0x7f,0x69,0x34,0x43,0x40,0xc6,0x69,0x43,0x64,0xff,0x36,0x43,0x7e,0x90,0x6a,0x43,0x71,0xcc,0x39,
13393 0x43,0x08,0xbc,0x5a,0x6b,0x43,0x51,0x73,0x3b,0x43,0xc1,0x49,0x6c,0x43,0xa5,0xd0,0x3c,0x43,0xe0,0xba,
13394 0x6e,0x43,0xb8,0x74,0x3c,0x43,0x08,0x6b,0x1c,0x73,0x43,0x13,0xc1,0x3e,0x43,0x40,0xf6,0x71,0x43,0xce,
13395 0x1f,0x41,0x43,0x55,0x89,0x72,0x43,0x8d,0x7e,0x43,0x43,0x08,0x68,0x2d,0x72,0x43,0x89,0xae,0x4b,0x43,
13396 0xc1,0x79,0x74,0x43,0xcb,0x78,0x4c,0x43,0x55,0xa1,0x76,0x43,0x5b,0xb1,0x4d,0x43,0x08,0xa2,0x38,0x7a,
13397 0x43,0xd1,0x56,0x4e,0x43,0x85,0xb6,0x78,0x43,0xb1,0x15,0x54,0x43,0x83,0xc7,0x77,0x43,0x89,0x0e,0x5c,
13398 0x43,0x08,0xcf,0x46,0x77,0x43,0x0f,0x81,0x5f,0x43,0x1a,0xde,0x7a,0x43,0xce,0xc7,0x5d,0x43,0x42,0x73,
13399 0x80,0x43,0x99,0xc3,0x5a,0x43,0x08,0x85,0x2c,0x82,0x43,0xf6,0xe6,0x59,0x43,0x81,0x3d,0x81,0x43,0x16,
13400 0x10,0x50,0x43,0xd6,0x8e,0x80,0x43,0x5b,0x99,0x49,0x43,0x08,0xc4,0xea,0x80,0x43,0x22,0x95,0x46,0x43,
13401 0xfa,0xe2,0x81,0x43,0xda,0xec,0x43,0x43,0x78,0x77,0x83,0x43,0xe4,0xb2,0x41,0x43,0x08,0x8a,0x27,0x85,
13402 0x43,0x86,0x77,0x3e,0x43,0x0c,0x9f,0x85,0x43,0x07,0xf4,0x3b,0x43,0x8f,0x16,0x86,0x43,0xe6,0x82,0x39,
13403 0x43,0x08,0x85,0x44,0x86,0x43,0x37,0xd9,0x35,0x43,0x1e,0x4f,0x87,0x43,0xe1,0x7b,0x34,0x43,0xdf,0x90,
13404 0x88,0x43,0xb6,0x55,0x33,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0xe5,0x31,0x43,0xfa,0x12,0x8a,0x43,0xbf,
13405 0x03,0x2d,0x43,0x19,0x78,0x8a,0x43,0x45,0x5e,0x2c,0x43,0x08,0x03,0xf1,0x8b,0x43,0xac,0x47,0x29,0x43,
13406 0x2f,0x17,0x8d,0x43,0x45,0x46,0x28,0x43,0xc8,0x21,0x8e,0x43,0x30,0xb3,0x27,0x43,0x08,0xa9,0xc8,0x8f,
13407 0x43,0xef,0xe8,0x26,0x43,0xbf,0x5b,0x90,0x43,0x5b,0xc1,0x24,0x43,0x10,0xca,0x90,0x43,0xa0,0x62,0x22,
13408 0x43,0x08,0x26,0x5d,0x91,0x43,0xbb,0xcc,0x1f,0x43,0xf0,0x70,0x92,0x43,0x78,0x13,0x1e,0x43,0x77,0xd7,
13409 0x93,0x43,0x73,0x24,0x1d,0x43,0x08,0x65,0x3f,0x96,0x43,0xce,0x58,0x1b,0x43,0xbe,0x7f,0x96,0x43,0xbf,
13410 0x8b,0x18,0x43,0x60,0x5c,0x97,0x43,0xb6,0xad,0x16,0x43,0x08,0xba,0xa8,0x99,0x43,0x78,0xcb,0x11,0x43,
13411 0x49,0xe1,0x9a,0x43,0x78,0xcb,0x11,0x43,0x01,0x51,0x9c,0x43,0x73,0xdc,0x10,0x43,0x08,0x72,0x24,0x9d,
13412 0x43,0xd2,0xff,0x0f,0x43,0x1c,0xd3,0x9d,0x43,0x07,0xec,0x0e,0x43,0xeb,0xc9,0x9d,0x43,0xe8,0x7a,0x0c,
13413 0x43,0x08,0x60,0x80,0x9d,0x43,0xd7,0xbe,0x08,0x43,0x4d,0xe8,0x9f,0x43,0x86,0x50,0x08,0x43,0x25,0xbd,
13414 0xa1,0x43,0x5b,0x2a,0x07,0x43,0x08,0x99,0x7f,0xa3,0x43,0xc9,0xf1,0x05,0x43,0x48,0x1d,0xa5,0x43,0x86,
13415 0x38,0x04,0x43,0x6c,0x71,0xa6,0x43,0x18,0x59,0x01,0x43,0x08,0x32,0x96,0xa6,0x43,0x6e,0x64,0xff,0x42,
13416 0x48,0x29,0xa7,0x43,0xed,0xcf,0xfd,0x42,0x5f,0xbc,0xa7,0x43,0x71,0x3b,0xfc,0x42,0x08,0xf3,0xe3,0xa9,
13417 0x43,0xf7,0x7d,0xf7,0x42,0xd8,0x6d,0xaa,0x43,0x45,0xe5,0xf2,0x42,0x48,0x41,0xab,0x43,0xcb,0x27,0xee,
13418 0x42,0x08,0x24,0xf9,0xab,0x43,0x52,0x6a,0xe9,0x42,0xee,0x0c,0xad,0x43,0x4c,0x8c,0xe7,0x42,0x1b,0x33,
13419 0xae,0x43,0xcc,0xf7,0xe5,0x42,0x08,0xaa,0x6b,0xaf,0x43,0xe8,0x61,0xe3,0x42,0x90,0xf5,0xaf,0x43,0xc9,
13420 0xf0,0xe0,0x42,0xe0,0x63,0xb0,0x43,0xe5,0x5a,0xde,0x42,0x08,0xaa,0x83,0xb3,0x43,0x29,0x2d,0x09,0x43,
13421 0x6a,0xfe,0x8e,0x43,0xb8,0x74,0x3c,0x43,0xd5,0x06,0x95,0x43,0xe6,0x79,0x67,0x43,0x08,0x2f,0x53,0x97,
13422 0x43,0xe9,0xb0,0x74,0x43,0xa8,0x28,0xa0,0x43,0x43,0xfd,0x76,0x43,0x83,0x28,0xad,0x43,0x17,0x59,0x81,
13423 0x43,0x08,0x3d,0xe7,0xbf,0x43,0x4b,0x8d,0x8c,0x43,0xae,0x96,0xba,0x43,0x66,0x27,0x92,0x43,0x15,0xe0,
13424 0xc7,0x43,0x6f,0x11,0x96,0x43,0x08,0x7e,0x5d,0xb2,0x43,0xdb,0x01,0x98,0x43,0x9e,0x56,0xa0,0x43,0x80,
13425 0xc1,0x97,0x43,0x69,0x2e,0x97,0x43,0x31,0x17,0x8d,0x43,0x09,0x06,0xab,0xa7,0x39,0x43,0x67,0x0f,0x0e,
13426 0x43,0x08,0xdb,0xbc,0x3b,0x43,0xe8,0x92,0x10,0x43,0xb5,0x85,0x3b,0x43,0x97,0x3c,0x14,0x43,0xab,0xa7,
13427 0x39,0x43,0x0c,0x0b,0x18,0x43,0x09,0x06,0xca,0x30,0x40,0x43,0x30,0x3b,0x13,0x43,0x08,0x17,0xc8,0x43,
13428 0x43,0xa5,0x09,0x17,0x43,0x7e,0xc9,0x44,0x43,0x1a,0xd8,0x1a,0x43,0x9d,0x22,0x43,0x43,0x8d,0xa6,0x1e,
13429 0x43,0x09,0x06,0xc8,0x78,0x4c,0x43,0xed,0xc9,0x1d,0x43,0x08,0x0b,0x32,0x4e,0x43,0x22,0xce,0x20,0x43,
13430 0x23,0xc5,0x4e,0x43,0x58,0xd2,0x23,0x43,0x0b,0x32,0x4e,0x43,0x2b,0xc4,0x26,0x43,0x09,0x06,0xec,0x08,
13431 0x58,0x43,0xc7,0xb1,0x26,0x43,0x08,0x02,0x9c,0x58,0x43,0xef,0x00,0x2b,0x43,0xd9,0x64,0x58,0x43,0x02,
13432 0xbd,0x2e,0x43,0x10,0x51,0x57,0x43,0x37,0xc1,0x31,0x43,0x09,0x06,0xcb,0xdf,0x61,0x43,0x4a,0x65,0x31,
13433 0x43,0x08,0xbe,0x2a,0x63,0x43,0xbd,0x33,0x35,0x43,0x32,0xe1,0x62,0x43,0x56,0x4a,0x38,0x43,0xde,0x83,
13434 0x61,0x43,0x3c,0xe0,0x3a,0x43,0x09,0x06,0x1c,0x7e,0x6a,0x43,0x5b,0x39,0x39,0x43,0x08,0x31,0x11,0x6b,
13435 0x43,0x0c,0xd2,0x3d,0x43,0x1c,0x7e,0x6a,0x43,0x13,0xd9,0x42,0x43,0xd9,0xc4,0x68,0x43,0xcb,0x60,0x48,
13436 0x43,0x09,0x06,0xe5,0xc1,0x73,0x43,0x16,0xf8,0x4b,0x43,0x08,0xa6,0xf7,0x72,0x43,0xb1,0xfd,0x4f,0x43,
13437 0x3b,0x07,0x71,0x43,0x4a,0x14,0x53,0x43,0xa2,0xf0,0x6d,0x43,0x7c,0x29,0x55,0x43,0x09,0x06,0x00,0x8d,
13438 0xa6,0x43,0xef,0x21,0x01,0x43,0x08,0x52,0xfb,0xa6,0x43,0xce,0xc8,0x02,0x43,0xe6,0x16,0xa7,0x43,0x51,
13439 0x4c,0x05,0x43,0x3b,0x68,0xa6,0x43,0x4c,0x75,0x08,0x43,0x09,0x06,0xde,0x20,0xa1,0x43,0x86,0x50,0x08,
13440 0x43,0x08,0xd4,0x4e,0xa1,0x43,0xd3,0xe7,0x0b,0x43,0xb5,0xe9,0xa0,0x43,0x59,0x5a,0x0f,0x43,0xba,0xcc,
13441 0x9f,0x43,0x54,0x83,0x12,0x43,0x09,0x06,0x77,0xfb,0x99,0x43,0x6c,0x16,0x13,0x43,0x08,0xde,0xfc,0x9a,
13442 0x43,0x4a,0xbd,0x14,0x43,0x06,0x34,0x9b,0x43,0xfe,0x55,0x19,0x43,0x13,0xe9,0x99,0x43,0x41,0x27,0x1f,
13443 0x43,0x09,0x06,0x46,0xce,0x93,0x43,0x26,0xa5,0x1d,0x43,0x08,0xe7,0xaa,0x94,0x43,0xbb,0xcc,0x1f,0x43,
13444 0x18,0xb4,0x94,0x43,0xa8,0x40,0x24,0x43,0xe2,0xbb,0x93,0x43,0x21,0xfe,0x28,0x43,0x09,0x06,0xb1,0x8e,
13445 0x8d,0x43,0xa8,0x58,0x28,0x43,0x08,0x19,0x90,0x8e,0x43,0x54,0x13,0x2b,0x43,0xa4,0xd9,0x8e,0x43,0x84,
13446 0x40,0x31,0x43,0x46,0xaa,0x8d,0x43,0x29,0x24,0x37,0x43,0x09,0x06,0xd6,0xbe,0x88,0x43,0xef,0x30,0x33,
13447 0x43,0x08,0x0c,0xb7,0x89,0x43,0x0e,0xa2,0x35,0x43,0xc0,0x37,0x8a,0x43,0x7a,0xaa,0x3b,0x43,0xbb,0x48,
13448 0x89,0x43,0xbb,0x7b,0x41,0x43,0x09,0x06,0x3a,0xad,0x82,0x43,0xc4,0x59,0x43,0x43,0x08,0xd2,0xb7,0x83,
13449 0x43,0x2b,0x5b,0x44,0x43,0x35,0xd6,0x85,0x43,0x48,0xf5,0x49,0x43,0x42,0x97,0x86,0x43,0xc4,0xa1,0x4f,
13450 0x43,0x09,0x06,0x9c,0xb3,0x80,0x43,0x48,0x55,0x5a,0x43,0x08,0xff,0xc5,0x80,0x43,0x09,0x73,0x55,0x43,
13451 0x93,0xe1,0x80,0x43,0x0f,0x39,0x53,0x43,0xf1,0xbe,0x7e,0x43,0x18,0xe7,0x4c,0x43,0x09,0x06,0xe0,0x02,
13452 0x7b,0x43,0x92,0xec,0x5d,0x43,0x08,0x09,0x3a,0x7b,0x43,0xf0,0xf7,0x58,0x43,0x09,0x3a,0x7b,0x43,0xe6,
13453 0x31,0x5b,0x43,0xe0,0x02,0x7b,0x43,0xa8,0x4f,0x56,0x43,0x09,0x06,0x39,0x4f,0x7d,0x43,0x3e,0x8f,0x5c,
13454 0x43,0x08,0xe9,0xe0,0x7c,0x43,0x03,0x9c,0x58,0x43,0x1e,0x2b,0x81,0x43,0x7f,0x30,0x5a,0x43,0xff,0x73,
13455 0x7d,0x43,0xf6,0xb6,0x51,0x43,0x09,0x06,0x5c,0xb8,0x52,0x43,0x28,0x21,0x87,0x43,0x08,0xae,0x3e,0x57,
13456 0x43,0x12,0x9a,0x88,0x43,0x23,0xf5,0x56,0x43,0x04,0xf1,0x8b,0x43,0x25,0xfc,0x5b,0x43,0x85,0x74,0x8e,
13457 0x43,0x08,0x2f,0xf2,0x61,0x43,0x8e,0x52,0x90,0x43,0xd9,0xdc,0x6c,0x43,0x85,0x74,0x8e,0x43,0xc6,0x20,
13458 0x69,0x43,0x3d,0xd8,0x8d,0x43,0x08,0x6d,0x8c,0x5a,0x43,0xf5,0x3b,0x8d,0x43,0x3d,0x77,0x58,0x43,0xa1,
13459 0xc6,0x87,0x43,0xf8,0xed,0x5e,0x43,0x5e,0x0d,0x86,0x43,0x09,0x06,0xde,0xcc,0x92,0x43,0xf7,0x17,0x87,
13460 0x43,0x08,0xb6,0x89,0x90,0x43,0xae,0x87,0x88,0x43,0x4a,0xa5,0x90,0x43,0xa1,0xde,0x8b,0x43,0xf9,0x2a,
13461 0x8e,0x43,0x23,0x62,0x8e,0x43,0x08,0xf5,0x2f,0x8b,0x43,0x5c,0x49,0x90,0x43,0x35,0xd6,0x85,0x43,0x8e,
13462 0x46,0x8e,0x43,0x3d,0xb4,0x87,0x43,0x47,0xaa,0x8d,0x43,0x08,0x6a,0xfe,0x8e,0x43,0xff,0x0d,0x8d,0x43,
13463 0xbb,0x6c,0x8f,0x43,0xf7,0x17,0x87,0x43,0x5c,0x31,0x8c,0x43,0xb2,0x5e,0x85,0x43,0x09,0x06,0x60,0x38,
13464 0x91,0x43,0x69,0x5d,0x7a,0x43,0x08,0x34,0x1e,0x92,0x43,0x1e,0x5b,0x89,0x43,0x04,0x63,0x7e,0x43,0x5e,
13465 0x01,0x84,0x43,0x59,0x2a,0x87,0x43,0x0d,0xcf,0x8d,0x43,0x09,0x03,0x04,0x06,0x5a,0x18,0x63,0x43,0x82,
13466 0x79,0x8b,0x43,0x08,0x25,0x2c,0x64,0x43,0x82,0x79,0x8b,0x43,0x2a,0x1b,0x65,0x43,0x9d,0xef,0x8a,0x43,
13467 0x2a,0x1b,0x65,0x43,0xc1,0x37,0x8a,0x43,0x08,0x2a,0x1b,0x65,0x43,0x17,0x89,0x89,0x43,0x25,0x2c,0x64,
13468 0x43,0x31,0xff,0x88,0x43,0x5a,0x18,0x63,0x43,0x31,0xff,0x88,0x43,0x08,0xf3,0x16,0x62,0x43,0x31,0xff,
13469 0x88,0x43,0xee,0x27,0x61,0x43,0x17,0x89,0x89,0x43,0xee,0x27,0x61,0x43,0xc1,0x37,0x8a,0x43,0x08,0xee,
13470 0x27,0x61,0x43,0x9d,0xef,0x8a,0x43,0xf3,0x16,0x62,0x43,0x82,0x79,0x8b,0x43,0x5a,0x18,0x63,0x43,0x82,
13471 0x79,0x8b,0x43,0x09,0x06,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x08,0x34,0xee,0x89,0x43,0x82,0x79,
13472 0x8b,0x43,0x85,0x5c,0x8a,0x43,0x9d,0xef,0x8a,0x43,0x85,0x5c,0x8a,0x43,0xc1,0x37,0x8a,0x43,0x08,0x85,
13473 0x5c,0x8a,0x43,0x17,0x89,0x89,0x43,0x34,0xee,0x89,0x43,0x31,0xff,0x88,0x43,0x4f,0x64,0x89,0x43,0x31,
13474 0xff,0x88,0x43,0x08,0x9c,0xe3,0x88,0x43,0x31,0xff,0x88,0x43,0x19,0x6c,0x88,0x43,0x17,0x89,0x89,0x43,
13475 0x19,0x6c,0x88,0x43,0xc1,0x37,0x8a,0x43,0x08,0x19,0x6c,0x88,0x43,0x9d,0xef,0x8a,0x43,0x9c,0xe3,0x88,
13476 0x43,0x82,0x79,0x8b,0x43,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x09,0x02,0x04,0x06,0x19,0x60,0x86,
13477 0x43,0xec,0xed,0xa3,0x43,0x08,0x35,0xd6,0x85,0x43,0x76,0x43,0xa6,0x43,0x93,0xe1,0x80,0x43,0x57,0x02,
13478 0xac,0x43,0x61,0xd8,0x80,0x43,0x87,0x17,0xae,0x43,0x08,0xa5,0x85,0x80,0x43,0xc3,0xfe,0xaf,0x43,0xce,
13479 0xbc,0x80,0x43,0x83,0x40,0xb1,0x43,0xa5,0x91,0x82,0x43,0x79,0x6e,0xb1,0x43,0x08,0x23,0x26,0x84,0x43,
13480 0x40,0x93,0xb1,0x43,0x30,0xe7,0x84,0x43,0xbe,0x1b,0xb1,0x43,0x11,0x82,0x84,0x43,0xab,0x6b,0xaf,0x43,
13481 0x08,0xb7,0x41,0x84,0x43,0x3b,0x98,0xae,0x43,0xb7,0x41,0x84,0x43,0xc3,0xf2,0xad,0x43,0xa1,0xae,0x83,
13482 0x43,0x83,0x28,0xad,0x43,0x08,0xb2,0x52,0x83,0x43,0x80,0x39,0xac,0x43,0x81,0x49,0x83,0x43,0xf0,0x00,
13483 0xab,0x43,0xe4,0x67,0x85,0x43,0x76,0x4f,0xa8,0x43,0x08,0x9c,0xd7,0x86,0x43,0xd1,0x83,0xa6,0x43,0xec,
13484 0x45,0x87,0x43,0x01,0x75,0xa2,0x43,0x19,0x60,0x86,0x43,0xec,0xed,0xa3,0x43,0x09,0x06,0xd9,0xdc,0x6c,
13485 0x43,0x14,0x25,0xa4,0x43,0x08,0xa2,0xf0,0x6d,0x43,0x9f,0x7a,0xa6,0x43,0x47,0xec,0x77,0x43,0x80,0x39,
13486 0xac,0x43,0xa9,0xfe,0x77,0x43,0xb0,0x4e,0xae,0x43,0x08,0x23,0xa4,0x78,0x43,0xea,0x35,0xb0,0x43,0xd2,
13487 0x35,0x78,0x43,0xab,0x77,0xb1,0x43,0xc1,0x79,0x74,0x43,0xa2,0xa5,0xb1,0x43,0x08,0xc6,0x50,0x71,0x43,
13488 0x68,0xca,0xb1,0x43,0xab,0xce,0x6f,0x43,0xe7,0x52,0xb1,0x43,0xea,0x98,0x70,0x43,0xd4,0xa2,0xaf,0x43,
13489 0x08,0x9d,0x19,0x71,0x43,0x96,0xd8,0xae,0x43,0x9d,0x19,0x71,0x43,0xec,0x29,0xae,0x43,0xca,0x3f,0x72,
13490 0x43,0xab,0x5f,0xad,0x43,0x08,0xa6,0xf7,0x72,0x43,0xa7,0x70,0xac,0x43,0x09,0x0a,0x73,0x43,0x17,0x38,
13491 0xab,0x43,0x44,0xcd,0x6e,0x43,0x9f,0x86,0xa8,0x43,0x08,0xd4,0xed,0x6b,0x43,0xf8,0xba,0xa6,0x43,0x31,
13492 0x11,0x6b,0x43,0x2a,0xac,0xa2,0x43,0xd9,0xdc,0x6c,0x43,0x14,0x25,0xa4,0x43,0x09,0x01,0x05,0x06,0x66,
13493 0x5d,0x7a,0x43,0x74,0xeb,0xc2,0x43,0x08,0x09,0x22,0x77,0x43,0x50,0xbb,0xc7,0x43,0xe9,0xe0,0x7c,0x43,
13494 0xf5,0x86,0xc9,0x43,0x8f,0x94,0x7a,0x43,0xc5,0x95,0xcd,0x43,0x09,0x06,0x08,0x98,0x80,0x43,0x6b,0x19,
13495 0xc3,0x43,0x08,0xb7,0x35,0x82,0x43,0x79,0xf2,0xc7,0x43,0xf1,0xbe,0x7e,0x43,0x1e,0xbe,0xc9,0x43,0x73,
13496 0x7c,0x80,0x43,0xec,0xcc,0xcd,0x43,0x09,0x06,0x28,0xab,0x7d,0x43,0xae,0xde,0xc6,0x43,0x08,0x1e,0xcd,
13497 0x7b,0x43,0x8a,0xa2,0xc9,0x43,0x30,0x89,0x7f,0x43,0x5c,0x94,0xcc,0x43,0x28,0xab,0x7d,0x43,0x42,0x2a,
13498 0xcf,0x43,0x09,0x01,0x05,0x06,0x24,0x14,0xe0,0x42,0xf5,0x77,0x97,0x43,0x08,0xf7,0x1d,0xe7,0x42,0x74,
13499 0x00,0x97,0x43,0x4d,0x93,0xec,0x42,0xdb,0xf5,0x95,0x43,0x29,0x4b,0xed,0x42,0xcd,0x34,0x95,0x43,0x09,
13500 0x06,0x29,0x7b,0xf5,0x42,0x6f,0x1d,0x98,0x43,0x08,0xe4,0xf1,0xfb,0x42,0x61,0x5c,0x97,0x43,0xdb,0x7d,
13501 0x01,0x43,0xb2,0xbe,0x95,0x43,0x55,0x23,0x02,0x43,0xe7,0xaa,0x94,0x43,0x09,0x06,0x98,0xdc,0x03,0x43,
13502 0xbe,0x8b,0x98,0x43,0x08,0x66,0xdf,0x05,0x43,0x47,0xe6,0x97,0x43,0xae,0x87,0x08,0x43,0x98,0x48,0x96,
13503 0x43,0x61,0x08,0x09,0x43,0xd6,0x06,0x95,0x43,0x09,0x06,0x31,0x0b,0x0b,0x43,0x8e,0x82,0x98,0x43,0x08,
13504 0xdb,0xc5,0x0d,0x43,0x80,0xc1,0x97,0x43,0xd6,0xee,0x10,0x43,0xa9,0xec,0x95,0x43,0x79,0xcb,0x11,0x43,
13505 0x55,0x8f,0x94,0x43,0x09,0x06,0xd1,0x2f,0x18,0x43,0xdb,0x01,0x98,0x43,0x08,0xad,0xe7,0x18,0x43,0x38,
13506 0x25,0x97,0x43,0x8a,0x9f,0x19,0x43,0x80,0xb5,0x95,0x43,0xd6,0x1e,0x19,0x43,0xe0,0xd8,0x94,0x43,0x09,
13507 0x06,0x9a,0x5b,0x1d,0x43,0x58,0x8a,0x97,0x43,0x08,0x01,0x5d,0x1e,0x43,0xf1,0x88,0x96,0x43,0x2f,0x83,
13508 0x1f,0x43,0x19,0xb4,0x94,0x43,0x19,0xf0,0x1e,0x43,0x6f,0x05,0x94,0x43,0x09,0x06,0x0b,0x53,0x24,0x43,
13509 0xae,0xdb,0x96,0x43,0x08,0x25,0xd5,0x25,0x43,0x50,0xac,0x95,0x43,0x53,0xfb,0x26,0x43,0x8a,0x7b,0x93,
13510 0x43,0x76,0x43,0x26,0x43,0xb7,0x95,0x92,0x43,0x09,0x06,0x76,0x5b,0x2a,0x43,0x47,0xda,0x95,0x43,0x08,
13511 0xf3,0xef,0x2b,0x43,0x10,0xe2,0x94,0x43,0x6d,0x95,0x2c,0x43,0xae,0xc3,0x92,0x43,0x68,0xa6,0x2b,0x43,
13512 0x47,0xc2,0x91,0x43,0x09,0x06,0x36,0xc1,0x31,0x43,0x2c,0x58,0x94,0x43,0x08,0x8c,0x1e,0x33,0x43,0x31,
13513 0x3b,0x93,0x43,0x79,0x7a,0x33,0x43,0xff,0x25,0x91,0x43,0xd9,0x9d,0x32,0x43,0xc1,0x5b,0x90,0x43,0x09,
13514 0x06,0x25,0x35,0x36,0x43,0x31,0x3b,0x93,0x43,0x08,0x3f,0xb7,0x37,0x43,0xc1,0x67,0x92,0x43,0xe0,0x93,
13515 0x38,0x43,0xae,0xb7,0x90,0x43,0x7e,0x81,0x38,0x43,0x0d,0xdb,0x8f,0x43,0x09,0x06,0xb5,0x85,0x3b,0x43,
13516 0xe4,0xaf,0x91,0x43,0x08,0xcf,0x07,0x3d,0x43,0x9d,0x13,0x91,0x43,0xbc,0x63,0x3d,0x43,0x47,0xb6,0x8f,
13517 0x43,0xe5,0x9a,0x3d,0x43,0x74,0xd0,0x8e,0x43,0x09,0x06,0xae,0xc6,0x42,0x43,0xa4,0xd9,0x8e,0x43,0x08,
13518 0xca,0x48,0x44,0x43,0xfa,0x2a,0x8e,0x43,0xa2,0x11,0x44,0x43,0x9d,0xfb,0x8c,0x43,0x55,0x92,0x44,0x43,
13519 0x0d,0xc3,0x8b,0x43,0x09,0x06,0x39,0x10,0xc3,0x43,0x34,0x36,0x96,0x43,0x08,0x92,0x44,0xc1,0x43,0xe4,
13520 0xc7,0x95,0x43,0x6f,0xf0,0xbf,0x43,0x4b,0xbd,0x94,0x43,0x47,0xb9,0xbf,0x43,0x0b,0xf3,0x93,0x43,0x09,
13521 0x06,0x8f,0x49,0xbe,0x43,0xb7,0xad,0x96,0x43,0x08,0x11,0xb5,0xbc,0x43,0x77,0xe3,0x95,0x43,0x9c,0xf2,
13522 0xba,0x43,0xfa,0x4e,0x94,0x43,0xae,0x96,0xba,0x43,0x31,0x3b,0x93,0x43,0x09,0x06,0xdb,0xb0,0xb9,0x43,
13523 0x10,0xee,0x96,0x43,0x08,0x42,0xa6,0xb8,0x43,0xc8,0x51,0x96,0x43,0x50,0x5b,0xb7,0x43,0x19,0xb4,0x94,
13524 0x43,0xf7,0x1a,0xb7,0x43,0x58,0x72,0x93,0x43,0x09,0x06,0xf2,0x2b,0xb6,0x43,0x10,0xee,0x96,0x43,0x08,
13525 0x9d,0xce,0xb4,0x43,0x04,0x2d,0x96,0x43,0xed,0x30,0xb3,0x43,0x2c,0x58,0x94,0x43,0xce,0xcb,0xb2,0x43,
13526 0xd6,0xfa,0x92,0x43,0x09,0x06,0x5a,0x09,0xb1,0x43,0x19,0xc0,0x96,0x43,0x08,0x6c,0xad,0xb0,0x43,0x77,
13527 0xe3,0x95,0x43,0x7e,0x51,0xb0,0x43,0xc0,0x73,0x94,0x43,0xd8,0x91,0xb0,0x43,0x1e,0x97,0x93,0x43,0x09,
13528 0x06,0x48,0x4d,0xad,0x43,0xbe,0x7f,0x96,0x43,0x08,0x95,0xcc,0xac,0x43,0x58,0x7e,0x95,0x43,0x4d,0x30,
13529 0xac,0x43,0x80,0xa9,0x93,0x43,0xd8,0x79,0xac,0x43,0xd6,0xfa,0x92,0x43,0x09,0x06,0x90,0xd1,0xa9,0x43,
13530 0x14,0xd1,0x95,0x43,0x08,0x83,0x10,0xa9,0x43,0xb7,0xa1,0x94,0x43,0x3b,0x74,0xa8,0x43,0xf1,0x70,0x92,
13531 0x43,0x29,0xd0,0xa8,0x43,0x1e,0x8b,0x91,0x43,0x09,0x06,0x5a,0xcd,0xa6,0x43,0x8a,0x87,0x95,0x43,0x08,
13532 0x1c,0x03,0xa6,0x43,0x23,0x86,0x94,0x43,0x5f,0xb0,0xa5,0x43,0xc1,0x67,0x92,0x43,0xe1,0x27,0xa6,0x43,
13533 0x8a,0x6f,0x91,0x43,0x09,0x06,0xd4,0x5a,0xa3,0x43,0x2c,0x58,0x94,0x43,0x08,0x29,0xac,0xa2,0x43,0x31,
13534 0x3b,0x93,0x43,0x32,0x7e,0xa2,0x43,0xff,0x25,0x91,0x43,0x83,0xec,0xa2,0x43,0x8e,0x52,0x90,0x43,0x09,
13535 0x06,0xf8,0x96,0xa0,0x43,0x1e,0x97,0x93,0x43,0x08,0xeb,0xd5,0x9f,0x43,0x7b,0xba,0x92,0x43,0x99,0x67,
13536 0x9f,0x43,0x9d,0x13,0x91,0x43,0x99,0x67,0x9f,0x43,0xfa,0x36,0x90,0x43,0x09,0x06,0xeb,0xc9,0x9d,0x43,
13537 0xc8,0x39,0x92,0x43,0x08,0xde,0x08,0x9d,0x43,0xb2,0xa6,0x91,0x43,0xe6,0xda,0x9c,0x43,0x2c,0x40,0x90,
13538 0x43,0x52,0xbf,0x9c,0x43,0x5a,0x5a,0x8f,0x43,0x09,0x06,0x37,0x3d,0x9b,0x43,0x85,0x80,0x90,0x43,0x08,
13539 0x2a,0x7c,0x9a,0x43,0xdb,0xd1,0x8f,0x43,0xf0,0xa0,0x9a,0x43,0x7d,0xa2,0x8e,0x43,0x65,0x57,0x9a,0x43,
13540 0xee,0x69,0x8d,0x43,0x09,0x02,0x04,0x06,0x2a,0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x08,0x0d,0x8a,0x31,
13541 0x42,0x9f,0x0e,0x94,0x43,0xf3,0x1f,0x34,0x42,0x3d,0xfc,0x93,0x43,0x63,0xff,0x36,0x42,0xa9,0xe0,0x93,
13542 0x43,0x08,0xb5,0x34,0x5d,0x42,0x0b,0xf3,0x93,0x43,0x6d,0xa4,0x5e,0x42,0x03,0x39,0x98,0x43,0xe7,0x31,
13543 0x5b,0x42,0x93,0x89,0x9d,0x43,0x08,0x02,0x9c,0x58,0x42,0xd4,0x5a,0xa3,0x43,0x38,0x70,0x53,0x42,0x14,
13544 0x49,0xaa,0x43,0xf8,0xed,0x5e,0x42,0x83,0x28,0xad,0x43,0x08,0xea,0x68,0x68,0x42,0x20,0x22,0xaf,0x43,
13545 0x12,0xb8,0x6c,0x42,0xb5,0x49,0xb1,0x43,0x2a,0x4b,0x6d,0x42,0x0d,0x96,0xb3,0x43,0x07,0x2a,0x4b,0x6d,
13546 0x42,0xc6,0x05,0xb5,0x43,0x08,0x87,0x6e,0x6c,0x42,0x68,0xee,0xb7,0x43,0x1c,0x66,0x66,0x42,0x31,0x0e,
13547 0xbb,0x43,0x57,0x11,0x5e,0x42,0x8f,0x49,0xbe,0x43,0x08,0x66,0x96,0x54,0x42,0xb9,0x5c,0xb8,0x43,0x2c,
13548 0x2b,0x3c,0x42,0x68,0xd6,0xb3,0x43,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x07,0x2a,0xf4,0x2e,0x42,
13549 0x61,0xa4,0xa3,0x43,0x08,0x55,0x1a,0x30,0x42,0xf0,0xd0,0xa2,0x43,0xf8,0xf6,0x30,0x42,0xb2,0x06,0xa2,
13550 0x43,0x98,0xd3,0x31,0x42,0xd6,0x4e,0xa1,0x43,0x08,0x1c,0x6f,0x38,0x42,0x2a,0x94,0x9e,0x43,0xc1,0x22,
13551 0x36,0x42,0xf5,0x9b,0x9d,0x43,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x57,
13552 0xa2,0x9b,0x43,0x08,0xab,0x8f,0x35,0x42,0x8a,0xab,0x9b,0x43,0xe9,0x71,0x3a,0x42,0xb2,0xe2,0x9b,0x43,
13553 0xb7,0x74,0x3c,0x42,0x34,0x5a,0x9c,0x43,0x08,0x23,0x7d,0x42,0x42,0x0b,0x2f,0x9e,0x43,0xe5,0x9a,0x3d,
13554 0x42,0x38,0x6d,0xa3,0x43,0x36,0xd9,0x35,0x42,0xf3,0xd7,0xa7,0x43,0x08,0x12,0x61,0x2e,0x42,0xb0,0x42,
13555 0xac,0x43,0x63,0xff,0x36,0x42,0xdd,0x74,0xaf,0x43,0x1e,0xa6,0x45,0x42,0x44,0x82,0xb2,0x43,0x08,0x74,
13556 0x1b,0x4b,0x42,0x79,0x7a,0xb3,0x43,0x10,0x21,0x4f,0x42,0x2a,0x18,0xb5,0x43,0xdb,0x4c,0x54,0x42,0x91,
13557 0x19,0xb6,0x43,0x08,0xee,0x3f,0x65,0x42,0x5f,0x28,0xba,0x43,0xa7,0xaf,0x66,0x42,0xb9,0x50,0xb6,0x43,
13558 0x14,0x58,0x5c,0x42,0xca,0xdc,0xb1,0x43,0x08,0x2c,0x8b,0x4c,0x42,0x4e,0x30,0xac,0x43,0x19,0xcf,0x48,
13559 0x42,0x2a,0xd0,0xa8,0x43,0xbc,0xab,0x49,0x42,0xa9,0x4c,0xa6,0x43,0x08,0x61,0x5f,0x47,0x42,0xfa,0xa2,
13560 0xa2,0x43,0xa7,0xaf,0x66,0x42,0x85,0x98,0x94,0x43,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x07,0x2a,
13561 0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x09,0x06,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x08,0xdc,0xe3,
13562 0xf1,0x41,0xe9,0x9e,0x92,0x43,0xd2,0xe7,0x0b,0x42,0xd6,0x06,0x95,0x43,0x2a,0xf4,0x2e,0x42,0x04,0x21,
13563 0x94,0x43,0x07,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x08,0x87,0x17,0x2e,0x42,0xc3,0x62,0x95,0x43,
13564 0xe7,0x3a,0x2d,0x42,0xf5,0x6b,0x95,0x43,0x44,0x5e,0x2c,0x42,0xf5,0x6b,0x95,0x43,0x08,0xd1,0x47,0x1c,
13565 0x42,0x19,0xc0,0x96,0x43,0x66,0xdf,0x05,0x42,0x38,0x19,0x95,0x43,0x12,0x6a,0x00,0x42,0xb2,0xbe,0x95,
13566 0x43,0x08,0xbb,0x6b,0xea,0x41,0xd6,0x12,0x97,0x43,0x2d,0x82,0xfa,0x41,0x61,0x74,0x9b,0x43,0x7e,0x72,
13567 0x06,0x42,0x8a,0xab,0x9b,0x43,0x08,0xc8,0x39,0x12,0x42,0x4e,0xd0,0x9b,0x43,0x53,0xe3,0x22,0x42,0xc3,
13568 0x86,0x9b,0x43,0x2a,0xf4,0x2e,0x42,0x57,0xa2,0x9b,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,
13569 0x08,0x01,0xa5,0x2a,0x42,0xa4,0x2d,0x9d,0x43,0x96,0x9c,0x24,0x42,0x06,0x40,0x9d,0x43,0x8a,0xb7,0x1d,
13570 0x42,0x9a,0x5b,0x9d,0x43,0x08,0x6b,0x16,0x13,0x42,0xcd,0x64,0x9d,0x43,0x42,0xc7,0x0e,0x42,0x9a,0x5b,
13571 0x9d,0x43,0x23,0x26,0x04,0x42,0xcd,0x64,0x9d,0x43,0x08,0xe6,0x91,0xeb,0x41,0x38,0x49,0x9d,0x43,0x73,
13572 0x7b,0xdb,0x41,0xf5,0x83,0x99,0x43,0x7f,0x60,0xe2,0x41,0x0b,0x0b,0x98,0x43,0x08,0x7f,0x60,0xe2,0x41,
13573 0xec,0x99,0x95,0x43,0xe3,0x5a,0xde,0x41,0xbe,0x7f,0x96,0x43,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,
13574 0x07,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x09,0x06,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x08,
13575 0xd4,0x7e,0x29,0x42,0xab,0x6b,0xaf,0x43,0x4e,0x0c,0x26,0x42,0x44,0x6a,0xae,0x43,0x38,0x79,0x25,0x42,
13576 0xd4,0x96,0xad,0x43,0x08,0x25,0xbd,0x21,0x42,0xe2,0x4b,0xac,0x43,0x49,0x35,0x29,0x42,0x9a,0x97,0xa7,
13577 0x43,0x2a,0xf4,0x2e,0x42,0x61,0xa4,0xa3,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x09,0x06,
13578 0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x08,0x86,0x20,0x80,0x43,0x57,0x41,0xe6,0x43,0x7d,0x4e,0x80,
13579 0x43,0x25,0x38,0xe6,0x43,0xa5,0x85,0x80,0x43,0xf3,0x2e,0xe6,0x43,0x08,0x35,0xca,0x83,0x43,0xd4,0xc9,
13580 0xe5,0x43,0x9c,0xd7,0x86,0x43,0x44,0x91,0xe4,0x43,0xd5,0xca,0x8a,0x43,0x91,0x1c,0xe6,0x43,0x08,0x53,
13581 0x5f,0x8c,0x43,0xf8,0x1d,0xe7,0x43,0x2f,0x17,0x8d,0x43,0x4e,0x7b,0xe8,0x43,0x92,0x29,0x8d,0x43,0x2f,
13582 0x22,0xea,0x43,0x07,0x92,0x29,0x8d,0x43,0x44,0xb5,0xea,0x43,0x08,0xfe,0x0d,0x8d,0x43,0x2a,0x4b,0xed,
13583 0x43,0xe3,0x8b,0x8b,0x43,0x55,0x7d,0xf0,0x43,0xec,0x51,0x89,0x43,0x72,0x0b,0xf4,0x43,0x08,0xcd,0xd4,
13584 0x84,0x43,0x9d,0x55,0xfb,0x43,0xc9,0xe5,0x83,0x43,0x74,0x1e,0xfb,0x43,0x73,0x94,0x84,0x43,0x5a,0x90,
13585 0xf7,0x43,0x08,0xe8,0x62,0x88,0x43,0xfd,0x30,0xee,0x43,0x39,0xc5,0x86,0x43,0xdd,0xbf,0xeb,0x43,0x35,
13586 0xbe,0x81,0x43,0x40,0xde,0xed,0x43,0x08,0x4f,0x34,0x81,0x43,0x36,0x0c,0xee,0x43,0x08,0x98,0x80,0x43,
13587 0xfd,0x30,0xee,0x43,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x40,0xec,
13588 0x43,0x08,0x35,0xbe,0x81,0x43,0x06,0xf7,0xeb,0x43,0x15,0x65,0x83,0x43,0x49,0xa4,0xeb,0x43,0x1e,0x43,
13589 0x85,0x43,0xbe,0x5a,0xeb,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0x18,0xea,0x43,0x42,0x97,0x86,0x43,0x5f,
13590 0x67,0xf4,0x43,0xa9,0x98,0x87,0x43,0xd4,0x1d,0xf4,0x43,0x08,0x5c,0x25,0x8a,0x43,0xcf,0x16,0xef,0x43,
13591 0x46,0xaa,0x8d,0x43,0x5a,0x3c,0xe9,0x43,0x19,0x6c,0x88,0x43,0x53,0x5e,0xe7,0x43,0x08,0xc4,0x02,0x85,
13592 0x43,0x96,0x0b,0xe7,0x43,0x85,0x2c,0x82,0x43,0x83,0x67,0xe7,0x43,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,
13593 0x43,0x07,0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x09,0x06,0xfd,0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,
13594 0x08,0xfa,0x6c,0x78,0x43,0xd1,0xc2,0xe0,0x43,0x25,0x5c,0x6c,0x43,0x25,0x44,0xe8,0x43,0x1d,0xe5,0x7f,
13595 0x43,0x87,0x4a,0xe6,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,0x43,0x08,0xa6,0x27,0x7b,0x43,0x91,
13596 0x28,0xe8,0x43,0xbc,0xa2,0x77,0x43,0xb0,0x8d,0xe8,0x43,0xc6,0x68,0x75,0x43,0x57,0x4d,0xe8,0x43,0x08,
13597 0xe0,0xd2,0x72,0x43,0xab,0x9e,0xe7,0x43,0x50,0x9a,0x71,0x43,0x2a,0x27,0xe7,0x43,0xea,0x98,0x70,0x43,
13598 0x57,0x35,0xe4,0x43,0x08,0x94,0x3b,0x6f,0x43,0x14,0x7c,0xe2,0x43,0xff,0x13,0x6d,0x43,0x06,0xbb,0xe1,
13599 0x43,0xcf,0xfe,0x6a,0x43,0x06,0xbb,0xe1,0x43,0x08,0x44,0x9d,0x66,0x43,0x77,0x8e,0xe2,0x43,0x3b,0xef,
13600 0x6c,0x43,0x91,0x10,0xe4,0x43,0xfd,0x24,0x6c,0x43,0xb0,0x81,0xe6,0x43,0x08,0x96,0x23,0x6b,0x43,0xee,
13601 0x57,0xe9,0x43,0xca,0x0f,0x6a,0x43,0x5f,0x37,0xec,0x43,0x55,0x71,0x6e,0x43,0x9f,0x01,0xed,0x43,0x08,
13602 0xdb,0xfb,0x75,0x43,0x3b,0xef,0xec,0x43,0x09,0x3a,0x7b,0x43,0xb0,0xa5,0xec,0x43,0x1d,0xe5,0x7f,0x43,
13603 0x91,0x40,0xec,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x08,0xa9,0x16,0x7c,0x43,0xb0,0xb1,
13604 0xee,0x43,0x47,0xec,0x77,0x43,0xd9,0xe8,0xee,0x43,0x1e,0x9d,0x73,0x43,0xcf,0x16,0xef,0x43,0x08,0x0e,
13605 0xc9,0x6b,0x43,0xee,0x7b,0xef,0x43,0x7e,0x90,0x6a,0x43,0xfd,0x30,0xee,0x43,0x01,0xfc,0x68,0x43,0x4e,
13606 0x93,0xec,0x43,0x08,0x31,0xf9,0x66,0x43,0x4e,0x87,0xea,0x43,0x31,0x11,0x6b,0x43,0xd4,0xd5,0xe7,0x43,
13607 0xd9,0xc4,0x68,0x43,0xd4,0xc9,0xe5,0x43,0x08,0xe5,0x79,0x67,0x43,0x77,0x9a,0xe4,0x43,0x44,0x9d,0x66,
13608 0x43,0xab,0x86,0xe3,0x43,0x7e,0x78,0x66,0x43,0x0b,0xaa,0xe2,0x43,0x07,0x7e,0x78,0x66,0x43,0x57,0x29,
13609 0xe2,0x43,0x08,0xa7,0xaf,0x66,0x43,0xbe,0x1e,0xe1,0x43,0x87,0x56,0x68,0x43,0x77,0x82,0xe0,0x43,0xfd,
13610 0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,0x09,0x06,0xc4,0x41,0xbf,0x43,0x85,0xc0,0x72,0x42,0x08,0x73,0xdf,
13611 0xc0,0x43,0xf4,0x76,0x72,0x42,0x97,0x33,0xc2,0x43,0x85,0xc0,0x72,0x42,0xb2,0xb5,0xc3,0x43,0x64,0x56,
13612 0x75,0x42,0x08,0x03,0x24,0xc4,0x43,0x5e,0x7f,0x78,0x42,0xfa,0x51,0xc4,0x43,0x01,0x85,0x7c,0x42,0x5c,
13613 0x64,0xc4,0x43,0xa0,0xb3,0x80,0x42,0x07,0x5c,0x64,0xc4,0x43,0x10,0x93,0x83,0x42,0x08,0xc8,0x48,0xc4,
13614 0x43,0x1c,0x78,0x8a,0x42,0x27,0x6c,0xc3,0x43,0xaf,0xcf,0x94,0x42,0x23,0x7d,0xc2,0x43,0x99,0x9c,0xa4,
13615 0x42,0x08,0x3d,0xe7,0xbf,0x43,0xfb,0xfd,0xb5,0x42,0xb3,0x9d,0xbf,0x43,0x88,0x17,0xae,0x42,0xc4,0x41,
13616 0xbf,0x43,0x69,0x76,0xa3,0x42,0x07,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x08,0x4f,0x8b,0xbf,0x43,
13617 0xed,0x81,0x91,0x42,0xe4,0xa6,0xbf,0x43,0x5d,0x61,0x94,0x42,0xfa,0x39,0xc0,0x43,0x3b,0x49,0x9d,0x42,
13618 0x08,0x2b,0x43,0xc0,0x43,0x28,0xed,0xa9,0x42,0x61,0x3b,0xc1,0x43,0x00,0x9e,0xa5,0x42,0xe4,0xb2,0xc1,
13619 0x43,0x5d,0x91,0x9c,0x42,0x08,0x78,0xce,0xc1,0x43,0xfd,0x36,0x90,0x42,0x22,0x89,0xc4,0x43,0x81,0x72,
13620 0x86,0x42,0xae,0xc6,0xc2,0x43,0xa0,0xb3,0x80,0x42,0x08,0x54,0x86,0xc2,0x43,0x58,0xd1,0x7e,0x42,0x30,
13621 0x32,0xc1,0x43,0xce,0x5e,0x7b,0x42,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x07,0xc4,0x41,0xbf,0x43,
13622 0x85,0xc0,0x72,0x42,0x09,0x06,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x08,0x35,0xfd,0xbb,0x43,0xa4,
13623 0xa1,0x5c,0x42,0x5e,0x34,0xbc,0x43,0x9d,0x2a,0x70,0x42,0x5e,0x40,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,
13624 0x4c,0x9c,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,0xef,0xbe,0x43,0x0e,0x0a,0x73,0x42,0xc4,0x41,0xbf,0x43,
13625 0x85,0xc0,0x72,0x42,0x07,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x08,0xcd,0x13,0xbf,0x43,0xe8,0xf1,
13626 0x7b,0x42,0xd6,0xe5,0xbe,0x43,0x71,0x3b,0x7c,0x42,0xdf,0xb7,0xbe,0x43,0x71,0x3b,0x7c,0x42,0x08,0x08,
13627 0xe3,0xbc,0x43,0xa4,0x61,0x7d,0x42,0x28,0x3c,0xbb,0x43,0x91,0x45,0x69,0x42,0x28,0x3c,0xbb,0x43,0x58,
13628 0x71,0x6e,0x42,0x08,0xce,0xfb,0xba,0x43,0xd5,0x35,0x78,0x42,0x59,0x45,0xbb,0x43,0x58,0x23,0x82,0x42,
13629 0xa1,0xe1,0xbb,0x43,0xd7,0xbe,0x88,0x42,0x08,0xc9,0x18,0xbc,0x43,0xaf,0x9f,0x8c,0x42,0x1e,0x76,0xbd,
13630 0x43,0x51,0x7c,0x8d,0x42,0xd6,0xe5,0xbe,0x43,0xf4,0x58,0x8e,0x42,0x08,0x9c,0x0a,0xbf,0x43,0x45,0xc7,
13631 0x8e,0x42,0x30,0x26,0xbf,0x43,0x96,0x35,0x8f,0x42,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x07,0xc4,
13632 0x41,0xbf,0x43,0x69,0x76,0xa3,0x42,0x08,0x08,0xef,0xbe,0x43,0xb1,0xd6,0x99,0x42,0xe8,0x89,0xbe,0x43,
13633 0xde,0xc5,0x8d,0x42,0xc0,0x46,0xbc,0x43,0xc2,0x5b,0x90,0x42,0x08,0x9c,0xf2,0xba,0x43,0x86,0x80,0x90,
13634 0x42,0xf2,0x43,0xba,0x43,0xe8,0x73,0x87,0x42,0x8f,0x31,0xba,0x43,0xb6,0xf4,0x7d,0x42,0x07,0x8f,0x31,
13635 0xba,0x43,0x21,0xc6,0x76,0x42,0x08,0xc0,0x3a,0xba,0x43,0x5f,0x48,0x6b,0x42,0xae,0x96,0xba,0x43,0xe3,
13636 0x83,0x61,0x42,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x09,0x06,0xea,0x74,0xea,0x43,0x61,0x44,0x93,
13637 0x43,0x08,0x24,0x5c,0xec,0x43,0x31,0x3b,0x93,0x43,0xfb,0x30,0xee,0x43,0x93,0x4d,0x93,0x43,0x0d,0xe1,
13638 0xef,0x43,0x80,0xa9,0x93,0x43,0x08,0x8f,0x58,0xf0,0x43,0xd1,0x17,0x94,0x43,0xb7,0x8f,0xf0,0x43,0x10,
13639 0xe2,0x94,0x43,0xea,0x98,0xf0,0x43,0xa9,0xec,0x95,0x43,0x07,0xea,0x98,0xf0,0x43,0x38,0x25,0x97,0x43,
13640 0x08,0x23,0x74,0xf0,0x43,0x9f,0x32,0x9a,0x43,0x5a,0x60,0xef,0x43,0x53,0xcb,0x9e,0x43,0x2d,0x3a,0xee,
13641 0x43,0xfd,0x91,0xa3,0x43,0x08,0xa2,0xf0,0xed,0x43,0xdd,0x38,0xa5,0x43,0x17,0xa7,0xed,0x43,0xbe,0xdf,
13642 0xa6,0x43,0x5a,0x54,0xed,0x43,0x9f,0x86,0xa8,0x43,0x08,0xfc,0x24,0xec,0x43,0xca,0xc4,0xad,0x43,0x48,
13643 0xa4,0xeb,0x43,0x40,0x6f,0xab,0x43,0x28,0x3f,0xeb,0x43,0x1c,0x0f,0xa8,0x43,0x08,0x1f,0x6d,0xeb,0x43,
13644 0x72,0x48,0xa3,0x43,0x67,0x09,0xec,0x43,0xd1,0x53,0x9e,0x43,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,0x43,
13645 0x07,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x08,0x7e,0x90,0xea,0x43,0x8a,0x9f,0x99,0x43,0x12,0xac,
13646 0xea,0x43,0xbc,0xa8,0x99,0x43,0xa7,0xc7,0xea,0x43,0xbc,0xa8,0x99,0x43,0x08,0x51,0x76,0xeb,0x43,0x9f,
13647 0x32,0x9a,0x43,0x5e,0x37,0xec,0x43,0x49,0xed,0x9c,0x43,0xb0,0xa5,0xec,0x43,0x2a,0xa0,0xa0,0x43,0x08,
13648 0x09,0xe6,0xec,0x43,0xd1,0x77,0xa4,0x43,0x28,0x4b,0xed,0x43,0x61,0xa4,0xa3,0x43,0xab,0xc2,0xed,0x43,
13649 0x8e,0xb2,0xa0,0x43,0x08,0x70,0xe7,0xed,0x43,0xde,0x08,0x9d,0x43,0x87,0x86,0xf0,0x43,0x2f,0x53,0x97,
13650 0x43,0x87,0x7a,0xee,0x43,0xec,0x99,0x95,0x43,0x08,0xca,0x27,0xee,0x43,0xff,0x3d,0x95,0x43,0x74,0xca,
13651 0xec,0x43,0x55,0x8f,0x94,0x43,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x07,0xea,0x74,0xea,0x43,0x61,
13652 0x44,0x93,0x43,0x09,0x06,0x05,0xd3,0xe5,0x43,0x19,0x9c,0x90,0x43,0x08,0x09,0xc2,0xe6,0x43,0xd1,0xff,
13653 0x8f,0x43,0x4d,0x6f,0xe6,0x43,0x74,0xe8,0x92,0x43,0x3b,0xd7,0xe8,0x43,0xc3,0x56,0x93,0x43,0x08,0x1f,
13654 0x61,0xe9,0x43,0x93,0x4d,0x93,0x43,0x05,0xeb,0xe9,0x43,0x93,0x4d,0x93,0x43,0xea,0x74,0xea,0x43,0x61,
13655 0x44,0x93,0x43,0x07,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x08,0x24,0x50,0xea,0x43,0xe7,0xaa,0x94,
13656 0x43,0x2d,0x22,0xea,0x43,0xe7,0xaa,0x94,0x43,0x36,0xf4,0xe9,0x43,0xe7,0xaa,0x94,0x43,0x08,0xa2,0xcc,
13657 0xe7,0x43,0xe0,0xd8,0x94,0x43,0xd4,0xc9,0xe5,0x43,0x19,0xa8,0x92,0x43,0xd4,0xc9,0xe5,0x43,0x27,0x69,
13658 0x93,0x43,0x08,0x17,0x77,0xe5,0x43,0xe0,0xd8,0x94,0x43,0x67,0xe5,0xe5,0x43,0x47,0xda,0x95,0x43,0x43,
13659 0x9d,0xe6,0x43,0xe2,0xd3,0x97,0x43,0x08,0x9d,0xdd,0xe6,0x43,0xad,0xe7,0x98,0x43,0x09,0xce,0xe8,0x43,
13660 0xff,0x55,0x99,0x43,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x07,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,
13661 0x43,0x08,0x71,0xcf,0xe9,0x43,0x53,0xb3,0x9a,0x43,0xa7,0xbb,0xe8,0x43,0xdb,0x0d,0x9a,0x43,0xc6,0x14,
13662 0xe7,0x43,0xdb,0x0d,0x9a,0x43,0x08,0x48,0x80,0xe5,0x43,0xdb,0x0d,0x9a,0x43,0x0a,0xb6,0xe4,0x43,0xc3,
13663 0x6e,0x97,0x43,0x76,0x9a,0xe4,0x43,0x74,0xf4,0x94,0x43,0x07,0x76,0x9a,0xe4,0x43,0x79,0xd7,0x93,0x43,
13664 0x08,0xd8,0xac,0xe4,0x43,0x66,0x27,0x92,0x43,0x29,0x1b,0xe5,0x43,0xe0,0xc0,0x90,0x43,0x05,0xd3,0xe5,
13665 0x43,0x19,0x9c,0x90,0x43,0x09,0x06,0x1b,0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x08,0x71,0x0b,0xf4,0x42,
13666 0x00,0x0e,0x8d,0x42,0x8c,0x0f,0x01,0x43,0x3e,0xc0,0x89,0x42,0xf3,0x28,0x06,0x43,0x48,0x9e,0x8b,0x42,
13667 0x08,0x15,0x89,0x09,0x43,0x00,0x0e,0x8d,0x42,0xe0,0x9c,0x0a,0x43,0xc1,0x8b,0x98,0x42,0xa6,0xc1,0x0a,
13668 0x43,0x02,0xa5,0xaa,0x42,0x07,0xa6,0xc1,0x0a,0x43,0xf9,0xf6,0xb0,0x42,0x08,0xa6,0xc1,0x0a,0x43,0x47,
13669 0x8e,0xb4,0x42,0x42,0xaf,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0xe0,0x9c,0x0a,0x43,0xba,0x74,0xbc,0x42,0x08,
13670 0xa1,0xd2,0x09,0x43,0x40,0x47,0xd0,0x42,0x0d,0xab,0x07,0x43,0x91,0xb5,0xd0,0x42,0x3b,0xb9,0x04,0x43,
13671 0xec,0x71,0xba,0x42,0x08,0xe5,0x5b,0x03,0x43,0xe3,0x33,0xa8,0x42,0x63,0xd8,0x00,0x43,0xce,0x70,0x9f,
13672 0x42,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x07,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,0x42,0x08,0xed,
13673 0x6f,0xed,0x42,0x73,0x24,0x9d,0x42,0xd8,0x0c,0xf5,0x42,0x99,0x6c,0x9c,0x42,0x27,0xab,0xfd,0x42,0xea,
13674 0xda,0x9c,0x42,0x08,0x36,0xca,0x03,0x43,0x2b,0x94,0x9e,0x42,0x68,0xc7,0x01,0x43,0x8f,0xbe,0xa2,0x42,
13675 0xfa,0x06,0x08,0x43,0x73,0xb4,0xb5,0x42,0x08,0x8e,0x2e,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0x9d,0xe3,0x08,
13676 0x43,0xd7,0x1e,0x99,0x42,0x28,0x15,0x05,0x43,0x32,0x3b,0x93,0x42,0x08,0x63,0xf0,0x04,0x43,0x70,0xed,
13677 0x8f,0x42,0x71,0x0b,0xf4,0x42,0x32,0x3b,0x93,0x42,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x07,0x1b,
13678 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x09,0x06,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,0x08,0x8e,0x55,
13679 0xc0,0x42,0xb8,0x4d,0x86,0x42,0x60,0xbf,0xd7,0x42,0x3e,0xf0,0x91,0x42,0x63,0xf6,0xe4,0x42,0x70,0xed,
13680 0x8f,0x42,0x08,0x7a,0x89,0xe5,0x42,0xac,0xc8,0x8f,0x42,0xcc,0xf7,0xe5,0x42,0xac,0xc8,0x8f,0x42,0x1b,
13681 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x07,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x08,0x63,0xf6,0xe4,
13682 0x42,0x3b,0x19,0x95,0x42,0xe6,0x61,0xe3,0x42,0x00,0x3e,0x95,0x42,0xf4,0x16,0xe2,0x42,0xc4,0x62,0x95,
13683 0x42,0x08,0x6e,0x74,0xd6,0x42,0x15,0xd1,0x95,0x42,0x97,0x63,0xca,0x42,0xaf,0xcf,0x94,0x42,0xfb,0x2d,
13684 0xbe,0x42,0x86,0x80,0x90,0x42,0x08,0x97,0x03,0xba,0x42,0xce,0x10,0x8f,0x42,0x5e,0x28,0xba,0x42,0x3e,
13685 0xf0,0x91,0x42,0xf2,0x4f,0xbc,0x42,0x45,0xf7,0x96,0x42,0x08,0x27,0x54,0xbf,0x42,0x73,0x24,0x9d,0x42,
13686 0xa5,0xe8,0xc0,0x42,0x86,0xe0,0xa0,0x42,0xe4,0xca,0xc5,0x42,0xed,0x11,0xaa,0x42,0x08,0x54,0xaa,0xc8,
13687 0x42,0x86,0x40,0xb1,0x42,0x59,0x81,0xc5,0x42,0xa1,0x11,0xc4,0x42,0x3e,0xe7,0xbf,0x42,0xfb,0x8d,0xce,
13688 0x42,0x08,0xb4,0x6d,0xb7,0x42,0x30,0xc2,0xd9,0x42,0x46,0xf5,0xc9,0x42,0xdf,0x53,0xd9,0x42,0x38,0x40,
13689 0xcb,0x42,0x62,0x8f,0xcf,0x42,0x08,0x7d,0xf9,0xcc,0x42,0xec,0xa1,0xc2,0x42,0x07,0x43,0xcd,0x42,0x6c,
13690 0xdd,0xb8,0x42,0x2b,0x8b,0xcc,0x42,0x92,0xf5,0xaf,0x42,0x08,0xf9,0x8d,0xce,0x42,0x41,0x57,0xa7,0x42,
13691 0x5b,0xb8,0xd2,0x42,0xae,0x2f,0xa5,0x42,0x18,0x2f,0xd9,0x42,0x13,0x2a,0xa1,0x42,0x08,0x41,0x7e,0xdd,
13692 0x42,0xe3,0x03,0xa0,0x42,0x2e,0xf2,0xe1,0x42,0x7c,0x02,0x9f,0x42,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,
13693 0x42,0x07,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x08,0x4d,0x63,0xe4,0x42,0x00,0x9e,0xa5,0x42,0xf4,
13694 0x16,0xe2,0x42,0x15,0x31,0xa6,0x42,0x99,0xca,0xdf,0x42,0x2b,0xc4,0xa6,0x42,0x08,0xc0,0x82,0xc6,0x42,
13695 0xc4,0xc2,0xa5,0x42,0x57,0xe1,0xd5,0x42,0x91,0xb5,0xd0,0x42,0x54,0xda,0xd0,0x42,0x97,0x93,0xd2,0x42,
13696 0x08,0x9c,0x3a,0xc7,0x42,0x17,0x58,0xdc,0x42,0x9c,0x0a,0xbf,0x42,0x6e,0xa4,0xde,0x42,0x90,0x25,0xb8,
13697 0x42,0xdf,0x53,0xd9,0x42,0x08,0x59,0x21,0xb5,0x42,0xf2,0xdf,0xd4,0x42,0x51,0x43,0xb3,0x42,0x91,0xb5,
13698 0xd0,0x42,0xc5,0x29,0xbb,0x42,0x0e,0x1a,0xca,0x42,0x08,0x65,0x36,0xc4,0x42,0xd0,0x07,0xbd,0x42,0x3e,
13699 0xe7,0xbf,0x42,0x37,0x09,0xbe,0x42,0x0c,0xea,0xc1,0x42,0xcd,0xd0,0xaf,0x42,0x08,0x2b,0x5b,0xc4,0x42,
13700 0x18,0x08,0xa3,0x42,0x67,0xa6,0xab,0x42,0x99,0x3c,0x94,0x42,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,
13701 0x09,];
13703 private struct ThePath {
13704 public:
13705 enum Command {
13706 Bounds, // always first, has 4 args (x0, y0, x1, y1)
13707 StrokeMode,
13708 FillMode,
13709 StrokeFillMode,
13710 NormalStroke,
13711 ThinStroke,
13712 MoveTo,
13713 LineTo,
13714 CubicTo, // cubic bezier
13715 EndPath,
13718 public:
13719 const(ubyte)[] path;
13720 uint ppos;
13722 public:
13723 this (const(void)[] apath) pure nothrow @trusted @nogc {
13724 path = cast(const(ubyte)[])apath;
13727 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (ppos >= path.length); }
13729 Command getCommand () nothrow @trusted @nogc {
13730 pragma(inline, true);
13731 if (ppos >= cast(uint)path.length) assert(0, "invalid path");
13732 return cast(Command)(path.ptr[ppos++]);
13735 // number of (x,y) pairs for this command
13736 static int argCount (in Command cmd) nothrow @safe @nogc {
13737 version(aliced) pragma(inline, true);
13738 if (cmd == Command.Bounds) return 2;
13739 else if (cmd == Command.MoveTo || cmd == Command.LineTo) return 1;
13740 else if (cmd == Command.CubicTo) return 3;
13741 else return 0;
13744 void skipArgs (int argc) nothrow @trusted @nogc {
13745 pragma(inline, true);
13746 ppos += cast(uint)(float.sizeof*2*argc);
13749 float getFloat () nothrow @trusted @nogc {
13750 pragma(inline, true);
13751 if (ppos >= cast(uint)path.length || cast(uint)path.length-ppos < float.sizeof) assert(0, "invalid path");
13752 version(LittleEndian) {
13753 float res = *cast(const(float)*)(&path.ptr[ppos]);
13754 ppos += cast(uint)float.sizeof;
13755 return res;
13756 } else {
13757 static assert(float.sizeof == 4);
13758 uint xp = path.ptr[ppos]|(path.ptr[ppos+1]<<8)|(path.ptr[ppos+2]<<16)|(path.ptr[ppos+3]<<24);
13759 ppos += cast(uint)float.sizeof;
13760 return *cast(const(float)*)(&xp);
13765 // this will add baphomet's background path to the current NanoVega path, so you can fill it.
13766 public void addBaphometBack (NVGContext nvg, float ofsx=0, float ofsy=0, float scalex=1, float scaley=1) nothrow @trusted @nogc {
13767 if (nvg is null) return;
13769 auto path = ThePath(baphometPath);
13771 float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
13772 float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
13774 bool inPath = false;
13775 while (!path.empty) {
13776 auto cmd = path.getCommand();
13777 switch (cmd) {
13778 case ThePath.Command.MoveTo:
13779 inPath = true;
13780 immutable float ex = getScaledX();
13781 immutable float ey = getScaledY();
13782 nvg.moveTo(ex, ey);
13783 break;
13784 case ThePath.Command.LineTo:
13785 inPath = true;
13786 immutable float ex = getScaledX();
13787 immutable float ey = getScaledY();
13788 nvg.lineTo(ex, ey);
13789 break;
13790 case ThePath.Command.CubicTo: // cubic bezier
13791 inPath = true;
13792 immutable float x1 = getScaledX();
13793 immutable float y1 = getScaledY();
13794 immutable float x2 = getScaledX();
13795 immutable float y2 = getScaledY();
13796 immutable float ex = getScaledX();
13797 immutable float ey = getScaledY();
13798 nvg.bezierTo(x1, y1, x2, y2, ex, ey);
13799 break;
13800 case ThePath.Command.EndPath:
13801 if (inPath) return;
13802 break;
13803 default:
13804 path.skipArgs(path.argCount(cmd));
13805 break;
13810 // this will add baphomet's pupil paths to the current NanoVega path, so you can fill it.
13811 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 {
13812 // pupils starts with "fill-and-stroke" mode
13813 if (nvg is null) return;
13815 auto path = ThePath(baphometPath);
13817 float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
13818 float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
13820 bool inPath = false;
13821 bool pupLeft = true;
13822 while (!path.empty) {
13823 auto cmd = path.getCommand();
13824 switch (cmd) {
13825 case ThePath.Command.StrokeFillMode: inPath = true; break;
13826 case ThePath.Command.MoveTo:
13827 if (!inPath) goto default;
13828 static if (!left) { if (pupLeft) goto default; }
13829 static if (!right) { if (!pupLeft) goto default; }
13830 immutable float ex = getScaledX();
13831 immutable float ey = getScaledY();
13832 nvg.moveTo(ex, ey);
13833 break;
13834 case ThePath.Command.LineTo:
13835 if (!inPath) goto default;
13836 static if (!left) { if (pupLeft) goto default; }
13837 static if (!right) { if (!pupLeft) goto default; }
13838 immutable float ex = getScaledX();
13839 immutable float ey = getScaledY();
13840 nvg.lineTo(ex, ey);
13841 break;
13842 case ThePath.Command.CubicTo: // cubic bezier
13843 if (!inPath) goto default;
13844 static if (!left) { if (pupLeft) goto default; }
13845 static if (!right) { if (!pupLeft) goto default; }
13846 immutable float x1 = getScaledX();
13847 immutable float y1 = getScaledY();
13848 immutable float x2 = getScaledX();
13849 immutable float y2 = getScaledY();
13850 immutable float ex = getScaledX();
13851 immutable float ey = getScaledY();
13852 nvg.bezierTo(x1, y1, x2, y2, ex, ey);
13853 break;
13854 case ThePath.Command.EndPath:
13855 if (inPath) {
13856 if (pupLeft) pupLeft = false; else return;
13858 break;
13859 default:
13860 path.skipArgs(path.argCount(cmd));
13861 break;
13866 // mode: 'f' to allow fills; 's' to allow strokes; 'w' to allow stroke widths; 'c' to replace fills with strokes
13867 public void renderBaphomet(string mode="fs") (NVGContext nvg, float ofsx=0, float ofsy=0, float scalex=1, float scaley=1) nothrow @trusted @nogc {
13868 template hasChar(char ch, string s) {
13869 static if (s.length == 0) enum hasChar = false;
13870 else static if (s[0] == ch) enum hasChar = true;
13871 else enum hasChar = hasChar!(ch, s[1..$]);
13873 enum AllowStroke = hasChar!('s', mode);
13874 enum AllowFill = hasChar!('f', mode);
13875 enum AllowWidth = hasChar!('w', mode);
13876 enum Contour = hasChar!('c', mode);
13877 //static assert(AllowWidth || AllowFill);
13879 if (nvg is null) return;
13881 auto path = ThePath(baphometPath);
13883 float getScaledX () nothrow @trusted @nogc { pragma(inline, true); return (ofsx+path.getFloat()*scalex); }
13884 float getScaledY () nothrow @trusted @nogc { pragma(inline, true); return (ofsy+path.getFloat()*scaley); }
13886 int mode = 0;
13887 int sw = ThePath.Command.NormalStroke;
13888 nvg.beginPath();
13889 while (!path.empty) {
13890 auto cmd = path.getCommand();
13891 switch (cmd) {
13892 case ThePath.Command.StrokeMode: mode = ThePath.Command.StrokeMode; break;
13893 case ThePath.Command.FillMode: mode = ThePath.Command.FillMode; break;
13894 case ThePath.Command.StrokeFillMode: mode = ThePath.Command.StrokeFillMode; break;
13895 case ThePath.Command.NormalStroke: sw = ThePath.Command.NormalStroke; break;
13896 case ThePath.Command.ThinStroke: sw = ThePath.Command.ThinStroke; break;
13897 case ThePath.Command.MoveTo:
13898 immutable float ex = getScaledX();
13899 immutable float ey = getScaledY();
13900 nvg.moveTo(ex, ey);
13901 break;
13902 case ThePath.Command.LineTo:
13903 immutable float ex = getScaledX();
13904 immutable float ey = getScaledY();
13905 nvg.lineTo(ex, ey);
13906 break;
13907 case ThePath.Command.CubicTo: // cubic bezier
13908 immutable float x1 = getScaledX();
13909 immutable float y1 = getScaledY();
13910 immutable float x2 = getScaledX();
13911 immutable float y2 = getScaledY();
13912 immutable float ex = getScaledX();
13913 immutable float ey = getScaledY();
13914 nvg.bezierTo(x1, y1, x2, y2, ex, ey);
13915 break;
13916 case ThePath.Command.EndPath:
13917 if (mode == ThePath.Command.FillMode || mode == ThePath.Command.StrokeFillMode) {
13918 static if (AllowFill || Contour) {
13919 static if (Contour) {
13920 if (mode == ThePath.Command.FillMode) { nvg.strokeWidth = 1; nvg.stroke(); }
13921 } else {
13922 nvg.fill();
13926 if (mode == ThePath.Command.StrokeMode || mode == ThePath.Command.StrokeFillMode) {
13927 static if (AllowStroke || Contour) {
13928 static if (AllowWidth) {
13929 if (sw == ThePath.Command.NormalStroke) nvg.strokeWidth = 1;
13930 else if (sw == ThePath.Command.ThinStroke) nvg.strokeWidth = 0.5;
13931 else assert(0, "wtf?!");
13933 nvg.stroke();
13936 nvg.newPath();
13937 break;
13938 default:
13939 path.skipArgs(path.argCount(cmd));
13940 break;
13943 nvg.newPath();