2 // Copyright (c) 2013 Mikko Mononen memon@inside.org
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/>.
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.
47 NVGContext vg = nvgCreateContext();
50 $(WARNING You must use created context ONLY in that thread where you created it.
51 There is no way to "transfer" context between threads. Trying to do so
54 $(WARNING Never issue any commands outside of [beginFrame]/[endFrame]. Trying to
55 do so will lead to UB.)
58 Drawing shapes with NanoVega
59 ============================
61 Drawing a simple shape using NanoVega consists of four steps:
64 * define the path to draw,
66 * and finally fill or stroke the path.
71 vg.rect(100, 100, 120, 30);
72 vg.fillColor(nvgRGBA(255, 192, 0, 255));
76 Calling [beginPath] will clear any existing paths and start drawing from blank slate.
77 There are number of number of functions to define the path to draw, such as rectangle,
78 rounded rectangle and ellipse, or you can use the common moveTo, lineTo, bezierTo and
79 arcTo API to compose the paths step by step.
82 Understanding Composite Paths
83 =============================
85 Because of the way the rendering backend is built in NanoVega, drawing a composite path,
86 that is path consisting from multiple paths defining holes and fills, is a bit more
87 involved. NanoVega uses non-zero filling rule and by default, and paths are wound in counter
88 clockwise order. Keep that in mind when drawing using the low level draw API. In order to
89 wind one of the predefined shapes as a hole, you should call `pathWinding(NVGSolidity.Hole)`,
90 or `pathWinding(NVGSolidity.Solid)` $(B after) defining the path.
94 vg.rect(100, 100, 120, 30);
95 vg.circle(120, 120, 5);
96 vg.pathWinding(NVGSolidity.Hole); // mark circle as a hole
97 vg.fillColor(nvgRGBA(255, 192, 0, 255));
102 Rendering is wrong, what to do?
103 ===============================
106 * make sure you have created NanoVega context using [nvgCreateContext] call
107 * make sure you have initialised OpenGL with $(B stencil buffer)
108 * make sure you have cleared stencil buffer
109 * make sure all rendering calls happen between [beginFrame] and [endFrame]
110 * to enable more checks for OpenGL errors, add `NVGContextFlag.Debug` flag to [nvgCreateContext]
114 OpenGL state touched by the backend
115 ===================================
117 The OpenGL back-end touches following states:
119 When textures are uploaded or updated, the following pixel store is set to defaults:
120 `GL_UNPACK_ALIGNMENT`, `GL_UNPACK_ROW_LENGTH`, `GL_UNPACK_SKIP_PIXELS`, `GL_UNPACK_SKIP_ROWS`.
121 Texture binding is also affected. Texture updates can happen when the user loads images,
122 or when new font glyphs are added. Glyphs are added as needed between calls to [beginFrame]
125 The data for the whole frame is buffered and flushed in [endFrame].
126 The following code illustrates the OpenGL state touched by the rendering code:
130 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
131 glEnable(GL_CULL_FACE);
135 glDisable(GL_DEPTH_TEST);
136 glDisable(GL_SCISSOR_TEST);
137 glDisable(GL_COLOR_LOGIC_OP);
138 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
139 glStencilMask(0xffffffff);
140 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
141 glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
142 glActiveTexture(GL_TEXTURE1);
143 glActiveTexture(GL_TEXTURE0);
144 glBindBuffer(GL_UNIFORM_BUFFER, buf);
145 glBindVertexArray(arr);
146 glBindBuffer(GL_ARRAY_BUFFER, buf);
147 glBindTexture(GL_TEXTURE_2D, tex);
148 glUniformBlockBinding(... , GLNVG_FRAG_BINDING);
154 ## Context Management
156 Functions to create and destory NanoVega context.
161 To start drawing with NanoVega context, you have to "begin frame", and then
162 "end frame" to flush your rendering commands to GPU.
164 composite_operation =
165 ## Composite Operation
167 The composite operations in NanoVega are modeled after HTML Canvas API, and
168 the blend func is based on OpenGL (see corresponding manuals for more info).
169 The colors in the blending state have premultiplied alpha.
174 Colors in NanoVega are stored as ARGB. Zero alpha means "transparent color".
177 ## Matrices and Transformations
179 The paths, gradients, patterns and scissor region are transformed by an transformation
180 matrix at the time when they are passed to the API.
181 The current transformation matrix is an affine matrix:
183 ----------------------
187 ----------------------
189 Where: (sx, sy) define scaling, (kx, ky) skewing, and (tx, ty) translation.
190 The last row is assumed to be (0, 0, 1) and is not stored.
192 Apart from [resetTransform], each transformation function first creates
193 specific transformation matrix and pre-multiplies the current transformation by it.
195 Current coordinate system (transformation) can be saved and restored using [save] and [restore].
197 The following functions can be used to make calculations on 2x3 transformation matrices.
198 A 2x3 matrix is represented as float[6].
203 NanoVega contains state which represents how paths will be rendered.
204 The state contains transform, fill and stroke styles, text and font styles,
205 and scissor clipping.
210 Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.
211 Solid color is simply defined as a color value, different kinds of paints can be created
212 using [linearGradient], [boxGradient], [radialGradient] and [imagePattern].
214 Current render style can be saved and restored using [save] and [restore].
216 Note that if you want "almost perfect" pixel rendering, you should set aspect ratio to 1,
217 and use `integerCoord+0.5f` as pixel coordinates.
219 render_transformations =
220 ## Render Transformations
222 Transformation matrix management for the current rendering style. Transformations are applied in
223 backwards order. I.e. if you first translate, and then rotate, your path will be rotated around
224 it's origin, and then translated to the destination point.
229 Scissoring allows you to clip the rendering into a rectangle. This is useful for various
230 user interface cases like rendering a text edit or a timeline.
235 NanoVega allows you to load image files in various formats (if arsd loaders are in place) to be used for rendering.
236 In addition you can upload your own image.
237 The parameter imageFlagsList is a list of flags defined in [NVGImageFlag].
239 If you will use your image as fill pattern, it will be scaled by default. To make it repeat, pass
240 [NVGImageFlag.RepeatX] and [NVGImageFlag.RepeatY] flags to image creation function respectively.
245 NanoVega supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.
246 These can be used as paints for strokes and fills.
249 ## Render-Time Affine Transformations
251 It is possible to set affine transformation matrix for GPU. That matrix will
252 be applied by the shader code. This can be used to quickly translate and rotate
253 saved paths. Call this $(B only) between [beginFrame] and [endFrame].
255 Note that [beginFrame] resets this matrix to identity one.
257 $(WARNING Don't use this for scaling or skewing, or your image will be heavily distorted!)
262 Drawing a new shape starts with [beginPath], it clears all the currently defined paths.
263 Then you define one or more paths and sub-paths which describe the shape. The are functions
264 to draw common shapes like rectangles and circles, and lower level step-by-step functions,
265 which allow to define a path curve by curve.
267 NanoVega uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise
268 winding and holes should have counter clockwise order. To specify winding of a path you can
269 call [pathWinding]. This is useful especially for the common shapes, which are drawn CCW.
271 Finally you can fill the path using current fill style by calling [fill], and stroke it
272 with current stroke style by calling [stroke].
274 The curve segments and sub-paths are transformed by the current transform.
279 This is picking API that works directly on paths, without rasterizing them first.
281 [beginFrame] resets picking state. Then you can create paths as usual, but
282 there is a possibility to perform hit checks $(B before) rasterizing a path.
283 Call either id assigning functions ([currFillHitId]/[currStrokeHitId]), or
284 immediate hit test functions ([hitTestCurrFill]/[hitTestCurrStroke])
285 before rasterizing (i.e. calling [fill] or [stroke]) to perform hover
286 effects, for example.
288 Also note that picking API is ignoring GPU affine transformation matrix.
289 You can "untransform" picking coordinates before checking with [gpuUntransformPoint].
291 $(WARNING Picking API completely ignores clipping. If you want to check for
292 clip regions, you have to manually register them as fill/stroke paths,
293 and perform the necessary logic. See [hitTestForId] function.)
296 ## Clipping with paths
298 If scissoring is not enough for you, you can clip rendering with arbitrary path,
299 or with combination of paths. Clip region is saved by [save] and restored by
300 [restore] NanoVega functions. You can combine clip paths with various logic
301 operations, see [NVGClipMode].
303 Note that both [clip] and [clipStroke] are ignoring scissoring (i.e. clip mask
304 is created as if there was no scissor set). Actual rendering is affected by
310 NanoVega allows you to load .ttf files and use the font to render text.
311 You have to load some font, and set proper font size before doing anything
312 with text, as there is no "default" font provided by NanoVega. Also, don't
313 forget to check return value of `createFont()`, 'cause NanoVega won't fail
314 if it cannot load font, it will silently try to render nothing.
316 The appearance of the text can be defined by setting the current text style
317 and by specifying the fill color. Common text and font settings such as
318 font size, letter spacing and text align are supported. Font blur allows you
319 to create simple text effects such as drop shadows.
321 At render time the font face can be set based on the font handles or name.
323 Font measure functions return values in local space, the calculations are
324 carried in the same resolution as the final rendering. This is done because
325 the text glyph positions are snapped to the nearest pixels sharp rendering.
327 The local space means that values are not rotated or scale as per the current
328 transformation. For example if you set font size to 12, which would mean that
329 line height is 16, then regardless of the current scaling and rotation, the
330 returned line height is always 16. Some measures may vary because of the scaling
331 since aforementioned pixel snapping.
333 While this may sound a little odd, the setup allows you to always render the
334 same way regardless of scaling. I.e. following works regardless of scaling:
336 ----------------------
337 string txt = "Text me up.";
338 vg.textBounds(x, y, txt, bounds);
340 vg.roundedRect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1], 6);
342 ----------------------
344 Note: currently only solid color fill is supported for text.
347 ## Low-Level Font Engine (FontStash)
349 FontStash is used to load fonts, to manage font atlases, and to get various text metrics.
350 You don't need any graphics context to use FontStash, so you can do things like text
351 layouting outside of your rendering code. Loaded fonts are refcounted, so it is cheap
352 to create new FontStash, copy fonts from NanoVega context into it, and use that new
353 FontStash to do some UI layouting, for example. Also note that you can get text metrics
354 without creating glyph bitmaps (by using [FONSTextBoundsIterator], for example); this way
355 you don't need to waste CPU and memory resources to render unneeded images into font atlas,
356 and you can layout alot of text very fast.
358 Note that "FontStash" is abbrevated as "FONS". So when you see some API that contains
359 word "fons" in it, this is not a typo, and it should not read "font" intead.
361 TODO for Ketmar: write some nice example code here, and finish documenting FontStash API.
363 module iv
.nanovega
.nanovega
;
370 private alias usize
= size_t
;
372 private template Unqual(T
) {
373 static if (is(T U
== immutable U
)) alias Unqual
= U
;
374 else static if (is(T U
== shared inout const U
)) alias Unqual
= U
;
375 else static if (is(T U
== shared inout U
)) alias Unqual
= U
;
376 else static if (is(T U
== shared const U
)) alias Unqual
= U
;
377 else static if (is(T U
== shared U
)) alias Unqual
= U
;
378 else static if (is(T U
== inout const U
)) alias Unqual
= U
;
379 else static if (is(T U
== inout U
)) alias Unqual
= U
;
380 else static if (is(T U
== const U
)) alias Unqual
= U
;
381 else alias Unqual
= T
;
383 private template isAnyCharType(T
, bool unqual
=false) {
384 static if (unqual
) private alias UT
= Unqual
!T
; else private alias UT
= T
;
385 enum isAnyCharType
= is(UT
== char) ||
is(UT
== wchar) ||
is(UT
== dchar);
387 private template isWideCharType(T
, bool unqual
=false) {
388 static if (unqual
) private alias UT
= Unqual
!T
; else private alias UT
= T
;
389 enum isWideCharType
= is(UT
== wchar) ||
is(UT
== dchar);
392 version(nanovg_disable_vfs
) {
393 enum NanoVegaHasIVVFS
= false;
395 static if (is(typeof((){import iv
.vfs
;}))) {
396 enum NanoVegaHasIVVFS
= true;
399 enum NanoVegaHasIVVFS
= false;
403 // ////////////////////////////////////////////////////////////////////////// //
405 // ////////////////////////////////////////////////////////////////////////// //
406 import core
.stdc
.stdlib
: malloc
, realloc
, free
;
407 import core
.stdc
.string
: memset
, memcpy
, strlen
;
408 import std
.math
: PI
;
410 //version = nanovg_force_stb_ttf;
413 version = nanovg_use_freetype
;
415 version = nanovg_disable_fontconfig
;
418 version = nanovg_default_no_font_aa
;
419 version = nanovg_builtin_fontconfig_bindings
;
420 version = nanovg_builtin_freetype_bindings
;
421 version = nanovg_builtin_opengl_bindings
; // use `arsd.simpledisplay` to get basic bindings
423 version = nanovg_builtin_fontconfig_bindings
;
424 version = nanovg_builtin_freetype_bindings
;
425 version = nanovg_builtin_opengl_bindings
; // use `arsd.simpledisplay` to get basic bindings
428 version(nanovg_disable_fontconfig
) {
429 public enum NanoVegaHasFontConfig
= false;
431 public enum NanoVegaHasFontConfig
= true;
432 version(nanovg_builtin_fontconfig_bindings
) {} else import iv
.fontconfig
;
435 //version = nanovg_bench_flatten;
438 Annotation to indicate it is compatible with [arsd.script]
440 package(iv
) enum scriptable
= "arsd_jsvar_compatible";
445 enum NanoVegaHasArsdColor
= (is(typeof((){ import arsd
.color
; })));
446 enum NanoVegaHasArsdImage
= (is(typeof((){ import arsd
.color
; import arsd
.image
; })));
448 static if (NanoVegaHasArsdColor
) private import arsd
.color
;
449 static if (NanoVegaHasArsdImage
) {
450 private import arsd
.image
;
452 void stbi_set_unpremultiply_on_load (int flag_true_if_should_unpremultiply
) {}
453 void stbi_convert_iphone_png_to_rgb (int flag_true_if_should_convert
) {}
454 ubyte* stbi_load (const(char)* filename
, int* x
, int* y
, int* comp
, int req_comp
) { return null; }
455 ubyte* stbi_load_from_memory (const(void)* buffer
, int len
, int* x
, int* y
, int* comp
, int req_comp
) { return null; }
456 void stbi_image_free (void* retval_from_stbi_load
) {}
459 version(nanovg_default_no_font_aa
) {
460 __gshared
bool NVG_INVERT_FONT_AA
= false;
462 __gshared
bool NVG_INVERT_FONT_AA
= true;
466 /// this is branchless for ints on x86, and even for longs on x86_64
467 public ubyte nvgClampToByte(T
) (T n
) pure nothrow @safe @nogc if (__traits(isIntegral
, T
)) {
468 static if (__VERSION__
> 2067) pragma(inline
, true);
469 static if (T
.sizeof
== 2 || T
.sizeof
== 4) {
470 static if (__traits(isUnsigned
, T
)) {
471 return cast(ubyte)(n
&0xff|
(255-((-cast(int)(n
< 256))>>24)));
473 n
&= -cast(int)(n
>= 0);
474 return cast(ubyte)(n|
((255-cast(int)n
)>>31));
476 } else static if (T
.sizeof
== 1) {
477 static assert(__traits(isUnsigned
, T
), "clampToByte: signed byte? no, really?");
479 } else static if (T
.sizeof
== 8) {
480 static if (__traits(isUnsigned
, T
)) {
481 return cast(ubyte)(n
&0xff|
(255-((-cast(long)(n
< 256))>>56)));
483 n
&= -cast(long)(n
>= 0);
484 return cast(ubyte)(n|
((255-cast(long)n
)>>63));
487 static assert(false, "clampToByte: integer too big");
492 /// NanoVega RGBA color
493 /// Group: color_utils
494 public align(1) struct NVGColor
{
497 float[4] rgba
= 0; /// default color is transparent (a=1 is opaque)
500 @property string
toString () const @safe { import std
.string
: format
; return "NVGColor(%s,%s,%s,%s)".format(r
, g
, b
, a
); }
503 enum transparent
= NVGColor(0.0f, 0.0f, 0.0f, 0.0f);
504 enum k8orange
= NVGColor(1.0f, 0.5f, 0.0f, 1.0f);
506 enum aliceblue
= NVGColor(240, 248, 255);
507 enum antiquewhite
= NVGColor(250, 235, 215);
508 enum aqua
= NVGColor(0, 255, 255);
509 enum aquamarine
= NVGColor(127, 255, 212);
510 enum azure
= NVGColor(240, 255, 255);
511 enum beige
= NVGColor(245, 245, 220);
512 enum bisque
= NVGColor(255, 228, 196);
513 enum black
= NVGColor(0, 0, 0); // basic color
514 enum blanchedalmond
= NVGColor(255, 235, 205);
515 enum blue
= NVGColor(0, 0, 255); // basic color
516 enum blueviolet
= NVGColor(138, 43, 226);
517 enum brown
= NVGColor(165, 42, 42);
518 enum burlywood
= NVGColor(222, 184, 135);
519 enum cadetblue
= NVGColor(95, 158, 160);
520 enum chartreuse
= NVGColor(127, 255, 0);
521 enum chocolate
= NVGColor(210, 105, 30);
522 enum coral
= NVGColor(255, 127, 80);
523 enum cornflowerblue
= NVGColor(100, 149, 237);
524 enum cornsilk
= NVGColor(255, 248, 220);
525 enum crimson
= NVGColor(220, 20, 60);
526 enum cyan
= NVGColor(0, 255, 255); // basic color
527 enum darkblue
= NVGColor(0, 0, 139);
528 enum darkcyan
= NVGColor(0, 139, 139);
529 enum darkgoldenrod
= NVGColor(184, 134, 11);
530 enum darkgray
= NVGColor(169, 169, 169);
531 enum darkgreen
= NVGColor(0, 100, 0);
532 enum darkgrey
= NVGColor(169, 169, 169);
533 enum darkkhaki
= NVGColor(189, 183, 107);
534 enum darkmagenta
= NVGColor(139, 0, 139);
535 enum darkolivegreen
= NVGColor(85, 107, 47);
536 enum darkorange
= NVGColor(255, 140, 0);
537 enum darkorchid
= NVGColor(153, 50, 204);
538 enum darkred
= NVGColor(139, 0, 0);
539 enum darksalmon
= NVGColor(233, 150, 122);
540 enum darkseagreen
= NVGColor(143, 188, 143);
541 enum darkslateblue
= NVGColor(72, 61, 139);
542 enum darkslategray
= NVGColor(47, 79, 79);
543 enum darkslategrey
= NVGColor(47, 79, 79);
544 enum darkturquoise
= NVGColor(0, 206, 209);
545 enum darkviolet
= NVGColor(148, 0, 211);
546 enum deeppink
= NVGColor(255, 20, 147);
547 enum deepskyblue
= NVGColor(0, 191, 255);
548 enum dimgray
= NVGColor(105, 105, 105);
549 enum dimgrey
= NVGColor(105, 105, 105);
550 enum dodgerblue
= NVGColor(30, 144, 255);
551 enum firebrick
= NVGColor(178, 34, 34);
552 enum floralwhite
= NVGColor(255, 250, 240);
553 enum forestgreen
= NVGColor(34, 139, 34);
554 enum fuchsia
= NVGColor(255, 0, 255);
555 enum gainsboro
= NVGColor(220, 220, 220);
556 enum ghostwhite
= NVGColor(248, 248, 255);
557 enum gold
= NVGColor(255, 215, 0);
558 enum goldenrod
= NVGColor(218, 165, 32);
559 enum gray
= NVGColor(128, 128, 128); // basic color
560 enum green
= NVGColor(0, 128, 0); // basic color
561 enum greenyellow
= NVGColor(173, 255, 47);
562 enum grey
= NVGColor(128, 128, 128); // basic color
563 enum honeydew
= NVGColor(240, 255, 240);
564 enum hotpink
= NVGColor(255, 105, 180);
565 enum indianred
= NVGColor(205, 92, 92);
566 enum indigo
= NVGColor(75, 0, 130);
567 enum ivory
= NVGColor(255, 255, 240);
568 enum khaki
= NVGColor(240, 230, 140);
569 enum lavender
= NVGColor(230, 230, 250);
570 enum lavenderblush
= NVGColor(255, 240, 245);
571 enum lawngreen
= NVGColor(124, 252, 0);
572 enum lemonchiffon
= NVGColor(255, 250, 205);
573 enum lightblue
= NVGColor(173, 216, 230);
574 enum lightcoral
= NVGColor(240, 128, 128);
575 enum lightcyan
= NVGColor(224, 255, 255);
576 enum lightgoldenrodyellow
= NVGColor(250, 250, 210);
577 enum lightgray
= NVGColor(211, 211, 211);
578 enum lightgreen
= NVGColor(144, 238, 144);
579 enum lightgrey
= NVGColor(211, 211, 211);
580 enum lightpink
= NVGColor(255, 182, 193);
581 enum lightsalmon
= NVGColor(255, 160, 122);
582 enum lightseagreen
= NVGColor(32, 178, 170);
583 enum lightskyblue
= NVGColor(135, 206, 250);
584 enum lightslategray
= NVGColor(119, 136, 153);
585 enum lightslategrey
= NVGColor(119, 136, 153);
586 enum lightsteelblue
= NVGColor(176, 196, 222);
587 enum lightyellow
= NVGColor(255, 255, 224);
588 enum lime
= NVGColor(0, 255, 0);
589 enum limegreen
= NVGColor(50, 205, 50);
590 enum linen
= NVGColor(250, 240, 230);
591 enum magenta
= NVGColor(255, 0, 255); // basic color
592 enum maroon
= NVGColor(128, 0, 0);
593 enum mediumaquamarine
= NVGColor(102, 205, 170);
594 enum mediumblue
= NVGColor(0, 0, 205);
595 enum mediumorchid
= NVGColor(186, 85, 211);
596 enum mediumpurple
= NVGColor(147, 112, 219);
597 enum mediumseagreen
= NVGColor(60, 179, 113);
598 enum mediumslateblue
= NVGColor(123, 104, 238);
599 enum mediumspringgreen
= NVGColor(0, 250, 154);
600 enum mediumturquoise
= NVGColor(72, 209, 204);
601 enum mediumvioletred
= NVGColor(199, 21, 133);
602 enum midnightblue
= NVGColor(25, 25, 112);
603 enum mintcream
= NVGColor(245, 255, 250);
604 enum mistyrose
= NVGColor(255, 228, 225);
605 enum moccasin
= NVGColor(255, 228, 181);
606 enum navajowhite
= NVGColor(255, 222, 173);
607 enum navy
= NVGColor(0, 0, 128);
608 enum oldlace
= NVGColor(253, 245, 230);
609 enum olive
= NVGColor(128, 128, 0);
610 enum olivedrab
= NVGColor(107, 142, 35);
611 enum orange
= NVGColor(255, 165, 0);
612 enum orangered
= NVGColor(255, 69, 0);
613 enum orchid
= NVGColor(218, 112, 214);
614 enum palegoldenrod
= NVGColor(238, 232, 170);
615 enum palegreen
= NVGColor(152, 251, 152);
616 enum paleturquoise
= NVGColor(175, 238, 238);
617 enum palevioletred
= NVGColor(219, 112, 147);
618 enum papayawhip
= NVGColor(255, 239, 213);
619 enum peachpuff
= NVGColor(255, 218, 185);
620 enum peru
= NVGColor(205, 133, 63);
621 enum pink
= NVGColor(255, 192, 203);
622 enum plum
= NVGColor(221, 160, 221);
623 enum powderblue
= NVGColor(176, 224, 230);
624 enum purple
= NVGColor(128, 0, 128);
625 enum red
= NVGColor(255, 0, 0); // basic color
626 enum rosybrown
= NVGColor(188, 143, 143);
627 enum royalblue
= NVGColor(65, 105, 225);
628 enum saddlebrown
= NVGColor(139, 69, 19);
629 enum salmon
= NVGColor(250, 128, 114);
630 enum sandybrown
= NVGColor(244, 164, 96);
631 enum seagreen
= NVGColor(46, 139, 87);
632 enum seashell
= NVGColor(255, 245, 238);
633 enum sienna
= NVGColor(160, 82, 45);
634 enum silver
= NVGColor(192, 192, 192);
635 enum skyblue
= NVGColor(135, 206, 235);
636 enum slateblue
= NVGColor(106, 90, 205);
637 enum slategray
= NVGColor(112, 128, 144);
638 enum slategrey
= NVGColor(112, 128, 144);
639 enum snow
= NVGColor(255, 250, 250);
640 enum springgreen
= NVGColor(0, 255, 127);
641 enum steelblue
= NVGColor(70, 130, 180);
642 enum tan
= NVGColor(210, 180, 140);
643 enum teal
= NVGColor(0, 128, 128);
644 enum thistle
= NVGColor(216, 191, 216);
645 enum tomato
= NVGColor(255, 99, 71);
646 enum turquoise
= NVGColor(64, 224, 208);
647 enum violet
= NVGColor(238, 130, 238);
648 enum wheat
= NVGColor(245, 222, 179);
649 enum white
= NVGColor(255, 255, 255); // basic color
650 enum whitesmoke
= NVGColor(245, 245, 245);
651 enum yellow
= NVGColor(255, 255, 0); // basic color
652 enum yellowgreen
= NVGColor(154, 205, 50);
657 this (ubyte ar
, ubyte ag
, ubyte ab
, ubyte aa
=255) pure {
658 pragma(inline
, true);
666 this (float ar
, float ag
, float ab
, float aa
=1.0f) pure {
667 pragma(inline
, true);
674 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
676 pragma(inline
, true);
678 g
= ((c
>>8)&0xff)/255.0f;
679 b
= ((c
>>16)&0xff)/255.0f;
680 a
= ((c
>>24)&0xff)/255.0f;
683 /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
684 this (const(char)[] srgb
) {
685 static int c2d (char ch
) pure nothrow @safe @nogc {
686 pragma(inline
, true);
688 ch
>= '0' && ch
<= '9' ? ch
-'0' :
689 ch
>= 'A' && ch
<= 'F' ? ch
-'A'+10 :
690 ch
>= 'a' && ch
<= 'f' ? ch
-'a'+10 :
695 foreach (immutable char ch
; srgb
) {
696 if (ch
<= ' ') continue;
698 if (dc
!= -1) { dc
= -1; break; }
701 if (dc
>= digs
.length
) { dc
= -1; break; }
702 if ((digs
[dc
++] = c2d(ch
)) < 0) { dc
= -1; break; }
720 r
= (digs
[0]*16+digs
[1])/255.0f;
721 g
= (digs
[2]*16+digs
[3])/255.0f;
722 b
= (digs
[4]*16+digs
[5])/255.0f;
725 a
= (digs
[0]*16+digs
[1])/255.0f;
726 r
= (digs
[2]*16+digs
[3])/255.0f;
727 g
= (digs
[4]*16+digs
[5])/255.0f;
728 b
= (digs
[6]*16+digs
[7])/255.0f;
735 /// Is this color completely opaque?
736 @property bool isOpaque () const pure nothrow @trusted @nogc { pragma(inline
, true); return (rgba
.ptr
[3] >= 1.0f); }
737 /// Is this color completely transparent?
738 @property bool isTransparent () const pure nothrow @trusted @nogc { pragma(inline
, true); return (rgba
.ptr
[3] <= 0.0f); }
740 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
741 @property uint asUint () const pure {
742 pragma(inline
, true);
745 (cast(uint)(g
*255)<<8)|
746 (cast(uint)(b
*255)<<16)|
747 (cast(uint)(a
*255)<<24);
750 alias asUintABGR
= asUint
; /// Ditto.
752 /// AABBGGRR (same format as little-endian RGBA image, coincidentally, the same as arsd.color)
753 static NVGColor
fromUint (uint c
) pure { pragma(inline
, true); return NVGColor(c
); }
755 alias fromUintABGR
= fromUint
; /// Ditto.
758 @property uint asUintARGB () const pure {
759 pragma(inline
, true);
762 (cast(uint)(g
*255)<<8)|
763 (cast(uint)(r
*255)<<16)|
764 (cast(uint)(a
*255)<<24);
768 static NVGColor
fromUintARGB (uint c
) pure { pragma(inline
, true); return NVGColor((c
>>16)&0xff, (c
>>8)&0xff, c
&0xff, (c
>>24)&0xff); }
770 @property ref inout(float) r () inout pure @trusted { pragma(inline
, true); return rgba
.ptr
[0]; } ///
771 @property ref inout(float) g () inout pure @trusted { pragma(inline
, true); return rgba
.ptr
[1]; } ///
772 @property ref inout(float) b () inout pure @trusted { pragma(inline
, true); return rgba
.ptr
[2]; } ///
773 @property ref inout(float) a () inout pure @trusted { pragma(inline
, true); return rgba
.ptr
[3]; } ///
775 ref NVGColor
applyTint() (in auto ref NVGColor tint
) nothrow @trusted @nogc {
776 if (tint
.a
== 0) return this;
777 foreach (immutable idx
, ref float v
; rgba
[0..4]) {
778 v
= nvg__clamp(v
*tint
.rgba
.ptr
[idx
], 0.0f, 1.0f);
783 NVGHSL
asHSL() (bool useWeightedLightness
=false) const { pragma(inline
, true); return NVGHSL
.fromColor(this, useWeightedLightness
); } ///
784 static fromHSL() (in auto ref NVGHSL hsl
) { pragma(inline
, true); return hsl
.asColor
; } ///
786 static if (NanoVegaHasArsdColor
) {
787 Color
toArsd () const { pragma(inline
, true); return Color(cast(int)(r
*255), cast(int)(g
*255), cast(int)(b
*255), cast(int)(a
*255)); } ///
788 static NVGColor
fromArsd (in Color c
) { pragma(inline
, true); return NVGColor(c
.r
, c
.g
, c
.b
, c
.a
); } ///
791 version(aliced
) pragma(inline
, true);
801 /// NanoVega A-HSL color
802 /// Group: color_utils
803 public align(1) struct NVGHSL
{
805 float h
=0, s
=0, l
=1, a
=1; ///
807 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
)); }
812 this (float ah
, float as
, float al
, float aa
=1) pure { pragma(inline
, true); h
= ah
; s
= as
; l
= al
; a
= aa
; }
814 NVGColor
asColor () const { pragma(inline
, true); return nvgHSLA(h
, s
, l
, a
); } ///
816 // taken from Adam's arsd.color
817 /** Converts an RGB color into an HSL triplet.
818 * [useWeightedLightness] will try to get a better value for luminosity for the human eye,
819 * which is more sensitive to green than red and more to red than blue.
820 * If it is false, it just does average of the rgb. */
821 static NVGHSL
fromColor() (in auto ref NVGColor c
, bool useWeightedLightness
=false) pure {
829 if (g1
> maxColor
) maxColor
= g1
;
830 if (b1
> maxColor
) maxColor
= b1
;
832 if (g1
< minColor
) minColor
= g1
;
833 if (b1
< minColor
) minColor
= b1
;
835 res
.l
= (maxColor
+minColor
)/2;
836 if (useWeightedLightness
) {
837 // the colors don't affect the eye equally
838 // this is a little more accurate than plain HSL numbers
839 res
.l
= 0.2126*r1
+0.7152*g1
+0.0722*b1
;
841 if (maxColor
!= minColor
) {
843 res
.s
= (maxColor
-minColor
)/(maxColor
+minColor
);
845 res
.s
= (maxColor
-minColor
)/(2.0-maxColor
-minColor
);
847 if (r1
== maxColor
) {
848 res
.h
= (g1
-b1
)/(maxColor
-minColor
);
849 } else if(g1
== maxColor
) {
850 res
.h
= 2.0+(b1
-r1
)/(maxColor
-minColor
);
852 res
.h
= 4.0+(r1
-g1
)/(maxColor
-minColor
);
857 if (res
.h
< 0) res
.h
+= 360;
865 //version = nanovega_debug_image_manager;
866 //version = nanovega_debug_image_manager_rc;
868 /** NanoVega image handle.
870 * This is refcounted struct, so you don't need to do anything special to free it once it is allocated.
877 int id
; // backend image id
881 this() (in auto ref NVGImage src
) nothrow @trusted @nogc {
882 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; if (src
.id
!= 0) printf("NVGImage %p created from %p (imgid=%d)\n", &this, src
, src
.id
); }
883 if (src
.id
> 0 && src
.ctx
!is null) {
884 ctx
= cast(NVGContext
)src
.ctx
;
886 ctx
.nvg__imageIncRef(id
);
891 ~this () nothrow @trusted @nogc { version(aliced
) pragma(inline
, true); clear(); }
894 this (this) nothrow @trusted @nogc {
895 version(aliced
) pragma(inline
, true);
896 if (id
> 0 && ctx
!is null) {
897 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; printf("NVGImage %p postblit (imgid=%d)\n", &this, id
); }
898 ctx
.nvg__imageIncRef(id
);
903 void opAssign() (in auto ref NVGImage src
) nothrow @trusted @nogc {
904 if (src
.id
<= 0 || src
.ctx
is null) {
907 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
); }
908 if (src
.id
> 0 && src
.ctx
!is null) (cast(NVGContext
)src
.ctx
).nvg__imageIncRef(src
.id
);
909 if (id
> 0 && ctx
!is null) ctx
.nvg__imageDecRef(id
);
910 ctx
= cast(NVGContext
)src
.ctx
;
916 void clear () nothrow @trusted @nogc {
917 if (id
> 0 && ctx
!is null) {
918 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; printf("NVGImage %p cleared (imgid=%d)\n", &this, id
); }
919 ctx
.nvg__imageDecRef(id
);
925 /// Is this image valid?
926 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (id
> 0 && ctx
.valid
); }
928 /// Is the given image valid and comes from the same context?
929 @property bool isSameContext (const(NVGContext
) actx
) const pure nothrow @safe @nogc { pragma(inline
, true); return (actx
!is null && ctx
is actx
); }
931 /// Returns image width, or zero for invalid image.
932 int width () const nothrow @trusted @nogc {
936 ctx
.params
.renderGetTextureSize(cast(void*)ctx
.params
.userPtr
, id
, &w
, &h
);
941 /// Returns image height, or zero for invalid image.
942 int height () const nothrow @trusted @nogc {
946 ctx
.params
.renderGetTextureSize(cast(void*)ctx
.params
.userPtr
, id
, &w
, &h
);
953 /// Paint parameters for various fills. Don't change anything here!
954 /// Group: render_styles
955 public struct NVGPaint
{
957 float[2] extent
= 0.0f;
959 float feather
= 0.0f;
960 NVGColor innerColor
; /// this can be used to modulate images (fill/font)
961 NVGColor middleColor
;
963 float midp
= -1; // middle stop for 3-color gradient
965 bool simpleColor
; /// if `true`, only innerColor is used, and this is solid-color paint
967 this() (in auto ref NVGPaint p
) nothrow @trusted @nogc {
969 extent
[] = p
.extent
[];
972 innerColor
= p
.innerColor
;
973 middleColor
= p
.middleColor
;
975 outerColor
= p
.outerColor
;
977 simpleColor
= p
.simpleColor
;
980 void opAssign() (in auto ref NVGPaint p
) nothrow @trusted @nogc {
982 extent
[] = p
.extent
[];
985 innerColor
= p
.innerColor
;
986 middleColor
= p
.middleColor
;
988 outerColor
= p
.outerColor
;
990 simpleColor
= p
.simpleColor
;
993 void clear () nothrow @trusted @nogc {
994 version(aliced
) pragma(inline
, true);
995 import core
.stdc
.string
: memset
;
997 memset(&this, 0, this.sizeof
);
1004 public enum NVGWinding
{
1005 CCW
= 1, /// Winding for solid shapes
1006 CW
= 2, /// Winding for holes
1011 public enum NVGSolidity
{
1012 Solid
= 1, /// Solid shape (CCW winding).
1013 Hole
= 2, /// Hole (CW winding).
1017 /// Group: render_styles
1018 public enum NVGLineCap
{
1028 public align(1) struct NVGTextAlign
{
1030 /// Horizontal align.
1032 Left
= 0, /// Default, align text horizontally to left.
1033 Center
= 1, /// Align text horizontally to center.
1034 Right
= 2, /// Align text horizontally to right.
1039 Baseline
= 0, /// Default, align text vertically to baseline.
1040 Top
= 1, /// Align text vertically to top.
1041 Middle
= 2, /// Align text vertically to middle.
1042 Bottom
= 3, /// Align text vertically to bottom.
1045 pure nothrow @safe @nogc:
1047 this (H h
) { pragma(inline
, true); value
= h
; } ///
1048 this (V v
) { pragma(inline
, true); value
= cast(ubyte)(v
<<4); } ///
1049 this (H h
, V v
) { pragma(inline
, true); value
= cast(ubyte)(h|
(v
<<4)); } ///
1050 this (V v
, H h
) { pragma(inline
, true); value
= cast(ubyte)(h|
(v
<<4)); } ///
1051 void reset () { pragma(inline
, true); value
= 0; } ///
1052 void reset (H h
, V v
) { pragma(inline
, true); value
= cast(ubyte)(h|
(v
<<4)); } ///
1053 void reset (V v
, H h
) { pragma(inline
, true); value
= cast(ubyte)(h|
(v
<<4)); } ///
1055 bool left () const { pragma(inline
, true); return ((value
&0x0f) == H
.Left
); } ///
1056 void left (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0xf0)|
(v ? H
.Left
: 0)); } ///
1057 bool center () const { pragma(inline
, true); return ((value
&0x0f) == H
.Center
); } ///
1058 void center (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0xf0)|
(v ? H
.Center
: 0)); } ///
1059 bool right () const { pragma(inline
, true); return ((value
&0x0f) == H
.Right
); } ///
1060 void right (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0xf0)|
(v ? H
.Right
: 0)); } ///
1062 bool baseline () const { pragma(inline
, true); return (((value
>>4)&0x0f) == V
.Baseline
); } ///
1063 void baseline (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0x0f)|
(v ? V
.Baseline
<<4 : 0)); } ///
1064 bool top () const { pragma(inline
, true); return (((value
>>4)&0x0f) == V
.Top
); } ///
1065 void top (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0x0f)|
(v ? V
.Top
<<4 : 0)); } ///
1066 bool middle () const { pragma(inline
, true); return (((value
>>4)&0x0f) == V
.Middle
); } ///
1067 void middle (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0x0f)|
(v ? V
.Middle
<<4 : 0)); } ///
1068 bool bottom () const { pragma(inline
, true); return (((value
>>4)&0x0f) == V
.Bottom
); } ///
1069 void bottom (bool v
) { pragma(inline
, true); value
= cast(ubyte)((value
&0x0f)|
(v ? V
.Bottom
<<4 : 0)); } ///
1071 H
horizontal () const { pragma(inline
, true); return cast(H
)(value
&0x0f); } ///
1072 void horizontal (H v
) { pragma(inline
, true); value
= (value
&0xf0)|v
; } ///
1074 V
vertical () const { pragma(inline
, true); return cast(V
)((value
>>4)&0x0f); } ///
1075 void vertical (V v
) { pragma(inline
, true); value
= (value
&0x0f)|
cast(ubyte)(v
<<4); } ///
1078 ubyte value
= 0; // low nibble: horizontal; high nibble: vertical
1082 /// Group: composite_operation
1083 public enum NVGBlendFactor
{
1086 SrcColor
= 1<<2, ///
1087 OneMinusSrcColor
= 1<<3, ///
1088 DstColor
= 1<<4, ///
1089 OneMinusDstColor
= 1<<5, ///
1090 SrcAlpha
= 1<<6, ///
1091 OneMinusSrcAlpha
= 1<<7, ///
1092 DstAlpha
= 1<<8, ///
1093 OneMinusDstAlpha
= 1<<9, ///
1094 SrcAlphaSaturate
= 1<<10, ///
1097 /// Composite operation (HTML5-alike).
1098 /// Group: composite_operation
1099 public enum NVGCompositeOperation
{
1104 DestinationOver
, ///
1107 DestinationAtop
, ///
1113 /// Composite operation state.
1114 /// Group: composite_operation
1115 public struct NVGCompositeOperationState
{
1116 bool simple
; /// `true`: use `glBlendFunc()` instead of `glBlendFuncSeparate()`
1117 NVGBlendFactor srcRGB
; ///
1118 NVGBlendFactor dstRGB
; ///
1119 NVGBlendFactor srcAlpha
; ///
1120 NVGBlendFactor dstAlpha
; ///
1123 /// Mask combining more
1125 public enum NVGClipMode
{
1126 None
, /// normal rendering (i.e. render path instead of modifying clip region)
1127 Union
, /// old mask will be masked with the current one; this is the default mode for [clip]
1128 Or
, /// new mask will be added to the current one (logical `OR` operation);
1129 Xor
, /// new mask will be logically `XOR`ed with the current one
1130 Sub
, /// "subtract" current path from mask
1131 Replace
, /// replace current mask
1132 Add
= Or
, /// Synonym
1135 /// Glyph position info.
1137 public struct NVGGlyphPosition
{
1138 usize strpos
; /// Position of the glyph in the input string.
1139 float x
; /// The x-coordinate of the logical glyph position.
1140 float minx
, maxx
; /// The bounds of the glyph shape.
1143 /// Text row storage.
1145 public struct NVGTextRow(CT
) if (isAnyCharType
!CT
) {
1146 alias CharType
= CT
;
1148 int start
; /// Index in the input text where the row starts.
1149 int end
; /// Index in the input text where the row ends (one past the last character).
1150 float width
; /// Logical width of the row.
1151 float minx
, maxx
; /// Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.
1152 /// Get rest of the string.
1153 @property const(CT
)[] rest () const pure nothrow @trusted @nogc { pragma(inline
, true); return (end
<= s
.length ? s
[end
..$] : null); }
1154 /// Get current row.
1155 @property const(CT
)[] row () const pure nothrow @trusted @nogc { pragma(inline
, true); return s
[start
..end
]; }
1156 @property const(CT
)[] string () const pure nothrow @trusted @nogc { pragma(inline
, true); return s
; }
1157 @property void string(CT
) (const(CT
)[] v
) pure nothrow @trusted @nogc { pragma(inline
, true); s
= v
; }
1160 /// Image creation flags.
1162 public enum NVGImageFlag
: uint {
1163 None
= 0, /// Nothing special.
1164 GenerateMipmaps
= 1<<0, /// Generate mipmaps during creation of the image.
1165 RepeatX
= 1<<1, /// Repeat image in X direction.
1166 RepeatY
= 1<<2, /// Repeat image in Y direction.
1167 FlipY
= 1<<3, /// Flips (inverses) image in Y direction when rendered.
1168 Premultiplied
= 1<<4, /// Image data has premultiplied alpha.
1169 NoFiltering
= 1<<8, /// use GL_NEAREST instead of GL_LINEAR
1170 Nearest
= NoFiltering
, /// compatibility with original NanoVG
1171 NoDelete
= 1<<16,/// Do not delete GL texture handle.
1174 alias NVGImageFlags
= NVGImageFlag
; /// Backwards compatibility for [NVGImageFlag].
1177 // ////////////////////////////////////////////////////////////////////////// //
1180 static T
* xdup(T
) (const(T
)* ptr
, int count
) nothrow @trusted @nogc {
1181 import core
.stdc
.stdlib
: malloc
;
1182 import core
.stdc
.string
: memcpy
;
1183 if (count
== 0) return null;
1184 T
* res
= cast(T
*)malloc(T
.sizeof
*count
);
1185 if (res
is null) assert(0, "NanoVega: out of memory");
1186 memcpy(res
, ptr
, T
.sizeof
*count
);
1190 // Internal Render API
1198 float[2] extent
= -1.0f;
1201 /// General NanoVega vertex struct. Contains geometry coordinates, and (sometimes unused) texture coordinates.
1202 public struct NVGVertex
{
1215 NVGWinding mWinding
;
1219 @disable this (this); // no copies
1220 void opAssign() (in auto ref NVGpath a
) { static assert(0, "no copies!"); }
1222 void clear () nothrow @trusted @nogc {
1223 import core
.stdc
.stdlib
: free
;
1224 import core
.stdc
.string
: memset
;
1226 if (stroke
!is null && stroke
!is fill
) free(stroke
);
1227 if (fill
!is null) free(fill
);
1229 memset(&this, 0, this.sizeof
);
1232 // won't clear current path
1233 void copyFrom (const NVGpath
* src
) nothrow @trusted @nogc {
1234 import core
.stdc
.string
: memcpy
;
1235 assert(src
!is null);
1236 memcpy(&this, src
, NVGpath
.sizeof
);
1237 this.fill
= xdup(src
.fill
, src
.nfill
);
1238 if (src
.stroke
is src
.fill
) {
1239 this.stroke
= this.fill
;
1241 this.stroke
= xdup(src
.stroke
, src
.nstroke
);
1246 public @property const(NVGVertex
)[] fillVertices () const pure nothrow @trusted @nogc {
1247 pragma(inline
, true);
1248 return (nfill
> 0 ? fill
[0..nfill
] : null);
1251 public @property const(NVGVertex
)[] strokeVertices () const pure nothrow @trusted @nogc {
1252 pragma(inline
, true);
1253 return (nstroke
> 0 ? stroke
[0..nstroke
] : null);
1256 public @property NVGWinding
winding () const pure nothrow @trusted @nogc { pragma(inline
, true); return mWinding
; }
1257 public @property bool complex () const pure nothrow @trusted @nogc { pragma(inline
, true); return !convex
; }
1265 bool function (void* uptr
) nothrow @trusted @nogc renderCreate
;
1266 int function (void* uptr
, NVGtexture type
, int w
, int h
, int imageFlags
, const(ubyte)* data
) nothrow @trusted @nogc renderCreateTexture
;
1267 bool function (void* uptr
, int image
) nothrow @trusted @nogc renderTextureIncRef
;
1268 bool function (void* uptr
, int image
) nothrow @trusted @nogc renderDeleteTexture
; // this basically does decref; also, it should be thread-safe, and postpone real deletion to next `renderViewport()` call
1269 bool function (void* uptr
, int image
, int x
, int y
, int w
, int h
, const(ubyte)* data
) nothrow @trusted @nogc renderUpdateTexture
;
1270 bool function (void* uptr
, int image
, int* w
, int* h
) nothrow @trusted @nogc renderGetTextureSize
;
1271 void function (void* uptr
, int width
, int height
) nothrow @trusted @nogc renderViewport
; // called in [beginFrame]
1272 void function (void* uptr
) nothrow @trusted @nogc renderCancel
;
1273 void function (void* uptr
) nothrow @trusted @nogc renderFlush
;
1274 void function (void* uptr
) nothrow @trusted @nogc renderPushClip
; // backend should support stack of at least [NVG_MAX_STATES] elements
1275 void function (void* uptr
) nothrow @trusted @nogc renderPopClip
; // backend should support stack of at least [NVG_MAX_STATES] elements
1276 void function (void* uptr
) nothrow @trusted @nogc renderResetClip
; // reset current clip region to `non-clipped`
1277 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
;
1278 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
;
1279 void function (void* uptr
, NVGCompositeOperationState compositeOperation
, NVGClipMode clipmode
, NVGPaint
* paint
, NVGscissor
* scissor
, const(NVGVertex
)* verts
, int nverts
) nothrow @trusted @nogc renderTriangles
;
1280 void function (void* uptr
, in ref NVGMatrix mat
) nothrow @trusted @nogc renderSetAffine
;
1281 void function (void* uptr
) nothrow @trusted @nogc renderDelete
;
1284 // ////////////////////////////////////////////////////////////////////////// //
1287 enum NVG_INIT_FONTIMAGE_SIZE
= 512;
1288 enum NVG_MAX_FONTIMAGE_SIZE
= 2048;
1289 enum NVG_MAX_FONTIMAGES
= 4;
1291 enum NVG_INIT_COMMANDS_SIZE
= 256;
1292 enum NVG_INIT_POINTS_SIZE
= 128;
1293 enum NVG_INIT_PATHS_SIZE
= 16;
1294 enum NVG_INIT_VERTS_SIZE
= 256;
1295 enum NVG_MAX_STATES
= 32;
1297 public enum NVG_KAPPA90
= 0.5522847493f; /// Length proportional to radius of a cubic bezier handle for 90deg arcs.
1298 enum NVG_MIN_FEATHER
= 0.001f; // it should be greater than zero, 'cause it is used in shader for divisions
1308 enum PointFlag
: int {
1312 InnerBevelPR
= 0x08,
1316 NVGCompositeOperationState compositeOperation
;
1317 bool shapeAntiAlias
= true;
1320 float strokeWidth
= 1.0f;
1321 float miterLimit
= 10.0f;
1322 NVGLineCap lineJoin
= NVGLineCap
.Miter
;
1323 NVGLineCap lineCap
= NVGLineCap
.Butt
;
1327 float fontSize
= 16.0f;
1328 float letterSpacing
= 0.0f;
1329 float lineHeight
= 1.0f;
1330 float fontBlur
= 0.0f;
1331 NVGTextAlign textAlign
;
1333 bool evenOddMode
= false; // use even-odd filling rule (required for some svgs); otherwise use non-zero fill
1335 enum MaxDashes
= 32; // max 16 dashes
1336 float[MaxDashes
] dashes
;
1338 uint lastFlattenDashCount
= 0;
1339 float dashStart
= 0;
1341 bool firstDashIsGap
= false;
1342 // dasher state for flattener
1343 bool dasherActive
= false;
1345 void clearPaint () nothrow @trusted @nogc {
1359 struct NVGpathCache
{
1370 // this is required for saved paths
1373 float strokeAlphaMul
;
1377 NVGClipMode clipmode
;
1378 // non-saved path will not have this
1382 @disable this (this); // no copies
1383 void opAssign() (in auto ref NVGpathCache a
) { static assert(0, "no copies!"); }
1385 // won't clear current path
1386 void copyFrom (const NVGpathCache
* src
) nothrow @trusted @nogc {
1387 import core
.stdc
.stdlib
: malloc
;
1388 import core
.stdc
.string
: memcpy
, memset
;
1389 assert(src
!is null);
1390 memcpy(&this, src
, NVGpathCache
.sizeof
);
1391 this.points
= xdup(src
.points
, src
.npoints
);
1392 this.cpoints
= src
.npoints
;
1393 this.verts
= xdup(src
.verts
, src
.nverts
);
1394 this.cverts
= src
.nverts
;
1395 this.commands
= xdup(src
.commands
, src
.ncommands
);
1396 if (src
.npaths
> 0) {
1397 this.paths
= cast(NVGpath
*)malloc(src
.npaths
*NVGpath
.sizeof
);
1398 memset(this.paths
, 0, npaths
*NVGpath
.sizeof
);
1399 foreach (immutable pidx
; 0..npaths
) this.paths
[pidx
].copyFrom(&src
.paths
[pidx
]);
1400 this.cpaths
= src
.npaths
;
1402 this.npaths
= this.cpaths
= 0;
1406 void clear () nothrow @trusted @nogc {
1407 import core
.stdc
.stdlib
: free
;
1408 import core
.stdc
.string
: memset
;
1409 if (paths
!is null) {
1410 foreach (ref p
; paths
[0..npaths
]) p
.clear();
1413 if (points
!is null) free(points
);
1414 if (verts
!is null) free(verts
);
1415 if (commands
!is null) free(commands
);
1416 memset(&this, 0, this.sizeof
);
1420 /// Pointer to opaque NanoVega context structure.
1421 /// Group: context_management
1422 public alias NVGContext
= NVGcontextinternal
*;
1424 /// FontStash context
1425 /// Group: font_stash
1426 public alias FONSContext
= FONScontextInternal
*;
1428 /// Returns FontStash context of the given NanoVega context.
1429 /// Group: font_stash
1430 public FONSContext
fonsContext (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null && ctx
.contextAlive ? ctx
.fs
: null); }
1432 /// Returns scale that should be applied to FontStash parameters due to matrix transformations on the context (or 1)
1433 /// Group: font_stash
1434 public float fonsScale (NVGContext ctx
) /*pure*/ nothrow @trusted @nogc {
1435 pragma(inline
, true);
1436 return (ctx
!is null && ctx
.contextAlive
&& ctx
.nstates
> 0 ?
nvg__getFontScale(&ctx
.states
.ptr
[ctx
.nstates
-1])*ctx
.devicePxRatio
: 1);
1439 /// Setup FontStash from the given NanoVega context font parameters. Note that this will apply transformation scale too.
1440 /// Returns `false` if FontStash or NanoVega context is not active.
1441 /// Group: font_stash
1442 public bool setupFonsFrom (FONSContext stash
, NVGContext ctx
) nothrow @trusted @nogc {
1443 if (stash
is null || ctx
is null ||
!ctx
.contextAlive || ctx
.nstates
== 0) return false;
1444 NVGstate
* state
= nvg__getState(ctx
);
1445 immutable float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
1446 stash
.size
= state
.fontSize
*scale
;
1447 stash
.spacing
= state
.letterSpacing
*scale
;
1448 stash
.blur
= state
.fontBlur
*scale
;
1449 stash
.textAlign
= state
.textAlign
;
1450 stash
.fontId
= state
.fontId
;
1454 /// Setup NanoVega context font parameters from the given FontStash. Note that NanoVega can apply transformation scale later.
1455 /// Returns `false` if FontStash or NanoVega context is not active.
1456 /// Group: font_stash
1457 public bool setupCtxFrom (NVGContext ctx
, FONSContext stash
) nothrow @trusted @nogc {
1458 if (stash
is null || ctx
is null ||
!ctx
.contextAlive || ctx
.nstates
== 0) return false;
1459 NVGstate
* state
= nvg__getState(ctx
);
1460 immutable float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
1461 state
.fontSize
= stash
.size
;
1462 state
.letterSpacing
= stash
.spacing
;
1463 state
.fontBlur
= stash
.blur
;
1464 state
.textAlign
= stash
.textAlign
;
1465 state
.fontId
= stash
.fontId
;
1469 /** Bezier curve rasterizer.
1471 * De Casteljau Bezier rasterizer is faster, but currently rasterizing curves with cusps sligtly wrong.
1472 * It doesn't really matter in practice.
1474 * AFD tesselator is somewhat slower, but does cusps better.
1476 * McSeem rasterizer should have the best quality, bit it is the slowest method. Basically, you will
1477 * never notice any visial difference (and this code is not really debugged), so you probably should
1478 * not use it. It is there for further experiments.
1480 public enum NVGTesselation
{
1481 DeCasteljau
, /// default: standard well-known tesselation algorithm
1482 AFD
, /// adaptive forward differencing
1483 DeCasteljauMcSeem
, /// standard well-known tesselation algorithm, with improvements from Maxim Shemanarev; slowest one, but should give best results
1486 /// Default tesselator for Bezier curves.
1487 public __gshared NVGTesselation NVG_DEFAULT_TESSELATOR
= NVGTesselation
.DeCasteljau
;
1492 /// valid only inside [beginFrame]/[endFrame]
1493 /// Group: context_management
1494 public int width (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.mWidth
: 0); }
1496 /// valid only inside [beginFrame]/[endFrame]
1497 /// Group: context_management
1498 public int height (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.mHeight
: 0); }
1500 /// valid only inside [beginFrame]/[endFrame]
1501 /// Group: context_management
1502 public float devicePixelRatio (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.devicePxRatio
: float.nan
); }
1504 /// Returns `true` if [beginFrame] was called, and neither [endFrame], nor [cancelFrame] were.
1505 /// Group: context_management
1506 public bool inFrame (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null && ctx
.contextAlive ? ctx
.nstates
> 0 : false); }
1508 // path autoregistration
1510 /// [pickid] to stop autoregistration.
1511 /// Group: context_management
1512 public enum NVGNoPick
= -1;
1514 /// >=0: this pickid will be assigned to all filled/stroked paths
1515 /// Group: context_management
1516 public int pickid (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.pathPickId
: NVGNoPick
); }
1518 /// >=0: this pickid will be assigned to all filled/stroked paths
1519 /// Group: context_management
1520 public void pickid (NVGContext ctx
, int v
) nothrow @trusted @nogc { pragma(inline
, true); if (ctx
!is null) ctx
.pathPickId
= v
; }
1522 /// pick autoregistration mode; see [NVGPickKind]
1523 /// Group: context_management
1524 public uint pickmode (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.pathPickRegistered
&NVGPickKind
.All
: 0); }
1526 /// pick autoregistration mode; see [NVGPickKind]
1527 /// Group: context_management
1528 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
); }
1530 // tesselator options
1532 /// Get current Bezier tesselation mode. See [NVGTesselation].
1533 /// Group: context_management
1534 public NVGTesselation
tesselation (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null ? ctx
.tesselatortype
: NVGTesselation
.DeCasteljau
); }
1536 /// Set current Bezier tesselation mode. See [NVGTesselation].
1537 /// Group: context_management
1538 public void tesselation (NVGContext ctx
, NVGTesselation v
) nothrow @trusted @nogc { pragma(inline
, true); if (ctx
!is null) ctx
.tesselatortype
= v
; }
1541 private struct NVGcontextinternal
{
1547 float commandx
, commandy
;
1548 NVGstate
[NVG_MAX_STATES
] states
;
1550 NVGpathCache
* cache
;
1551 public float tessTol
;
1552 public float angleTol
; // 0.0f -- angle tolerance for McSeem Bezier rasterizer
1553 public float cuspLimit
; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
1555 public float fringeWidth
;
1556 float devicePxRatio
;
1558 NVGImage
[NVG_MAX_FONTIMAGES
] fontImages
;
1564 NVGTesselation tesselatortype
;
1566 NVGpickScene
* pickScene
;
1567 int pathPickId
; // >=0: register all paths for picking using this id
1568 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
1571 int recstart
; // used to cancel recording
1574 NVGMatrix gpuAffine
;
1575 int mWidth
, mHeight
;
1577 shared int imageCount
; // number of alive images in this context
1578 bool contextAlive
; // context can be dead, but still contain some images
1580 @disable this (this); // no copies
1581 void opAssign() (in auto ref NVGcontextinternal a
) { static assert(0, "no copies!"); }
1584 public @property int getImageCount () nothrow @trusted @nogc {
1586 return atomicLoad(imageCount
);
1590 /** Returns number of tesselated pathes in context.
1592 * Tesselated pathes are either triangle strips (for strokes), or
1593 * triangle fans (for fills). Note that NanoVega can generate some
1594 * surprising pathes (like fringe stroke for antialiasing, for example).
1596 * One render path can contain vertices both for fill, and for stroke triangles.
1598 public int renderPathCount (NVGContext ctx
) pure nothrow @trusted @nogc {
1599 pragma(inline
, true);
1600 return (ctx
!is null && ctx
.contextAlive ? ctx
.cache
.npaths
: 0);
1603 /** Get vertices of "fill" triangle fan for the given render path. Can return empty slice.
1605 * $(WARNING Returned slice can be invalidated by any other NanoVega API call
1606 * (except the calls to render path accessors), and using it in such
1607 * case is UB. So copy vertices to freshly allocated array if you want
1608 * to keep them for further processing.)
1610 public const(NVGVertex
)[] renderPathFillVertices (NVGContext ctx
, int pathidx
) pure nothrow @trusted @nogc {
1611 pragma(inline
, true);
1612 return (ctx
!is null && ctx
.contextAlive
&& pathidx
>= 0 && pathidx
< ctx
.cache
.npaths ? ctx
.cache
.paths
[pathidx
].fillVertices
: null);
1615 /** Get vertices of "stroke" triangle strip for the given render path. Can return empty slice.
1617 * $(WARNING Returned slice can be invalidated by any other NanoVega API call
1618 * (except the calls to render path accessors), and using it in such
1619 * case is UB. So copy vertices to freshly allocated array if you want
1620 * to keep them for further processing.)
1622 public const(NVGVertex
)[] renderPathStrokeVertices (NVGContext ctx
, int pathidx
) pure nothrow @trusted @nogc {
1623 pragma(inline
, true);
1624 return (ctx
!is null && ctx
.contextAlive
&& pathidx
>= 0 && pathidx
< ctx
.cache
.npaths ? ctx
.cache
.paths
[pathidx
].strokeVertices
: null);
1628 /// Returns winding for the given render path.
1629 public NVGWinding
renderPathWinding (NVGContext ctx
, int pathidx
) pure nothrow @trusted @nogc {
1630 pragma(inline
, true);
1631 return (ctx
!is null && ctx
.contextAlive
&& pathidx
>= 0 && pathidx
< ctx
.cache
.npaths ? ctx
.cache
.paths
[pathidx
].winding
: NVGWinding
.CCW
);
1635 /// Returns "complex path" flag for the given render path.
1636 public bool renderPathComplex (NVGContext ctx
, int pathidx
) pure nothrow @trusted @nogc {
1637 pragma(inline
, true);
1638 return (ctx
!is null && ctx
.contextAlive
&& pathidx
>= 0 && pathidx
< ctx
.cache
.npaths ? ctx
.cache
.paths
[pathidx
].complex
: false);
1642 void nvg__imageIncRef (NVGContext ctx
, int imgid
, bool increfInGL
=true) nothrow @trusted @nogc {
1643 if (ctx
!is null && imgid
> 0) {
1644 import core
.atomic
: atomicOp
;
1645 atomicOp
!"+="(ctx
.imageCount
, 1);
1646 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; printf("image[++]ref: context %p: %d image refs (%d)\n", ctx
, ctx
.imageCount
, imgid
); }
1647 if (ctx
.contextAlive
&& increfInGL
) ctx
.params
.renderTextureIncRef(ctx
.params
.userPtr
, imgid
);
1651 void nvg__imageDecRef (NVGContext ctx
, int imgid
) nothrow @trusted @nogc {
1652 if (ctx
!is null && imgid
> 0) {
1653 import core
.atomic
: atomicOp
;
1654 int icnt
= atomicOp
!"-="(ctx
.imageCount
, 1);
1655 if (icnt
< 0) assert(0, "NanoVega: internal image refcounting error");
1656 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; printf("image[--]ref: context %p: %d image refs (%d)\n", ctx
, ctx
.imageCount
, imgid
); }
1657 if (ctx
.contextAlive
) ctx
.params
.renderDeleteTexture(ctx
.params
.userPtr
, imgid
);
1658 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
); }
1659 if (!ctx
.contextAlive
&& icnt
== 0) {
1660 // it is finally safe to free context memory
1661 import core
.stdc
.stdlib
: free
;
1662 version(nanovega_debug_image_manager
) { import core
.stdc
.stdio
; printf("killed zombie context %p\n", ctx
); }
1669 public import core
.stdc
.math
:
1675 nvg__atan2f
= atan2f
,
1680 public int nvg__lrintf (float f
) nothrow @trusted @nogc { pragma(inline
, true); return cast(int)(f
+0.5); }
1682 public import core
.stdc
.math
: nvg__lrintf
= lrintf
;
1685 public auto nvg__min(T
) (T a
, T b
) { pragma(inline
, true); return (a
< b ? a
: b
); }
1686 public auto nvg__max(T
) (T a
, T b
) { pragma(inline
, true); return (a
> b ? a
: b
); }
1687 public auto nvg__clamp(T
) (T a
, T mn
, T mx
) { pragma(inline
, true); return (a
< mn ? mn
: (a
> mx ? mx
: a
)); }
1688 //float nvg__absf() (float a) { pragma(inline, true); return (a >= 0.0f ? a : -a); }
1689 public auto nvg__sign(T
) (T a
) { pragma(inline
, true); return (a
>= cast(T
)0 ?
cast(T
)1 : cast(T
)(-1)); }
1690 public float nvg__cross() (float dx0
, float dy0
, float dx1
, float dy1
) { pragma(inline
, true); return (dx1
*dy0
-dx0
*dy1
); }
1692 //public import core.stdc.math : nvg__absf = fabsf;
1693 public import core
.math
: nvg__absf
= fabs;
1696 float nvg__normalize (float* x
, float* y
) nothrow @safe @nogc {
1697 float d
= nvg__sqrtf((*x
)*(*x
)+(*y
)*(*y
));
1699 immutable float id
= 1.0f/d
;
1706 void nvg__deletePathCache (ref NVGpathCache
* c
) nothrow @trusted @nogc {
1713 NVGpathCache
* nvg__allocPathCache () nothrow @trusted @nogc {
1714 NVGpathCache
* c
= cast(NVGpathCache
*)malloc(NVGpathCache
.sizeof
);
1715 if (c
is null) goto error
;
1716 memset(c
, 0, NVGpathCache
.sizeof
);
1718 c
.points
= cast(NVGpoint
*)malloc(NVGpoint
.sizeof
*NVG_INIT_POINTS_SIZE
);
1719 if (c
.points
is null) goto error
;
1720 assert(c
.npoints
== 0);
1721 c
.cpoints
= NVG_INIT_POINTS_SIZE
;
1723 c
.paths
= cast(NVGpath
*)malloc(NVGpath
.sizeof
*NVG_INIT_PATHS_SIZE
);
1724 if (c
.paths
is null) goto error
;
1725 assert(c
.npaths
== 0);
1726 c
.cpaths
= NVG_INIT_PATHS_SIZE
;
1728 c
.verts
= cast(NVGVertex
*)malloc(NVGVertex
.sizeof
*NVG_INIT_VERTS_SIZE
);
1729 if (c
.verts
is null) goto error
;
1730 assert(c
.nverts
== 0);
1731 c
.cverts
= NVG_INIT_VERTS_SIZE
;
1736 nvg__deletePathCache(c
);
1740 void nvg__setDevicePixelRatio (NVGContext ctx
, float ratio
) pure nothrow @safe @nogc {
1741 ctx
.tessTol
= 0.25f/ratio
;
1742 ctx
.distTol
= 0.01f/ratio
;
1743 ctx
.fringeWidth
= 1.0f/ratio
;
1744 ctx
.devicePxRatio
= ratio
;
1747 NVGCompositeOperationState
nvg__compositeOperationState (NVGCompositeOperation op
) pure nothrow @safe @nogc {
1748 NVGCompositeOperationState state
;
1749 NVGBlendFactor sfactor
, dfactor
;
1751 if (op
== NVGCompositeOperation
.SourceOver
) { sfactor
= NVGBlendFactor
.One
; dfactor
= NVGBlendFactor
.OneMinusSrcAlpha
;}
1752 else if (op
== NVGCompositeOperation
.SourceIn
) { sfactor
= NVGBlendFactor
.DstAlpha
; dfactor
= NVGBlendFactor
.Zero
; }
1753 else if (op
== NVGCompositeOperation
.SourceOut
) { sfactor
= NVGBlendFactor
.OneMinusDstAlpha
; dfactor
= NVGBlendFactor
.Zero
; }
1754 else if (op
== NVGCompositeOperation
.SourceAtop
) { sfactor
= NVGBlendFactor
.DstAlpha
; dfactor
= NVGBlendFactor
.OneMinusSrcAlpha
; }
1755 else if (op
== NVGCompositeOperation
.DestinationOver
) { sfactor
= NVGBlendFactor
.OneMinusDstAlpha
; dfactor
= NVGBlendFactor
.One
; }
1756 else if (op
== NVGCompositeOperation
.DestinationIn
) { sfactor
= NVGBlendFactor
.Zero
; dfactor
= NVGBlendFactor
.SrcAlpha
; }
1757 else if (op
== NVGCompositeOperation
.DestinationOut
) { sfactor
= NVGBlendFactor
.Zero
; dfactor
= NVGBlendFactor
.OneMinusSrcAlpha
; }
1758 else if (op
== NVGCompositeOperation
.DestinationAtop
) { sfactor
= NVGBlendFactor
.OneMinusDstAlpha
; dfactor
= NVGBlendFactor
.SrcAlpha
; }
1759 else if (op
== NVGCompositeOperation
.Lighter
) { sfactor
= NVGBlendFactor
.One
; dfactor
= NVGBlendFactor
.One
; }
1760 else if (op
== NVGCompositeOperation
.Copy
) { sfactor
= NVGBlendFactor
.One
; dfactor
= NVGBlendFactor
.Zero
; }
1761 else if (op
== NVGCompositeOperation
.Xor
) {
1762 state
.simple
= false;
1763 state
.srcRGB
= NVGBlendFactor
.OneMinusDstColor
;
1764 state
.srcAlpha
= NVGBlendFactor
.OneMinusDstAlpha
;
1765 state
.dstRGB
= NVGBlendFactor
.OneMinusSrcColor
;
1766 state
.dstAlpha
= NVGBlendFactor
.OneMinusSrcAlpha
;
1769 else { sfactor
= NVGBlendFactor
.One
; dfactor
= NVGBlendFactor
.OneMinusSrcAlpha
; } // default value for invalid op: SourceOver
1771 state
.simple
= true;
1772 state
.srcAlpha
= sfactor
;
1773 state
.dstAlpha
= dfactor
;
1777 NVGstate
* nvg__getState (NVGContext ctx
) pure nothrow @trusted @nogc {
1778 pragma(inline
, true);
1779 if (ctx
is null ||
!ctx
.contextAlive || ctx
.nstates
== 0) assert(0, "NanoVega: cannot perform commands on inactive context");
1780 return &ctx
.states
.ptr
[ctx
.nstates
-1];
1783 // Constructor called by the render back-end.
1784 NVGContext
createInternal (NVGparams
* params
) nothrow @trusted @nogc {
1785 FONSParams fontParams
;
1786 NVGContext ctx
= cast(NVGContext
)malloc(NVGcontextinternal
.sizeof
);
1787 if (ctx
is null) goto error
;
1788 memset(ctx
, 0, NVGcontextinternal
.sizeof
);
1790 ctx
.angleTol
= 0; // angle tolerance for McSeem Bezier rasterizer
1791 ctx
.cuspLimit
= 0; // cusp limit for McSeem Bezier rasterizer (0: real cusps)
1793 ctx
.contextAlive
= true;
1795 ctx
.params
= *params
;
1796 //ctx.fontImages[0..NVG_MAX_FONTIMAGES] = 0;
1798 ctx
.commands
= cast(float*)malloc(float.sizeof
*NVG_INIT_COMMANDS_SIZE
);
1799 if (ctx
.commands
is null) goto error
;
1801 ctx
.ccommands
= NVG_INIT_COMMANDS_SIZE
;
1803 ctx
.cache
= nvg__allocPathCache();
1804 if (ctx
.cache
is null) goto error
;
1809 nvg__setDevicePixelRatio(ctx
, 1.0f);
1810 ctx
.mWidth
= ctx
.mHeight
= 0;
1812 if (!ctx
.params
.renderCreate(ctx
.params
.userPtr
)) goto error
;
1814 // init font rendering
1815 memset(&fontParams
, 0, fontParams
.sizeof
);
1816 fontParams
.width
= NVG_INIT_FONTIMAGE_SIZE
;
1817 fontParams
.height
= NVG_INIT_FONTIMAGE_SIZE
;
1818 fontParams
.flags
= FONSParams
.Flag
.ZeroTopLeft
;
1819 fontParams
.renderCreate
= null;
1820 fontParams
.renderUpdate
= null;
1821 fontParams
.renderDelete
= null;
1822 fontParams
.userPtr
= null;
1823 ctx
.fs
= FONSContext
.create(fontParams
);
1824 if (ctx
.fs
is null) goto error
;
1826 // create font texture
1827 ctx
.fontImages
[0].id
= ctx
.params
.renderCreateTexture(ctx
.params
.userPtr
, NVGtexture
.Alpha
, fontParams
.width
, fontParams
.height
, (ctx
.params
.fontAA ?
0 : NVGImageFlag
.NoFiltering
), null);
1828 if (ctx
.fontImages
[0].id
== 0) goto error
;
1829 ctx
.fontImages
[0].ctx
= ctx
;
1830 ctx
.nvg__imageIncRef(ctx
.fontImages
[0].id
, false); // don't increment driver refcount
1831 ctx
.fontImageIdx
= 0;
1833 ctx
.pathPickId
= -1;
1834 ctx
.tesselatortype
= NVG_DEFAULT_TESSELATOR
;
1839 ctx
.deleteInternal();
1843 // Called by render backend.
1844 NVGparams
* internalParams (NVGContext ctx
) nothrow @trusted @nogc {
1848 // Destructor called by the render back-end.
1849 void deleteInternal (ref NVGContext ctx
) nothrow @trusted @nogc {
1850 if (ctx
is null) return;
1851 if (ctx
.contextAlive
) {
1852 if (ctx
.commands
!is null) free(ctx
.commands
);
1853 nvg__deletePathCache(ctx
.cache
);
1855 if (ctx
.fs
) ctx
.fs
.kill();
1857 foreach (uint i
; 0..NVG_MAX_FONTIMAGES
) ctx
.fontImages
[i
].clear();
1859 if (ctx
.params
.renderDelete
!is null) ctx
.params
.renderDelete(ctx
.params
.userPtr
);
1861 if (ctx
.pickScene
!is null) nvg__deletePickScene(ctx
.pickScene
);
1863 ctx
.contextAlive
= false;
1865 import core
.atomic
: atomicLoad
;
1866 if (atomicLoad(ctx
.imageCount
) == 0) {
1867 version(nanovega_debug_image_manager
) { import core
.stdc
.stdio
; printf("destroyed context %p\n", ctx
); }
1870 version(nanovega_debug_image_manager
) { import core
.stdc
.stdio
; printf("context %p is zombie now (%d image refs)\n", ctx
, ctx
.imageCount
); }
1875 /// Delete NanoVega context.
1876 /// Group: context_management
1877 public void kill (ref NVGContext ctx
) nothrow @trusted @nogc {
1879 ctx
.deleteInternal();
1884 /// Returns `true` if the given context is not `null` and can be used for painting.
1885 /// Group: context_management
1886 public bool valid (in NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null && ctx
.contextAlive
); }
1889 // ////////////////////////////////////////////////////////////////////////// //
1892 /** Begin drawing a new frame.
1894 * Calls to NanoVega drawing API should be wrapped in [beginFrame] and [endFrame]
1896 * [beginFrame] defines the size of the window to render to in relation currently
1897 * set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
1898 * control the rendering on Hi-DPI devices.
1900 * For example, GLFW returns two dimension for an opened window: window size and
1901 * frame buffer size. In that case you would set windowWidth/windowHeight to the window size,
1902 * devicePixelRatio to: `windowWidth/windowHeight`.
1904 * Default ratio is `1`.
1906 * Note that fractional ratio can (and will) distort your fonts and images.
1908 * This call also resets pick marks (see picking API for non-rasterized paths),
1909 * path recording, and GPU affine transformatin matrix.
1911 * see also [glNVGClearFlags], which returns necessary flags for [glClear].
1913 * Group: frame_management
1915 public void beginFrame (NVGContext ctx
, int windowWidth
, int windowHeight
, float devicePixelRatio
=1.0f) nothrow @trusted @nogc {
1916 import std
.math
: isNaN
;
1918 printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
1919 ctx.drawCallCount, ctx.fillTriCount, ctx.strokeTriCount, ctx.textTriCount,
1920 ctx.fillTriCount+ctx.strokeTriCount+ctx.textTriCount);
1922 if (ctx
.nstates
> 0) ctx
.cancelFrame();
1924 if (windowWidth
< 1) windowWidth
= 1;
1925 if (windowHeight
< 1) windowHeight
= 1;
1927 if (isNaN(devicePixelRatio
)) devicePixelRatio
= (windowHeight
> 0 ?
cast(float)windowWidth
/cast(float)windowHeight
: 1024.0/768.0);
1929 foreach (ref NVGstate st
; ctx
.states
[0..ctx
.nstates
]) st
.clearPaint();
1934 nvg__setDevicePixelRatio(ctx
, devicePixelRatio
);
1936 ctx
.params
.renderViewport(ctx
.params
.userPtr
, windowWidth
, windowHeight
);
1937 ctx
.mWidth
= windowWidth
;
1938 ctx
.mHeight
= windowHeight
;
1943 ctx
.pathPickId
= NVGNoPick
;
1944 ctx
.pathPickRegistered
= 0;
1946 ctx
.drawCallCount
= 0;
1947 ctx
.fillTriCount
= 0;
1948 ctx
.strokeTriCount
= 0;
1949 ctx
.textTriCount
= 0;
1952 ctx
.pathPickRegistered
= 0;
1953 nvg__clearPathCache(ctx
);
1955 ctx
.gpuAffine
= NVGMatrix
.Identity
;
1957 nvg__pickBeginFrame(ctx
, windowWidth
, windowHeight
);
1960 /// Cancels drawing the current frame. Cancels path recording.
1961 /// Group: frame_management
1962 public void cancelFrame (NVGContext ctx
) nothrow @trusted @nogc {
1963 ctx
.cancelRecording();
1966 // cancel render queue
1967 ctx
.params
.renderCancel(ctx
.params
.userPtr
);
1968 // clear saved states (this may free some textures)
1969 foreach (ref NVGstate st
; ctx
.states
[0..ctx
.nstates
]) st
.clearPaint();
1973 /// Ends drawing the current frame (flushing remaining render state). Commits recorded paths.
1974 /// Group: frame_management
1975 public void endFrame (NVGContext ctx
) nothrow @trusted @nogc {
1976 if (ctx
.recset
!is null) ctx
.recset
.takeCurrentPickScene(ctx
);
1977 ctx
.stopRecording();
1980 // flush render queue
1981 NVGstate
* state
= nvg__getState(ctx
);
1982 ctx
.params
.renderFlush(ctx
.params
.userPtr
);
1983 if (ctx
.fontImageIdx
!= 0) {
1984 auto fontImage
= ctx
.fontImages
[ctx
.fontImageIdx
];
1986 // delete images that smaller than current one
1987 if (!fontImage
.valid
) return;
1988 ctx
.imageSize(fontImage
, iw
, ih
);
1989 foreach (int i
; 0..ctx
.fontImageIdx
) {
1990 if (ctx
.fontImages
[i
].valid
) {
1992 ctx
.imageSize(ctx
.fontImages
[i
], nw
, nh
);
1993 if (nw
< iw || nh
< ih
) {
1994 ctx
.deleteImage(ctx
.fontImages
[i
]);
1996 ctx
.fontImages
[j
++] = ctx
.fontImages
[i
];
2000 // make current font image to first
2001 ctx
.fontImages
[j
++] = ctx
.fontImages
[0];
2002 ctx
.fontImages
[0] = fontImage
;
2003 ctx
.fontImageIdx
= 0;
2004 // clear all images after j
2005 ctx
.fontImages
[j
..NVG_MAX_FONTIMAGES
] = NVGImage
.init
;
2007 // clear saved states (this may free some textures)
2008 foreach (ref NVGstate st
; ctx
.states
[0..ctx
.nstates
]) st
.clearPaint();
2013 // ////////////////////////////////////////////////////////////////////////// //
2014 // Recording and Replaying Pathes
2017 // Group: path_recording
2018 public alias NVGPathSet
= NVGPathSetS
*;
2021 //TODO: save scissor info?
2022 struct NVGPathSetS
{
2024 // either path cache, or text item
2025 static struct Node
{
2033 NVGpickScene
* pickscene
;
2034 //int npickscenes, cpickscenes;
2035 NVGContext svctx
; // used to do some sanity checks, and to free resources
2038 Node
* allocNode () nothrow @trusted @nogc {
2039 import core
.stdc
.string
: memset
;
2040 // grow buffer if necessary
2041 if (nnodes
+1 > cnodes
) {
2042 import core
.stdc
.stdlib
: realloc
;
2043 int newsz
= (cnodes
== 0 ?
8 : cnodes
<= 1024 ? cnodes
*2 : cnodes
+1024);
2044 nodes
= cast(Node
*)realloc(nodes
, newsz
*Node
.sizeof
);
2045 if (nodes
is null) assert(0, "NanoVega: out of memory");
2046 //memset(svp.caches+svp.ccaches, 0, (newsz-svp.ccaches)*NVGpathCache.sizeof);
2049 assert(nnodes
< cnodes
);
2050 memset(nodes
+nnodes
, 0, Node
.sizeof
);
2051 return &nodes
[nnodes
++];
2054 Node
* allocPathNode () nothrow @trusted @nogc {
2055 import core
.stdc
.stdlib
: malloc
;
2056 import core
.stdc
.string
: memset
;
2057 auto node
= allocNode();
2058 // allocate path cache
2059 auto pc
= cast(NVGpathCache
*)malloc(NVGpathCache
.sizeof
);
2060 if (pc
is null) assert(0, "NanoVega: out of memory");
2065 void clearNode (int idx
) nothrow @trusted @nogc {
2066 if (idx
< 0 || idx
>= nnodes
) return;
2067 Node
* node
= &nodes
[idx
];
2068 if (svctx
!is null && node
.paint
.image
.valid
) node
.paint
.image
.clear();
2069 if (node
.path
!is null) node
.path
.clear();
2073 void takeCurrentPickScene (NVGContext ctx
) nothrow @trusted @nogc {
2074 NVGpickScene
* ps
= ctx
.pickScene
;
2075 if (ps
is null) return; // nothing to do
2076 if (ps
.npaths
== 0) return; // pick scene is empty
2077 ctx
.pickScene
= null;
2081 void replay (NVGContext ctx
, in ref NVGColor fillTint
, in ref NVGColor strokeTint
) nothrow @trusted @nogc {
2082 NVGstate
* state
= nvg__getState(ctx
);
2083 foreach (ref node
; nodes
[0..nnodes
]) {
2084 if (auto cc
= node
.path
) {
2085 if (cc
.npaths
<= 0) continue;
2088 NVGPaint fillPaint
= node
.paint
;
2090 // apply global alpha
2091 fillPaint
.innerColor
.a
*= state
.alpha
;
2092 fillPaint
.middleColor
.a
*= state
.alpha
;
2093 fillPaint
.outerColor
.a
*= state
.alpha
;
2095 fillPaint
.innerColor
.applyTint(fillTint
);
2096 fillPaint
.middleColor
.applyTint(fillTint
);
2097 fillPaint
.outerColor
.applyTint(fillTint
);
2099 ctx
.params
.renderFill(ctx
.params
.userPtr
, state
.compositeOperation
, cc
.clipmode
, &fillPaint
, &state
.scissor
, cc
.fringeWidth
, cc
.bounds
.ptr
, cc
.paths
, cc
.npaths
, cc
.evenOddMode
);
2102 foreach (int i
; 0..cc
.npaths
) {
2103 NVGpath
* path
= &cc
.paths
[i
];
2104 ctx
.fillTriCount
+= path
.nfill
-2;
2105 ctx
.fillTriCount
+= path
.nstroke
-2;
2106 ctx
.drawCallCount
+= 2;
2110 if (cc
.strokeReady
) {
2111 NVGPaint strokePaint
= node
.paint
;
2113 strokePaint
.innerColor
.a
*= cc
.strokeAlphaMul
;
2114 strokePaint
.middleColor
.a
*= cc
.strokeAlphaMul
;
2115 strokePaint
.outerColor
.a
*= cc
.strokeAlphaMul
;
2117 // apply global alpha
2118 strokePaint
.innerColor
.a
*= state
.alpha
;
2119 strokePaint
.middleColor
.a
*= state
.alpha
;
2120 strokePaint
.outerColor
.a
*= state
.alpha
;
2122 strokePaint
.innerColor
.applyTint(strokeTint
);
2123 strokePaint
.middleColor
.applyTint(strokeTint
);
2124 strokePaint
.outerColor
.applyTint(strokeTint
);
2126 ctx
.params
.renderStroke(ctx
.params
.userPtr
, state
.compositeOperation
, cc
.clipmode
, &strokePaint
, &state
.scissor
, cc
.fringeWidth
, cc
.strokeWidth
, cc
.paths
, cc
.npaths
);
2129 foreach (int i
; 0..cc
.npaths
) {
2130 NVGpath
* path
= &cc
.paths
[i
];
2131 ctx
.strokeTriCount
+= path
.nstroke
-2;
2132 ++ctx
.drawCallCount
;
2140 @disable this (this); // no copies
2141 void opAssign() (in auto ref NVGPathSetS a
) { static assert(0, "no copies!"); }
2144 // Call delegate [dg] for each path under the specified position (in no particular order).
2145 // Returns the id of the path for which delegate [dg] returned true or -1.
2146 // dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
2147 int hitTestDG(bool bestOrder
=false, DG
) (in float x
, in float y
, NVGPickKind kind
, scope DG dg
) if (IsGoodHitTestDG
!DG || IsGoodHitTestInternalDG
!DG
) {
2148 if (pickscene
is null) return -1;
2150 NVGpickScene
* ps
= pickscene
;
2151 int levelwidth
= 1<<(ps
.nlevels
-1);
2152 int cellx
= nvg__clamp(cast(int)(x
/ps
.xdim
), 0, levelwidth
);
2153 int celly
= nvg__clamp(cast(int)(y
/ps
.ydim
), 0, levelwidth
);
2156 for (int lvl
= ps
.nlevels
-1; lvl
>= 0; --lvl
) {
2157 NVGpickPath
* pp
= ps
.levels
[lvl
][celly
*levelwidth
+cellx
];
2158 while (pp
!is null) {
2159 if (nvg__pickPathTestBounds(svctx
, ps
, pp
, x
, y
)) {
2161 if ((kind
&NVGPickKind
.Stroke
) && (pp
.flags
&NVGPathFlags
.Stroke
)) hit
= nvg__pickPathStroke(ps
, pp
, x
, y
);
2162 if (!hit
&& (kind
&NVGPickKind
.Fill
) && (pp
.flags
&NVGPathFlags
.Fill
)) hit
= nvg__pickPath(ps
, pp
, x
, y
);
2164 static if (IsGoodHitTestDG
!DG
) {
2165 static if (__traits(compiles
, (){ DG dg
; bool res
= dg(cast(int)42, cast(int)666); })) {
2166 if (dg(pp
.id
, cast(int)pp
.order
)) return pp
.id
;
2168 dg(pp
.id
, cast(int)pp
.order
);
2171 static if (__traits(compiles
, (){ DG dg
; NVGpickPath
* pp
; bool res
= dg(pp
); })) {
2172 if (dg(pp
)) return pp
.id
;
2189 // Fills ids with a list of the top most hit ids under the specified position.
2190 // Returns the slice of [ids].
2191 int[] hitTestAll (in float x
, in float y
, NVGPickKind kind
, int[] ids
) nothrow @trusted @nogc {
2192 if (pickscene
is null || ids
.length
== 0) return ids
[0..0];
2195 NVGpickScene
* ps
= pickscene
;
2197 hitTestDG
!false(x
, y
, kind
, delegate (NVGpickPath
* pp
) nothrow @trusted @nogc {
2198 if (npicked
== ps
.cpicked
) {
2199 int cpicked
= ps
.cpicked
+ps
.cpicked
;
2200 NVGpickPath
** picked
= cast(NVGpickPath
**)realloc(ps
.picked
, (NVGpickPath
*).sizeof
*ps
.cpicked
);
2201 if (picked
is null) return true; // abort
2202 ps
.cpicked
= cpicked
;
2205 ps
.picked
[npicked
] = pp
;
2207 return false; // go on
2210 qsort(ps
.picked
, npicked
, (NVGpickPath
*).sizeof
, &nvg__comparePaths
);
2212 assert(npicked
>= 0);
2213 if (npicked
> ids
.length
) npicked
= cast(int)ids
.length
;
2214 foreach (immutable nidx
, ref int did
; ids
[0..npicked
]) did
= ps
.picked
[nidx
].id
;
2216 return ids
[0..npicked
];
2219 // Returns the id of the pickable shape containing x,y or -1 if no shape was found.
2220 int hitTest (in float x
, in float y
, NVGPickKind kind
) nothrow @trusted @nogc {
2221 if (pickscene
is null) return -1;
2226 hitTestDG
!true(x
, y
, kind
, delegate (NVGpickPath
* pp
) nothrow @trusted @nogc {
2227 if (pp
.order
> bestOrder
) {
2228 bestOrder
= pp
.order
;
2237 // Append current path to existing path set. Is is safe to call this with `null` [svp].
2238 void appendCurrentPathToCache (NVGContext ctx
, NVGPathSet svp
, in ref NVGPaint paint
) nothrow @trusted @nogc {
2239 if (ctx
is null || svp
is null) return;
2240 if (ctx
!is svp
.svctx
) assert(0, "NanoVega: cannot save paths from different contexts");
2241 if (ctx
.ncommands
== 0) {
2242 assert(ctx
.cache
.npaths
== 0);
2245 if (!ctx
.cache
.fillReady
&& !ctx
.cache
.strokeReady
) return;
2247 // tesselate current path
2248 //if (!ctx.cache.fillReady) nvg__prepareFill(ctx);
2249 //if (!ctx.cache.strokeReady) nvg__prepareStroke(ctx);
2251 auto node
= svp
.allocPathNode();
2252 NVGpathCache
* cc
= node
.path
;
2253 cc
.copyFrom(ctx
.cache
);
2255 // copy path commands (we may need 'em for picking)
2257 cc
.ncommands
= ctx
.ncommands
;
2259 import core
.stdc
.stdlib
: malloc
;
2260 import core
.stdc
.string
: memcpy
;
2261 cc
.commands
= cast(float*)malloc(ctx
.ncommands
*float.sizeof
);
2262 if (cc
.commands
is null) assert(0, "NanoVega: out of memory");
2263 memcpy(cc
.commands
, ctx
.commands
, ctx
.ncommands
*float.sizeof
);
2270 // Create new empty path set.
2271 // Group: path_recording
2272 public NVGPathSet
newPathSet (NVGContext ctx
) nothrow @trusted @nogc {
2273 import core
.stdc
.stdlib
: malloc
;
2274 import core
.stdc
.string
: memset
;
2275 if (ctx
is null) return null;
2276 NVGPathSet res
= cast(NVGPathSet
)malloc(NVGPathSetS
.sizeof
);
2277 if (res
is null) assert(0, "NanoVega: out of memory");
2278 memset(res
, 0, NVGPathSetS
.sizeof
);
2283 // Is the given path set empty? Empty path set can be `null`.
2284 // Group: path_recording
2285 public bool empty (NVGPathSet svp
) pure nothrow @safe @nogc { pragma(inline
, true); return (svp
is null || svp
.nnodes
== 0); }
2287 // Clear path set contents. Will release $(B some) allocated memory (this function is meant to clear something that will be reused).
2288 // Group: path_recording
2289 public void clear (NVGPathSet svp
) nothrow @trusted @nogc {
2291 import core
.stdc
.stdlib
: free
;
2292 foreach (immutable idx
; 0.. svp
.nnodes
) svp
.clearNode(idx
);
2297 // Destroy path set (frees all allocated memory).
2298 // Group: path_recording
2299 public void kill (ref NVGPathSet svp
) nothrow @trusted @nogc {
2301 import core
.stdc
.stdlib
: free
;
2303 if (svp
.nodes
!is null) free(svp
.nodes
);
2305 if (svp
.pickscene
!is null) nvg__deletePickScene(svp
.pickscene
);
2310 // Start path recording. [svp] should be alive until recording is cancelled or stopped.
2311 // Group: path_recording
2312 public void startRecording (NVGContext ctx
, NVGPathSet svp
) nothrow @trusted @nogc {
2313 if (svp
!is null && svp
.svctx
!is ctx
) assert(0, "NanoVega: cannot share path set between contexts");
2314 ctx
.stopRecording();
2316 ctx
.recstart
= (svp
!is null ? svp
.nnodes
: -1);
2317 ctx
.recblockdraw
= false;
2320 /* Start path recording. [svp] should be alive until recording is cancelled or stopped.
2322 * This will block all rendering, so you can call your rendering functions to record paths without actual drawing.
2323 * Commiting or cancelling will re-enable rendering.
2324 * You can call this with `null` svp to block rendering without recording any paths.
2326 * Group: path_recording
2328 public void startBlockingRecording (NVGContext ctx
, NVGPathSet svp
) nothrow @trusted @nogc {
2329 if (svp
!is null && svp
.svctx
!is ctx
) assert(0, "NanoVega: cannot share path set between contexts");
2330 ctx
.stopRecording();
2332 ctx
.recstart
= (svp
!is null ? svp
.nnodes
: -1);
2333 ctx
.recblockdraw
= true;
2336 // Commit recorded paths. It is safe to call this when recording is not started.
2337 // Group: path_recording
2338 public void stopRecording (NVGContext ctx
) nothrow @trusted @nogc {
2339 if (ctx
.recset
!is null && ctx
.recset
.svctx
!is ctx
) assert(0, "NanoVega: cannot share path set between contexts");
2340 if (ctx
.recset
!is null) ctx
.recset
.takeCurrentPickScene(ctx
);
2343 ctx
.recblockdraw
= false;
2346 // Cancel path recording.
2347 // Group: path_recording
2348 public void cancelRecording (NVGContext ctx
) nothrow @trusted @nogc {
2349 if (ctx
.recset
!is null) {
2350 if (ctx
.recset
.svctx
!is ctx
) assert(0, "NanoVega: cannot share path set between contexts");
2351 assert(ctx
.recstart
>= 0 && ctx
.recstart
<= ctx
.recset
.nnodes
);
2352 foreach (immutable idx
; ctx
.recstart
..ctx
.recset
.nnodes
) ctx
.recset
.clearNode(idx
);
2353 ctx
.recset
.nnodes
= ctx
.recstart
;
2357 ctx
.recblockdraw
= false;
2360 /* Replay saved path set.
2362 * Replaying record while you're recording another one is undefined behavior.
2364 * Group: path_recording
2366 public void replayRecording() (NVGContext ctx
, NVGPathSet svp
, in auto ref NVGColor fillTint
, in auto ref NVGColor strokeTint
) nothrow @trusted @nogc {
2367 if (svp
!is null && svp
.svctx
!is ctx
) assert(0, "NanoVega: cannot share path set between contexts");
2368 svp
.replay(ctx
, fillTint
, strokeTint
);
2372 public void replayRecording() (NVGContext ctx
, NVGPathSet svp
, in auto ref NVGColor fillTint
) nothrow @trusted @nogc { ctx
.replayRecording(svp
, fillTint
, NVGColor
.transparent
); }
2375 public void replayRecording (NVGContext ctx
, NVGPathSet svp
) nothrow @trusted @nogc { ctx
.replayRecording(svp
, NVGColor
.transparent
, NVGColor
.transparent
); }
2378 // ////////////////////////////////////////////////////////////////////////// //
2379 // Composite operation
2381 /// Sets the composite operation.
2382 /// Group: composite_operation
2383 public void globalCompositeOperation (NVGContext ctx
, NVGCompositeOperation op
) nothrow @trusted @nogc {
2384 NVGstate
* state
= nvg__getState(ctx
);
2385 state
.compositeOperation
= nvg__compositeOperationState(op
);
2388 /// Sets the composite operation with custom pixel arithmetic.
2389 /// Group: composite_operation
2390 public void globalCompositeBlendFunc (NVGContext ctx
, NVGBlendFactor sfactor
, NVGBlendFactor dfactor
) nothrow @trusted @nogc {
2391 ctx
.globalCompositeBlendFuncSeparate(sfactor
, dfactor
, sfactor
, dfactor
);
2394 /// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately.
2395 /// Group: composite_operation
2396 public void globalCompositeBlendFuncSeparate (NVGContext ctx
, NVGBlendFactor srcRGB
, NVGBlendFactor dstRGB
, NVGBlendFactor srcAlpha
, NVGBlendFactor dstAlpha
) nothrow @trusted @nogc {
2397 NVGCompositeOperationState op
;
2401 op
.srcAlpha
= srcAlpha
;
2402 op
.dstAlpha
= dstAlpha
;
2403 NVGstate
* state
= nvg__getState(ctx
);
2404 state
.compositeOperation
= op
;
2408 // ////////////////////////////////////////////////////////////////////////// //
2411 /// Returns a color value from string form.
2412 /// Supports: "#rgb", "#rrggbb", "#argb", "#aarrggbb"
2413 /// Group: color_utils
2414 public NVGColor
nvgRGB (const(char)[] srgb
) nothrow @trusted @nogc { pragma(inline
, true); return NVGColor(srgb
); }
2417 public NVGColor
nvgRGBA (const(char)[] srgb
) nothrow @trusted @nogc { pragma(inline
, true); return NVGColor(srgb
); }
2419 /// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).
2420 /// Group: color_utils
2421 public NVGColor
nvgRGB (int r
, int g
, int b
) nothrow @trusted @nogc { pragma(inline
, true); return NVGColor(nvgClampToByte(r
), nvgClampToByte(g
), nvgClampToByte(b
), 255); }
2423 /// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.
2424 /// Group: color_utils
2425 public NVGColor
nvgRGBf (float r
, float g
, float b
) nothrow @trusted @nogc { pragma(inline
, true); return NVGColor(r
, g
, b
, 1.0f); }
2427 /// Returns a color value from red, green, blue and alpha values.
2428 /// Group: color_utils
2429 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
)); }
2431 /// Returns a color value from red, green, blue and alpha values.
2432 /// Group: color_utils
2433 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
); }
2435 /// Returns new color with transparency (alpha) set to [a].
2436 /// Group: color_utils
2437 public NVGColor
nvgTransRGBA (NVGColor c
, ubyte a
) nothrow @trusted @nogc {
2438 pragma(inline
, true);
2444 public NVGColor
nvgTransRGBAf (NVGColor c
, float a
) nothrow @trusted @nogc {
2445 pragma(inline
, true);
2450 /// Linearly interpolates from color c0 to c1, and returns resulting color value.
2451 /// Group: color_utils
2452 public NVGColor
nvgLerpRGBA() (in auto ref NVGColor c0
, in auto ref NVGColor c1
, float u
) nothrow @trusted @nogc {
2453 NVGColor cint
= void;
2454 u
= nvg__clamp(u
, 0.0f, 1.0f);
2455 float oneminu
= 1.0f-u
;
2456 foreach (uint i
; 0..4) cint
.rgba
.ptr
[i
] = c0
.rgba
.ptr
[i
]*oneminu
+c1
.rgba
.ptr
[i
]*u
;
2461 public NVGColor nvgHSL() (float h, float s, float l) {
2462 //pragma(inline, true); // alas
2463 return nvgHSLA(h, s, l, 255);
2467 float nvg__hue (float h
, float m1
, float m2
) pure nothrow @safe @nogc {
2470 if (h
< 1.0f/6.0f) return m1
+(m2
-m1
)*h
*6.0f;
2471 if (h
< 3.0f/6.0f) return m2
;
2472 if (h
< 4.0f/6.0f) return m1
+(m2
-m1
)*(2.0f/3.0f-h
)*6.0f;
2476 /// Returns color value specified by hue, saturation and lightness.
2477 /// HSL values are all in range [0..1], alpha will be set to 255.
2478 /// Group: color_utils
2479 public alias nvgHSL
= nvgHSLA
; // trick to allow inlining
2481 /// Returns color value specified by hue, saturation and lightness and alpha.
2482 /// HSL values are all in range [0..1], alpha in range [0..255].
2483 /// Group: color_utils
2484 public NVGColor
nvgHSLA (float h
, float s
, float l
, ubyte a
=255) nothrow @trusted @nogc {
2485 pragma(inline
, true);
2486 NVGColor col
= void;
2487 h
= nvg__modf(h
, 1.0f);
2488 if (h
< 0.0f) h
+= 1.0f;
2489 s
= nvg__clamp(s
, 0.0f, 1.0f);
2490 l
= nvg__clamp(l
, 0.0f, 1.0f);
2491 immutable float m2
= (l
<= 0.5f ? l
*(1+s
) : l
+s
-l
*s
);
2492 immutable float m1
= 2*l
-m2
;
2493 col
.r
= nvg__clamp(nvg__hue(h
+1.0f/3.0f, m1
, m2
), 0.0f, 1.0f);
2494 col
.g
= nvg__clamp(nvg__hue(h
, m1
, m2
), 0.0f, 1.0f);
2495 col
.b
= nvg__clamp(nvg__hue(h
-1.0f/3.0f, m1
, m2
), 0.0f, 1.0f);
2500 /// Returns color value specified by hue, saturation and lightness and alpha.
2501 /// HSL values and alpha are all in range [0..1].
2502 /// Group: color_utils
2503 public NVGColor
nvgHSLA (float h
, float s
, float l
, float a
) nothrow @trusted @nogc {
2504 // sorry for copypasta, it is for inliner
2505 static if (__VERSION__
>= 2072) pragma(inline
, true);
2506 NVGColor col
= void;
2507 h
= nvg__modf(h
, 1.0f);
2508 if (h
< 0.0f) h
+= 1.0f;
2509 s
= nvg__clamp(s
, 0.0f, 1.0f);
2510 l
= nvg__clamp(l
, 0.0f, 1.0f);
2511 immutable m2
= (l
<= 0.5f ? l
*(1+s
) : l
+s
-l
*s
);
2512 immutable m1
= 2*l
-m2
;
2513 col
.r
= nvg__clamp(nvg__hue(h
+1.0f/3.0f, m1
, m2
), 0.0f, 1.0f);
2514 col
.g
= nvg__clamp(nvg__hue(h
, m1
, m2
), 0.0f, 1.0f);
2515 col
.b
= nvg__clamp(nvg__hue(h
-1.0f/3.0f, m1
, m2
), 0.0f, 1.0f);
2521 // ////////////////////////////////////////////////////////////////////////// //
2522 // Matrices and Transformations
2528 public align(1) struct NVGMatrix
{
2531 static immutable float[6] IdentityMat
= [
2538 /// Matrix values. Initial value is identity matrix.
2545 public nothrow @trusted @nogc:
2546 /// Create Matrix with the given values.
2547 this (const(float)[] amat
...) {
2548 pragma(inline
, true);
2549 if (amat
.length
>= 6) {
2550 mat
.ptr
[0..6] = amat
.ptr
[0..6];
2553 mat
.ptr
[0..amat
.length
] = amat
[];
2557 /// Can be used to check validity of [inverted] result
2558 @property bool valid () const { import core
.stdc
.math
: isfinite
; return (isfinite(mat
.ptr
[0]) != 0); }
2560 /// Returns `true` if this matrix is identity matrix.
2561 @property bool isIdentity () const { version(aliced
) pragma(inline
, true); return (mat
[] == IdentityMat
[]); }
2563 /// Returns new inverse matrix.
2564 /// If inverted matrix cannot be calculated, `res.valid` fill be `false`.
2565 NVGMatrix
inverted () const {
2566 NVGMatrix res
= this;
2571 /// Inverts this matrix.
2572 /// If inverted matrix cannot be calculated, `this.valid` fill be `false`.
2573 ref NVGMatrix
invert () {
2574 float[6] inv
= void;
2575 immutable double det
= cast(double)mat
.ptr
[0]*mat
.ptr
[3]-cast(double)mat
.ptr
[2]*mat
.ptr
[1];
2576 if (det
> -1e-6 && det
< 1e-6) {
2579 immutable double invdet
= 1.0/det
;
2580 inv
.ptr
[0] = cast(float)(mat
.ptr
[3]*invdet
);
2581 inv
.ptr
[2] = cast(float)(-mat
.ptr
[2]*invdet
);
2582 inv
.ptr
[4] = cast(float)((cast(double)mat
.ptr
[2]*mat
.ptr
[5]-cast(double)mat
.ptr
[3]*mat
.ptr
[4])*invdet
);
2583 inv
.ptr
[1] = cast(float)(-mat
.ptr
[1]*invdet
);
2584 inv
.ptr
[3] = cast(float)(mat
.ptr
[0]*invdet
);
2585 inv
.ptr
[5] = cast(float)((cast(double)mat
.ptr
[1]*mat
.ptr
[4]-cast(double)mat
.ptr
[0]*mat
.ptr
[5])*invdet
);
2587 mat
.ptr
[0..6] = inv
.ptr
[0..6];
2591 /// Sets this matrix to identity matrix.
2592 ref NVGMatrix
identity () { version(aliced
) pragma(inline
, true); mat
[] = IdentityMat
[]; return this; }
2594 /// Translate this matrix.
2595 ref NVGMatrix
translate (in float tx
, in float ty
) {
2596 version(aliced
) pragma(inline
, true);
2597 return this.mul(Translated(tx
, ty
));
2600 /// Scale this matrix.
2601 ref NVGMatrix
scale (in float sx
, in float sy
) {
2602 version(aliced
) pragma(inline
, true);
2603 return this.mul(Scaled(sx
, sy
));
2606 /// Rotate this matrix.
2607 ref NVGMatrix
rotate (in float a
) {
2608 version(aliced
) pragma(inline
, true);
2609 return this.mul(Rotated(a
));
2612 /// Skew this matrix by X axis.
2613 ref NVGMatrix
skewX (in float a
) {
2614 version(aliced
) pragma(inline
, true);
2615 return this.mul(SkewedX(a
));
2618 /// Skew this matrix by Y axis.
2619 ref NVGMatrix
skewY (in float a
) {
2620 version(aliced
) pragma(inline
, true);
2621 return this.mul(SkewedY(a
));
2624 /// Skew this matrix by both axes.
2625 ref NVGMatrix
skewY (in float ax
, in float ay
) {
2626 version(aliced
) pragma(inline
, true);
2627 return this.mul(SkewedXY(ax
, ay
));
2630 /// Transform point with this matrix. `null` destinations are allowed.
2631 /// [sx] and [sy] is the source point. [dx] and [dy] may point to the same variables.
2632 void point (float* dx
, float* dy
, float sx
, float sy
) nothrow @trusted @nogc {
2633 version(aliced
) pragma(inline
, true);
2634 if (dx
!is null) *dx
= sx
*mat
.ptr
[0]+sy
*mat
.ptr
[2]+mat
.ptr
[4];
2635 if (dy
!is null) *dy
= sx
*mat
.ptr
[1]+sy
*mat
.ptr
[3]+mat
.ptr
[5];
2638 /// Transform point with this matrix.
2639 void point (ref float x
, ref float y
) nothrow @trusted @nogc {
2640 version(aliced
) pragma(inline
, true);
2641 immutable float nx
= x
*mat
.ptr
[0]+y
*mat
.ptr
[2]+mat
.ptr
[4];
2642 immutable float ny
= x
*mat
.ptr
[1]+y
*mat
.ptr
[3]+mat
.ptr
[5];
2647 /// Sets this matrix to the result of multiplication of `this` and [s] (this * S).
2648 ref NVGMatrix
mul() (in auto ref NVGMatrix s
) {
2649 immutable float t0
= mat
.ptr
[0]*s
.mat
.ptr
[0]+mat
.ptr
[1]*s
.mat
.ptr
[2];
2650 immutable float t2
= mat
.ptr
[2]*s
.mat
.ptr
[0]+mat
.ptr
[3]*s
.mat
.ptr
[2];
2651 immutable float t4
= mat
.ptr
[4]*s
.mat
.ptr
[0]+mat
.ptr
[5]*s
.mat
.ptr
[2]+s
.mat
.ptr
[4];
2652 mat
.ptr
[1] = mat
.ptr
[0]*s
.mat
.ptr
[1]+mat
.ptr
[1]*s
.mat
.ptr
[3];
2653 mat
.ptr
[3] = mat
.ptr
[2]*s
.mat
.ptr
[1]+mat
.ptr
[3]*s
.mat
.ptr
[3];
2654 mat
.ptr
[5] = mat
.ptr
[4]*s
.mat
.ptr
[1]+mat
.ptr
[5]*s
.mat
.ptr
[3]+s
.mat
.ptr
[5];
2661 /// Sets this matrix to the result of multiplication of [s] and `this` (S * this).
2662 /// Sets the transform to the result of multiplication of two transforms, of A = B*A.
2664 ref NVGMatrix
premul() (in auto ref NVGMatrix s
) {
2671 /// Multiply this matrix by [s], return result as new matrix.
2672 /// Performs operations in this left-to-right order.
2673 NVGMatrix
opBinary(string op
="*") (in auto ref NVGMatrix s
) const {
2674 version(aliced
) pragma(inline
, true);
2675 NVGMatrix res
= this;
2680 /// Multiply this matrix by [s].
2681 /// Performs operations in this left-to-right order.
2682 ref NVGMatrix
opOpAssign(string op
="*") (in auto ref NVGMatrix s
) {
2683 version(aliced
) pragma(inline
, true);
2687 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.
2688 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.
2689 float rotation () const { pragma(inline
, true); return nvg__atan2f(mat
.ptr
[1], mat
.ptr
[0]); } /// Returns rotation of this matrix.
2690 float tx () const { pragma(inline
, true); return mat
.ptr
[4]; } /// Returns x translation of this matrix.
2691 float ty () const { pragma(inline
, true); return mat
.ptr
[5]; } /// Returns y translation of this matrix.
2693 ref NVGMatrix
scaleX (in float v
) { pragma(inline
, true); return scaleRotateTransform(v
, scaleY
, rotation
, tx
, ty
); } /// Sets x scaling of this matrix.
2694 ref NVGMatrix
scaleY (in float v
) { pragma(inline
, true); return scaleRotateTransform(scaleX
, v
, rotation
, tx
, ty
); } /// Sets y scaling of this matrix.
2695 ref NVGMatrix
rotation (in float v
) { pragma(inline
, true); return scaleRotateTransform(scaleX
, scaleY
, v
, tx
, ty
); } /// Sets rotation of this matrix.
2696 ref NVGMatrix
tx (in float v
) { pragma(inline
, true); mat
.ptr
[4] = v
; return this; } /// Sets x translation of this matrix.
2697 ref NVGMatrix
ty (in float v
) { pragma(inline
, true); mat
.ptr
[5] = v
; return this; } /// Sets y translation of this matrix.
2699 /// Utility function to be used in `setXXX()`.
2700 /// This is the same as doing: `mat.identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2701 ref NVGMatrix
scaleRotateTransform (in float xscale
, in float yscale
, in float a
, in float tx
, in float ty
) {
2702 immutable float cs
= nvg__cosf(a
), sn
= nvg__sinf(a
);
2703 mat
.ptr
[0] = xscale
*cs
; mat
.ptr
[1] = yscale
*sn
;
2704 mat
.ptr
[2] = xscale
*-sn
; mat
.ptr
[3] = yscale
*cs
;
2705 mat
.ptr
[4] = tx
; mat
.ptr
[5] = ty
;
2709 /// This is the same as doing: `mat.identity.rotate(a).translate(tx, ty)`, only faster
2710 ref NVGMatrix
rotateTransform (in float a
, in float tx
, in float ty
) {
2711 immutable float cs
= nvg__cosf(a
), sn
= nvg__sinf(a
);
2712 mat
.ptr
[0] = cs
; mat
.ptr
[1] = sn
;
2713 mat
.ptr
[2] = -sn
; mat
.ptr
[3] = cs
;
2714 mat
.ptr
[4] = tx
; mat
.ptr
[5] = ty
;
2718 /// Returns new identity matrix.
2719 static NVGMatrix
Identity () { pragma(inline
, true); return NVGMatrix
.init
; }
2721 /// Returns new translation matrix.
2722 static NVGMatrix
Translated (in float tx
, in float ty
) {
2723 version(aliced
) pragma(inline
, true);
2724 NVGMatrix res
= void;
2725 res
.mat
.ptr
[0] = 1.0f; res
.mat
.ptr
[1] = 0.0f;
2726 res
.mat
.ptr
[2] = 0.0f; res
.mat
.ptr
[3] = 1.0f;
2727 res
.mat
.ptr
[4] = tx
; res
.mat
.ptr
[5] = ty
;
2731 /// Returns new scaling matrix.
2732 static NVGMatrix
Scaled (in float sx
, in float sy
) {
2733 version(aliced
) pragma(inline
, true);
2734 NVGMatrix res
= void;
2735 res
.mat
.ptr
[0] = sx
; res
.mat
.ptr
[1] = 0.0f;
2736 res
.mat
.ptr
[2] = 0.0f; res
.mat
.ptr
[3] = sy
;
2737 res
.mat
.ptr
[4] = 0.0f; res
.mat
.ptr
[5] = 0.0f;
2741 /// Returns new rotation matrix. Angle is specified in radians.
2742 static NVGMatrix
Rotated (in float a
) {
2743 version(aliced
) pragma(inline
, true);
2744 immutable float cs
= nvg__cosf(a
), sn
= nvg__sinf(a
);
2745 NVGMatrix res
= void;
2746 res
.mat
.ptr
[0] = cs
; res
.mat
.ptr
[1] = sn
;
2747 res
.mat
.ptr
[2] = -sn
; res
.mat
.ptr
[3] = cs
;
2748 res
.mat
.ptr
[4] = 0.0f; res
.mat
.ptr
[5] = 0.0f;
2752 /// Returns new x-skewing matrix. Angle is specified in radians.
2753 static NVGMatrix
SkewedX (in float a
) {
2754 version(aliced
) pragma(inline
, true);
2755 NVGMatrix res
= void;
2756 res
.mat
.ptr
[0] = 1.0f; res
.mat
.ptr
[1] = 0.0f;
2757 res
.mat
.ptr
[2] = nvg__tanf(a
); res
.mat
.ptr
[3] = 1.0f;
2758 res
.mat
.ptr
[4] = 0.0f; res
.mat
.ptr
[5] = 0.0f;
2762 /// Returns new y-skewing matrix. Angle is specified in radians.
2763 static NVGMatrix
SkewedY (in float a
) {
2764 version(aliced
) pragma(inline
, true);
2765 NVGMatrix res
= void;
2766 res
.mat
.ptr
[0] = 1.0f; res
.mat
.ptr
[1] = nvg__tanf(a
);
2767 res
.mat
.ptr
[2] = 0.0f; res
.mat
.ptr
[3] = 1.0f;
2768 res
.mat
.ptr
[4] = 0.0f; res
.mat
.ptr
[5] = 0.0f;
2772 /// Returns new xy-skewing matrix. Angles are specified in radians.
2773 static NVGMatrix
SkewedXY (in float ax
, in float ay
) {
2774 version(aliced
) pragma(inline
, true);
2775 NVGMatrix res
= void;
2776 res
.mat
.ptr
[0] = 1.0f; res
.mat
.ptr
[1] = nvg__tanf(ay
);
2777 res
.mat
.ptr
[2] = nvg__tanf(ax
); res
.mat
.ptr
[3] = 1.0f;
2778 res
.mat
.ptr
[4] = 0.0f; res
.mat
.ptr
[5] = 0.0f;
2782 /// Utility function to be used in `setXXX()`.
2783 /// This is the same as doing: `NVGMatrix.Identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
2784 static NVGMatrix
ScaledRotatedTransformed (in float xscale
, in float yscale
, in float a
, in float tx
, in float ty
) {
2785 NVGMatrix res
= void;
2786 res
.scaleRotateTransform(xscale
, yscale
, a
, tx
, ty
);
2790 /// This is the same as doing: `NVGMatrix.Identity.rotate(a).translate(tx, ty)`, only faster
2791 static NVGMatrix
RotatedTransformed (in float a
, in float tx
, in float ty
) {
2792 NVGMatrix res
= void;
2793 res
.rotateTransform(a
, tx
, ty
);
2799 /// Converts degrees to radians.
2801 public float nvgDegToRad() (in float deg
) pure nothrow @safe @nogc { pragma(inline
, true); return deg
/180.0f*NVG_PI
; }
2803 /// Converts radians to degrees.
2805 public float nvgRadToDeg() (in float rad
) pure nothrow @safe @nogc { pragma(inline
, true); return rad
/NVG_PI
*180.0f; }
2807 public alias nvgDegrees
= nvgDegToRad
; /// Use this like `42.nvgDegrees`
2808 public float nvgRadians() (in float rad
) pure nothrow @safe @nogc { pragma(inline
, true); return rad
; } /// Use this like `0.1.nvgRadians`
2811 // ////////////////////////////////////////////////////////////////////////// //
2812 void nvg__setPaintColor() (ref NVGPaint p
, in auto ref NVGColor color
) nothrow @trusted @nogc {
2817 p
.innerColor
= p
.middleColor
= p
.outerColor
= color
;
2819 p
.simpleColor
= true;
2823 // ////////////////////////////////////////////////////////////////////////// //
2826 version(nanovega_debug_clipping
) {
2827 public void nvgClipDumpOn (NVGContext ctx
) { glnvg__clipDebugDump(ctx
.params
.userPtr
, true); }
2828 public void nvgClipDumpOff (NVGContext ctx
) { glnvg__clipDebugDump(ctx
.params
.userPtr
, false); }
2831 /** Pushes and saves the current render state into a state stack.
2832 * A matching [restore] must be used to restore the state.
2833 * Returns `false` if state stack overflowed.
2835 * Group: state_handling
2837 public bool save (NVGContext ctx
) nothrow @trusted @nogc {
2838 if (ctx
.nstates
>= NVG_MAX_STATES
) return false;
2839 if (ctx
.nstates
> 0) {
2840 //memcpy(&ctx.states[ctx.nstates], &ctx.states[ctx.nstates-1], NVGstate.sizeof);
2841 ctx
.states
[ctx
.nstates
] = ctx
.states
[ctx
.nstates
-1];
2842 ctx
.params
.renderPushClip(ctx
.params
.userPtr
);
2848 /// Pops and restores current render state.
2849 /// Group: state_handling
2850 public bool restore (NVGContext ctx
) nothrow @trusted @nogc {
2851 if (ctx
.nstates
<= 1) return false;
2852 ctx
.states
[ctx
.nstates
-1].clearPaint();
2853 ctx
.params
.renderPopClip(ctx
.params
.userPtr
);
2858 /// Resets current render state to default values. Does not affect the render state stack.
2859 /// Group: state_handling
2860 public void reset (NVGContext ctx
) nothrow @trusted @nogc {
2861 NVGstate
* state
= nvg__getState(ctx
);
2864 nvg__setPaintColor(state
.fill
, nvgRGBA(255, 255, 255, 255));
2865 nvg__setPaintColor(state
.stroke
, nvgRGBA(0, 0, 0, 255));
2866 state
.compositeOperation
= nvg__compositeOperationState(NVGCompositeOperation
.SourceOver
);
2867 state
.shapeAntiAlias
= true;
2868 state
.strokeWidth
= 1.0f;
2869 state
.miterLimit
= 10.0f;
2870 state
.lineCap
= NVGLineCap
.Butt
;
2871 state
.lineJoin
= NVGLineCap
.Miter
;
2873 state
.xform
.identity
;
2875 state
.scissor
.extent
[] = -1.0f;
2877 state
.fontSize
= 16.0f;
2878 state
.letterSpacing
= 0.0f;
2879 state
.lineHeight
= 1.0f;
2880 state
.fontBlur
= 0.0f;
2881 state
.textAlign
.reset
;
2883 state
.evenOddMode
= false;
2884 state
.dashCount
= 0;
2885 state
.lastFlattenDashCount
= 0;
2886 state
.dashStart
= 0;
2887 state
.firstDashIsGap
= false;
2888 state
.dasherActive
= false;
2890 ctx
.params
.renderResetClip(ctx
.params
.userPtr
);
2893 /** Returns `true` if we have any room in state stack.
2894 * It is guaranteed to have at least 32 stack slots.
2896 * Group: state_handling
2898 public bool canSave (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
.nstates
< NVG_MAX_STATES
); }
2900 /** Returns `true` if we have any saved state.
2902 * Group: state_handling
2904 public bool canRestore (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
.nstates
> 1); }
2906 /// Returns `true` if rendering is currently blocked.
2907 /// Group: state_handling
2908 public bool renderBlocked (NVGContext ctx
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ctx
!is null && ctx
.contextAlive ? ctx
.recblockdraw
: false); }
2910 /// Blocks/unblocks rendering
2911 /// Group: state_handling
2912 public void renderBlocked (NVGContext ctx
, bool v
) pure nothrow @trusted @nogc { pragma(inline
, true); if (ctx
!is null && ctx
.contextAlive
) ctx
.recblockdraw
= v
; }
2914 /// Blocks/unblocks rendering; returns previous state.
2915 /// Group: state_handling
2916 public bool setRenderBlocked (NVGContext ctx
, bool v
) pure nothrow @trusted @nogc { pragma(inline
, true); if (ctx
!is null && ctx
.contextAlive
) { bool res
= ctx
.recblockdraw
; ctx
.recblockdraw
= v
; return res
; } else return false; }
2919 // ////////////////////////////////////////////////////////////////////////// //
2922 /// Sets filling mode to "even-odd".
2923 /// Group: render_styles
2924 public void evenOddFill (NVGContext ctx
) nothrow @trusted @nogc {
2925 NVGstate
* state
= nvg__getState(ctx
);
2926 state
.evenOddMode
= true;
2929 /// Sets filling mode to "non-zero" (this is default mode).
2930 /// Group: render_styles
2931 public void nonZeroFill (NVGContext ctx
) nothrow @trusted @nogc {
2932 NVGstate
* state
= nvg__getState(ctx
);
2933 state
.evenOddMode
= false;
2936 /// Sets whether to draw antialias for [stroke] and [fill]. It's enabled by default.
2937 /// Group: render_styles
2938 public void shapeAntiAlias (NVGContext ctx
, bool enabled
) {
2939 NVGstate
* state
= nvg__getState(ctx
);
2940 state
.shapeAntiAlias
= enabled
;
2943 /// Sets the stroke width of the stroke style.
2944 /// Group: render_styles
2946 public void strokeWidth (NVGContext ctx
, float width
) nothrow @trusted @nogc {
2947 NVGstate
* state
= nvg__getState(ctx
);
2948 state
.strokeWidth
= width
;
2951 /// Sets the miter limit of the stroke style. Miter limit controls when a sharp corner is beveled.
2952 /// Group: render_styles
2953 public void miterLimit (NVGContext ctx
, float limit
) nothrow @trusted @nogc {
2954 NVGstate
* state
= nvg__getState(ctx
);
2955 state
.miterLimit
= limit
;
2958 /// Sets how the end of the line (cap) is drawn,
2959 /// Can be one of: NVGLineCap.Butt (default), NVGLineCap.Round, NVGLineCap.Square.
2960 /// Group: render_styles
2961 public void lineCap (NVGContext ctx
, NVGLineCap cap
) nothrow @trusted @nogc {
2962 NVGstate
* state
= nvg__getState(ctx
);
2963 state
.lineCap
= cap
;
2966 /// Sets how sharp path corners are drawn.
2967 /// Can be one of NVGLineCap.Miter (default), NVGLineCap.Round, NVGLineCap.Bevel.
2968 /// Group: render_styles
2969 public void lineJoin (NVGContext ctx
, NVGLineCap join
) nothrow @trusted @nogc {
2970 NVGstate
* state
= nvg__getState(ctx
);
2971 state
.lineJoin
= join
;
2974 /// Sets stroke dashing, using (dash_length, gap_length) pairs.
2975 /// Current limit is 16 pairs.
2976 /// Resets dash start to zero.
2977 /// Group: render_styles
2978 public void setLineDash (NVGContext ctx
, const(float)[] dashdata
) nothrow @trusted @nogc {
2979 NVGstate
* state
= nvg__getState(ctx
);
2980 state
.dashCount
= 0;
2981 state
.dashStart
= 0;
2982 state
.firstDashIsGap
= false;
2983 if (dashdata
.length
>= 2) {
2984 bool curFIsGap
= true; // trick
2985 foreach (immutable idx
, float f
; dashdata
) {
2986 curFIsGap
= !curFIsGap
;
2987 if (f
< 0.01f) continue; // skip it
2989 // register first dash
2990 state
.firstDashIsGap
= curFIsGap
;
2991 state
.dashes
.ptr
[state
.dashCount
++] = f
;
2993 if ((idx
&1) != ((state
.dashCount
&1)^
cast(uint)state
.firstDashIsGap
)) {
2994 // oops, continuation
2995 state
.dashes
[state
.dashCount
-1] += f
;
2997 if (state
.dashCount
== state
.dashes
.length
) break;
2998 state
.dashes
[state
.dashCount
++] = f
;
3002 if (state
.dashCount
&1) {
3003 if (state
.dashCount
== 1) {
3004 state
.dashCount
= 0;
3006 assert(state
.dashCount
< state
.dashes
.length
);
3007 state
.dashes
[state
.dashCount
++] = 0;
3010 // calculate total dash path length
3011 state
.totalDashLen
= 0;
3012 foreach (float f
; state
.dashes
.ptr
[0..state
.dashCount
]) state
.totalDashLen
+= f
;
3013 if (state
.totalDashLen
< 0.01f) {
3014 state
.dashCount
= 0; // nothing to do
3016 if (state
.lastFlattenDashCount
!= 0) state
.lastFlattenDashCount
= uint.max
; // force re-flattening
3021 public alias lineDash
= setLineDash
; /// Ditto.
3023 /// Sets stroke dashing, using (dash_length, gap_length) pairs.
3024 /// Current limit is 16 pairs.
3025 /// Group: render_styles
3026 public void setLineDashStart (NVGContext ctx
, in float dashStart
) nothrow @trusted @nogc {
3027 NVGstate
* state
= nvg__getState(ctx
);
3028 if (state
.lastFlattenDashCount
!= 0 && state
.dashStart
!= dashStart
) {
3029 state
.lastFlattenDashCount
= uint.max
; // force re-flattening
3031 state
.dashStart
= dashStart
;
3034 public alias lineDashStart
= setLineDashStart
; /// Ditto.
3036 /// Sets the transparency applied to all rendered shapes.
3037 /// Already transparent paths will get proportionally more transparent as well.
3038 /// Group: render_styles
3039 public void globalAlpha (NVGContext ctx
, float alpha
) nothrow @trusted @nogc {
3040 NVGstate
* state
= nvg__getState(ctx
);
3041 state
.alpha
= alpha
;
3044 static if (NanoVegaHasArsdColor
) {
3045 /// Sets current stroke style to a solid color.
3046 /// Group: render_styles
3047 public void strokeColor (NVGContext ctx
, Color color
) nothrow @trusted @nogc {
3048 NVGstate
* state
= nvg__getState(ctx
);
3049 nvg__setPaintColor(state
.stroke
, NVGColor(color
));
3053 /// Sets current stroke style to a solid color.
3054 /// Group: render_styles
3055 public void strokeColor() (NVGContext ctx
, in auto ref NVGColor color
) nothrow @trusted @nogc {
3056 NVGstate
* state
= nvg__getState(ctx
);
3057 nvg__setPaintColor(state
.stroke
, color
);
3060 /// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
3061 /// Group: render_styles
3062 public void strokePaint() (NVGContext ctx
, in auto ref NVGPaint paint
) nothrow @trusted @nogc {
3063 NVGstate
* state
= nvg__getState(ctx
);
3064 state
.stroke
= paint
;
3065 //nvgTransformMultiply(state.stroke.xform[], state.xform[]);
3066 state
.stroke
.xform
.mul(state
.xform
);
3069 // this is a hack to work around https://issues.dlang.org/show_bug.cgi?id=16206
3070 // for scriptable reflection. it just needs to be declared first among the overloads
3071 private void fillColor (NVGContext ctx
) nothrow @trusted @nogc { }
3073 static if (NanoVegaHasArsdColor
) {
3074 /// Sets current fill style to a solid color.
3075 /// Group: render_styles
3077 public void fillColor (NVGContext ctx
, Color color
) nothrow @trusted @nogc {
3078 NVGstate
* state
= nvg__getState(ctx
);
3079 nvg__setPaintColor(state
.fill
, NVGColor(color
));
3083 /// Sets current fill style to a solid color.
3084 /// Group: render_styles
3085 public void fillColor() (NVGContext ctx
, in auto ref NVGColor color
) nothrow @trusted @nogc {
3086 NVGstate
* state
= nvg__getState(ctx
);
3087 nvg__setPaintColor(state
.fill
, color
);
3090 /// Sets current fill style to a paint, which can be a one of the gradients or a pattern.
3091 /// Group: render_styles
3092 public void fillPaint() (NVGContext ctx
, in auto ref NVGPaint paint
) nothrow @trusted @nogc {
3093 NVGstate
* state
= nvg__getState(ctx
);
3095 //nvgTransformMultiply(state.fill.xform[], state.xform[]);
3096 state
.fill
.xform
.mul(state
.xform
);
3099 /// Sets current fill style to a multistop linear gradient.
3100 /// Group: render_styles
3101 public void fillPaint() (NVGContext ctx
, in auto ref NVGLGS
lgs) nothrow @trusted @nogc {
3104 memset(&p
, 0, p
.sizeof
);
3105 nvg__setPaintColor(p
, NVGColor
.red
);
3107 } else if (lgs.midp
>= -1) {
3108 //{ import core.stdc.stdio; printf("SIMPLE! midp=%f\n", cast(double)lgs.midp); }
3109 ctx
.fillPaint
= ctx
.linearGradient(lgs.cx
, lgs.cy
, lgs.dimx
, lgs.dimy
, lgs.ic
, lgs.midp
, lgs.mc
, lgs.oc
);
3111 ctx
.fillPaint
= ctx
.imagePattern(lgs.cx
, lgs.cy
, lgs.dimx
, lgs.dimy
, lgs.angle
, lgs.imgid
);
3115 /// Returns current transformation matrix.
3116 /// Group: render_transformations
3117 public NVGMatrix
currTransform (NVGContext ctx
) pure nothrow @trusted @nogc {
3118 NVGstate
* state
= nvg__getState(ctx
);
3122 /// Sets current transformation matrix.
3123 /// Group: render_transformations
3124 public void currTransform() (NVGContext ctx
, in auto ref NVGMatrix m
) nothrow @trusted @nogc {
3125 NVGstate
* state
= nvg__getState(ctx
);
3129 /// Resets current transform to an identity matrix.
3130 /// Group: render_transformations
3132 public void resetTransform (NVGContext ctx
) nothrow @trusted @nogc {
3133 NVGstate
* state
= nvg__getState(ctx
);
3134 state
.xform
.identity
;
3137 /// Premultiplies current coordinate system by specified matrix.
3138 /// Group: render_transformations
3139 public void transform() (NVGContext ctx
, in auto ref NVGMatrix mt
) nothrow @trusted @nogc {
3140 NVGstate
* state
= nvg__getState(ctx
);
3141 //nvgTransformPremultiply(state.xform[], t[]);
3145 /// Translates current coordinate system.
3146 /// Group: render_transformations
3148 public void translate (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
3149 NVGstate
* state
= nvg__getState(ctx
);
3150 //NVGMatrix t = void;
3151 //nvgTransformTranslate(t[], x, y);
3152 //nvgTransformPremultiply(state.xform[], t[]);
3153 state
.xform
.premul(NVGMatrix
.Translated(x
, y
));
3156 /// Rotates current coordinate system. Angle is specified in radians.
3157 /// Group: render_transformations
3159 public void rotate (NVGContext ctx
, in float angle
) nothrow @trusted @nogc {
3160 NVGstate
* state
= nvg__getState(ctx
);
3161 //NVGMatrix t = void;
3162 //nvgTransformRotate(t[], angle);
3163 //nvgTransformPremultiply(state.xform[], t[]);
3164 state
.xform
.premul(NVGMatrix
.Rotated(angle
));
3167 /// Skews the current coordinate system along X axis. Angle is specified in radians.
3168 /// Group: render_transformations
3170 public void skewX (NVGContext ctx
, in float angle
) nothrow @trusted @nogc {
3171 NVGstate
* state
= nvg__getState(ctx
);
3172 //NVGMatrix t = void;
3173 //nvgTransformSkewX(t[], angle);
3174 //nvgTransformPremultiply(state.xform[], t[]);
3175 state
.xform
.premul(NVGMatrix
.SkewedX(angle
));
3178 /// Skews the current coordinate system along Y axis. Angle is specified in radians.
3179 /// Group: render_transformations
3181 public void skewY (NVGContext ctx
, in float angle
) nothrow @trusted @nogc {
3182 NVGstate
* state
= nvg__getState(ctx
);
3183 //NVGMatrix t = void;
3184 //nvgTransformSkewY(t[], angle);
3185 //nvgTransformPremultiply(state.xform[], t[]);
3186 state
.xform
.premul(NVGMatrix
.SkewedY(angle
));
3189 /// Scales the current coordinate system.
3190 /// Group: render_transformations
3192 public void scale (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
3193 NVGstate
* state
= nvg__getState(ctx
);
3194 //NVGMatrix t = void;
3195 //nvgTransformScale(t[], x, y);
3196 //nvgTransformPremultiply(state.xform[], t[]);
3197 state
.xform
.premul(NVGMatrix
.Scaled(x
, y
));
3201 // ////////////////////////////////////////////////////////////////////////// //
3204 /// Creates image by loading it from the disk from specified file name.
3205 /// Returns handle to the image or 0 on error.
3207 public NVGImage
createImage() (NVGContext ctx
, const(char)[] filename
, const(NVGImageFlag
)[] imageFlagsList
...) {
3208 static if (NanoVegaHasArsdImage
) {
3210 // do we have new arsd API to load images?
3211 static if (!is(typeof(MemoryImage
.fromImageFile
)) ||
!is(typeof(MemoryImage
.clearInternal
))) {
3212 static assert(0, "Sorry, your ARSD is too old. Please, update it.");
3215 auto oimg
= MemoryImage
.fromImageFile(filename
);
3216 if (auto img
= cast(TrueColorImage
)oimg
) {
3217 scope(exit
) oimg
.clearInternal();
3218 return ctx
.createImageRGBA(img
.width
, img
.height
, img
.imageData
.bytes
[], imageFlagsList
);
3220 TrueColorImage img
= oimg
.getAsTrueColorImage
;
3221 scope(exit
) img
.clearInternal();
3222 oimg
.clearInternal(); // drop original image, as `getAsTrueColorImage()` MUST create a new one here
3224 return ctx
.createImageRGBA(img
.width
, img
.height
, img
.imageData
.bytes
[], imageFlagsList
);
3226 } catch (Exception
) {}
3227 return NVGImage
.init
;
3229 import std
.internal
.cstring
;
3232 stbi_set_unpremultiply_on_load(1);
3233 stbi_convert_iphone_png_to_rgb(1);
3234 img
= stbi_load(filename
.tempCString
, &w
, &h
, &n
, 4);
3236 //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
3237 return NVGImage
.init
;
3239 auto image
= ctx
.createImageRGBA(w
, h
, img
[0..w
*h
*4], imageFlagsList
);
3240 stbi_image_free(img
);
3245 static if (NanoVegaHasArsdImage
) {
3246 /// Creates image by loading it from the specified memory image.
3247 /// Returns handle to the image or 0 on error.
3249 public NVGImage
createImageFromMemoryImage() (NVGContext ctx
, MemoryImage img
, const(NVGImageFlag
)[] imageFlagsList
...) {
3250 if (img
is null) return NVGImage
.init
;
3251 if (auto tc
= cast(TrueColorImage
)img
) {
3252 return ctx
.createImageRGBA(tc
.width
, tc
.height
, tc
.imageData
.bytes
[], imageFlagsList
);
3254 auto tc
= img
.getAsTrueColorImage
;
3255 scope(exit
) tc
.clearInternal(); // here, it is guaranteed that `tc` is newly allocated image, so it is safe to kill it
3256 return ctx
.createImageRGBA(tc
.width
, tc
.height
, tc
.imageData
.bytes
[], imageFlagsList
);
3260 /// Creates image by loading it from the specified chunk of memory.
3261 /// Returns handle to the image or 0 on error.
3263 public NVGImage
createImageMem() (NVGContext ctx
, const(ubyte)* data
, int ndata
, const(NVGImageFlag
)[] imageFlagsList
...) {
3265 ubyte* img
= stbi_load_from_memory(data
, ndata
, &w
, &h
, &n
, 4);
3267 //printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
3268 return NVGImage
.init
;
3270 image
= ctx
.createImageRGBA(w
, h
, img
[0..w
*h
*4], imageFlagsList
);
3271 stbi_image_free(img
);
3276 /// Creates image from specified image data.
3277 /// Returns handle to the image or 0 on error.
3279 public NVGImage
createImageRGBA (NVGContext ctx
, int w
, int h
, const(void)[] data
, const(NVGImageFlag
)[] imageFlagsList
...) nothrow @trusted @nogc {
3280 if (w
< 1 || h
< 1 || data
.length
< w
*h
*4) return NVGImage
.init
;
3281 uint imageFlags
= 0;
3282 foreach (immutable uint flag
; imageFlagsList
) imageFlags |
= flag
;
3284 res
.id
= ctx
.params
.renderCreateTexture(ctx
.params
.userPtr
, NVGtexture
.RGBA
, w
, h
, imageFlags
, cast(const(ubyte)*)data
.ptr
);
3286 version(nanovega_debug_image_manager_rc
) { import core
.stdc
.stdio
; printf("createImageRGBA: img=%p; imgid=%d\n", &res
, res
.id
); }
3288 ctx
.nvg__imageIncRef(res
.id
, false); // don't increment driver refcount
3293 /// Updates image data specified by image handle.
3295 public void updateImage() (NVGContext ctx
, auto ref NVGImage image
, const(void)[] data
) nothrow @trusted @nogc {
3298 if (image
.ctx
!is ctx
) assert(0, "NanoVega: you cannot use image from one context in another context");
3299 ctx
.params
.renderGetTextureSize(ctx
.params
.userPtr
, image
.id
, &w
, &h
);
3300 ctx
.params
.renderUpdateTexture(ctx
.params
.userPtr
, image
.id
, 0, 0, w
, h
, cast(const(ubyte)*)data
.ptr
);
3304 /// Returns the dimensions of a created image.
3306 public void imageSize() (NVGContext ctx
, in auto ref NVGImage image
, out int w
, out int h
) nothrow @trusted @nogc {
3308 if (image
.ctx
!is ctx
) assert(0, "NanoVega: you cannot use image from one context in another context");
3309 ctx
.params
.renderGetTextureSize(cast(void*)ctx
.params
.userPtr
, image
.id
, &w
, &h
);
3313 /// Deletes created image.
3315 public void deleteImage() (NVGContext ctx
, ref NVGImage image
) nothrow @trusted @nogc {
3316 if (ctx
is null ||
!image
.valid
) return;
3317 if (image
.ctx
!is ctx
) assert(0, "NanoVega: you cannot use image from one context in another context");
3322 // ////////////////////////////////////////////////////////////////////////// //
3325 static if (NanoVegaHasArsdColor
) {
3326 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3327 * of the linear gradient, icol specifies the start color and ocol the end color.
3328 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3332 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 {
3333 return ctx
.linearGradient(sx
, sy
, ex
, ey
, NVGColor(icol
), NVGColor(ocol
));
3335 /** Creates and returns a linear gradient with middle stop. Parameters `(sx, sy) (ex, ey)` specify the start
3336 * and end coordinates of the linear gradient, icol specifies the start color, midp specifies stop point in
3337 * range `(0..1)`, and ocol the end color.
3338 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3342 public NVGPaint
linearGradient (NVGContext ctx
, in float sx
, in float sy
, in float ex
, in float ey
, in Color icol
, in float midp
, in Color mcol
, in Color ocol
) nothrow @trusted @nogc {
3343 return ctx
.linearGradient(sx
, sy
, ex
, ey
, NVGColor(icol
), midp
, NVGColor(mcol
), NVGColor(ocol
));
3347 /** Creates and returns a linear gradient. Parameters `(sx, sy) (ex, ey)` specify the start and end coordinates
3348 * of the linear gradient, icol specifies the start color and ocol the end color.
3349 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3353 public NVGPaint
linearGradient() (NVGContext ctx
, float sx
, float sy
, float ex
, float ey
, in auto ref NVGColor icol
, in auto ref NVGColor ocol
) nothrow @trusted @nogc {
3357 memset(&p
, 0, p
.sizeof
);
3358 p
.simpleColor
= false;
3360 // Calculate transform aligned to the line
3363 immutable float d
= nvg__sqrtf(dx
*dx
+dy
*dy
);
3372 p
.xform
.mat
.ptr
[0] = dy
; p
.xform
.mat
.ptr
[1] = -dx
;
3373 p
.xform
.mat
.ptr
[2] = dx
; p
.xform
.mat
.ptr
[3] = dy
;
3374 p
.xform
.mat
.ptr
[4] = sx
-dx
*large
; p
.xform
.mat
.ptr
[5] = sy
-dy
*large
;
3376 p
.extent
.ptr
[0] = large
;
3377 p
.extent
.ptr
[1] = large
+d
*0.5f;
3381 p
.feather
= nvg__max(NVG_MIN_FEATHER
, d
);
3383 p
.innerColor
= p
.middleColor
= icol
;
3384 p
.outerColor
= ocol
;
3390 /** Creates and returns a linear gradient with middle stop. Parameters `(sx, sy) (ex, ey)` specify the start
3391 * and end coordinates of the linear gradient, icol specifies the start color, midp specifies stop point in
3392 * range `(0..1)`, and ocol the end color.
3393 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3397 public NVGPaint
linearGradient() (NVGContext ctx
, float sx
, float sy
, float ex
, float ey
, in auto ref NVGColor icol
, in float midp
, in auto ref NVGColor mcol
, in auto ref NVGColor ocol
) nothrow @trusted @nogc {
3401 memset(&p
, 0, p
.sizeof
);
3402 p
.simpleColor
= false;
3404 // Calculate transform aligned to the line
3407 immutable float d
= nvg__sqrtf(dx
*dx
+dy
*dy
);
3416 p
.xform
.mat
.ptr
[0] = dy
; p
.xform
.mat
.ptr
[1] = -dx
;
3417 p
.xform
.mat
.ptr
[2] = dx
; p
.xform
.mat
.ptr
[3] = dy
;
3418 p
.xform
.mat
.ptr
[4] = sx
-dx
*large
; p
.xform
.mat
.ptr
[5] = sy
-dy
*large
;
3420 p
.extent
.ptr
[0] = large
;
3421 p
.extent
.ptr
[1] = large
+d
*0.5f;
3425 p
.feather
= nvg__max(NVG_MIN_FEATHER
, d
);
3428 p
.innerColor
= p
.middleColor
= mcol
;
3430 } else if (midp
> 1) {
3431 p
.innerColor
= p
.middleColor
= icol
;
3434 p
.innerColor
= icol
;
3435 p
.middleColor
= mcol
;
3438 p
.outerColor
= ocol
;
3443 static if (NanoVegaHasArsdColor
) {
3444 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3445 * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3446 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3450 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 {
3451 return ctx
.radialGradient(cx
, cy
, inr
, outr
, NVGColor(icol
), NVGColor(ocol
));
3455 /** Creates and returns a radial gradient. Parameters (cx, cy) specify the center, inr and outr specify
3456 * the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
3457 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3461 public NVGPaint
radialGradient() (NVGContext ctx
, float cx
, float cy
, float inr
, float outr
, in auto ref NVGColor icol
, in auto ref NVGColor ocol
) nothrow @trusted @nogc {
3462 immutable float r
= (inr
+outr
)*0.5f;
3463 immutable float f
= (outr
-inr
);
3466 memset(&p
, 0, p
.sizeof
);
3467 p
.simpleColor
= false;
3470 p
.xform
.mat
.ptr
[4] = cx
;
3471 p
.xform
.mat
.ptr
[5] = cy
;
3473 p
.extent
.ptr
[0] = r
;
3474 p
.extent
.ptr
[1] = r
;
3478 p
.feather
= nvg__max(NVG_MIN_FEATHER
, f
);
3480 p
.innerColor
= p
.middleColor
= icol
;
3481 p
.outerColor
= ocol
;
3487 static if (NanoVegaHasArsdColor
) {
3488 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3489 * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3490 * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3491 * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3492 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3496 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 {
3497 return ctx
.boxGradient(x
, y
, w
, h
, r
, f
, NVGColor(icol
), NVGColor(ocol
));
3501 /** Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
3502 * drop shadows or highlights for boxes. Parameters (x, y) define the top-left corner of the rectangle,
3503 * (w, h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
3504 * the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
3505 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3509 public NVGPaint
boxGradient() (NVGContext ctx
, float x
, float y
, float w
, float h
, float r
, float f
, in auto ref NVGColor icol
, in auto ref NVGColor ocol
) nothrow @trusted @nogc {
3511 memset(&p
, 0, p
.sizeof
);
3512 p
.simpleColor
= false;
3515 p
.xform
.mat
.ptr
[4] = x
+w
*0.5f;
3516 p
.xform
.mat
.ptr
[5] = y
+h
*0.5f;
3518 p
.extent
.ptr
[0] = w
*0.5f;
3519 p
.extent
.ptr
[1] = h
*0.5f;
3523 p
.feather
= nvg__max(NVG_MIN_FEATHER
, f
);
3525 p
.innerColor
= p
.middleColor
= icol
;
3526 p
.outerColor
= ocol
;
3532 /** Creates and returns an image pattern. Parameters `(cx, cy)` specify the left-top location of the image pattern,
3533 * `(w, h)` the size of one image, [angle] rotation around the top-left corner, [image] is handle to the image to render.
3534 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3538 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 {
3540 memset(&p
, 0, p
.sizeof
);
3541 p
.simpleColor
= false;
3543 p
.xform
.identity
.rotate(angle
);
3544 p
.xform
.mat
.ptr
[4] = cx
;
3545 p
.xform
.mat
.ptr
[5] = cy
;
3547 p
.extent
.ptr
[0] = w
;
3548 p
.extent
.ptr
[1] = h
;
3552 p
.innerColor
= p
.middleColor
= p
.outerColor
= nvgRGBAf(1, 1, 1, alpha
);
3558 /// Linear gradient with multiple stops.
3559 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3561 public struct NVGLGS
{
3563 NVGColor ic
, mc
, oc
; // inner, middle, out
3566 // [imagePattern] arguments
3567 float cx
, cy
, dimx
, dimy
; // dimx and dimy are ex and ey for simple gradients
3568 public float angle
; ///
3571 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (imgid
.valid || midp
>= -1); } ///
3572 void clear () nothrow @safe @nogc { pragma(inline
, true); imgid
.clear(); midp
= float.nan
; } ///
3575 /** Returns [NVGPaint] for linear gradient with stops, created with [createLinearGradientWithStops].
3576 * The gradient is transformed by the current transform when it is passed to [fillPaint] or [strokePaint].
3578 * $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3581 public NVGPaint
asPaint() (NVGContext ctx
, in auto ref NVGLGS
lgs) nothrow @trusted @nogc {
3584 memset(&p
, 0, p
.sizeof
);
3585 nvg__setPaintColor(p
, NVGColor
.red
);
3587 } else if (lgs.midp
>= -1) {
3588 return ctx
.linearGradient(lgs.cx
, lgs.cy
, lgs.dimx
, lgs.dimy
, lgs.ic
, lgs.midp
, lgs.mc
, lgs.oc
);
3590 return ctx
.imagePattern(lgs.cx
, lgs.cy
, lgs.dimx
, lgs.dimy
, lgs.angle
, lgs.imgid
);
3594 /// Gradient Stop Point.
3595 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3597 public struct NVGGradientStop
{
3598 float offset
= 0; /// [0..1]
3601 this() (in float aofs
, in auto ref NVGColor aclr
) nothrow @trusted @nogc { pragma(inline
, true); offset
= aofs
; color
= aclr
; } ///
3602 static if (NanoVegaHasArsdColor
) {
3603 this() (in float aofs
, in Color aclr
) nothrow @trusted @nogc { pragma(inline
, true); offset
= aofs
; color
= NVGColor(aclr
); } ///
3607 /// Create linear gradient data suitable to use with `linearGradient(res)`.
3608 /// Don't forget to destroy the result when you don't need it anymore with `ctx.kill(res);`.
3609 /// $(WARNING THIS IS EXPERIMENTAL API AND MAY BE CHANGED/BROKEN IN NEXT RELEASES!)
3611 public NVGLGS
createLinearGradientWithStops (NVGContext ctx
, in float sx
, in float sy
, in float ex
, in float ey
, const(NVGGradientStop
)[] stops
...) nothrow @trusted @nogc {
3612 // based on the code by Jorge Acereda <jacereda@gmail.com>
3613 enum NVG_GRADIENT_SAMPLES
= 1024;
3614 static void gradientSpan (uint* dst
, const(NVGGradientStop
)* s0
, const(NVGGradientStop
)* s1
) nothrow @trusted @nogc {
3615 immutable float s0o
= nvg__clamp(s0
.offset
, 0.0f, 1.0f);
3616 immutable float s1o
= nvg__clamp(s1
.offset
, 0.0f, 1.0f);
3617 uint s
= cast(uint)(s0o
*NVG_GRADIENT_SAMPLES
);
3618 uint e
= cast(uint)(s1o
*NVG_GRADIENT_SAMPLES
);
3619 uint sc
= 0xffffffffU
;
3621 uint r
= cast(uint)(s0
.color
.rgba
[0]*sc
);
3622 uint g
= cast(uint)(s0
.color
.rgba
[1]*sc
);
3623 uint b
= cast(uint)(s0
.color
.rgba
[2]*sc
);
3624 uint a
= cast(uint)(s0
.color
.rgba
[3]*sc
);
3625 uint dr
= cast(uint)((s1
.color
.rgba
[0]*sc
-r
)/(e
-s
));
3626 uint dg
= cast(uint)((s1
.color
.rgba
[1]*sc
-g
)/(e
-s
));
3627 uint db = cast(uint)((s1
.color
.rgba
[2]*sc
-b
)/(e
-s
));
3628 uint da = cast(uint)((s1
.color
.rgba
[3]*sc
-a
)/(e
-s
));
3630 foreach (immutable _
; s
..e
) {
3631 version(BigEndian
) {
3632 *dst
++ = ((r
>>sh
)<<24)+((g
>>sh
)<<16)+((b
>>sh
)<<8)+((a
>>sh
)<<0);
3634 *dst
++ = ((a
>>sh
)<<24)+((b
>>sh
)<<16)+((g
>>sh
)<<8)+((r
>>sh
)<<0);
3647 if (stops
.length
== 2 && stops
.ptr
[0].offset
<= 0 && stops
.ptr
[1].offset
>= 1) {
3648 // create simple linear gradient
3649 res
.ic
= res
.mc
= stops
.ptr
[0].color
;
3650 res
.oc
= stops
.ptr
[1].color
;
3654 } else if (stops
.length
== 3 && stops
.ptr
[0].offset
<= 0 && stops
.ptr
[2].offset
>= 1) {
3655 // create simple linear gradient with middle stop
3656 res
.ic
= stops
.ptr
[0].color
;
3657 res
.mc
= stops
.ptr
[1].color
;
3658 res
.oc
= stops
.ptr
[2].color
;
3659 res
.midp
= stops
.ptr
[1].offset
;
3663 // create image gradient
3664 uint[NVG_GRADIENT_SAMPLES
] data
= void;
3665 immutable float w
= ex
-sx
;
3666 immutable float h
= ey
-sy
;
3667 res
.dimx
= nvg__sqrtf(w
*w
+h
*h
);
3671 (/*nvg__absf(h) < 0.0001 ? 0 :
3672 nvg__absf(w) < 0.0001 ? 90.nvgDegrees :*/
3673 nvg__atan2f(h
/*ey-sy*/, w
/*ex-sx*/));
3675 if (stops
.length
> 0) {
3676 auto s0
= NVGGradientStop(0, nvgRGBAf(0, 0, 0, 1));
3677 auto s1
= NVGGradientStop(1, nvgRGBAf(1, 1, 1, 1));
3678 if (stops
.length
> 64) stops
= stops
[0..64];
3680 s0
.color
= stops
[0].color
;
3681 s1
.color
= stops
[$-1].color
;
3683 gradientSpan(data
.ptr
, &s0
, (stops
.length ? stops
.ptr
: &s1
));
3684 foreach (immutable i
; 0..stops
.length
-1) gradientSpan(data
.ptr
, stops
.ptr
+i
, stops
.ptr
+i
+1);
3685 gradientSpan(data
.ptr
, (stops
.length ? stops
.ptr
+stops
.length
-1 : &s0
), &s1
);
3686 res
.imgid
= ctx
.createImageRGBA(NVG_GRADIENT_SAMPLES
, 1, data
[]/*, NVGImageFlag.RepeatX, NVGImageFlag.RepeatY*/);
3693 // ////////////////////////////////////////////////////////////////////////// //
3696 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3697 /// Group: scissoring
3698 public void scissor (NVGContext ctx
, in float x
, in float y
, float w
, float h
) nothrow @trusted @nogc {
3699 NVGstate
* state
= nvg__getState(ctx
);
3701 w
= nvg__max(0.0f, w
);
3702 h
= nvg__max(0.0f, h
);
3704 state
.scissor
.xform
.identity
;
3705 state
.scissor
.xform
.mat
.ptr
[4] = x
+w
*0.5f;
3706 state
.scissor
.xform
.mat
.ptr
[5] = y
+h
*0.5f;
3707 //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3708 state
.scissor
.xform
.mul(state
.xform
);
3710 state
.scissor
.extent
.ptr
[0] = w
*0.5f;
3711 state
.scissor
.extent
.ptr
[1] = h
*0.5f;
3714 /// Sets the current scissor rectangle. The scissor rectangle is transformed by the current transform.
3715 /// Arguments: [x, y, w, h]*
3716 /// Group: scissoring
3717 public void scissor (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
3719 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [scissor] call");
3720 if (args
.length
< ArgC
) return;
3721 NVGstate
* state
= nvg__getState(ctx
);
3722 const(float)* aptr
= args
.ptr
;
3723 foreach (immutable idx
; 0..args
.length
/ArgC
) {
3724 immutable x
= *aptr
++;
3725 immutable y
= *aptr
++;
3726 immutable w
= nvg__max(0.0f, *aptr
++);
3727 immutable h
= nvg__max(0.0f, *aptr
++);
3729 state
.scissor
.xform
.identity
;
3730 state
.scissor
.xform
.mat
.ptr
[4] = x
+w
*0.5f;
3731 state
.scissor
.xform
.mat
.ptr
[5] = y
+h
*0.5f;
3732 //nvgTransformMultiply(state.scissor.xform[], state.xform[]);
3733 state
.scissor
.xform
.mul(state
.xform
);
3735 state
.scissor
.extent
.ptr
[0] = w
*0.5f;
3736 state
.scissor
.extent
.ptr
[1] = h
*0.5f;
3740 void nvg__isectRects (float* dst
, float ax
, float ay
, float aw
, float ah
, float bx
, float by
, float bw
, float bh
) nothrow @trusted @nogc {
3741 immutable float minx
= nvg__max(ax
, bx
);
3742 immutable float miny
= nvg__max(ay
, by
);
3743 immutable float maxx
= nvg__min(ax
+aw
, bx
+bw
);
3744 immutable float maxy
= nvg__min(ay
+ah
, by
+bh
);
3747 dst
[2] = nvg__max(0.0f, maxx
-minx
);
3748 dst
[3] = nvg__max(0.0f, maxy
-miny
);
3751 /** Intersects current scissor rectangle with the specified rectangle.
3752 * The scissor rectangle is transformed by the current transform.
3753 * Note: in case the rotation of previous scissor rect differs from
3754 * the current one, the intersection will be done between the specified
3755 * rectangle and the previous scissor rectangle transformed in the current
3756 * transform space. The resulting shape is always rectangle.
3760 public void intersectScissor (NVGContext ctx
, in float x
, in float y
, in float w
, in float h
) nothrow @trusted @nogc {
3761 NVGstate
* state
= nvg__getState(ctx
);
3763 // If no previous scissor has been set, set the scissor as current scissor.
3764 if (state
.scissor
.extent
.ptr
[0] < 0) {
3765 ctx
.scissor(x
, y
, w
, h
);
3769 NVGMatrix pxform
= void;
3770 NVGMatrix invxorm
= void;
3771 float[4] rect
= void;
3773 // Transform the current scissor rect into current transform space.
3774 // If there is difference in rotation, this will be approximation.
3775 //memcpy(pxform.mat.ptr, state.scissor.xform.ptr, float.sizeof*6);
3776 pxform
= state
.scissor
.xform
;
3777 immutable float ex
= state
.scissor
.extent
.ptr
[0];
3778 immutable float ey
= state
.scissor
.extent
.ptr
[1];
3779 //nvgTransformInverse(invxorm[], state.xform[]);
3780 invxorm
= state
.xform
.inverted
;
3781 //nvgTransformMultiply(pxform[], invxorm[]);
3782 pxform
.mul(invxorm
);
3783 immutable float tex
= ex
*nvg__absf(pxform
.mat
.ptr
[0])+ey
*nvg__absf(pxform
.mat
.ptr
[2]);
3784 immutable float tey
= ex
*nvg__absf(pxform
.mat
.ptr
[1])+ey
*nvg__absf(pxform
.mat
.ptr
[3]);
3787 nvg__isectRects(rect
.ptr
, pxform
.mat
.ptr
[4]-tex
, pxform
.mat
.ptr
[5]-tey
, tex
*2, tey
*2, x
, y
, w
, h
);
3789 //ctx.scissor(rect.ptr[0], rect.ptr[1], rect.ptr[2], rect.ptr[3]);
3790 ctx
.scissor(rect
.ptr
[0..4]);
3793 /** Intersects current scissor rectangle with the specified rectangle.
3794 * The scissor rectangle is transformed by the current transform.
3795 * Note: in case the rotation of previous scissor rect differs from
3796 * the current one, the intersection will be done between the specified
3797 * rectangle and the previous scissor rectangle transformed in the current
3798 * transform space. The resulting shape is always rectangle.
3800 * Arguments: [x, y, w, h]*
3804 public void intersectScissor (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
3806 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [intersectScissor] call");
3807 if (args
.length
< ArgC
) return;
3808 const(float)* aptr
= args
.ptr
;
3809 foreach (immutable idx
; 0..args
.length
/ArgC
) {
3810 immutable x
= *aptr
++;
3811 immutable y
= *aptr
++;
3812 immutable w
= *aptr
++;
3813 immutable h
= *aptr
++;
3814 ctx
.intersectScissor(x
, y
, w
, h
);
3818 /// Reset and disables scissoring.
3819 /// Group: scissoring
3820 public void resetScissor (NVGContext ctx
) nothrow @trusted @nogc {
3821 NVGstate
* state
= nvg__getState(ctx
);
3822 state
.scissor
.xform
.mat
[] = 0.0f;
3823 state
.scissor
.extent
[] = -1.0f;
3827 // ////////////////////////////////////////////////////////////////////////// //
3828 // Render-Time Affine Transformations
3830 /// Sets GPU affine transformatin matrix. Don't do scaling or skewing here.
3831 /// This matrix won't be saved/restored with context state save/restore operations, as it is not a part of that state.
3832 /// Group: gpu_affine
3833 public void affineGPU() (NVGContext ctx
, in auto ref NVGMatrix mat
) nothrow @trusted @nogc {
3834 ctx
.gpuAffine
= mat
;
3835 ctx
.params
.renderSetAffine(ctx
.params
.userPtr
, ctx
.gpuAffine
);
3838 /// Get current GPU affine transformatin matrix.
3839 /// Group: gpu_affine
3840 public NVGMatrix
affineGPU (NVGContext ctx
) nothrow @safe @nogc {
3841 pragma(inline
, true);
3842 return ctx
.gpuAffine
;
3845 /// "Untransform" point using current GPU affine matrix.
3846 /// Group: gpu_affine
3847 public void gpuUntransformPoint (NVGContext ctx
, float *dx
, float *dy
, in float x
, in float y
) nothrow @safe @nogc {
3848 if (ctx
.gpuAffine
.isIdentity
) {
3849 if (dx
!is null) *dx
= x
;
3850 if (dy
!is null) *dy
= y
;
3852 // inverse GPU transformation
3853 NVGMatrix igpu
= ctx
.gpuAffine
.inverted
;
3854 igpu
.point(dx
, dy
, x
, y
);
3859 // ////////////////////////////////////////////////////////////////////////// //
3860 // rasterization (tesselation) code
3862 int nvg__ptEquals (float x1
, float y1
, float x2
, float y2
, float tol
) pure nothrow @safe @nogc {
3863 //pragma(inline, true);
3864 immutable float dx
= x2
-x1
;
3865 immutable float dy
= y2
-y1
;
3866 return dx
*dx
+dy
*dy
< tol
*tol
;
3869 float nvg__distPtSeg (float x
, float y
, float px
, float py
, float qx
, float qy
) pure nothrow @safe @nogc {
3870 immutable float pqx
= qx
-px
;
3871 immutable float pqy
= qy
-py
;
3874 immutable float d
= pqx
*pqx
+pqy
*pqy
;
3875 float t
= pqx
*dx
+pqy
*dy
;
3877 if (t
< 0) t
= 0; else if (t
> 1) t
= 1;
3883 void nvg__appendCommands(bool useCommand
=true) (NVGContext ctx
, Command acmd
, const(float)[] vals
...) nothrow @trusted @nogc {
3884 int nvals
= cast(int)vals
.length
;
3885 static if (useCommand
) {
3889 if (nvals
== 0) return; // nothing to do
3892 NVGstate
* state
= nvg__getState(ctx
);
3894 if (ctx
.ncommands
+nvals
+addon
> ctx
.ccommands
) {
3895 //int ccommands = ctx.ncommands+nvals+ctx.ccommands/2;
3896 int ccommands
= ((ctx
.ncommands
+(nvals
+addon
))|
0xfff)+1;
3897 float* commands
= cast(float*)realloc(ctx
.commands
, float.sizeof
*ccommands
);
3898 if (commands
is null) assert(0, "NanoVega: out of memory");
3899 ctx
.commands
= commands
;
3900 ctx
.ccommands
= ccommands
;
3901 assert(ctx
.ncommands
+(nvals
+addon
) <= ctx
.ccommands
);
3904 static if (!useCommand
) acmd
= cast(Command
)vals
.ptr
[0];
3906 if (acmd
!= Command
.Close
&& acmd
!= Command
.Winding
) {
3907 //assert(nvals+addon >= 3);
3908 ctx
.commandx
= vals
.ptr
[nvals
-2];
3909 ctx
.commandy
= vals
.ptr
[nvals
-1];
3913 float* vp
= ctx
.commands
+ctx
.ncommands
;
3914 static if (useCommand
) {
3915 vp
[0] = cast(float)acmd
;
3916 if (nvals
> 0) memcpy(vp
+1, vals
.ptr
, nvals
*float.sizeof
);
3918 memcpy(vp
, vals
.ptr
, nvals
*float.sizeof
);
3920 ctx
.ncommands
+= nvals
+addon
;
3922 // transform commands
3923 int i
= nvals
+addon
;
3926 final switch (cast(Command
)(*vp
)) {
3927 case Command
.MoveTo
:
3928 case Command
.LineTo
:
3930 state
.xform
.point(vp
+1, vp
+2, vp
[1], vp
[2]);
3933 case Command
.BezierTo
:
3935 state
.xform
.point(vp
+1, vp
+2, vp
[1], vp
[2]);
3936 state
.xform
.point(vp
+3, vp
+4, vp
[3], vp
[4]);
3937 state
.xform
.point(vp
+5, vp
+6, vp
[5], vp
[6]);
3943 case Command
.Winding
:
3947 assert(nlen
> 0 && nlen
<= i
);
3953 void nvg__clearPathCache (NVGContext ctx
) nothrow @trusted @nogc {
3954 // no need to clear paths, as data is not copied there
3955 //foreach (ref p; ctx.cache.paths[0..ctx.cache.npaths]) p.clear();
3956 ctx
.cache
.npoints
= 0;
3957 ctx
.cache
.npaths
= 0;
3958 ctx
.cache
.fillReady
= ctx
.cache
.strokeReady
= false;
3959 ctx
.cache
.clipmode
= NVGClipMode
.None
;
3962 NVGpath
* nvg__lastPath (NVGContext ctx
) nothrow @trusted @nogc {
3963 return (ctx
.cache
.npaths
> 0 ?
&ctx
.cache
.paths
[ctx
.cache
.npaths
-1] : null);
3966 void nvg__addPath (NVGContext ctx
) nothrow @trusted @nogc {
3967 import core
.stdc
.stdlib
: realloc
;
3968 import core
.stdc
.string
: memset
;
3970 if (ctx
.cache
.npaths
+1 > ctx
.cache
.cpaths
) {
3971 int cpaths
= ctx
.cache
.npaths
+1+ctx
.cache
.cpaths
/2;
3972 NVGpath
* paths
= cast(NVGpath
*)realloc(ctx
.cache
.paths
, NVGpath
.sizeof
*cpaths
);
3973 if (paths
is null) assert(0, "NanoVega: out of memory");
3974 ctx
.cache
.paths
= paths
;
3975 ctx
.cache
.cpaths
= cpaths
;
3978 NVGpath
* path
= &ctx
.cache
.paths
[ctx
.cache
.npaths
++];
3979 memset(path
, 0, NVGpath
.sizeof
);
3980 path
.first
= ctx
.cache
.npoints
;
3981 path
.mWinding
= NVGWinding
.CCW
;
3984 NVGpoint
* nvg__lastPoint (NVGContext ctx
) nothrow @trusted @nogc {
3985 return (ctx
.cache
.npoints
> 0 ?
&ctx
.cache
.points
[ctx
.cache
.npoints
-1] : null);
3988 void nvg__addPoint (NVGContext ctx
, float x
, float y
, int flags
) nothrow @trusted @nogc {
3989 NVGpath
* path
= nvg__lastPath(ctx
);
3990 if (path
is null) return;
3992 if (path
.count
> 0 && ctx
.cache
.npoints
> 0) {
3993 NVGpoint
* pt
= nvg__lastPoint(ctx
);
3994 if (nvg__ptEquals(pt
.x
, pt
.y
, x
, y
, ctx
.distTol
)) {
4000 if (ctx
.cache
.npoints
+1 > ctx
.cache
.cpoints
) {
4001 int cpoints
= ctx
.cache
.npoints
+1+ctx
.cache
.cpoints
/2;
4002 NVGpoint
* points
= cast(NVGpoint
*)realloc(ctx
.cache
.points
, NVGpoint
.sizeof
*cpoints
);
4003 if (points
is null) return;
4004 ctx
.cache
.points
= points
;
4005 ctx
.cache
.cpoints
= cpoints
;
4008 NVGpoint
* pt
= &ctx
.cache
.points
[ctx
.cache
.npoints
];
4009 memset(pt
, 0, (*pt
).sizeof
);
4012 pt
.flags
= cast(ubyte)flags
;
4014 ++ctx
.cache
.npoints
;
4018 void nvg__closePath (NVGContext ctx
) nothrow @trusted @nogc {
4019 NVGpath
* path
= nvg__lastPath(ctx
);
4020 if (path
is null) return;
4024 void nvg__pathWinding (NVGContext ctx
, NVGWinding winding
) nothrow @trusted @nogc {
4025 NVGpath
* path
= nvg__lastPath(ctx
);
4026 if (path
is null) return;
4027 path
.mWinding
= winding
;
4030 float nvg__getAverageScale() (in auto ref NVGMatrix t
) /*pure*/ nothrow @trusted @nogc {
4031 immutable float sx
= nvg__sqrtf(t
.mat
.ptr
[0]*t
.mat
.ptr
[0]+t
.mat
.ptr
[2]*t
.mat
.ptr
[2]);
4032 immutable float sy
= nvg__sqrtf(t
.mat
.ptr
[1]*t
.mat
.ptr
[1]+t
.mat
.ptr
[3]*t
.mat
.ptr
[3]);
4033 return (sx
+sy
)*0.5f;
4036 NVGVertex
* nvg__allocTempVerts (NVGContext ctx
, int nverts
) nothrow @trusted @nogc {
4037 if (nverts
> ctx
.cache
.cverts
) {
4038 int cverts
= (nverts
+0xff)&~0xff; // Round up to prevent allocations when things change just slightly.
4039 NVGVertex
* verts
= cast(NVGVertex
*)realloc(ctx
.cache
.verts
, NVGVertex
.sizeof
*cverts
);
4040 if (verts
is null) return null;
4041 ctx
.cache
.verts
= verts
;
4042 ctx
.cache
.cverts
= cverts
;
4045 return ctx
.cache
.verts
;
4048 float nvg__triarea2 (float ax
, float ay
, float bx
, float by
, float cx
, float cy
) pure nothrow @safe @nogc {
4049 immutable float abx
= bx
-ax
;
4050 immutable float aby
= by
-ay
;
4051 immutable float acx
= cx
-ax
;
4052 immutable float acy
= cy
-ay
;
4053 return acx
*aby
-abx
*acy
;
4056 float nvg__polyArea (NVGpoint
* pts
, int npts
) nothrow @trusted @nogc {
4058 foreach (int i
; 2..npts
) {
4059 NVGpoint
* a
= &pts
[0];
4060 NVGpoint
* b
= &pts
[i
-1];
4061 NVGpoint
* c
= &pts
[i
];
4062 area
+= nvg__triarea2(a
.x
, a
.y
, b
.x
, b
.y
, c
.x
, c
.y
);
4067 void nvg__polyReverse (NVGpoint
* pts
, int npts
) nothrow @trusted @nogc {
4068 NVGpoint tmp
= void;
4069 int i
= 0, j
= npts
-1;
4079 void nvg__vset (NVGVertex
* vtx
, float x
, float y
, float u
, float v
) nothrow @trusted @nogc {
4086 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 {
4087 if (level
> 10) return;
4089 // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case)
4091 if (level == 0 && ctx.tesselatortype == NVGTesselation.Combined) {
4092 static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc {
4093 immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y);
4094 return (nvg__absf(cz*cz) <= 0.01f); // arbitrary number, seems to work ok with NanoSVG output
4096 if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) {
4097 //{ import core.stdc.stdio; printf("AFD fallback!\n"); }
4098 ctx.nvg__tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4, type);
4104 immutable float x12
= (x1
+x2
)*0.5f;
4105 immutable float y12
= (y1
+y2
)*0.5f;
4106 immutable float x23
= (x2
+x3
)*0.5f;
4107 immutable float y23
= (y2
+y3
)*0.5f;
4108 immutable float x34
= (x3
+x4
)*0.5f;
4109 immutable float y34
= (y3
+y4
)*0.5f;
4110 immutable float x123
= (x12
+x23
)*0.5f;
4111 immutable float y123
= (y12
+y23
)*0.5f;
4113 immutable float dx
= x4
-x1
;
4114 immutable float dy
= y4
-y1
;
4115 immutable float d2
= nvg__absf(((x2
-x4
)*dy
-(y2
-y4
)*dx
));
4116 immutable float d3
= nvg__absf(((x3
-x4
)*dy
-(y3
-y4
)*dx
));
4118 if ((d2
+d3
)*(d2
+d3
) < ctx
.tessTol
*(dx
*dx
+dy
*dy
)) {
4119 nvg__addPoint(ctx
, x4
, y4
, type
);
4123 immutable float x234
= (x23
+x34
)*0.5f;
4124 immutable float y234
= (y23
+y34
)*0.5f;
4125 immutable float x1234
= (x123
+x234
)*0.5f;
4126 immutable float y1234
= (y123
+y234
)*0.5f;
4128 // "taxicab" / "manhattan" check for flat curves
4129 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) {
4130 nvg__addPoint(ctx
, x1234
, y1234
, type
);
4134 nvg__tesselateBezier(ctx
, x1
, y1
, x12
, y12
, x123
, y123
, x1234
, y1234
, level
+1, 0);
4135 nvg__tesselateBezier(ctx
, x1234
, y1234
, x234
, y234
, x34
, y34
, x4
, y4
, level
+1, type
);
4138 // based on the ideas and code of Maxim Shemanarev. Rest in Peace, bro!
4139 // see http://www.antigrain.com/research/adaptive_bezier/index.html
4140 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 {
4141 enum CollinearEPS
= 0.00000001f; // 0.00001f;
4142 enum AngleTolEPS
= 0.01f;
4144 static float distSquared (in float x1
, in float y1
, in float x2
, in float y2
) pure nothrow @safe @nogc {
4145 pragma(inline
, true);
4146 immutable float dx
= x2
-x1
;
4147 immutable float dy
= y2
-y1
;
4152 nvg__addPoint(ctx
, x1
, y1
, 0);
4153 nvg__tesselateBezierMcSeem(ctx
, x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
, 1, type
);
4154 nvg__addPoint(ctx
, x4
, y4
, type
);
4158 if (level
>= 32) return; // recurse limit; practically, it should be never reached, but...
4160 // calculate all the mid-points of the line segments
4161 immutable float x12
= (x1
+x2
)*0.5f;
4162 immutable float y12
= (y1
+y2
)*0.5f;
4163 immutable float x23
= (x2
+x3
)*0.5f;
4164 immutable float y23
= (y2
+y3
)*0.5f;
4165 immutable float x34
= (x3
+x4
)*0.5f;
4166 immutable float y34
= (y3
+y4
)*0.5f;
4167 immutable float x123
= (x12
+x23
)*0.5f;
4168 immutable float y123
= (y12
+y23
)*0.5f;
4169 immutable float x234
= (x23
+x34
)*0.5f;
4170 immutable float y234
= (y23
+y34
)*0.5f;
4171 immutable float x1234
= (x123
+x234
)*0.5f;
4172 immutable float y1234
= (y123
+y234
)*0.5f;
4174 // try to approximate the full cubic curve by a single straight line
4175 immutable float dx
= x4
-x1
;
4176 immutable float dy
= y4
-y1
;
4178 float d2
= nvg__absf(((x2
-x4
)*dy
-(y2
-y4
)*dx
));
4179 float d3
= nvg__absf(((x3
-x4
)*dy
-(y3
-y4
)*dx
));
4180 //immutable float da1, da2, k;
4182 final switch ((cast(int)(d2
> CollinearEPS
)<<1)+cast(int)(d3
> CollinearEPS
)) {
4184 // all collinear or p1 == p4
4185 float k
= dx
*dx
+dy
*dy
;
4187 d2
= distSquared(x1
, y1
, x2
, y2
);
4188 d3
= distSquared(x4
, y4
, x3
, y3
);
4193 d2
= k
*(da1
*dx
+da2
*dy
);
4196 d3
= k
*(da1
*dx
+da2
*dy
);
4197 if (d2
> 0 && d2
< 1 && d3
> 0 && d3
< 1) {
4198 // Simple collinear case, 1---2---3---4
4199 // We can leave just two endpoints
4202 if (d2
<= 0) d2
= distSquared(x2
, y2
, x1
, y1
);
4203 else if (d2
>= 1) d2
= distSquared(x2
, y2
, x4
, y4
);
4204 else d2
= distSquared(x2
, y2
, x1
+d2
*dx
, y1
+d2
*dy
);
4206 if (d3
<= 0) d3
= distSquared(x3
, y3
, x1
, y1
);
4207 else if (d3
>= 1) d3
= distSquared(x3
, y3
, x4
, y4
);
4208 else d3
= distSquared(x3
, y3
, x1
+d3
*dx
, y1
+d3
*dy
);
4211 if (d2
< ctx
.tessTol
) {
4212 nvg__addPoint(ctx
, x2
, y2
, type
);
4215 } if (d3
< ctx
.tessTol
) {
4216 nvg__addPoint(ctx
, x3
, y3
, type
);
4221 // p1,p2,p4 are collinear, p3 is significant
4222 if (d3
*d3
<= ctx
.tessTol
*(dx
*dx
+dy
*dy
)) {
4223 if (ctx
.angleTol
< AngleTolEPS
) {
4224 nvg__addPoint(ctx
, x23
, y23
, type
);
4228 float da1
= nvg__absf(nvg__atan2f(y4
-y3
, x4
-x3
)-nvg__atan2f(y3
-y2
, x3
-x2
));
4229 if (da1
>= NVG_PI
) da1
= 2*NVG_PI
-da1
;
4230 if (da1
< ctx
.angleTol
) {
4231 nvg__addPoint(ctx
, x2
, y2
, type
);
4232 nvg__addPoint(ctx
, x3
, y3
, type
);
4235 if (ctx
.cuspLimit
!= 0.0) {
4236 if (da1
> ctx
.cuspLimit
) {
4237 nvg__addPoint(ctx
, x3
, y3
, type
);
4245 // p1,p3,p4 are collinear, p2 is significant
4246 if (d2
*d2
<= ctx
.tessTol
*(dx
*dx
+dy
*dy
)) {
4247 if (ctx
.angleTol
< AngleTolEPS
) {
4248 nvg__addPoint(ctx
, x23
, y23
, type
);
4252 float da1
= nvg__absf(nvg__atan2f(y3
-y2
, x3
-x2
)-nvg__atan2f(y2
-y1
, x2
-x1
));
4253 if (da1
>= NVG_PI
) da1
= 2*NVG_PI
-da1
;
4254 if (da1
< ctx
.angleTol
) {
4255 nvg__addPoint(ctx
, x2
, y2
, type
);
4256 nvg__addPoint(ctx
, x3
, y3
, type
);
4259 if (ctx
.cuspLimit
!= 0.0) {
4260 if (da1
> ctx
.cuspLimit
) {
4261 nvg__addPoint(ctx
, x2
, y2
, type
);
4270 if ((d2
+d3
)*(d2
+d3
) <= ctx
.tessTol
*(dx
*dx
+dy
*dy
)) {
4271 // if the curvature doesn't exceed the distance tolerance value, we tend to finish subdivisions
4272 if (ctx
.angleTol
< AngleTolEPS
) {
4273 nvg__addPoint(ctx
, x23
, y23
, type
);
4276 // angle and cusp condition
4277 immutable float k
= nvg__atan2f(y3
-y2
, x3
-x2
);
4278 float da1
= nvg__absf(k
-nvg__atan2f(y2
-y1
, x2
-x1
));
4279 float da2
= nvg__absf(nvg__atan2f(y4
-y3
, x4
-x3
)-k
);
4280 if (da1
>= NVG_PI
) da1
= 2*NVG_PI
-da1
;
4281 if (da2
>= NVG_PI
) da2
= 2*NVG_PI
-da2
;
4282 if (da1
+da2
< ctx
.angleTol
) {
4283 // finally we can stop the recursion
4284 nvg__addPoint(ctx
, x23
, y23
, type
);
4287 if (ctx
.cuspLimit
!= 0.0) {
4288 if (da1
> ctx
.cuspLimit
) {
4289 nvg__addPoint(ctx
, x2
, y2
, type
);
4292 if (da2
> ctx
.cuspLimit
) {
4293 nvg__addPoint(ctx
, x3
, y3
, type
);
4302 // continue subdivision
4303 nvg__tesselateBezierMcSeem(ctx
, x1
, y1
, x12
, y12
, x123
, y123
, x1234
, y1234
, level
+1, 0);
4304 nvg__tesselateBezierMcSeem(ctx
, x1234
, y1234
, x234
, y234
, x34
, y34
, x4
, y4
, level
+1, type
);
4308 // Adaptive forward differencing for bezier tesselation.
4309 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt.
4310 // "Adaptive forward differencing for rendering curves and surfaces."
4311 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987.
4312 // original code by Taylor Holliday <taylor@audulus.com>
4313 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 {
4314 enum AFD_ONE
= (1<<10);
4317 immutable float ax
= -x1
+3*x2
-3*x3
+x4
;
4318 immutable float ay
= -y1
+3*y2
-3*y3
+y4
;
4319 immutable float bx
= 3*x1
-6*x2
+3*x3
;
4320 immutable float by
= 3*y1
-6*y2
+3*y3
;
4321 immutable float cx
= -3*x1
+3*x2
;
4322 immutable float cy
= -3*y1
+3*y2
;
4324 // Transform to forward difference basis (stepsize 1)
4327 float dx
= ax
+bx
+cx
;
4328 float dy
= ay
+by
+cy
;
4329 float ddx
= 6*ax
+2*bx
;
4330 float ddy
= 6*ay
+2*by
;
4334 //printf("dx: %f, dy: %f\n", dx, dy);
4335 //printf("ddx: %f, ddy: %f\n", ddx, ddy);
4336 //printf("dddx: %f, dddy: %f\n", dddx, dddy);
4341 immutable float tol
= ctx
.tessTol
*4;
4343 while (t
< AFD_ONE
) {
4344 // Flatness measure.
4345 float d
= ddx
*ddx
+ddy
*ddy
+dddx
*dddx
+dddy
*dddy
;
4347 // printf("d: %f, th: %f\n", d, th);
4349 // Go to higher resolution if we're moving a lot or overshooting the end.
4350 while ((d
> tol
&& dt > 1) ||
(t
+dt > AFD_ONE
)) {
4353 // Apply L to the curve. Increase curve resolution.
4354 dx
= 0.5f*dx
-(1.0f/8.0f)*ddx
+(1.0f/16.0f)*dddx
;
4355 dy
= 0.5f*dy
-(1.0f/8.0f)*ddy
+(1.0f/16.0f)*dddy
;
4356 ddx
= (1.0f/4.0f)*ddx
-(1.0f/8.0f)*dddx
;
4357 ddy
= (1.0f/4.0f)*ddy
-(1.0f/8.0f)*dddy
;
4358 dddx
= (1.0f/8.0f)*dddx
;
4359 dddy
= (1.0f/8.0f)*dddy
;
4361 // Half the stepsize.
4365 d
= ddx
*ddx
+ddy
*ddy
+dddx
*dddx
+dddy
*dddy
;
4368 // Go to lower resolution if we're really flat
4369 // and we aren't going to overshoot the end.
4370 // XXX: tol/32 is just a guess for when we are too flat.
4371 while ((d
> 0 && d
< tol
/32.0f && dt < AFD_ONE
) && (t
+2*dt <= AFD_ONE
)) {
4372 // printf("down\n");
4374 // Apply L^(-1) to the curve. Decrease curve resolution.
4382 // Double the stepsize.
4386 d
= ddx
*ddx
+ddy
*ddy
+dddx
*dddx
+dddy
*dddy
;
4389 // Forward differencing.
4398 nvg__addPoint(ctx
, px
, py
, (t
> 0 ? type
: 0));
4400 // Advance along the curve.
4403 // Ensure we don't overshoot.
4404 assert(t
<= AFD_ONE
);
4409 void nvg__dashLastPath (NVGContext ctx
) nothrow @trusted @nogc {
4410 import core
.stdc
.stdlib
: realloc
;
4411 import core
.stdc
.string
: memcpy
;
4413 NVGpathCache
* cache
= ctx
.cache
;
4414 if (cache
.npaths
== 0) return;
4416 NVGpath
* path
= nvg__lastPath(ctx
);
4417 if (path
is null) return;
4419 NVGstate
* state
= nvg__getState(ctx
);
4420 if (!state
.dasherActive
) return;
4422 static NVGpoint
* pts
= null;
4423 static uint ptsCount
= 0;
4424 static uint ptsSize
= 0;
4426 if (path
.count
< 2) return; // just in case
4428 // copy path points (reserve one point for closed pathes)
4429 if (ptsSize
< path
.count
+1) {
4430 ptsSize
= cast(uint)(path
.count
+1);
4431 pts
= cast(NVGpoint
*)realloc(pts
, ptsSize
*NVGpoint
.sizeof
);
4432 if (pts
is null) assert(0, "NanoVega: out of memory");
4434 ptsCount
= cast(uint)path
.count
;
4435 memcpy(pts
, &cache
.points
[path
.first
], ptsCount
*NVGpoint
.sizeof
);
4436 // add closing point for closed pathes
4437 if (path
.closed
&& !nvg__ptEquals(pts
[0].x
, pts
[0].y
, pts
[ptsCount
-1].x
, pts
[ptsCount
-1].y
, ctx
.distTol
)) {
4438 pts
[ptsCount
++] = pts
[0];
4441 // remove last path (with its points)
4443 cache
.npoints
-= path
.count
;
4445 // add stroked pathes
4446 const(float)* dashes
= state
.dashes
.ptr
;
4447 immutable uint dashCount
= state
.dashCount
;
4448 float currDashStart
= 0;
4449 uint currDashIdx
= 0;
4450 immutable bool firstIsGap
= state
.firstDashIsGap
;
4452 // calculate lengthes
4454 NVGpoint
* v1
= &pts
[0];
4455 NVGpoint
* v2
= &pts
[1];
4456 foreach (immutable _
; 0..ptsCount
) {
4457 float dx
= v2
.x
-v1
.x
;
4458 float dy
= v2
.y
-v1
.y
;
4459 v1
.len
= nvg__normalize(&dx
, &dy
);
4464 void calcDashStart (float ds) {
4466 ds = ds%state
.totalDashLen
;
4467 while (ds < 0) ds += state
.totalDashLen
;
4472 if (ds > dashes
[currDashIdx
]) {
4473 ds -= dashes
[currDashIdx
];
4476 if (currDashIdx
>= dashCount
) currDashIdx
= 0;
4484 calcDashStart(state
.dashStart
);
4486 uint srcPointIdx
= 1;
4487 const(NVGpoint
)* v1
= &pts
[0];
4488 const(NVGpoint
)* v2
= &pts
[1];
4489 float currRest
= v1
.len
;
4491 nvg__addPoint(ctx
, v1
.x
, v1
.y
, PointFlag
.Corner
);
4493 void fixLastPoint () {
4494 auto lpt
= nvg__lastPath(ctx
);
4495 if (lpt
!is null && lpt
.count
> 0) {
4497 if (auto lps
= nvg__lastPoint(ctx
)) lps
.flags
= PointFlag
.Corner
;
4499 NVGpathCache
* cache
= ctx
.cache
;
4500 cache
.points
[lpt
.first
].flags
= PointFlag
.Corner
;
4505 immutable float dlen
= dashes
[currDashIdx
];
4508 if (currDashIdx
>= dashCount
) currDashIdx
= 0;
4511 immutable float dashRest
= dlen
-currDashStart
;
4512 if ((currDashIdx
&1) != firstIsGap
) {
4513 // this is "moveto" command, so create new path
4517 if (currRest
> dashRest
) {
4518 currRest
-= dashRest
;
4520 if (currDashIdx
>= dashCount
) currDashIdx
= 0;
4523 v2
.x
-(v2
.x
-v1
.x
)*currRest
/v1
.len
,
4524 v2
.y
-(v2
.y
-v1
.y
)*currRest
/v1
.len
,
4528 currDashStart
+= currRest
;
4529 nvg__addPoint(ctx
, v2
.x
, v2
.y
, v1
.flags
); //k8:fix flags here?
4533 if (srcPointIdx
>= ptsCount
) break;
4534 v2
= &pts
[srcPointIdx
];
4541 version(nanovg_bench_flatten
) import iv
.timer
: Timer
;
4543 void nvg__flattenPaths(bool asStroke
) (NVGContext ctx
) nothrow @trusted @nogc {
4544 version(nanovg_bench_flatten
) {
4549 NVGpathCache
* cache
= ctx
.cache
;
4550 NVGstate
* state
= nvg__getState(ctx
);
4552 // check if we already did flattening
4553 static if (asStroke
) {
4554 if (state
.dashCount
>= 2) {
4555 if (cache
.npaths
> 0 && state
.lastFlattenDashCount
== state
.dashCount
) return; // already flattened
4556 state
.dasherActive
= true;
4557 state
.lastFlattenDashCount
= state
.dashCount
;
4559 if (cache
.npaths
> 0 && state
.lastFlattenDashCount
== 0) return; // already flattened
4560 state
.dasherActive
= false;
4561 state
.lastFlattenDashCount
= 0;
4564 if (cache
.npaths
> 0 && state
.lastFlattenDashCount
== 0) return; // already flattened
4565 state
.lastFlattenDashCount
= 0; // so next stroke flattening will redo it
4566 state
.dasherActive
= false;
4574 version(nanovg_bench_flatten
) timer
.restart();
4576 while (i
< ctx
.ncommands
) {
4577 final switch (cast(Command
)ctx
.commands
[i
]) {
4578 case Command
.MoveTo
:
4579 //assert(i+3 <= ctx.ncommands);
4580 static if (asStroke
) {
4581 if (cache
.npaths
> 0 && state
.dasherActive
) nvg__dashLastPath(ctx
);
4584 const p
= &ctx
.commands
[i
+1];
4585 nvg__addPoint(ctx
, p
[0], p
[1], PointFlag
.Corner
);
4588 case Command
.LineTo
:
4589 //assert(i+3 <= ctx.ncommands);
4590 const p
= &ctx
.commands
[i
+1];
4591 nvg__addPoint(ctx
, p
[0], p
[1], PointFlag
.Corner
);
4594 case Command
.BezierTo
:
4595 //assert(i+7 <= ctx.ncommands);
4596 const last
= nvg__lastPoint(ctx
);
4597 if (last
!is null) {
4598 const cp1
= &ctx
.commands
[i
+1];
4599 const cp2
= &ctx
.commands
[i
+3];
4600 const p
= &ctx
.commands
[i
+5];
4601 if (ctx
.tesselatortype
== NVGTesselation
.DeCasteljau
) {
4602 nvg__tesselateBezier(ctx
, last
.x
, last
.y
, cp1
[0], cp1
[1], cp2
[0], cp2
[1], p
[0], p
[1], 0, PointFlag
.Corner
);
4603 } else if (ctx
.tesselatortype
== NVGTesselation
.DeCasteljauMcSeem
) {
4604 nvg__tesselateBezierMcSeem(ctx
, last
.x
, last
.y
, cp1
[0], cp1
[1], cp2
[0], cp2
[1], p
[0], p
[1], 0, PointFlag
.Corner
);
4606 nvg__tesselateBezierAFD(ctx
, last
.x
, last
.y
, cp1
[0], cp1
[1], cp2
[0], cp2
[1], p
[0], p
[1], PointFlag
.Corner
);
4608 version(nanovg_bench_flatten
) ++bzcount
;
4613 //assert(i+1 <= ctx.ncommands);
4614 nvg__closePath(ctx
);
4617 case Command
.Winding
:
4618 //assert(i+2 <= ctx.ncommands);
4619 nvg__pathWinding(ctx
, cast(NVGWinding
)ctx
.commands
[i
+1]);
4624 static if (asStroke
) {
4625 if (cache
.npaths
> 0 && state
.dasherActive
) nvg__dashLastPath(ctx
);
4627 version(nanovg_bench_flatten
) {{
4629 auto xb
= timer
.toBuffer(tmbuf
[]);
4630 import core
.stdc
.stdio
: printf
;
4631 printf("flattening time: [%.*s] (%d beziers)\n", cast(uint)xb
.length
, xb
.ptr
, bzcount
);
4634 cache
.bounds
.ptr
[0] = cache
.bounds
.ptr
[1] = float.max
;
4635 cache
.bounds
.ptr
[2] = cache
.bounds
.ptr
[3] = -float.max
;
4637 // calculate the direction and length of line segments
4638 version(nanovg_bench_flatten
) timer
.restart();
4639 foreach (int j
; 0..cache
.npaths
) {
4640 NVGpath
* path
= &cache
.paths
[j
];
4641 NVGpoint
* pts
= &cache
.points
[path
.first
];
4643 // if the first and last points are the same, remove the last, mark as closed path
4644 NVGpoint
* p0
= &pts
[path
.count
-1];
4645 NVGpoint
* p1
= &pts
[0];
4646 if (nvg__ptEquals(p0
.x
, p0
.y
, p1
.x
, p1
.y
, ctx
.distTol
)) {
4648 p0
= &pts
[path
.count
-1];
4653 if (path
.count
> 2) {
4654 immutable float area
= nvg__polyArea(pts
, path
.count
);
4655 if (path
.mWinding
== NVGWinding
.CCW
&& area
< 0.0f) nvg__polyReverse(pts
, path
.count
);
4656 if (path
.mWinding
== NVGWinding
.CW
&& area
> 0.0f) nvg__polyReverse(pts
, path
.count
);
4659 foreach (immutable _
; 0..path
.count
) {
4660 // calculate segment direction and length
4663 p0
.len
= nvg__normalize(&p0
.dx
, &p0
.dy
);
4665 cache
.bounds
.ptr
[0] = nvg__min(cache
.bounds
.ptr
[0], p0
.x
);
4666 cache
.bounds
.ptr
[1] = nvg__min(cache
.bounds
.ptr
[1], p0
.y
);
4667 cache
.bounds
.ptr
[2] = nvg__max(cache
.bounds
.ptr
[2], p0
.x
);
4668 cache
.bounds
.ptr
[3] = nvg__max(cache
.bounds
.ptr
[3], p0
.y
);
4673 version(nanovg_bench_flatten
) {{
4675 auto xb
= timer
.toBuffer(tmbuf
[]);
4676 import core
.stdc
.stdio
: printf
;
4677 printf("segment calculation time: [%.*s]\n", cast(uint)xb
.length
, xb
.ptr
);
4681 int nvg__curveDivs (float r
, float arc
, float tol
) nothrow @trusted @nogc {
4682 immutable float da = nvg__acosf(r
/(r
+tol
))*2.0f;
4683 return nvg__max(2, cast(int)nvg__ceilf(arc
/da));
4686 void nvg__chooseBevel (int bevel
, NVGpoint
* p0
, NVGpoint
* p1
, float w
, float* x0
, float* y0
, float* x1
, float* y1
) nothrow @trusted @nogc {
4693 *x0
= p1
.x
+p1
.dmx
*w
;
4694 *y0
= p1
.y
+p1
.dmy
*w
;
4695 *x1
= p1
.x
+p1
.dmx
*w
;
4696 *y1
= p1
.y
+p1
.dmy
*w
;
4700 NVGVertex
* nvg__roundJoin (NVGVertex
* dst
, NVGpoint
* p0
, NVGpoint
* p1
, float lw
, float rw
, float lu
, float ru
, int ncap
, float fringe
) nothrow @trusted @nogc {
4702 float dly0
= -p0
.dx
;
4704 float dly1
= -p1
.dx
;
4705 //NVG_NOTUSED(fringe);
4707 if (p1
.flags
&PointFlag
.Left
) {
4708 float lx0
= void, ly0
= void, lx1
= void, ly1
= void;
4709 nvg__chooseBevel(p1
.flags
&PointFlag
.InnerBevelPR
, p0
, p1
, lw
, &lx0
, &ly0
, &lx1
, &ly1
);
4710 immutable float a0
= nvg__atan2f(-dly0
, -dlx0
);
4711 float a1
= nvg__atan2f(-dly1
, -dlx1
);
4712 if (a1
> a0
) a1
-= NVG_PI
*2;
4714 nvg__vset(dst
, lx0
, ly0
, lu
, 1); ++dst
;
4715 nvg__vset(dst
, p1
.x
-dlx0
*rw
, p1
.y
-dly0
*rw
, ru
, 1); ++dst
;
4717 int n
= nvg__clamp(cast(int)nvg__ceilf(((a0
-a1
)/NVG_PI
)*ncap
), 2, ncap
);
4718 for (int i
= 0; i
< n
; ++i
) {
4719 float u
= i
/cast(float)(n
-1);
4720 float a
= a0
+u
*(a1
-a0
);
4721 float rx
= p1
.x
+nvg__cosf(a
)*rw
;
4722 float ry
= p1
.y
+nvg__sinf(a
)*rw
;
4723 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4724 nvg__vset(dst
, rx
, ry
, ru
, 1); ++dst
;
4727 nvg__vset(dst
, lx1
, ly1
, lu
, 1); ++dst
;
4728 nvg__vset(dst
, p1
.x
-dlx1
*rw
, p1
.y
-dly1
*rw
, ru
, 1); ++dst
;
4731 float rx0
= void, ry0
= void, rx1
= void, ry1
= void;
4732 nvg__chooseBevel(p1
.flags
&PointFlag
.InnerBevelPR
, p0
, p1
, -rw
, &rx0
, &ry0
, &rx1
, &ry1
);
4733 immutable float a0
= nvg__atan2f(dly0
, dlx0
);
4734 float a1
= nvg__atan2f(dly1
, dlx1
);
4735 if (a1
< a0
) a1
+= NVG_PI
*2;
4737 nvg__vset(dst
, p1
.x
+dlx0
*rw
, p1
.y
+dly0
*rw
, lu
, 1); ++dst
;
4738 nvg__vset(dst
, rx0
, ry0
, ru
, 1); ++dst
;
4740 int n
= nvg__clamp(cast(int)nvg__ceilf(((a1
-a0
)/NVG_PI
)*ncap
), 2, ncap
);
4741 for (int i
= 0; i
< n
; i
++) {
4742 float u
= i
/cast(float)(n
-1);
4743 float a
= a0
+u
*(a1
-a0
);
4744 float lx
= p1
.x
+nvg__cosf(a
)*lw
;
4745 float ly
= p1
.y
+nvg__sinf(a
)*lw
;
4746 nvg__vset(dst
, lx
, ly
, lu
, 1); ++dst
;
4747 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4750 nvg__vset(dst
, p1
.x
+dlx1
*rw
, p1
.y
+dly1
*rw
, lu
, 1); ++dst
;
4751 nvg__vset(dst
, rx1
, ry1
, ru
, 1); ++dst
;
4757 NVGVertex
* nvg__bevelJoin (NVGVertex
* dst
, NVGpoint
* p0
, NVGpoint
* p1
, float lw
, float rw
, float lu
, float ru
, float fringe
) nothrow @trusted @nogc {
4758 float rx0
, ry0
, rx1
, ry1
;
4759 float lx0
, ly0
, lx1
, ly1
;
4761 float dly0
= -p0
.dx
;
4763 float dly1
= -p1
.dx
;
4764 //NVG_NOTUSED(fringe);
4766 if (p1
.flags
&PointFlag
.Left
) {
4767 nvg__chooseBevel(p1
.flags
&PointFlag
.InnerBevelPR
, p0
, p1
, lw
, &lx0
, &ly0
, &lx1
, &ly1
);
4769 nvg__vset(dst
, lx0
, ly0
, lu
, 1); ++dst
;
4770 nvg__vset(dst
, p1
.x
-dlx0
*rw
, p1
.y
-dly0
*rw
, ru
, 1); ++dst
;
4772 if (p1
.flags
&PointFlag
.Bevel
) {
4773 nvg__vset(dst
, lx0
, ly0
, lu
, 1); ++dst
;
4774 nvg__vset(dst
, p1
.x
-dlx0
*rw
, p1
.y
-dly0
*rw
, ru
, 1); ++dst
;
4776 nvg__vset(dst
, lx1
, ly1
, lu
, 1); ++dst
;
4777 nvg__vset(dst
, p1
.x
-dlx1
*rw
, p1
.y
-dly1
*rw
, ru
, 1); ++dst
;
4779 rx0
= p1
.x
-p1
.dmx
*rw
;
4780 ry0
= p1
.y
-p1
.dmy
*rw
;
4782 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4783 nvg__vset(dst
, p1
.x
-dlx0
*rw
, p1
.y
-dly0
*rw
, ru
, 1); ++dst
;
4785 nvg__vset(dst
, rx0
, ry0
, ru
, 1); ++dst
;
4786 nvg__vset(dst
, rx0
, ry0
, ru
, 1); ++dst
;
4788 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4789 nvg__vset(dst
, p1
.x
-dlx1
*rw
, p1
.y
-dly1
*rw
, ru
, 1); ++dst
;
4792 nvg__vset(dst
, lx1
, ly1
, lu
, 1); ++dst
;
4793 nvg__vset(dst
, p1
.x
-dlx1
*rw
, p1
.y
-dly1
*rw
, ru
, 1); ++dst
;
4796 nvg__chooseBevel(p1
.flags
&PointFlag
.InnerBevelPR
, p0
, p1
, -rw
, &rx0
, &ry0
, &rx1
, &ry1
);
4798 nvg__vset(dst
, p1
.x
+dlx0
*lw
, p1
.y
+dly0
*lw
, lu
, 1); ++dst
;
4799 nvg__vset(dst
, rx0
, ry0
, ru
, 1); ++dst
;
4801 if (p1
.flags
&PointFlag
.Bevel
) {
4802 nvg__vset(dst
, p1
.x
+dlx0
*lw
, p1
.y
+dly0
*lw
, lu
, 1); ++dst
;
4803 nvg__vset(dst
, rx0
, ry0
, ru
, 1); ++dst
;
4805 nvg__vset(dst
, p1
.x
+dlx1
*lw
, p1
.y
+dly1
*lw
, lu
, 1); ++dst
;
4806 nvg__vset(dst
, rx1
, ry1
, ru
, 1); ++dst
;
4808 lx0
= p1
.x
+p1
.dmx
*lw
;
4809 ly0
= p1
.y
+p1
.dmy
*lw
;
4811 nvg__vset(dst
, p1
.x
+dlx0
*lw
, p1
.y
+dly0
*lw
, lu
, 1); ++dst
;
4812 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4814 nvg__vset(dst
, lx0
, ly0
, lu
, 1); ++dst
;
4815 nvg__vset(dst
, lx0
, ly0
, lu
, 1); ++dst
;
4817 nvg__vset(dst
, p1
.x
+dlx1
*lw
, p1
.y
+dly1
*lw
, lu
, 1); ++dst
;
4818 nvg__vset(dst
, p1
.x
, p1
.y
, 0.5f, 1); ++dst
;
4821 nvg__vset(dst
, p1
.x
+dlx1
*lw
, p1
.y
+dly1
*lw
, lu
, 1); ++dst
;
4822 nvg__vset(dst
, rx1
, ry1
, ru
, 1); ++dst
;
4828 NVGVertex
* nvg__buttCapStart (NVGVertex
* dst
, NVGpoint
* p
, float dx
, float dy
, float w
, float d
, float aa
) nothrow @trusted @nogc {
4829 immutable float px
= p
.x
-dx
*d
;
4830 immutable float py
= p
.y
-dy
*d
;
4831 immutable float dlx
= dy
;
4832 immutable float dly
= -dx
;
4833 nvg__vset(dst
, px
+dlx
*w
-dx
*aa
, py
+dly
*w
-dy
*aa
, 0, 0); ++dst
;
4834 nvg__vset(dst
, px
-dlx
*w
-dx
*aa
, py
-dly
*w
-dy
*aa
, 1, 0); ++dst
;
4835 nvg__vset(dst
, px
+dlx
*w
, py
+dly
*w
, 0, 1); ++dst
;
4836 nvg__vset(dst
, px
-dlx
*w
, py
-dly
*w
, 1, 1); ++dst
;
4840 NVGVertex
* nvg__buttCapEnd (NVGVertex
* dst
, NVGpoint
* p
, float dx
, float dy
, float w
, float d
, float aa
) nothrow @trusted @nogc {
4841 immutable float px
= p
.x
+dx
*d
;
4842 immutable float py
= p
.y
+dy
*d
;
4843 immutable float dlx
= dy
;
4844 immutable float dly
= -dx
;
4845 nvg__vset(dst
, px
+dlx
*w
, py
+dly
*w
, 0, 1); ++dst
;
4846 nvg__vset(dst
, px
-dlx
*w
, py
-dly
*w
, 1, 1); ++dst
;
4847 nvg__vset(dst
, px
+dlx
*w
+dx
*aa
, py
+dly
*w
+dy
*aa
, 0, 0); ++dst
;
4848 nvg__vset(dst
, px
-dlx
*w
+dx
*aa
, py
-dly
*w
+dy
*aa
, 1, 0); ++dst
;
4852 NVGVertex
* nvg__roundCapStart (NVGVertex
* dst
, NVGpoint
* p
, float dx
, float dy
, float w
, int ncap
, float aa
) nothrow @trusted @nogc {
4853 immutable float px
= p
.x
;
4854 immutable float py
= p
.y
;
4855 immutable float dlx
= dy
;
4856 immutable float dly
= -dx
;
4858 immutable float ncpf
= cast(float)(ncap
-1);
4859 foreach (int i
; 0..ncap
) {
4860 float a
= i
/*/cast(float)(ncap-1)*//ncpf
*NVG_PI
;
4861 float ax
= nvg__cosf(a
)*w
, ay
= nvg__sinf(a
)*w
;
4862 nvg__vset(dst
, px
-dlx
*ax
-dx
*ay
, py
-dly
*ax
-dy
*ay
, 0, 1); ++dst
;
4863 nvg__vset(dst
, px
, py
, 0.5f, 1); ++dst
;
4865 nvg__vset(dst
, px
+dlx
*w
, py
+dly
*w
, 0, 1); ++dst
;
4866 nvg__vset(dst
, px
-dlx
*w
, py
-dly
*w
, 1, 1); ++dst
;
4870 NVGVertex
* nvg__roundCapEnd (NVGVertex
* dst
, NVGpoint
* p
, float dx
, float dy
, float w
, int ncap
, float aa
) nothrow @trusted @nogc {
4871 immutable float px
= p
.x
;
4872 immutable float py
= p
.y
;
4873 immutable float dlx
= dy
;
4874 immutable float dly
= -dx
;
4876 nvg__vset(dst
, px
+dlx
*w
, py
+dly
*w
, 0, 1); ++dst
;
4877 nvg__vset(dst
, px
-dlx
*w
, py
-dly
*w
, 1, 1); ++dst
;
4878 immutable float ncpf
= cast(float)(ncap
-1);
4879 foreach (int i
; 0..ncap
) {
4880 float a
= i
/*cast(float)(ncap-1)*//ncpf
*NVG_PI
;
4881 float ax
= nvg__cosf(a
)*w
, ay
= nvg__sinf(a
)*w
;
4882 nvg__vset(dst
, px
, py
, 0.5f, 1); ++dst
;
4883 nvg__vset(dst
, px
-dlx
*ax
+dx
*ay
, py
-dly
*ax
+dy
*ay
, 0, 1); ++dst
;
4888 void nvg__calculateJoins (NVGContext ctx
, float w
, int lineJoin
, float miterLimit
) nothrow @trusted @nogc {
4889 NVGpathCache
* cache
= ctx
.cache
;
4892 if (w
> 0.0f) iw
= 1.0f/w
;
4894 // Calculate which joins needs extra vertices to append, and gather vertex count.
4895 foreach (int i
; 0..cache
.npaths
) {
4896 NVGpath
* path
= &cache
.paths
[i
];
4897 NVGpoint
* pts
= &cache
.points
[path
.first
];
4898 NVGpoint
* p0
= &pts
[path
.count
-1];
4899 NVGpoint
* p1
= &pts
[0];
4904 foreach (int j
; 0..path
.count
) {
4905 //float dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
4906 immutable float dlx0
= p0
.dy
;
4907 immutable float dly0
= -p0
.dx
;
4908 immutable float dlx1
= p1
.dy
;
4909 immutable float dly1
= -p1
.dx
;
4910 // Calculate extrusions
4911 p1
.dmx
= (dlx0
+dlx1
)*0.5f;
4912 p1
.dmy
= (dly0
+dly1
)*0.5f;
4913 immutable float dmr2
= p1
.dmx
*p1
.dmx
+p1
.dmy
*p1
.dmy
;
4914 if (dmr2
> 0.000001f) {
4915 float scale
= 1.0f/dmr2
;
4916 if (scale
> 600.0f) scale
= 600.0f;
4921 // Clear flags, but keep the corner.
4922 p1
.flags
= (p1
.flags
&PointFlag
.Corner
) ? PointFlag
.Corner
: 0;
4924 // Keep track of left turns.
4925 immutable float cross
= p1
.dx
*p0
.dy
-p0
.dx
*p1
.dy
;
4928 p1
.flags |
= PointFlag
.Left
;
4931 // Calculate if we should use bevel or miter for inner join.
4932 immutable float limit
= nvg__max(1.01f, nvg__min(p0
.len
, p1
.len
)*iw
);
4933 if ((dmr2
*limit
*limit
) < 1.0f) p1
.flags |
= PointFlag
.InnerBevelPR
;
4935 // Check to see if the corner needs to be beveled.
4936 if (p1
.flags
&PointFlag
.Corner
) {
4937 if ((dmr2
*miterLimit
*miterLimit
) < 1.0f || lineJoin
== NVGLineCap
.Bevel || lineJoin
== NVGLineCap
.Round
) {
4938 p1
.flags |
= PointFlag
.Bevel
;
4942 if ((p1
.flags
&(PointFlag
.Bevel|PointFlag
.InnerBevelPR
)) != 0) path
.nbevel
++;
4947 path
.convex
= (nleft
== path
.count
);
4951 void nvg__expandStroke (NVGContext ctx
, float w
, int lineCap
, int lineJoin
, float miterLimit
) nothrow @trusted @nogc {
4952 NVGpathCache
* cache
= ctx
.cache
;
4953 immutable float aa
= ctx
.fringeWidth
;
4954 int ncap
= nvg__curveDivs(w
, NVG_PI
, ctx
.tessTol
); // Calculate divisions per half circle.
4956 nvg__calculateJoins(ctx
, w
, lineJoin
, miterLimit
);
4958 // Calculate max vertex usage.
4960 foreach (int i
; 0..cache
.npaths
) {
4961 NVGpath
* path
= &cache
.paths
[i
];
4962 immutable bool loop = path
.closed
;
4963 if (lineJoin
== NVGLineCap
.Round
) {
4964 cverts
+= (path
.count
+path
.nbevel
*(ncap
+2)+1)*2; // plus one for loop
4966 cverts
+= (path
.count
+path
.nbevel
*5+1)*2; // plus one for loop
4970 if (lineCap
== NVGLineCap
.Round
) {
4971 cverts
+= (ncap
*2+2)*2;
4978 NVGVertex
* verts
= nvg__allocTempVerts(ctx
, cverts
);
4979 if (verts
is null) return;
4981 foreach (int i
; 0..cache
.npaths
) {
4982 NVGpath
* path
= &cache
.paths
[i
];
4983 NVGpoint
* pts
= &cache
.points
[path
.first
];
4991 // Calculate fringe or stroke
4992 immutable bool loop = path
.closed
;
4993 NVGVertex
* dst
= verts
;
4998 p0
= &pts
[path
.count
-1];
5012 float dx
= p1
.x
-p0
.x
;
5013 float dy
= p1
.y
-p0
.y
;
5014 nvg__normalize(&dx
, &dy
);
5015 if (lineCap
== NVGLineCap
.Butt
) dst
= nvg__buttCapStart(dst
, p0
, dx
, dy
, w
, -aa
*0.5f, aa
);
5016 else if (lineCap
== NVGLineCap
.Butt || lineCap
== NVGLineCap
.Square
) dst
= nvg__buttCapStart(dst
, p0
, dx
, dy
, w
, w
-aa
, aa
);
5017 else if (lineCap
== NVGLineCap
.Round
) dst
= nvg__roundCapStart(dst
, p0
, dx
, dy
, w
, ncap
, aa
);
5020 foreach (int j
; s
..e
) {
5021 if ((p1
.flags
&(PointFlag
.Bevel|PointFlag
.InnerBevelPR
)) != 0) {
5022 if (lineJoin
== NVGLineCap
.Round
) {
5023 dst
= nvg__roundJoin(dst
, p0
, p1
, w
, w
, 0, 1, ncap
, aa
);
5025 dst
= nvg__bevelJoin(dst
, p0
, p1
, w
, w
, 0, 1, aa
);
5028 nvg__vset(dst
, p1
.x
+(p1
.dmx
*w
), p1
.y
+(p1
.dmy
*w
), 0, 1); ++dst
;
5029 nvg__vset(dst
, p1
.x
-(p1
.dmx
*w
), p1
.y
-(p1
.dmy
*w
), 1, 1); ++dst
;
5036 nvg__vset(dst
, verts
[0].x
, verts
[0].y
, 0, 1); ++dst
;
5037 nvg__vset(dst
, verts
[1].x
, verts
[1].y
, 1, 1); ++dst
;
5040 float dx
= p1
.x
-p0
.x
;
5041 float dy
= p1
.y
-p0
.y
;
5042 nvg__normalize(&dx
, &dy
);
5043 if (lineCap
== NVGLineCap
.Butt
) dst
= nvg__buttCapEnd(dst
, p1
, dx
, dy
, w
, -aa
*0.5f, aa
);
5044 else if (lineCap
== NVGLineCap
.Butt || lineCap
== NVGLineCap
.Square
) dst
= nvg__buttCapEnd(dst
, p1
, dx
, dy
, w
, w
-aa
, aa
);
5045 else if (lineCap
== NVGLineCap
.Round
) dst
= nvg__roundCapEnd(dst
, p1
, dx
, dy
, w
, ncap
, aa
);
5048 path
.nstroke
= cast(int)(dst
-verts
);
5054 void nvg__expandFill (NVGContext ctx
, float w
, int lineJoin
, float miterLimit
) nothrow @trusted @nogc {
5055 NVGpathCache
* cache
= ctx
.cache
;
5056 immutable float aa
= ctx
.fringeWidth
;
5057 bool fringe
= (w
> 0.0f);
5059 nvg__calculateJoins(ctx
, w
, lineJoin
, miterLimit
);
5061 // Calculate max vertex usage.
5063 foreach (int i
; 0..cache
.npaths
) {
5064 NVGpath
* path
= &cache
.paths
[i
];
5065 cverts
+= path
.count
+path
.nbevel
+1;
5066 if (fringe
) cverts
+= (path
.count
+path
.nbevel
*5+1)*2; // plus one for loop
5069 NVGVertex
* verts
= nvg__allocTempVerts(ctx
, cverts
);
5070 if (verts
is null) return;
5072 bool convex
= (cache
.npaths
== 1 && cache
.paths
[0].convex
);
5074 foreach (int i
; 0..cache
.npaths
) {
5075 NVGpath
* path
= &cache
.paths
[i
];
5076 NVGpoint
* pts
= &cache
.points
[path
.first
];
5078 // Calculate shape vertices.
5079 immutable float woff
= 0.5f*aa
;
5080 NVGVertex
* dst
= verts
;
5085 NVGpoint
* p0
= &pts
[path
.count
-1];
5086 NVGpoint
* p1
= &pts
[0];
5087 foreach (int j
; 0..path
.count
) {
5088 if (p1
.flags
&PointFlag
.Bevel
) {
5089 immutable float dlx0
= p0
.dy
;
5090 immutable float dly0
= -p0
.dx
;
5091 immutable float dlx1
= p1
.dy
;
5092 immutable float dly1
= -p1
.dx
;
5093 if (p1
.flags
&PointFlag
.Left
) {
5094 immutable float lx
= p1
.x
+p1
.dmx
*woff
;
5095 immutable float ly
= p1
.y
+p1
.dmy
*woff
;
5096 nvg__vset(dst
, lx
, ly
, 0.5f, 1); ++dst
;
5098 immutable float lx0
= p1
.x
+dlx0
*woff
;
5099 immutable float ly0
= p1
.y
+dly0
*woff
;
5100 immutable float lx1
= p1
.x
+dlx1
*woff
;
5101 immutable float ly1
= p1
.y
+dly1
*woff
;
5102 nvg__vset(dst
, lx0
, ly0
, 0.5f, 1); ++dst
;
5103 nvg__vset(dst
, lx1
, ly1
, 0.5f, 1); ++dst
;
5106 nvg__vset(dst
, p1
.x
+(p1
.dmx
*woff
), p1
.y
+(p1
.dmy
*woff
), 0.5f, 1); ++dst
;
5111 foreach (int j
; 0..path
.count
) {
5112 nvg__vset(dst
, pts
[j
].x
, pts
[j
].y
, 0.5f, 1);
5117 path
.nfill
= cast(int)(dst
-verts
);
5123 immutable float rw
= w
-woff
;
5125 immutable float ru
= 1;
5129 // Create only half a fringe for convex shapes so that
5130 // the shape can be rendered without stenciling.
5132 lw
= woff
; // This should generate the same vertex as fill inset above.
5133 lu
= 0.5f; // Set outline fade at middle.
5137 NVGpoint
* p0
= &pts
[path
.count
-1];
5138 NVGpoint
* p1
= &pts
[0];
5140 foreach (int j
; 0..path
.count
) {
5141 if ((p1
.flags
&(PointFlag
.Bevel|PointFlag
.InnerBevelPR
)) != 0) {
5142 dst
= nvg__bevelJoin(dst
, p0
, p1
, lw
, rw
, lu
, ru
, ctx
.fringeWidth
);
5144 nvg__vset(dst
, p1
.x
+(p1
.dmx
*lw
), p1
.y
+(p1
.dmy
*lw
), lu
, 1); ++dst
;
5145 nvg__vset(dst
, p1
.x
-(p1
.dmx
*rw
), p1
.y
-(p1
.dmy
*rw
), ru
, 1); ++dst
;
5151 nvg__vset(dst
, verts
[0].x
, verts
[0].y
, lu
, 1); ++dst
;
5152 nvg__vset(dst
, verts
[1].x
, verts
[1].y
, ru
, 1); ++dst
;
5154 path
.nstroke
= cast(int)(dst
-verts
);
5164 // ////////////////////////////////////////////////////////////////////////// //
5167 /// Clears the current path and sub-paths.
5170 public void beginPath (NVGContext ctx
) nothrow @trusted @nogc {
5172 ctx
.pathPickRegistered
&= NVGPickKind
.All
; // reset "registered" flags
5173 nvg__clearPathCache(ctx
);
5176 public alias newPath
= beginPath
; /// Ditto.
5178 /// Starts new sub-path with specified point as first point.
5181 public void moveTo (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
5182 nvg__appendCommands(ctx
, Command
.MoveTo
, x
, y
);
5185 /// Starts new sub-path with specified point as first point.
5186 /// Arguments: [x, y]*
5188 public void moveTo (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5190 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [moveTo] call");
5191 if (args
.length
< ArgC
) return;
5192 nvg__appendCommands(ctx
, Command
.MoveTo
, args
[$-2..$]);
5195 /// Adds line segment from the last point in the path to the specified point.
5198 public void lineTo (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
5199 nvg__appendCommands(ctx
, Command
.LineTo
, x
, y
);
5202 /// Adds line segment from the last point in the path to the specified point.
5203 /// Arguments: [x, y]*
5205 public void lineTo (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5207 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [lineTo] call");
5208 if (args
.length
< ArgC
) return;
5209 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5210 nvg__appendCommands(ctx
, Command
.LineTo
, args
.ptr
[idx
*ArgC
..idx
*ArgC
+ArgC
]);
5214 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
5216 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 {
5217 nvg__appendCommands(ctx
, Command
.BezierTo
, c1x
, c1y
, c2x
, c2y
, x
, y
);
5220 /// Adds cubic bezier segment from last point in the path via two control points to the specified point.
5221 /// Arguments: [c1x, c1y, c2x, c2y, x, y]*
5223 public void bezierTo (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5225 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [bezierTo] call");
5226 if (args
.length
< ArgC
) return;
5227 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5228 nvg__appendCommands(ctx
, Command
.BezierTo
, args
.ptr
[idx
*ArgC
..idx
*ArgC
+ArgC
]);
5232 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
5234 public void quadTo (NVGContext ctx
, in float cx
, in float cy
, in float x
, in float y
) nothrow @trusted @nogc {
5235 immutable float x0
= ctx
.commandx
;
5236 immutable float y0
= ctx
.commandy
;
5237 nvg__appendCommands(ctx
,
5239 x0
+2.0f/3.0f*(cx
-x0
), y0
+2.0f/3.0f*(cy
-y0
),
5240 x
+2.0f/3.0f*(cx
-x
), y
+2.0f/3.0f*(cy
-y
),
5245 /// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
5246 /// Arguments: [cx, cy, x, y]*
5248 public void quadTo (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5250 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [quadTo] call");
5251 if (args
.length
< ArgC
) return;
5252 const(float)* aptr
= args
.ptr
;
5253 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5254 immutable float x0
= ctx
.commandx
;
5255 immutable float y0
= ctx
.commandy
;
5256 immutable float cx
= *aptr
++;
5257 immutable float cy
= *aptr
++;
5258 immutable float x
= *aptr
++;
5259 immutable float y
= *aptr
++;
5260 nvg__appendCommands(ctx
,
5262 x0
+2.0f/3.0f*(cx
-x0
), y0
+2.0f/3.0f*(cy
-y0
),
5263 x
+2.0f/3.0f*(cx
-x
), y
+2.0f/3.0f*(cy
-y
),
5269 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
5271 public void arcTo (NVGContext ctx
, in float x1
, in float y1
, in float x2
, in float y2
, in float radius
) nothrow @trusted @nogc {
5272 if (ctx
.ncommands
== 0) return;
5274 immutable float x0
= ctx
.commandx
;
5275 immutable float y0
= ctx
.commandy
;
5277 // handle degenerate cases
5278 if (nvg__ptEquals(x0
, y0
, x1
, y1
, ctx
.distTol
) ||
5279 nvg__ptEquals(x1
, y1
, x2
, y2
, ctx
.distTol
) ||
5280 nvg__distPtSeg(x1
, y1
, x0
, y0
, x2
, y2
) < ctx
.distTol
*ctx
.distTol ||
5281 radius
< ctx
.distTol
)
5287 // calculate tangential circle to lines (x0, y0)-(x1, y1) and (x1, y1)-(x2, y2)
5292 nvg__normalize(&dx0
, &dy0
);
5293 nvg__normalize(&dx1
, &dy1
);
5294 immutable float a
= nvg__acosf(dx0
*dx1
+dy0
*dy1
);
5295 immutable float d
= radius
/nvg__tanf(a
/2.0f);
5297 //printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
5304 float cx
= void, cy
= void, a0
= void, a1
= void;
5306 if (nvg__cross(dx0
, dy0
, dx1
, dy1
) > 0.0f) {
5307 cx
= x1
+dx0
*d
+dy0
*radius
;
5308 cy
= y1
+dy0
*d
+-dx0
*radius
;
5309 a0
= nvg__atan2f(dx0
, -dy0
);
5310 a1
= nvg__atan2f(-dx1
, dy1
);
5311 dir
= NVGWinding
.CW
;
5312 //printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
5314 cx
= x1
+dx0
*d
+-dy0
*radius
;
5315 cy
= y1
+dy0
*d
+dx0
*radius
;
5316 a0
= nvg__atan2f(-dx0
, dy0
);
5317 a1
= nvg__atan2f(dx1
, -dy1
);
5318 dir
= NVGWinding
.CCW
;
5319 //printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
5322 ctx
.arc(dir
, cx
, cy
, radius
, a0
, a1
); // first is line
5326 /// Adds an arc segment at the corner defined by the last path point, and two specified points.
5327 /// Arguments: [x1, y1, x2, y2, radius]*
5329 public void arcTo (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5331 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [arcTo] call");
5332 if (args
.length
< ArgC
) return;
5333 if (ctx
.ncommands
== 0) return;
5334 const(float)* aptr
= args
.ptr
;
5335 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5336 immutable float x0
= ctx
.commandx
;
5337 immutable float y0
= ctx
.commandy
;
5338 immutable float x1
= *aptr
++;
5339 immutable float y1
= *aptr
++;
5340 immutable float x2
= *aptr
++;
5341 immutable float y2
= *aptr
++;
5342 immutable float radius
= *aptr
++;
5343 ctx
.arcTo(x1
, y1
, x2
, y2
, radius
);
5347 /// Closes current sub-path with a line segment.
5350 public void closePath (NVGContext ctx
) nothrow @trusted @nogc {
5351 nvg__appendCommands(ctx
, Command
.Close
);
5354 /// Sets the current sub-path winding, see NVGWinding and NVGSolidity.
5356 public void pathWinding (NVGContext ctx
, NVGWinding dir
) nothrow @trusted @nogc {
5357 nvg__appendCommands(ctx
, Command
.Winding
, cast(float)dir
);
5361 public void pathWinding (NVGContext ctx
, NVGSolidity dir
) nothrow @trusted @nogc {
5362 nvg__appendCommands(ctx
, Command
.Winding
, cast(float)dir
);
5365 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
5366 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
5367 * Angles are specified in radians.
5369 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
5373 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 {
5374 static assert(mode
== "original" || mode
== "move" || mode
== "line");
5376 float[3+5*7+100] vals
= void;
5377 //int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
5378 static if (mode
== "original") {
5379 immutable int move
= (ctx
.ncommands
> 0 ? Command
.LineTo
: Command
.MoveTo
);
5380 } else static if (mode
== "move") {
5381 enum move
= Command
.MoveTo
;
5382 } else static if (mode
== "line") {
5383 enum move
= Command
.LineTo
;
5385 static assert(0, "wtf?!");
5390 if (dir
== NVGWinding
.CW
) {
5391 if (nvg__absf(da) >= NVG_PI
*2) {
5394 while (da < 0.0f) da += NVG_PI
*2;
5397 if (nvg__absf(da) >= NVG_PI
*2) {
5400 while (da > 0.0f) da -= NVG_PI
*2;
5404 // Split arc into max 90 degree segments.
5405 immutable int ndivs
= nvg__max(1, nvg__min(cast(int)(nvg__absf(da)/(NVG_PI
*0.5f)+0.5f), 5));
5406 immutable float hda
= (da/cast(float)ndivs
)/2.0f;
5407 float kappa
= nvg__absf(4.0f/3.0f*(1.0f-nvg__cosf(hda
))/nvg__sinf(hda
));
5409 if (dir
== NVGWinding
.CCW
) kappa
= -kappa
;
5412 float px
= 0, py
= 0, ptanx
= 0, ptany
= 0;
5413 foreach (int i
; 0..ndivs
+1) {
5414 immutable float a
= a0
+da*(i
/cast(float)ndivs
);
5415 immutable float dx
= nvg__cosf(a
);
5416 immutable float dy
= nvg__sinf(a
);
5417 immutable float x
= cx
+dx
*r
;
5418 immutable float y
= cy
+dy
*r
;
5419 immutable float tanx
= -dy
*r
*kappa
;
5420 immutable float tany
= dx
*r
*kappa
;
5423 if (vals
.length
-nvals
< 3) {
5425 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, vals
.ptr
[0..nvals
]); // ignore command
5428 vals
.ptr
[nvals
++] = cast(float)move
;
5429 vals
.ptr
[nvals
++] = x
;
5430 vals
.ptr
[nvals
++] = y
;
5432 if (vals
.length
-nvals
< 7) {
5434 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, vals
.ptr
[0..nvals
]); // ignore command
5437 vals
.ptr
[nvals
++] = Command
.BezierTo
;
5438 vals
.ptr
[nvals
++] = px
+ptanx
;
5439 vals
.ptr
[nvals
++] = py
+ptany
;
5440 vals
.ptr
[nvals
++] = x
-tanx
;
5441 vals
.ptr
[nvals
++] = y
-tany
;
5442 vals
.ptr
[nvals
++] = x
;
5443 vals
.ptr
[nvals
++] = y
;
5451 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, vals
.ptr
[0..nvals
]); // ignore command
5455 /** Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
5456 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
5457 * Angles are specified in radians.
5459 * Arguments: [cx, cy, r, a0, a1]*
5461 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
5465 public void arc(string mode
="original") (NVGContext ctx
, NVGWinding dir
, in float[] args
) nothrow @trusted @nogc {
5466 static assert(mode
== "original" || mode
== "move" || mode
== "line");
5468 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [arc] call");
5469 if (args
.length
< ArgC
) return;
5470 const(float)* aptr
= args
.ptr
;
5471 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5472 immutable cx
= *aptr
++;
5473 immutable cy
= *aptr
++;
5474 immutable r
= *aptr
++;
5475 immutable a0
= *aptr
++;
5476 immutable a1
= *aptr
++;
5477 ctx
.arc
!mode(dir
, cx
, cy
, r
, a0
, a1
);
5481 /// Creates new rectangle shaped sub-path.
5484 public void rect (NVGContext ctx
, in float x
, in float y
, in float w
, in float h
) nothrow @trusted @nogc {
5485 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5486 Command
.MoveTo
, x
, y
,
5487 Command
.LineTo
, x
, y
+h
,
5488 Command
.LineTo
, x
+w
, y
+h
,
5489 Command
.LineTo
, x
+w
, y
,
5494 /// Creates new rectangle shaped sub-path.
5495 /// Arguments: [x, y, w, h]*
5497 public void rect (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5499 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [rect] call");
5500 if (args
.length
< ArgC
) return;
5501 const(float)* aptr
= args
.ptr
;
5502 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5503 immutable x
= *aptr
++;
5504 immutable y
= *aptr
++;
5505 immutable w
= *aptr
++;
5506 immutable h
= *aptr
++;
5507 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5508 Command
.MoveTo
, x
, y
,
5509 Command
.LineTo
, x
, y
+h
,
5510 Command
.LineTo
, x
+w
, y
+h
,
5511 Command
.LineTo
, x
+w
, y
,
5517 /// Creates new rounded rectangle shaped sub-path.
5520 public void roundedRect (NVGContext ctx
, in float x
, in float y
, in float w
, in float h
, in float radius
) nothrow @trusted @nogc {
5521 ctx
.roundedRectVarying(x
, y
, w
, h
, radius
, radius
, radius
, radius
);
5524 /// Creates new rounded rectangle shaped sub-path.
5525 /// Arguments: [x, y, w, h, radius]*
5527 public void roundedRect (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5529 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [roundedRect] call");
5530 if (args
.length
< ArgC
) return;
5531 const(float)* aptr
= args
.ptr
;
5532 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5533 immutable x
= *aptr
++;
5534 immutable y
= *aptr
++;
5535 immutable w
= *aptr
++;
5536 immutable h
= *aptr
++;
5537 immutable r
= *aptr
++;
5538 ctx
.roundedRectVarying(x
, y
, w
, h
, r
, r
, r
, r
);
5542 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5545 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 {
5546 if (rw
< 0.1f || rh
< 0.1f) {
5547 rect(ctx
, x
, y
, w
, h
);
5549 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5550 Command
.MoveTo
, x
+rw
, y
,
5551 Command
.LineTo
, x
+w
-rw
, y
,
5552 Command
.BezierTo
, x
+w
-rw
*(1-NVG_KAPPA90
), y
, x
+w
, y
+rh
*(1-NVG_KAPPA90
), x
+w
, y
+rh
,
5553 Command
.LineTo
, x
+w
, y
+h
-rh
,
5554 Command
.BezierTo
, x
+w
, y
+h
-rh
*(1-NVG_KAPPA90
), x
+w
-rw
*(1-NVG_KAPPA90
), y
+h
, x
+w
-rw
, y
+h
,
5555 Command
.LineTo
, x
+rw
, y
+h
,
5556 Command
.BezierTo
, x
+rw
*(1-NVG_KAPPA90
), y
+h
, x
, y
+h
-rh
*(1-NVG_KAPPA90
), x
, y
+h
-rh
,
5557 Command
.LineTo
, x
, y
+rh
,
5558 Command
.BezierTo
, x
, y
+rh
*(1-NVG_KAPPA90
), x
+rw
*(1-NVG_KAPPA90
), y
, x
+rw
, y
,
5564 /// Creates new rounded rectangle shaped sub-path. Specify ellipse width and height to round corners according to it.
5565 /// Arguments: [x, y, w, h, rw, rh]*
5567 public void roundedRectEllipse (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5569 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [roundedRectEllipse] call");
5570 if (args
.length
< ArgC
) return;
5571 const(float)* aptr
= args
.ptr
;
5572 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5573 immutable x
= *aptr
++;
5574 immutable y
= *aptr
++;
5575 immutable w
= *aptr
++;
5576 immutable h
= *aptr
++;
5577 immutable rw
= *aptr
++;
5578 immutable rh
= *aptr
++;
5579 if (rw
< 0.1f || rh
< 0.1f) {
5580 rect(ctx
, x
, y
, w
, h
);
5582 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5583 Command
.MoveTo
, x
+rw
, y
,
5584 Command
.LineTo
, x
+w
-rw
, y
,
5585 Command
.BezierTo
, x
+w
-rw
*(1-NVG_KAPPA90
), y
, x
+w
, y
+rh
*(1-NVG_KAPPA90
), x
+w
, y
+rh
,
5586 Command
.LineTo
, x
+w
, y
+h
-rh
,
5587 Command
.BezierTo
, x
+w
, y
+h
-rh
*(1-NVG_KAPPA90
), x
+w
-rw
*(1-NVG_KAPPA90
), y
+h
, x
+w
-rw
, y
+h
,
5588 Command
.LineTo
, x
+rw
, y
+h
,
5589 Command
.BezierTo
, x
+rw
*(1-NVG_KAPPA90
), y
+h
, x
, y
+h
-rh
*(1-NVG_KAPPA90
), x
, y
+h
-rh
,
5590 Command
.LineTo
, x
, y
+rh
,
5591 Command
.BezierTo
, x
, y
+rh
*(1-NVG_KAPPA90
), x
+rw
*(1-NVG_KAPPA90
), y
, x
+rw
, y
,
5598 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5600 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 {
5601 if (radTopLeft
< 0.1f && radTopRight
< 0.1f && radBottomRight
< 0.1f && radBottomLeft
< 0.1f) {
5602 ctx
.rect(x
, y
, w
, h
);
5604 immutable float halfw
= nvg__absf(w
)*0.5f;
5605 immutable float halfh
= nvg__absf(h
)*0.5f;
5606 immutable float rxBL
= nvg__min(radBottomLeft
, halfw
)*nvg__sign(w
), ryBL
= nvg__min(radBottomLeft
, halfh
)*nvg__sign(h
);
5607 immutable float rxBR
= nvg__min(radBottomRight
, halfw
)*nvg__sign(w
), ryBR
= nvg__min(radBottomRight
, halfh
)*nvg__sign(h
);
5608 immutable float rxTR
= nvg__min(radTopRight
, halfw
)*nvg__sign(w
), ryTR
= nvg__min(radTopRight
, halfh
)*nvg__sign(h
);
5609 immutable float rxTL
= nvg__min(radTopLeft
, halfw
)*nvg__sign(w
), ryTL
= nvg__min(radTopLeft
, halfh
)*nvg__sign(h
);
5610 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5611 Command
.MoveTo
, x
, y
+ryTL
,
5612 Command
.LineTo
, x
, y
+h
-ryBL
,
5613 Command
.BezierTo
, x
, y
+h
-ryBL
*(1-NVG_KAPPA90
), x
+rxBL
*(1-NVG_KAPPA90
), y
+h
, x
+rxBL
, y
+h
,
5614 Command
.LineTo
, x
+w
-rxBR
, y
+h
,
5615 Command
.BezierTo
, x
+w
-rxBR
*(1-NVG_KAPPA90
), y
+h
, x
+w
, y
+h
-ryBR
*(1-NVG_KAPPA90
), x
+w
, y
+h
-ryBR
,
5616 Command
.LineTo
, x
+w
, y
+ryTR
,
5617 Command
.BezierTo
, x
+w
, y
+ryTR
*(1-NVG_KAPPA90
), x
+w
-rxTR
*(1-NVG_KAPPA90
), y
, x
+w
-rxTR
, y
,
5618 Command
.LineTo
, x
+rxTL
, y
,
5619 Command
.BezierTo
, x
+rxTL
*(1-NVG_KAPPA90
), y
, x
, y
+ryTL
*(1-NVG_KAPPA90
), x
, y
+ryTL
,
5625 /// Creates new rounded rectangle shaped sub-path. This one allows you to specify different rounding radii for each corner.
5626 /// Arguments: [x, y, w, h, radTopLeft, radTopRight, radBottomRight, radBottomLeft]*
5628 public void roundedRectVarying (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5630 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [roundedRectVarying] call");
5631 if (args
.length
< ArgC
) return;
5632 const(float)* aptr
= args
.ptr
;
5633 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5634 immutable x
= *aptr
++;
5635 immutable y
= *aptr
++;
5636 immutable w
= *aptr
++;
5637 immutable h
= *aptr
++;
5638 immutable radTopLeft
= *aptr
++;
5639 immutable radTopRight
= *aptr
++;
5640 immutable radBottomRight
= *aptr
++;
5641 immutable radBottomLeft
= *aptr
++;
5642 if (radTopLeft
< 0.1f && radTopRight
< 0.1f && radBottomRight
< 0.1f && radBottomLeft
< 0.1f) {
5643 ctx
.rect(x
, y
, w
, h
);
5645 immutable float halfw
= nvg__absf(w
)*0.5f;
5646 immutable float halfh
= nvg__absf(h
)*0.5f;
5647 immutable float rxBL
= nvg__min(radBottomLeft
, halfw
)*nvg__sign(w
), ryBL
= nvg__min(radBottomLeft
, halfh
)*nvg__sign(h
);
5648 immutable float rxBR
= nvg__min(radBottomRight
, halfw
)*nvg__sign(w
), ryBR
= nvg__min(radBottomRight
, halfh
)*nvg__sign(h
);
5649 immutable float rxTR
= nvg__min(radTopRight
, halfw
)*nvg__sign(w
), ryTR
= nvg__min(radTopRight
, halfh
)*nvg__sign(h
);
5650 immutable float rxTL
= nvg__min(radTopLeft
, halfw
)*nvg__sign(w
), ryTL
= nvg__min(radTopLeft
, halfh
)*nvg__sign(h
);
5651 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5652 Command
.MoveTo
, x
, y
+ryTL
,
5653 Command
.LineTo
, x
, y
+h
-ryBL
,
5654 Command
.BezierTo
, x
, y
+h
-ryBL
*(1-NVG_KAPPA90
), x
+rxBL
*(1-NVG_KAPPA90
), y
+h
, x
+rxBL
, y
+h
,
5655 Command
.LineTo
, x
+w
-rxBR
, y
+h
,
5656 Command
.BezierTo
, x
+w
-rxBR
*(1-NVG_KAPPA90
), y
+h
, x
+w
, y
+h
-ryBR
*(1-NVG_KAPPA90
), x
+w
, y
+h
-ryBR
,
5657 Command
.LineTo
, x
+w
, y
+ryTR
,
5658 Command
.BezierTo
, x
+w
, y
+ryTR
*(1-NVG_KAPPA90
), x
+w
-rxTR
*(1-NVG_KAPPA90
), y
, x
+w
-rxTR
, y
,
5659 Command
.LineTo
, x
+rxTL
, y
,
5660 Command
.BezierTo
, x
+rxTL
*(1-NVG_KAPPA90
), y
, x
, y
+ryTL
*(1-NVG_KAPPA90
), x
, y
+ryTL
,
5667 /// Creates new ellipse shaped sub-path.
5669 public void ellipse (NVGContext ctx
, in float cx
, in float cy
, in float rx
, in float ry
) nothrow @trusted @nogc {
5670 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5671 Command
.MoveTo
, cx
-rx
, cy
,
5672 Command
.BezierTo
, cx
-rx
, cy
+ry
*NVG_KAPPA90
, cx
-rx
*NVG_KAPPA90
, cy
+ry
, cx
, cy
+ry
,
5673 Command
.BezierTo
, cx
+rx
*NVG_KAPPA90
, cy
+ry
, cx
+rx
, cy
+ry
*NVG_KAPPA90
, cx
+rx
, cy
,
5674 Command
.BezierTo
, cx
+rx
, cy
-ry
*NVG_KAPPA90
, cx
+rx
*NVG_KAPPA90
, cy
-ry
, cx
, cy
-ry
,
5675 Command
.BezierTo
, cx
-rx
*NVG_KAPPA90
, cy
-ry
, cx
-rx
, cy
-ry
*NVG_KAPPA90
, cx
-rx
, cy
,
5680 /// Creates new ellipse shaped sub-path.
5681 /// Arguments: [cx, cy, rx, ry]*
5683 public void ellipse (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5685 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [ellipse] call");
5686 if (args
.length
< ArgC
) return;
5687 const(float)* aptr
= args
.ptr
;
5688 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5689 immutable cx
= *aptr
++;
5690 immutable cy
= *aptr
++;
5691 immutable rx
= *aptr
++;
5692 immutable ry
= *aptr
++;
5693 nvg__appendCommands
!false(ctx
, Command
.MoveTo
, // ignore command
5694 Command
.MoveTo
, cx
-rx
, cy
,
5695 Command
.BezierTo
, cx
-rx
, cy
+ry
*NVG_KAPPA90
, cx
-rx
*NVG_KAPPA90
, cy
+ry
, cx
, cy
+ry
,
5696 Command
.BezierTo
, cx
+rx
*NVG_KAPPA90
, cy
+ry
, cx
+rx
, cy
+ry
*NVG_KAPPA90
, cx
+rx
, cy
,
5697 Command
.BezierTo
, cx
+rx
, cy
-ry
*NVG_KAPPA90
, cx
+rx
*NVG_KAPPA90
, cy
-ry
, cx
, cy
-ry
,
5698 Command
.BezierTo
, cx
-rx
*NVG_KAPPA90
, cy
-ry
, cx
-rx
, cy
-ry
*NVG_KAPPA90
, cx
-rx
, cy
,
5704 /// Creates new circle shaped sub-path.
5706 public void circle (NVGContext ctx
, in float cx
, in float cy
, in float r
) nothrow @trusted @nogc {
5707 ctx
.ellipse(cx
, cy
, r
, r
);
5710 /// Creates new circle shaped sub-path.
5711 /// Arguments: [cx, cy, r]*
5713 public void circle (NVGContext ctx
, in float[] args
) nothrow @trusted @nogc {
5715 if (args
.length
%ArgC
!= 0) assert(0, "NanoVega: invalid [circle] call");
5716 if (args
.length
< ArgC
) return;
5717 const(float)* aptr
= args
.ptr
;
5718 foreach (immutable idx
; 0..args
.length
/ArgC
) {
5719 immutable cx
= *aptr
++;
5720 immutable cy
= *aptr
++;
5721 immutable r
= *aptr
++;
5722 ctx
.ellipse(cx
, cy
, r
, r
);
5726 // Debug function to dump cached path data.
5727 debug public void debugDumpPathCache (NVGContext ctx
) nothrow @trusted @nogc {
5728 import core
.stdc
.stdio
: printf
;
5729 const(NVGpath
)* path
;
5730 printf("Dumping %d cached paths\n", ctx
.cache
.npaths
);
5731 for (int i
= 0; i
< ctx
.cache
.npaths
; ++i
) {
5732 path
= &ctx
.cache
.paths
[i
];
5733 printf("-Path %d\n", i
);
5735 printf("-fill: %d\n", path
.nfill
);
5736 for (int j
= 0; j
< path
.nfill
; ++j
) printf("%f\t%f\n", path
.fill
[j
].x
, path
.fill
[j
].y
);
5739 printf("-stroke: %d\n", path
.nstroke
);
5740 for (int j
= 0; j
< path
.nstroke
; ++j
) printf("%f\t%f\n", path
.stroke
[j
].x
, path
.stroke
[j
].y
);
5745 // Flatten path, prepare it for fill operation.
5746 void nvg__prepareFill (NVGContext ctx
) nothrow @trusted @nogc {
5747 NVGpathCache
* cache
= ctx
.cache
;
5748 NVGstate
* state
= nvg__getState(ctx
);
5750 nvg__flattenPaths
!false(ctx
);
5752 if (ctx
.params
.edgeAntiAlias
&& state
.shapeAntiAlias
) {
5753 nvg__expandFill(ctx
, ctx
.fringeWidth
, NVGLineCap
.Miter
, 2.4f);
5755 nvg__expandFill(ctx
, 0.0f, NVGLineCap
.Miter
, 2.4f);
5758 cache
.evenOddMode
= state
.evenOddMode
;
5759 cache
.fringeWidth
= ctx
.fringeWidth
;
5760 cache
.fillReady
= true;
5761 cache
.strokeReady
= false;
5762 cache
.clipmode
= NVGClipMode
.None
;
5765 // Flatten path, prepare it for stroke operation.
5766 void nvg__prepareStroke (NVGContext ctx
) nothrow @trusted @nogc {
5767 NVGstate
* state
= nvg__getState(ctx
);
5768 NVGpathCache
* cache
= ctx
.cache
;
5770 nvg__flattenPaths
!true(ctx
);
5772 immutable float scale
= nvg__getAverageScale(state
.xform
);
5773 float strokeWidth
= nvg__clamp(state
.strokeWidth
*scale
, 0.0f, 200.0f);
5775 if (strokeWidth
< ctx
.fringeWidth
) {
5776 // If the stroke width is less than pixel size, use alpha to emulate coverage.
5777 // Since coverage is area, scale by alpha*alpha.
5778 immutable float alpha
= nvg__clamp(strokeWidth
/ctx
.fringeWidth
, 0.0f, 1.0f);
5779 cache
.strokeAlphaMul
= alpha
*alpha
;
5780 strokeWidth
= ctx
.fringeWidth
;
5782 cache
.strokeAlphaMul
= 1.0f;
5784 cache
.strokeWidth
= strokeWidth
;
5786 if (ctx
.params
.edgeAntiAlias
&& state
.shapeAntiAlias
) {
5787 nvg__expandStroke(ctx
, strokeWidth
*0.5f+ctx
.fringeWidth
*0.5f, state
.lineCap
, state
.lineJoin
, state
.miterLimit
);
5789 nvg__expandStroke(ctx
, strokeWidth
*0.5f, state
.lineCap
, state
.lineJoin
, state
.miterLimit
);
5792 cache
.fringeWidth
= ctx
.fringeWidth
;
5793 cache
.fillReady
= false;
5794 cache
.strokeReady
= true;
5795 cache
.clipmode
= NVGClipMode
.None
;
5798 /// Fills the current path with current fill style.
5801 public void fill (NVGContext ctx
) nothrow @trusted @nogc {
5802 NVGstate
* state
= nvg__getState(ctx
);
5804 if (ctx
.pathPickId
>= 0 && (ctx
.pathPickRegistered
&(NVGPickKind
.Fill|
(NVGPickKind
.Fill
<<16))) == NVGPickKind
.Fill
) {
5805 ctx
.pathPickRegistered |
= NVGPickKind
.Fill
<<16;
5806 ctx
.currFillHitId
= ctx
.pathPickId
;
5809 nvg__prepareFill(ctx
);
5811 // apply global alpha
5812 NVGPaint fillPaint
= state
.fill
;
5813 fillPaint
.innerColor
.a
*= state
.alpha
;
5814 fillPaint
.middleColor
.a
*= state
.alpha
;
5815 fillPaint
.outerColor
.a
*= state
.alpha
;
5817 ctx
.appendCurrentPathToCache(ctx
.recset
, state
.fill
);
5819 if (ctx
.recblockdraw
) return;
5821 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
);
5824 foreach (int i
; 0..ctx
.cache
.npaths
) {
5825 NVGpath
* path
= &ctx
.cache
.paths
[i
];
5826 ctx
.fillTriCount
+= path
.nfill
-2;
5827 ctx
.fillTriCount
+= path
.nstroke
-2;
5828 ctx
.drawCallCount
+= 2;
5832 /// Fills the current path with current stroke style.
5835 public void stroke (NVGContext ctx
) nothrow @trusted @nogc {
5836 NVGstate
* state
= nvg__getState(ctx
);
5838 if (ctx
.pathPickId
>= 0 && (ctx
.pathPickRegistered
&(NVGPickKind
.Stroke|
(NVGPickKind
.Stroke
<<16))) == NVGPickKind
.Stroke
) {
5839 ctx
.pathPickRegistered |
= NVGPickKind
.Stroke
<<16;
5840 ctx
.currStrokeHitId
= ctx
.pathPickId
;
5843 nvg__prepareStroke(ctx
);
5845 NVGpathCache
* cache
= ctx
.cache
;
5847 NVGPaint strokePaint
= state
.stroke
;
5848 strokePaint
.innerColor
.a
*= cache
.strokeAlphaMul
;
5849 strokePaint
.middleColor
.a
*= cache
.strokeAlphaMul
;
5850 strokePaint
.outerColor
.a
*= cache
.strokeAlphaMul
;
5852 // apply global alpha
5853 strokePaint
.innerColor
.a
*= state
.alpha
;
5854 strokePaint
.middleColor
.a
*= state
.alpha
;
5855 strokePaint
.outerColor
.a
*= state
.alpha
;
5857 ctx
.appendCurrentPathToCache(ctx
.recset
, state
.stroke
);
5859 if (ctx
.recblockdraw
) return;
5861 ctx
.params
.renderStroke(ctx
.params
.userPtr
, state
.compositeOperation
, NVGClipMode
.None
, &strokePaint
, &state
.scissor
, ctx
.fringeWidth
, cache
.strokeWidth
, ctx
.cache
.paths
, ctx
.cache
.npaths
);
5864 foreach (int i
; 0..ctx
.cache
.npaths
) {
5865 NVGpath
* path
= &ctx
.cache
.paths
[i
];
5866 ctx
.strokeTriCount
+= path
.nstroke
-2;
5867 ++ctx
.drawCallCount
;
5871 /// Sets current path as clipping region.
5873 public void clip (NVGContext ctx
, NVGClipMode aclipmode
=NVGClipMode
.Union
) nothrow @trusted @nogc {
5874 NVGstate
* state
= nvg__getState(ctx
);
5876 if (aclipmode
== NVGClipMode
.None
) return;
5877 if (ctx
.recblockdraw
) return; //???
5879 if (aclipmode
== NVGClipMode
.Replace
) ctx
.params
.renderResetClip(ctx
.params
.userPtr
);
5882 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Fill|(NVGPickKind.Fill<<16))) == NVGPickKind.Fill) {
5883 ctx.pathPickRegistered |= NVGPickKind.Fill<<16;
5884 ctx.currFillHitId = ctx.pathPickId;
5888 nvg__prepareFill(ctx
);
5890 // apply global alpha
5891 NVGPaint fillPaint
= state
.fill
;
5892 fillPaint
.innerColor
.a
*= state
.alpha
;
5893 fillPaint
.middleColor
.a
*= state
.alpha
;
5894 fillPaint
.outerColor
.a
*= state
.alpha
;
5896 //ctx.appendCurrentPathToCache(ctx.recset, state.fill);
5898 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
);
5901 foreach (int i
; 0..ctx
.cache
.npaths
) {
5902 NVGpath
* path
= &ctx
.cache
.paths
[i
];
5903 ctx
.fillTriCount
+= path
.nfill
-2;
5904 ctx
.fillTriCount
+= path
.nstroke
-2;
5905 ctx
.drawCallCount
+= 2;
5909 /// Sets current path as clipping region.
5911 public alias clipFill
= clip
;
5913 /// Sets current path' stroke as clipping region.
5915 public void clipStroke (NVGContext ctx
, NVGClipMode aclipmode
=NVGClipMode
.Union
) nothrow @trusted @nogc {
5916 NVGstate
* state
= nvg__getState(ctx
);
5918 if (aclipmode
== NVGClipMode
.None
) return;
5919 if (ctx
.recblockdraw
) return; //???
5921 if (aclipmode
== NVGClipMode
.Replace
) ctx
.params
.renderResetClip(ctx
.params
.userPtr
);
5924 if (ctx.pathPickId >= 0 && (ctx.pathPickRegistered&(NVGPickKind.Stroke|(NVGPickKind.Stroke<<16))) == NVGPickKind.Stroke) {
5925 ctx.pathPickRegistered |= NVGPickKind.Stroke<<16;
5926 ctx.currStrokeHitId = ctx.pathPickId;
5930 nvg__prepareStroke(ctx
);
5932 NVGpathCache
* cache
= ctx
.cache
;
5934 NVGPaint strokePaint
= state
.stroke
;
5935 strokePaint
.innerColor
.a
*= cache
.strokeAlphaMul
;
5936 strokePaint
.middleColor
.a
*= cache
.strokeAlphaMul
;
5937 strokePaint
.outerColor
.a
*= cache
.strokeAlphaMul
;
5939 // apply global alpha
5940 strokePaint
.innerColor
.a
*= state
.alpha
;
5941 strokePaint
.middleColor
.a
*= state
.alpha
;
5942 strokePaint
.outerColor
.a
*= state
.alpha
;
5944 //ctx.appendCurrentPathToCache(ctx.recset, state.stroke);
5946 ctx
.params
.renderStroke(ctx
.params
.userPtr
, state
.compositeOperation
, aclipmode
, &strokePaint
, &state
.scissor
, ctx
.fringeWidth
, cache
.strokeWidth
, ctx
.cache
.paths
, ctx
.cache
.npaths
);
5949 foreach (int i
; 0..ctx
.cache
.npaths
) {
5950 NVGpath
* path
= &ctx
.cache
.paths
[i
];
5951 ctx
.strokeTriCount
+= path
.nstroke
-2;
5952 ++ctx
.drawCallCount
;
5957 // ////////////////////////////////////////////////////////////////////////// //
5960 // most of the code is by Michael Wynne <mike@mikesspace.net>
5961 // https://github.com/memononen/nanovg/pull/230
5962 // https://github.com/MikeWW/nanovg
5964 /// Pick type query. Used in [hitTest] and [hitTestAll].
5965 /// Group: picking_api
5966 public enum NVGPickKind
: ubyte {
5972 /// Marks the fill of the current path as pickable with the specified id.
5973 /// Note that you can create and mark path without rasterizing it.
5974 /// Group: picking_api
5975 public void currFillHitId (NVGContext ctx
, int id
) nothrow @trusted @nogc {
5976 NVGpickScene
* ps
= nvg__pickSceneGet(ctx
);
5977 NVGpickPath
* pp
= nvg__pickPathCreate(ctx
, ctx
.commands
[0..ctx
.ncommands
], id
, /*forStroke:*/false);
5978 nvg__pickSceneInsert(ps
, pp
);
5981 public alias currFillPickId
= currFillHitId
; /// Ditto.
5983 /// Marks the stroke of the current path as pickable with the specified id.
5984 /// Note that you can create and mark path without rasterizing it.
5985 /// Group: picking_api
5986 public void currStrokeHitId (NVGContext ctx
, int id
) nothrow @trusted @nogc {
5987 NVGpickScene
* ps
= nvg__pickSceneGet(ctx
);
5988 NVGpickPath
* pp
= nvg__pickPathCreate(ctx
, ctx
.commands
[0..ctx
.ncommands
], id
, /*forStroke:*/true);
5989 nvg__pickSceneInsert(ps
, pp
);
5992 public alias currStrokePickId
= currStrokeHitId
; /// Ditto.
5994 // Marks the saved path set (fill) as pickable with the specified id.
5995 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
5996 // Group: picking_api
5998 public void pathSetFillHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
5999 if (svp is null) return;
6000 if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
6001 foreach (ref cp; svp.caches[0..svp.ncaches]) {
6002 NVGpickScene* ps = nvg__pickSceneGet(ctx);
6003 NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/false);
6004 nvg__pickSceneInsert(ps, pp);
6009 // Marks the saved path set (stroke) as pickable with the specified id.
6010 // $(WARNING this doesn't work right yet (it is using current context transformation and other settings instead of record settings)!)
6011 // Group: picking_api
6013 public void pathSetStrokeHitId (NVGContext ctx, NVGPathSet svp, int id) nothrow @trusted @nogc {
6014 if (svp is null) return;
6015 if (svp.svctx !is ctx) assert(0, "NanoVega: cannot register path set from different context");
6016 foreach (ref cp; svp.caches[0..svp.ncaches]) {
6017 NVGpickScene* ps = nvg__pickSceneGet(ctx);
6018 NVGpickPath* pp = nvg__pickPathCreate(ctx, cp.commands[0..cp.ncommands], id, /*forStroke:*/true);
6019 nvg__pickSceneInsert(ps, pp);
6024 private template IsGoodHitTestDG(DG
) {
6025 enum IsGoodHitTestDG
=
6026 __traits(compiles
, (){ DG dg
; bool res
= dg(cast(int)42, cast(int)666); }) ||
6027 __traits(compiles
, (){ DG dg
; dg(cast(int)42, cast(int)666); });
6030 private template IsGoodHitTestInternalDG(DG
) {
6031 enum IsGoodHitTestInternalDG
=
6032 __traits(compiles
, (){ DG dg
; NVGpickPath
* pp
; bool res
= dg(pp
); }) ||
6033 __traits(compiles
, (){ DG dg
; NVGpickPath
* pp
; dg(pp
); });
6036 /// Call delegate [dg] for each path under the specified position (in no particular order).
6037 /// Returns the id of the path for which delegate [dg] returned true or [NVGNoPick].
6038 /// dg is: `bool delegate (int id, int order)` -- [order] is path ordering (ascending).
6039 /// Group: picking_api
6040 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
) {
6041 if (ctx
.pickScene
is null || ctx
.pickScene
.npaths
== 0 ||
(kind
&NVGPickKind
.All
) == 0) return -1;
6043 NVGpickScene
* ps
= ctx
.pickScene
;
6044 int levelwidth
= 1<<(ps
.nlevels
-1);
6045 int cellx
= nvg__clamp(cast(int)(x
/ps
.xdim
), 0, levelwidth
);
6046 int celly
= nvg__clamp(cast(int)(y
/ps
.ydim
), 0, levelwidth
);
6049 // if we are interested only in most-toplevel path, there is no reason to check paths with worser order.
6050 // but we cannot just get out on the first path found, 'cause we are using quad tree to speed up bounds
6051 // checking, so path walking order is not guaranteed.
6052 static if (bestOrder
) {
6053 int lastBestOrder
= int.min
;
6056 //{ import core.stdc.stdio; printf("npaths=%d\n", ps.npaths); }
6057 for (int lvl
= ps
.nlevels
-1; lvl
>= 0; --lvl
) {
6058 for (NVGpickPath
* pp
= ps
.levels
[lvl
][celly
*levelwidth
+cellx
]; pp
!is null; pp
= pp
.next
) {
6059 //{ 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); }
6060 static if (bestOrder
) {
6061 // reject earlier paths
6062 if (pp
.order
<= lastBestOrder
) continue; // not interesting
6064 immutable uint kpx
= kind
&pp
.flags
&3;
6065 if (kpx
== 0) continue; // not interesting
6066 if (!nvg__pickPathTestBounds(ctx
, ps
, pp
, x
, y
)) continue; // not interesting
6067 //{ import core.stdc.stdio; printf("in bounds!\n"); }
6069 if (kpx
&NVGPickKind
.Stroke
) hit
= nvg__pickPathStroke(ps
, pp
, x
, y
);
6070 if (!hit
&& (kpx
&NVGPickKind
.Fill
)) hit
= nvg__pickPath(ps
, pp
, x
, y
);
6072 //{ import core.stdc.stdio; printf(" HIT!\n"); }
6073 static if (bestOrder
) lastBestOrder
= pp
.order
;
6074 static if (IsGoodHitTestDG
!DG
) {
6075 static if (__traits(compiles
, (){ DG dg
; bool res
= dg(cast(int)42, cast(int)666); })) {
6076 if (dg(pp
.id
, cast(int)pp
.order
)) return pp
.id
;
6078 dg(pp
.id
, cast(int)pp
.order
);
6081 static if (__traits(compiles
, (){ DG dg
; NVGpickPath
* pp
; bool res
= dg(pp
); })) {
6082 if (dg(pp
)) return pp
.id
;
6096 /// Fills ids with a list of the top most hit ids (from bottom to top) under the specified position.
6097 /// Returns the slice of [ids].
6098 /// Group: picking_api
6099 public int[] hitTestAll (NVGContext ctx
, in float x
, in float y
, NVGPickKind kind
, int[] ids
) nothrow @trusted @nogc {
6100 if (ctx
.pickScene
is null || ids
.length
== 0) return ids
[0..0];
6103 NVGpickScene
* ps
= ctx
.pickScene
;
6105 ctx
.hitTestDG
!false(x
, y
, kind
, delegate (NVGpickPath
* pp
) nothrow @trusted @nogc {
6106 if (npicked
== ps
.cpicked
) {
6107 int cpicked
= ps
.cpicked
+ps
.cpicked
;
6108 NVGpickPath
** picked
= cast(NVGpickPath
**)realloc(ps
.picked
, (NVGpickPath
*).sizeof
*ps
.cpicked
);
6109 if (picked
is null) return true; // abort
6110 ps
.cpicked
= cpicked
;
6113 ps
.picked
[npicked
] = pp
;
6115 return false; // go on
6118 qsort(ps
.picked
, npicked
, (NVGpickPath
*).sizeof
, &nvg__comparePaths
);
6120 assert(npicked
>= 0);
6121 if (npicked
> ids
.length
) npicked
= cast(int)ids
.length
;
6122 foreach (immutable nidx
, ref int did
; ids
[0..npicked
]) did
= ps
.picked
[nidx
].id
;
6124 return ids
[0..npicked
];
6127 /// Returns the id of the pickable shape containing x,y or [NVGNoPick] if no shape was found.
6128 /// Group: picking_api
6129 public int hitTest (NVGContext ctx
, in float x
, in float y
, NVGPickKind kind
=NVGPickKind
.All
) nothrow @trusted @nogc {
6130 if (ctx
.pickScene
is null) return -1;
6132 int bestOrder
= int.min
;
6135 ctx
.hitTestDG
!true(x
, y
, kind
, delegate (NVGpickPath
* pp
) {
6136 if (pp
.order
> bestOrder
) {
6137 bestOrder
= pp
.order
;
6145 /// Returns `true` if the path with the given id contains x,y.
6146 /// Group: picking_api
6147 public bool hitTestForId (NVGContext ctx
, in int id
, in float x
, in float y
, NVGPickKind kind
=NVGPickKind
.All
) nothrow @trusted @nogc {
6148 if (ctx
.pickScene
is null || id
== NVGNoPick
) return false;
6152 ctx
.hitTestDG
!false(x
, y
, kind
, delegate (NVGpickPath
* pp
) {
6155 return true; // stop
6157 return false; // continue
6163 /// Returns `true` if the given point is within the fill of the currently defined path.
6164 /// This operation can be done before rasterizing the current path.
6165 /// Group: picking_api
6166 public bool hitTestCurrFill (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
6167 NVGpickScene
* ps
= nvg__pickSceneGet(ctx
);
6168 int oldnpoints
= ps
.npoints
;
6169 int oldnsegments
= ps
.nsegments
;
6170 NVGpickPath
* pp
= nvg__pickPathCreate(ctx
, ctx
.commands
[0..ctx
.ncommands
], 1, /*forStroke:*/false);
6171 if (pp
is null) return false; // oops
6173 nvg__freePickPath(ps
, pp
);
6174 ps
.npoints
= oldnpoints
;
6175 ps
.nsegments
= oldnsegments
;
6177 return (nvg__pointInBounds(x
, y
, pp
.bounds
) ?
nvg__pickPath(ps
, pp
, x
, y
) : false);
6180 public alias isPointInPath
= hitTestCurrFill
; /// Ditto.
6182 /// Returns `true` if the given point is within the stroke of the currently defined path.
6183 /// This operation can be done before rasterizing the current path.
6184 /// Group: picking_api
6185 public bool hitTestCurrStroke (NVGContext ctx
, in float x
, in float y
) nothrow @trusted @nogc {
6186 NVGpickScene
* ps
= nvg__pickSceneGet(ctx
);
6187 int oldnpoints
= ps
.npoints
;
6188 int oldnsegments
= ps
.nsegments
;
6189 NVGpickPath
* pp
= nvg__pickPathCreate(ctx
, ctx
.commands
[0..ctx
.ncommands
], 1, /*forStroke:*/true);
6190 if (pp
is null) return false; // oops
6192 nvg__freePickPath(ps
, pp
);
6193 ps
.npoints
= oldnpoints
;
6194 ps
.nsegments
= oldnsegments
;
6196 return (nvg__pointInBounds(x
, y
, pp
.bounds
) ?
nvg__pickPathStroke(ps
, pp
, x
, y
) : false);
6200 nothrow @trusted @nogc {
6202 private alias _compare_fp_t
= int function (const void*, const void*) nothrow @nogc;
6203 private extern(C
) void qsort (scope void* base
, size_t nmemb
, size_t size
, _compare_fp_t compar
) nothrow @nogc;
6205 extern(C
) int nvg__comparePaths (const void* a
, const void* b
) {
6206 return (*cast(const(NVGpickPath
)**)b
).order
-(*cast(const(NVGpickPath
)**)a
).order
;
6210 enum NVGPickEPS
= 0.0001f;
6213 enum NVGSegmentFlags
{
6222 enum NVGPathFlags
: ushort {
6223 Fill
= NVGPickKind
.Fill
,
6224 Stroke
= NVGPickKind
.Stroke
,
6229 int firstPoint
; // Index into NVGpickScene.points
6230 short type
; // NVG_LINETO or NVG_BEZIERTO
6231 short flags
; // Flags relate to the corner between the prev segment and this one.
6233 float[2] startDir
; // Direction at t == 0
6234 float[2] endDir
; // Direction at t == 1
6235 float[2] miterDir
; // Direction of miter of corner between the prev segment and this one.
6238 struct NVGpickSubPath
{
6239 short winding
; // TODO: Merge to flag field
6240 bool closed
; // TODO: Merge to flag field
6242 int firstSegment
; // Index into NVGpickScene.segments
6247 NVGpickSubPath
* next
;
6250 struct NVGpickPath
{
6261 int scissor
; // Indexes into ps->points and defines scissor rect as XVec, YVec and Center
6263 NVGpickSubPath
* subPaths
;
6265 NVGpickPath
* cellnext
;
6268 struct NVGpickScene
{
6271 NVGpickPath
* paths
; // Linked list of paths
6272 NVGpickPath
* lastPath
; // The last path in the paths linked list (the first path added)
6273 NVGpickPath
* freePaths
; // Linked list of free paths
6275 NVGpickSubPath
* freeSubPaths
; // Linked list of free sub paths
6280 // Points for all path sub paths.
6285 // Segments for all path sub paths
6286 NVGsegment
* segments
;
6290 // Implicit quadtree
6291 float xdim
; // Width / (1 << nlevels)
6292 float ydim
; // Height / (1 << nlevels)
6293 int ncells
; // Total number of cells in all levels
6295 NVGpickPath
*** levels
; // Index: [Level][LevelY * LevelW + LevelX] Value: Linked list of paths
6297 // Temp storage for picking
6299 NVGpickPath
** picked
;
6304 void nvg__initBounds (ref float[4] bounds
) {
6305 bounds
.ptr
[0] = bounds
.ptr
[1] = float.max
;
6306 bounds
.ptr
[2] = bounds
.ptr
[3] = -float.max
;
6309 void nvg__expandBounds (ref float[4] bounds
, const(float)* points
, int npoints
) {
6311 for (int i
= 0; i
< npoints
; i
+= 2) {
6312 bounds
.ptr
[0] = nvg__min(bounds
.ptr
[0], points
[i
]);
6313 bounds
.ptr
[1] = nvg__min(bounds
.ptr
[1], points
[i
+1]);
6314 bounds
.ptr
[2] = nvg__max(bounds
.ptr
[2], points
[i
]);
6315 bounds
.ptr
[3] = nvg__max(bounds
.ptr
[3], points
[i
+1]);
6319 void nvg__unionBounds (ref float[4] bounds
, in ref float[4] boundsB
) {
6320 bounds
.ptr
[0] = nvg__min(bounds
.ptr
[0], boundsB
.ptr
[0]);
6321 bounds
.ptr
[1] = nvg__min(bounds
.ptr
[1], boundsB
.ptr
[1]);
6322 bounds
.ptr
[2] = nvg__max(bounds
.ptr
[2], boundsB
.ptr
[2]);
6323 bounds
.ptr
[3] = nvg__max(bounds
.ptr
[3], boundsB
.ptr
[3]);
6326 void nvg__intersectBounds (ref float[4] bounds
, in ref float[4] boundsB
) {
6327 bounds
.ptr
[0] = nvg__max(boundsB
.ptr
[0], bounds
.ptr
[0]);
6328 bounds
.ptr
[1] = nvg__max(boundsB
.ptr
[1], bounds
.ptr
[1]);
6329 bounds
.ptr
[2] = nvg__min(boundsB
.ptr
[2], bounds
.ptr
[2]);
6330 bounds
.ptr
[3] = nvg__min(boundsB
.ptr
[3], bounds
.ptr
[3]);
6332 bounds
.ptr
[2] = nvg__max(bounds
.ptr
[0], bounds
.ptr
[2]);
6333 bounds
.ptr
[3] = nvg__max(bounds
.ptr
[1], bounds
.ptr
[3]);
6336 bool nvg__pointInBounds (in float x
, in float y
, in ref float[4] bounds
) {
6337 pragma(inline
, true);
6338 return (x
>= bounds
.ptr
[0] && x
<= bounds
.ptr
[2] && y
>= bounds
.ptr
[1] && y
<= bounds
.ptr
[3]);
6341 // building paths & sub paths
6342 int nvg__pickSceneAddPoints (NVGpickScene
* ps
, const(float)* xy
, int n
) {
6343 import core
.stdc
.string
: memcpy
;
6344 if (ps
.npoints
+n
> ps
.cpoints
) {
6345 import core
.stdc
.stdlib
: realloc
;
6346 int cpoints
= ps
.npoints
+n
+(ps
.cpoints
<<1);
6347 float* points
= cast(float*)realloc(ps
.points
, float.sizeof
*2*cpoints
);
6348 if (points
is null) assert(0, "NanoVega: out of memory");
6350 ps
.cpoints
= cpoints
;
6353 if (xy
!is null) memcpy(&ps
.points
[i
*2], xy
, float.sizeof
*2*n
);
6358 void nvg__pickSubPathAddSegment (NVGpickScene
* ps
, NVGpickSubPath
* psp
, int firstPoint
, int type
, short flags
) {
6359 NVGsegment
* seg
= null;
6360 if (ps
.nsegments
== ps
.csegments
) {
6361 int csegments
= 1+ps
.csegments
+(ps
.csegments
<<1);
6362 NVGsegment
* segments
= cast(NVGsegment
*)realloc(ps
.segments
, NVGsegment
.sizeof
*csegments
);
6363 if (segments
is null) assert(0, "NanoVega: out of memory");
6364 ps
.segments
= segments
;
6365 ps
.csegments
= csegments
;
6368 if (psp
.firstSegment
== -1) psp
.firstSegment
= ps
.nsegments
;
6370 seg
= &ps
.segments
[ps
.nsegments
];
6372 seg
.firstPoint
= firstPoint
;
6373 seg
.type
= cast(short)type
;
6377 nvg__segmentDir(ps
, psp
, seg
, 0, seg
.startDir
);
6378 nvg__segmentDir(ps
, psp
, seg
, 1, seg
.endDir
);
6381 void nvg__segmentDir (NVGpickScene
* ps
, NVGpickSubPath
* psp
, NVGsegment
* seg
, float t
, ref float[2] d
) {
6382 const(float)* points
= &ps
.points
[seg
.firstPoint
*2];
6383 immutable float x0
= points
[0*2+0], x1
= points
[1*2+0];
6384 immutable float y0
= points
[0*2+1], y1
= points
[1*2+1];
6386 case Command
.LineTo
:
6389 nvg__normalize(&d
.ptr
[0], &d
.ptr
[1]);
6391 case Command
.BezierTo
:
6392 immutable float x2
= points
[2*2+0];
6393 immutable float y2
= points
[2*2+1];
6394 immutable float x3
= points
[3*2+0];
6395 immutable float y3
= points
[3*2+1];
6397 immutable float omt
= 1.0f-t
;
6398 immutable float omt2
= omt
*omt
;
6399 immutable float t2
= t
*t
;
6411 nvg__normalize(&d
.ptr
[0], &d
.ptr
[1]);
6418 void nvg__pickSubPathAddFillSupports (NVGpickScene
* ps
, NVGpickSubPath
* psp
) {
6419 if (psp
.firstSegment
== -1) return;
6420 NVGsegment
* segments
= &ps
.segments
[psp
.firstSegment
];
6421 for (int s
= 0; s
< psp
.nsegments
; ++s
) {
6422 NVGsegment
* seg
= &segments
[s
];
6423 const(float)* points
= &ps
.points
[seg
.firstPoint
*2];
6424 if (seg
.type
== Command
.LineTo
) {
6425 nvg__initBounds(seg
.bounds
);
6426 nvg__expandBounds(seg
.bounds
, points
, 2);
6428 nvg__bezierBounds(points
, seg
.bounds
);
6433 void nvg__pickSubPathAddStrokeSupports (NVGpickScene
* ps
, NVGpickSubPath
* psp
, float strokeWidth
, int lineCap
, int lineJoin
, float miterLimit
) {
6434 if (psp
.firstSegment
== -1) return;
6435 immutable bool closed
= psp
.closed
;
6436 const(float)* points
= ps
.points
;
6437 NVGsegment
* seg
= null;
6438 NVGsegment
* segments
= &ps
.segments
[psp
.firstSegment
];
6439 int nsegments
= psp
.nsegments
;
6440 NVGsegment
* prevseg
= (closed ?
&segments
[psp
.nsegments
-1] : null);
6442 int ns
= 0; // nsupports
6443 float[32] supportingPoints
= void;
6444 int firstPoint
, lastPoint
;
6447 segments
[0].flags |
= NVGSegmentFlags
.Cap
;
6448 segments
[nsegments
-1].flags |
= NVGSegmentFlags
.Endcap
;
6451 for (int s
= 0; s
< nsegments
; ++s
) {
6453 nvg__initBounds(seg
.bounds
);
6455 firstPoint
= seg
.firstPoint
*2;
6456 lastPoint
= firstPoint
+(seg
.type
== Command
.LineTo ?
2 : 6);
6460 // First two supporting points are either side of the start point
6461 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]-seg
.startDir
.ptr
[1]*strokeWidth
;
6462 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]+seg
.startDir
.ptr
[0]*strokeWidth
;
6464 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]+seg
.startDir
.ptr
[1]*strokeWidth
;
6465 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]-seg
.startDir
.ptr
[0]*strokeWidth
;
6467 // Second two supporting points are either side of the end point
6468 supportingPoints
.ptr
[ns
++] = points
[lastPoint
]-seg
.endDir
.ptr
[1]*strokeWidth
;
6469 supportingPoints
.ptr
[ns
++] = points
[lastPoint
+1]+seg
.endDir
.ptr
[0]*strokeWidth
;
6471 supportingPoints
.ptr
[ns
++] = points
[lastPoint
]+seg
.endDir
.ptr
[1]*strokeWidth
;
6472 supportingPoints
.ptr
[ns
++] = points
[lastPoint
+1]-seg
.endDir
.ptr
[0]*strokeWidth
;
6474 if ((seg
.flags
&NVGSegmentFlags
.Corner
) && prevseg
!is null) {
6475 seg
.miterDir
.ptr
[0] = 0.5f*(-prevseg
.endDir
.ptr
[1]-seg
.startDir
.ptr
[1]);
6476 seg
.miterDir
.ptr
[1] = 0.5f*(prevseg
.endDir
.ptr
[0]+seg
.startDir
.ptr
[0]);
6478 immutable float M2
= seg
.miterDir
.ptr
[0]*seg
.miterDir
.ptr
[0]+seg
.miterDir
.ptr
[1]*seg
.miterDir
.ptr
[1];
6480 if (M2
> 0.000001f) {
6481 float scale
= 1.0f/M2
;
6482 if (scale
> 600.0f) scale
= 600.0f;
6483 seg
.miterDir
.ptr
[0] *= scale
;
6484 seg
.miterDir
.ptr
[1] *= scale
;
6487 //NVG_PICK_DEBUG_VECTOR_SCALE(&points[firstPoint], seg.miterDir, 10);
6489 // Add an additional support at the corner on the other line
6490 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]-prevseg
.endDir
.ptr
[1]*strokeWidth
;
6491 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]+prevseg
.endDir
.ptr
[0]*strokeWidth
;
6493 if (lineJoin
== NVGLineCap
.Miter || lineJoin
== NVGLineCap
.Bevel
) {
6494 // Set a corner as beveled if the join type is bevel or mitered and
6495 // miterLimit is hit.
6496 if (lineJoin
== NVGLineCap
.Bevel ||
(M2
*miterLimit
*miterLimit
) < 1.0f) {
6497 seg
.flags |
= NVGSegmentFlags
.Bevel
;
6499 // Corner is mitered - add miter point as a support
6500 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]+seg
.miterDir
.ptr
[0]*strokeWidth
;
6501 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]+seg
.miterDir
.ptr
[1]*strokeWidth
;
6503 } else if (lineJoin
== NVGLineCap
.Round
) {
6504 // ... and at the midpoint of the corner arc
6505 float[2] vertexN
= [ -seg
.startDir
.ptr
[0]+prevseg
.endDir
.ptr
[0], -seg
.startDir
.ptr
[1]+prevseg
.endDir
.ptr
[1] ];
6506 nvg__normalize(&vertexN
[0], &vertexN
[1]);
6508 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]+vertexN
[0]*strokeWidth
;
6509 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]+vertexN
[1]*strokeWidth
;
6513 if (seg
.flags
&NVGSegmentFlags
.Cap
) {
6515 case NVGLineCap
.Butt
:
6516 // supports for butt already added
6518 case NVGLineCap
.Square
:
6519 // square cap supports are just the original two supports moved out along the direction
6520 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[0]-seg
.startDir
.ptr
[0]*strokeWidth
;
6521 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[1]-seg
.startDir
.ptr
[1]*strokeWidth
;
6522 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[2]-seg
.startDir
.ptr
[0]*strokeWidth
;
6523 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[3]-seg
.startDir
.ptr
[1]*strokeWidth
;
6525 case NVGLineCap
.Round
:
6526 // add one additional support for the round cap along the dir
6527 supportingPoints
.ptr
[ns
++] = points
[firstPoint
]-seg
.startDir
.ptr
[0]*strokeWidth
;
6528 supportingPoints
.ptr
[ns
++] = points
[firstPoint
+1]-seg
.startDir
.ptr
[1]*strokeWidth
;
6535 if (seg
.flags
&NVGSegmentFlags
.Endcap
) {
6536 // end supporting points, either side of line
6539 case NVGLineCap
.Butt
:
6540 // supports for butt already added
6542 case NVGLineCap
.Square
:
6543 // square cap supports are just the original two supports moved out along the direction
6544 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[end
+0]+seg
.endDir
.ptr
[0]*strokeWidth
;
6545 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[end
+1]+seg
.endDir
.ptr
[1]*strokeWidth
;
6546 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[end
+2]+seg
.endDir
.ptr
[0]*strokeWidth
;
6547 supportingPoints
.ptr
[ns
++] = supportingPoints
.ptr
[end
+3]+seg
.endDir
.ptr
[1]*strokeWidth
;
6549 case NVGLineCap
.Round
:
6550 // add one additional support for the round cap along the dir
6551 supportingPoints
.ptr
[ns
++] = points
[lastPoint
]+seg
.endDir
.ptr
[0]*strokeWidth
;
6552 supportingPoints
.ptr
[ns
++] = points
[lastPoint
+1]+seg
.endDir
.ptr
[1]*strokeWidth
;
6559 nvg__expandBounds(seg
.bounds
, supportingPoints
.ptr
, ns
/2);
6565 NVGpickPath
* nvg__pickPathCreate (NVGContext context
, const(float)[] acommands
, int id
, bool forStroke
) {
6566 NVGpickScene
* ps
= nvg__pickSceneGet(context
);
6567 if (ps
is null) return null;
6571 int ncommands
= cast(int)acommands
.length
;
6572 const(float)* commands
= acommands
.ptr
;
6574 NVGpickPath
* pp
= null;
6575 NVGpickSubPath
* psp
= null;
6576 float[2] start
= void;
6579 //bool hasHoles = false;
6580 NVGpickSubPath
* prev
= null;
6582 float[8] points
= void;
6583 float[2] inflections
= void;
6584 int ninflections
= 0;
6586 NVGstate
* state
= nvg__getState(context
);
6587 float[4] totalBounds
= void;
6588 NVGsegment
* segments
= null;
6589 const(NVGsegment
)* seg
= null;
6590 NVGpickSubPath
*curpsp
;
6592 pp
= nvg__allocPickPath(ps
);
6593 if (pp
is null) return null;
6597 bool hasPoints
= false;
6600 if (psp
is null ||
!hasPoints
) return;
6601 if (ps
.points
[(ps
.npoints
-1)*2] != start
.ptr
[0] || ps
.points
[(ps
.npoints
-1)*2+1] != start
.ptr
[1]) {
6602 firstPoint
= nvg__pickSceneAddPoints(ps
, start
.ptr
, 1);
6603 nvg__pickSubPathAddSegment(ps
, psp
, firstPoint
-1, Command
.LineTo
, NVGSegmentFlags
.Corner
);
6608 while (i
< ncommands
) {
6609 int cmd
= cast(int)commands
[i
++];
6611 case Command
.MoveTo
: // one coordinate pair
6612 const(float)* tfxy
= commands
+i
;
6615 // new starting point
6616 start
.ptr
[0..2] = tfxy
[0..2];
6618 // start a new path for each sub path to handle sub paths that intersect other sub paths
6620 psp
= nvg__allocPickSubPath(ps
);
6621 if (psp
is null) { psp
= prev
; break; }
6622 psp
.firstSegment
= -1;
6623 psp
.winding
= NVGSolidity
.Solid
;
6626 nvg__pickSceneAddPoints(ps
, tfxy
, 1);
6629 case Command
.LineTo
: // one coordinate pair
6630 const(float)* tfxy
= commands
+i
;
6632 firstPoint
= nvg__pickSceneAddPoints(ps
, tfxy
, 1);
6633 nvg__pickSubPathAddSegment(ps
, psp
, firstPoint
-1, cmd
, NVGSegmentFlags
.Corner
);
6636 case Command
.BezierTo
: // three coordinate pairs
6637 const(float)* tfxy
= commands
+i
;
6640 // Split the curve at it's dx==0 or dy==0 inflection points.
6642 // A horizontal line only ever interects the curves once.
6644 // Finding the closest point on any curve converges more reliably.
6646 // NOTE: We could just split on dy==0 here.
6648 memcpy(&points
.ptr
[0], &ps
.points
[(ps
.npoints
-1)*2], float.sizeof
*2);
6649 memcpy(&points
.ptr
[2], tfxy
, float.sizeof
*2*3);
6652 nvg__bezierInflections(points
.ptr
, 1, &ninflections
, inflections
.ptr
);
6653 nvg__bezierInflections(points
.ptr
, 0, &ninflections
, inflections
.ptr
);
6657 float[8] pointsA
= void, pointsB
= void;
6659 nvg__smallsort(inflections
.ptr
, ninflections
);
6661 for (int infl
= 0; infl
< ninflections
; ++infl
) {
6662 if (nvg__absf(inflections
.ptr
[infl
]-previnfl
) < NVGPickEPS
) continue;
6664 immutable float t
= (inflections
.ptr
[infl
]-previnfl
)*(1.0f/(1.0f-previnfl
));
6666 previnfl
= inflections
.ptr
[infl
];
6668 nvg__splitBezier(points
.ptr
, t
, pointsA
.ptr
, pointsB
.ptr
);
6670 firstPoint
= nvg__pickSceneAddPoints(ps
, &pointsA
.ptr
[2], 3);
6671 nvg__pickSubPathAddSegment(ps
, psp
, firstPoint
-1, cmd
, (infl
== 0) ? NVGSegmentFlags
.Corner
: 0);
6673 memcpy(points
.ptr
, pointsB
.ptr
, float.sizeof
*8);
6676 firstPoint
= nvg__pickSceneAddPoints(ps
, &pointsB
.ptr
[2], 3);
6677 nvg__pickSubPathAddSegment(ps
, psp
, firstPoint
-1, cmd
, 0);
6679 firstPoint
= nvg__pickSceneAddPoints(ps
, tfxy
, 3);
6680 nvg__pickSubPathAddSegment(ps
, psp
, firstPoint
-1, cmd
, NVGSegmentFlags
.Corner
);
6687 case Command
.Winding
:
6688 psp
.winding
= cast(short)cast(int)commands
[i
];
6689 //if (psp.winding == NVGSolidity.Hole) hasHoles = true;
6697 // force-close filled paths
6698 if (psp
!is null && !forStroke
&& hasPoints
&& !psp
.closed
) closeIt();
6700 pp
.flags
= (forStroke ? NVGPathFlags
.Stroke
: NVGPathFlags
.Fill
);
6702 pp
.strokeWidth
= state
.strokeWidth
*0.5f;
6703 pp
.miterLimit
= state
.miterLimit
;
6704 pp
.lineCap
= cast(short)state
.lineCap
;
6705 pp
.lineJoin
= cast(short)state
.lineJoin
;
6706 pp
.evenOddMode
= nvg__getState(context
).evenOddMode
;
6708 nvg__initBounds(totalBounds
);
6710 for (curpsp
= psp
; curpsp
; curpsp
= curpsp
.next
) {
6712 nvg__pickSubPathAddStrokeSupports(ps
, curpsp
, pp
.strokeWidth
, pp
.lineCap
, pp
.lineJoin
, pp
.miterLimit
);
6714 nvg__pickSubPathAddFillSupports(ps
, curpsp
);
6717 if (curpsp
.firstSegment
== -1) continue;
6718 segments
= &ps
.segments
[curpsp
.firstSegment
];
6719 nvg__initBounds(curpsp
.bounds
);
6720 for (int s
= 0; s
< curpsp
.nsegments
; ++s
) {
6722 //NVG_PICK_DEBUG_BOUNDS(seg.bounds);
6723 nvg__unionBounds(curpsp
.bounds
, seg
.bounds
);
6726 nvg__unionBounds(totalBounds
, curpsp
.bounds
);
6729 // Store the scissor rect if present.
6730 if (state
.scissor
.extent
.ptr
[0] != -1.0f) {
6731 // Use points storage to store the scissor data
6732 pp
.scissor
= nvg__pickSceneAddPoints(ps
, null, 4);
6733 float* scissor
= &ps
.points
[pp
.scissor
*2];
6735 //memcpy(scissor, state.scissor.xform.ptr, 6*float.sizeof);
6736 scissor
[0..6] = state
.scissor
.xform
.mat
[];
6737 memcpy(scissor
+6, state
.scissor
.extent
.ptr
, 2*float.sizeof
);
6739 pp
.flags |
= NVGPathFlags
.Scissor
;
6742 memcpy(pp
.bounds
.ptr
, totalBounds
.ptr
, float.sizeof
*4);
6748 // Struct management
6749 NVGpickPath
* nvg__allocPickPath (NVGpickScene
* ps
) {
6750 NVGpickPath
* pp
= ps
.freePaths
;
6752 ps
.freePaths
= pp
.next
;
6754 pp
= cast(NVGpickPath
*)malloc(NVGpickPath
.sizeof
);
6756 memset(pp
, 0, NVGpickPath
.sizeof
);
6760 // Put a pick path and any sub paths (back) to the free lists.
6761 void nvg__freePickPath (NVGpickScene
* ps
, NVGpickPath
* pp
) {
6762 // Add all sub paths to the sub path free list.
6763 // Finds the end of the path sub paths, links that to the current
6764 // sub path free list head and replaces the head ptr with the
6765 // head path sub path entry.
6766 NVGpickSubPath
* psp
= null;
6767 for (psp
= pp
.subPaths
; psp
!is null && psp
.next
!is null; psp
= psp
.next
) {}
6770 psp
.next
= ps
.freeSubPaths
;
6771 ps
.freeSubPaths
= pp
.subPaths
;
6775 // Add the path to the path freelist
6776 pp
.next
= ps
.freePaths
;
6778 if (pp
.next
is null) ps
.lastPath
= pp
;
6781 NVGpickSubPath
* nvg__allocPickSubPath (NVGpickScene
* ps
) {
6782 NVGpickSubPath
* psp
= ps
.freeSubPaths
;
6784 ps
.freeSubPaths
= psp
.next
;
6786 psp
= cast(NVGpickSubPath
*)malloc(NVGpickSubPath
.sizeof
);
6787 if (psp
is null) return null;
6789 memset(psp
, 0, NVGpickSubPath
.sizeof
);
6793 void nvg__returnPickSubPath (NVGpickScene
* ps
, NVGpickSubPath
* psp
) {
6794 psp
.next
= ps
.freeSubPaths
;
6795 ps
.freeSubPaths
= psp
;
6798 NVGpickScene
* nvg__allocPickScene () {
6799 NVGpickScene
* ps
= cast(NVGpickScene
*)malloc(NVGpickScene
.sizeof
);
6800 if (ps
is null) return null;
6801 memset(ps
, 0, NVGpickScene
.sizeof
);
6806 void nvg__deletePickScene (NVGpickScene
* ps
) {
6808 NVGpickSubPath
* psp
;
6810 // Add all paths (and thus sub paths) to the free list(s).
6811 while (ps
.paths
!is null) {
6813 nvg__freePickPath(ps
, ps
.paths
);
6818 while (ps
.freePaths
!is null) {
6820 ps
.freePaths
= pp
.next
;
6821 while (pp
.subPaths
!is null) {
6823 pp
.subPaths
= psp
.next
;
6829 // Delete all sub paths
6830 while (ps
.freeSubPaths
!is null) {
6831 psp
= ps
.freeSubPaths
.next
;
6832 free(ps
.freeSubPaths
);
6833 ps
.freeSubPaths
= psp
;
6839 if (ps
.levels
!is null) {
6844 if (ps
.picked
!is null) free(ps
.picked
);
6845 if (ps
.points
!is null) free(ps
.points
);
6846 if (ps
.segments
!is null) free(ps
.segments
);
6851 NVGpickScene
* nvg__pickSceneGet (NVGContext ctx
) {
6852 if (ctx
.pickScene
is null) ctx
.pickScene
= nvg__allocPickScene();
6853 return ctx
.pickScene
;
6857 // Applies Casteljau's algorithm to a cubic bezier for a given parameter t
6858 // points is 4 points (8 floats)
6859 // lvl1 is 3 points (6 floats)
6860 // lvl2 is 2 points (4 floats)
6861 // lvl3 is 1 point (2 floats)
6862 void nvg__casteljau (const(float)* points
, float t
, float* lvl1
, float* lvl2
, float* lvl3
) {
6863 enum x0
= 0*2+0; enum x1
= 1*2+0; enum x2
= 2*2+0; enum x3
= 3*2+0;
6864 enum y0
= 0*2+1; enum y1
= 1*2+1; enum y2
= 2*2+1; enum y3
= 3*2+1;
6867 lvl1
[x0
] = (points
[x1
]-points
[x0
])*t
+points
[x0
];
6868 lvl1
[y0
] = (points
[y1
]-points
[y0
])*t
+points
[y0
];
6870 lvl1
[x1
] = (points
[x2
]-points
[x1
])*t
+points
[x1
];
6871 lvl1
[y1
] = (points
[y2
]-points
[y1
])*t
+points
[y1
];
6873 lvl1
[x2
] = (points
[x3
]-points
[x2
])*t
+points
[x2
];
6874 lvl1
[y2
] = (points
[y3
]-points
[y2
])*t
+points
[y2
];
6877 lvl2
[x0
] = (lvl1
[x1
]-lvl1
[x0
])*t
+lvl1
[x0
];
6878 lvl2
[y0
] = (lvl1
[y1
]-lvl1
[y0
])*t
+lvl1
[y0
];
6880 lvl2
[x1
] = (lvl1
[x2
]-lvl1
[x1
])*t
+lvl1
[x1
];
6881 lvl2
[y1
] = (lvl1
[y2
]-lvl1
[y1
])*t
+lvl1
[y1
];
6884 lvl3
[x0
] = (lvl2
[x1
]-lvl2
[x0
])*t
+lvl2
[x0
];
6885 lvl3
[y0
] = (lvl2
[y1
]-lvl2
[y0
])*t
+lvl2
[y0
];
6888 // Calculates a point on a bezier at point t.
6889 void nvg__bezierEval (const(float)* points
, float t
, ref float[2] tpoint
) {
6890 immutable float omt
= 1-t
;
6891 immutable float omt3
= omt
*omt
*omt
;
6892 immutable float omt2
= omt
*omt
;
6893 immutable float t3
= t
*t
*t
;
6894 immutable float t2
= t
*t
;
6898 points
[2]*3.0f*omt2
*t
+
6899 points
[4]*3.0f*omt
*t2
+
6904 points
[3]*3.0f*omt2
*t
+
6905 points
[5]*3.0f*omt
*t2
+
6909 // Splits a cubic bezier curve into two parts at point t.
6910 void nvg__splitBezier (const(float)* points
, float t
, float* pointsA
, float* pointsB
) {
6911 enum x0
= 0*2+0; enum x1
= 1*2+0; enum x2
= 2*2+0; enum x3
= 3*2+0;
6912 enum y0
= 0*2+1; enum y1
= 1*2+1; enum y2
= 2*2+1; enum y3
= 3*2+1;
6914 float[6] lvl1
= void;
6915 float[4] lvl2
= void;
6916 float[2] lvl3
= void;
6918 nvg__casteljau(points
, t
, lvl1
.ptr
, lvl2
.ptr
, lvl3
.ptr
);
6921 pointsA
[x0
] = points
[x0
];
6922 pointsA
[y0
] = points
[y0
];
6924 pointsA
[x1
] = lvl1
.ptr
[x0
];
6925 pointsA
[y1
] = lvl1
.ptr
[y0
];
6927 pointsA
[x2
] = lvl2
.ptr
[x0
];
6928 pointsA
[y2
] = lvl2
.ptr
[y0
];
6930 pointsA
[x3
] = lvl3
.ptr
[x0
];
6931 pointsA
[y3
] = lvl3
.ptr
[y0
];
6934 pointsB
[x0
] = lvl3
.ptr
[x0
];
6935 pointsB
[y0
] = lvl3
.ptr
[y0
];
6937 pointsB
[x1
] = lvl2
.ptr
[x1
];
6938 pointsB
[y1
] = lvl2
.ptr
[y1
];
6940 pointsB
[x2
] = lvl1
.ptr
[x2
];
6941 pointsB
[y2
] = lvl1
.ptr
[y2
];
6943 pointsB
[x3
] = points
[x3
];
6944 pointsB
[y3
] = points
[y3
];
6947 // Calculates the inflection points in coordinate coord (X = 0, Y = 1) of a cubic bezier.
6948 // Appends any found inflection points to the array inflections and increments *ninflections.
6949 // So finds the parameters where dx/dt or dy/dt is 0
6950 void nvg__bezierInflections (const(float)* points
, int coord
, int* ninflections
, float* inflections
) {
6951 immutable float v0
= points
[0*2+coord
], v1
= points
[1*2+coord
], v2
= points
[2*2+coord
], v3
= points
[3*2+coord
];
6953 int nvalid
= *ninflections
;
6955 immutable float a
= 3.0f*( -v0
+3.0f*v1
-3.0f*v2
+v3
);
6956 immutable float b
= 6.0f*( v0
-2.0f*v1
+v2
);
6957 immutable float c
= 3.0f*( v1
-v0
);
6959 float d
= b
*b
-4.0f*a
*c
;
6960 if (nvg__absf(d
-0.0f) < NVGPickEPS
) {
6962 t
.ptr
[0] = -b
/2.0f*a
;
6963 if (t
.ptr
[0] > NVGPickEPS
&& t
.ptr
[0] < (1.0f-NVGPickEPS
)) {
6964 inflections
[nvalid
] = t
.ptr
[0];
6967 } else if (d
> NVGPickEPS
) {
6968 // zero, one or two roots
6971 t
.ptr
[0] = (-b
+d
)/(2.0f*a
);
6972 t
.ptr
[1] = (-b
-d
)/(2.0f*a
);
6974 for (int i
= 0; i
< 2; ++i
) {
6975 if (t
.ptr
[i
] > NVGPickEPS
&& t
.ptr
[i
] < (1.0f-NVGPickEPS
)) {
6976 inflections
[nvalid
] = t
.ptr
[i
];
6984 *ninflections
= nvalid
;
6987 // Sort a small number of floats in ascending order (0 < n < 6)
6988 void nvg__smallsort (float* values
, int n
) {
6989 bool bSwapped
= true;
6990 for (int j
= 0; j
< n
-1 && bSwapped
; ++j
) {
6992 for (int i
= 0; i
< n
-1; ++i
) {
6993 if (values
[i
] > values
[i
+1]) {
6994 auto tmp
= values
[i
];
6995 values
[i
] = values
[i
+1];
7002 // Calculates the bounding rect of a given cubic bezier curve.
7003 void nvg__bezierBounds (const(float)* points
, ref float[4] bounds
) {
7004 float[4] inflections
= void;
7005 int ninflections
= 0;
7006 float[2] tpoint
= void;
7008 nvg__initBounds(bounds
);
7010 // Include start and end points in bounds
7011 nvg__expandBounds(bounds
, &points
[0], 1);
7012 nvg__expandBounds(bounds
, &points
[6], 1);
7014 // Calculate dx==0 and dy==0 inflection points and add them to the bounds
7016 nvg__bezierInflections(points
, 0, &ninflections
, inflections
.ptr
);
7017 nvg__bezierInflections(points
, 1, &ninflections
, inflections
.ptr
);
7019 foreach (immutable int i
; 0..ninflections
) {
7020 nvg__bezierEval(points
, inflections
[i
], tpoint
);
7021 nvg__expandBounds(bounds
, tpoint
.ptr
, 1);
7025 // Checks to see if a line originating from x,y along the +ve x axis
7026 // intersects the given line (points[0],points[1]) -> (points[2], points[3]).
7027 // Returns `true` on intersection.
7028 // Horizontal lines are never hit.
7029 bool nvg__intersectLine (const(float)* points
, float x
, float y
) {
7030 immutable float x1
= points
[0];
7031 immutable float y1
= points
[1];
7032 immutable float x2
= points
[2];
7033 immutable float y2
= points
[3];
7034 immutable float d
= y2
-y1
;
7035 if (d
> NVGPickEPS || d
< -NVGPickEPS
) {
7036 immutable float s
= (x2
-x1
)/d
;
7037 immutable float lineX
= x1
+(y
-y1
)*s
;
7044 // Checks to see if a line originating from x,y along the +ve x axis intersects the given bezier.
7045 // It is assumed that the line originates from within the bounding box of
7046 // the bezier and that the curve has no dy=0 inflection points.
7047 // Returns the number of intersections found (which is either 1 or 0).
7048 int nvg__intersectBezier (const(float)* points
, float x
, float y
) {
7049 immutable float x0
= points
[0*2+0], x1
= points
[1*2+0], x2
= points
[2*2+0], x3
= points
[3*2+0];
7050 immutable float y0
= points
[0*2+1], y1
= points
[1*2+1], y2
= points
[2*2+1], y3
= points
[3*2+1];
7052 if (y0
== y1
&& y1
== y2
&& y2
== y3
) return 0;
7056 if (y3
!= y0
) t
= (y
-y0
)/(y3
-y0
);
7057 else if (x3
!= x0
) t
= (x
-x0
)/(x3
-x0
);
7060 // A few Newton iterations
7061 for (int iter
= 0; iter
< 6; ++iter
) {
7062 immutable float omt
= 1-t
;
7063 immutable float omt2
= omt
*omt
;
7064 immutable float t2
= t
*t
;
7065 immutable float omt3
= omt2
*omt
;
7066 immutable float t3
= t2
*t
;
7068 immutable float ty
= y0
*omt3
+
7074 immutable float dty
= 3.0f*omt2
*(y1
-y0
) +
7075 6.0f*omt
*t
*(y2
-y1
) +
7078 // dty will never == 0 since:
7079 // Either omt, omt2 are zero OR t2 is zero
7080 // y0 != y1 != y2 != y3 (checked above)
7085 immutable float omt
= 1-t
;
7086 immutable float omt2
= omt
*omt
;
7087 immutable float t2
= t
*t
;
7088 immutable float omt3
= omt2
*omt
;
7089 immutable float t3
= t2
*t
;
7091 immutable float tx
=
7097 return (tx
> x ?
1 : 0);
7101 // Finds the closest point on a line to a given point
7102 void nvg__closestLine (const(float)* points
, float x
, float y
, float* closest
, float* ot
) {
7103 immutable float x1
= points
[0];
7104 immutable float y1
= points
[1];
7105 immutable float x2
= points
[2];
7106 immutable float y2
= points
[3];
7107 immutable float pqx
= x2
-x1
;
7108 immutable float pqz
= y2
-y1
;
7109 immutable float dx
= x
-x1
;
7110 immutable float dz
= y
-y1
;
7111 immutable float d
= pqx
*pqx
+pqz
*pqz
;
7112 float t
= pqx
*dx
+pqz
*dz
;
7114 if (t
< 0) t
= 0; else if (t
> 1) t
= 1;
7115 closest
[0] = x1
+t
*pqx
;
7116 closest
[1] = y1
+t
*pqz
;
7120 // Finds the closest point on a curve for a given point (x,y).
7121 // Assumes that the curve has no dx==0 or dy==0 inflection points.
7122 void nvg__closestBezier (const(float)* points
, float x
, float y
, float* closest
, float *ot
) {
7123 immutable float x0
= points
[0*2+0], x1
= points
[1*2+0], x2
= points
[2*2+0], x3
= points
[3*2+0];
7124 immutable float y0
= points
[0*2+1], y1
= points
[1*2+1], y2
= points
[2*2+1], y3
= points
[3*2+1];
7126 // This assumes that the curve has no dy=0 inflection points.
7131 // A few Newton iterations
7132 for (int iter
= 0; iter
< 6; ++iter
) {
7133 immutable float omt
= 1-t
;
7134 immutable float omt2
= omt
*omt
;
7135 immutable float t2
= t
*t
;
7136 immutable float omt3
= omt2
*omt
;
7137 immutable float t3
= t2
*t
;
7139 immutable float ty
=
7145 immutable float tx
=
7152 immutable float dty
=
7157 immutable float ddty
=
7158 6.0f*omt
*(y2
-2.0f*y1
+y0
)+
7159 6.0f*t
*(y3
-2.0f*y2
+y1
);
7161 immutable float dtx
=
7166 immutable float ddtx
=
7167 6.0f*omt
*(x2
-2.0f*x1
+x0
)+
7168 6.0f*t
*(x3
-2.0f*x2
+x1
);
7170 immutable float errorx
= tx
-x
;
7171 immutable float errory
= ty
-y
;
7173 immutable float n
= errorx
*dtx
+errory
*dty
;
7176 immutable float d
= dtx
*dtx
+dty
*dty
+errorx
*ddtx
+errory
*ddty
;
7177 if (d
!= 0) t
= t
-n
/d
; else break;
7180 t
= nvg__max(0, nvg__min(1.0, t
));
7183 immutable float omt
= 1-t
;
7184 immutable float omt2
= omt
*omt
;
7185 immutable float t2
= t
*t
;
7186 immutable float omt3
= omt2
*omt
;
7187 immutable float t3
= t2
*t
;
7189 immutable float ty
=
7195 immutable float tx
=
7207 // 1 If (x,y) is contained by the stroke of the path
7208 // 0 If (x,y) is not contained by the path.
7209 int nvg__pickSubPathStroke (const NVGpickScene
* ps
, const NVGpickSubPath
* psp
, float x
, float y
, float strokeWidth
, int lineCap
, int lineJoin
) {
7210 if (!nvg__pointInBounds(x
, y
, psp
.bounds
)) return 0;
7211 if (psp
.firstSegment
== -1) return 0;
7213 float[2] closest
= void;
7217 // trace a line from x,y out along the positive x axis and count the number of intersections
7218 int nsegments
= psp
.nsegments
;
7219 const(NVGsegment
)* seg
= ps
.segments
+psp
.firstSegment
;
7220 const(NVGsegment
)* prevseg
= (psp
.closed ?
&ps
.segments
[psp
.firstSegment
+nsegments
-1] : null);
7221 immutable float strokeWidthSqd
= strokeWidth
*strokeWidth
;
7223 for (int s
= 0; s
< nsegments
; ++s
, prevseg
= seg
, ++seg
) {
7224 if (nvg__pointInBounds(x
, y
, seg
.bounds
)) {
7225 // Line potentially hits stroke.
7227 case Command
.LineTo
:
7228 nvg__closestLine(&ps
.points
[seg
.firstPoint
*2], x
, y
, closest
.ptr
, &t
);
7230 case Command
.BezierTo
:
7231 nvg__closestBezier(&ps
.points
[seg
.firstPoint
*2], x
, y
, closest
.ptr
, &t
);
7237 d
.ptr
[0] = x
-closest
.ptr
[0];
7238 d
.ptr
[1] = y
-closest
.ptr
[1];
7240 if ((t
>= NVGPickEPS
&& t
<= 1.0f-NVGPickEPS
) ||
7241 (seg
.flags
&(NVGSegmentFlags
.Corner|NVGSegmentFlags
.Cap|NVGSegmentFlags
.Endcap
)) == 0 ||
7242 (lineJoin
== NVGLineCap
.Round
))
7244 // Closest point is in the middle of the line/curve, at a rounded join/cap
7245 // or at a smooth join
7246 immutable float distSqd
= d
.ptr
[0]*d
.ptr
[0]+d
.ptr
[1]*d
.ptr
[1];
7247 if (distSqd
< strokeWidthSqd
) return 1;
7248 } else if ((t
> 1.0f-NVGPickEPS
&& (seg
.flags
&NVGSegmentFlags
.Endcap
)) ||
7249 (t
< NVGPickEPS
&& (seg
.flags
&NVGSegmentFlags
.Cap
))) {
7251 case NVGLineCap
.Butt
:
7252 immutable float distSqd
= d
.ptr
[0]*d
.ptr
[0]+d
.ptr
[1]*d
.ptr
[1];
7253 immutable float dirD
= (t
< NVGPickEPS ?
7254 -(d
.ptr
[0]*seg
.startDir
.ptr
[0]+d
.ptr
[1]*seg
.startDir
.ptr
[1]) :
7255 d
.ptr
[0]*seg
.endDir
.ptr
[0]+d
.ptr
[1]*seg
.endDir
.ptr
[1]);
7256 if (dirD
< -NVGPickEPS
&& distSqd
< strokeWidthSqd
) return 1;
7258 case NVGLineCap
.Square
:
7259 if (nvg__absf(d
.ptr
[0]) < strokeWidth
&& nvg__absf(d
.ptr
[1]) < strokeWidth
) return 1;
7261 case NVGLineCap
.Round
:
7262 immutable float distSqd
= d
.ptr
[0]*d
.ptr
[0]+d
.ptr
[1]*d
.ptr
[1];
7263 if (distSqd
< strokeWidthSqd
) return 1;
7268 } else if (seg
.flags
&NVGSegmentFlags
.Corner
) {
7269 // Closest point is at a corner
7270 const(NVGsegment
)* seg0
, seg1
;
7272 if (t
< NVGPickEPS
) {
7277 seg1
= (s
== nsegments
-1 ?
&ps
.segments
[psp
.firstSegment
] : seg
+1);
7280 if (!(seg1
.flags
&NVGSegmentFlags
.Bevel
)) {
7281 immutable float prevNDist
= -seg0
.endDir
.ptr
[1]*d
.ptr
[0]+seg0
.endDir
.ptr
[0]*d
.ptr
[1];
7282 immutable float curNDist
= seg1
.startDir
.ptr
[1]*d
.ptr
[0]-seg1
.startDir
.ptr
[0]*d
.ptr
[1];
7283 if (nvg__absf(prevNDist
) < strokeWidth
&& nvg__absf(curNDist
) < strokeWidth
) return 1;
7285 d
.ptr
[0] -= -seg1
.startDir
.ptr
[1]*strokeWidth
;
7286 d
.ptr
[1] -= +seg1
.startDir
.ptr
[0]*strokeWidth
;
7287 if (seg1
.miterDir
.ptr
[0]*d
.ptr
[0]+seg1
.miterDir
.ptr
[1]*d
.ptr
[1] < 0) return 1;
7297 // 1 If (x,y) is contained by the path and the path is solid.
7298 // -1 If (x,y) is contained by the path and the path is a hole.
7299 // 0 If (x,y) is not contained by the path.
7300 int nvg__pickSubPath (const NVGpickScene
* ps
, const NVGpickSubPath
* psp
, float x
, float y
, bool evenOddMode
) {
7301 if (!nvg__pointInBounds(x
, y
, psp
.bounds
)) return 0;
7302 if (psp
.firstSegment
== -1) return 0;
7304 const(NVGsegment
)* seg
= &ps
.segments
[psp
.firstSegment
];
7305 int nsegments
= psp
.nsegments
;
7306 int nintersections
= 0;
7308 // trace a line from x,y out along the positive x axis and count the number of intersections
7309 for (int s
= 0; s
< nsegments
; ++s
, ++seg
) {
7310 if ((seg
.bounds
.ptr
[1]-NVGPickEPS
) < y
&&
7311 (seg
.bounds
.ptr
[3]-NVGPickEPS
) > y
&&
7312 seg
.bounds
.ptr
[2] > x
)
7314 // Line hits the box.
7316 case Command
.LineTo
:
7317 if (seg
.bounds
.ptr
[0] > x
) {
7318 // line originates outside the box
7321 // line originates inside the box
7322 nintersections
+= nvg__intersectLine(&ps
.points
[seg
.firstPoint
*2], x
, y
);
7325 case Command
.BezierTo
:
7326 if (seg
.bounds
.ptr
[0] > x
) {
7327 // line originates outside the box
7330 // line originates inside the box
7331 nintersections
+= nvg__intersectBezier(&ps
.points
[seg
.firstPoint
*2], x
, y
);
7341 return nintersections
;
7343 return (nintersections
&1 ?
(psp
.winding
== NVGSolidity
.Solid ?
1 : -1) : 0);
7347 bool nvg__pickPath (const(NVGpickScene
)* ps
, const(NVGpickPath
)* pp
, float x
, float y
) {
7349 const(NVGpickSubPath
)* psp
= pp
.subPaths
;
7350 while (psp
!is null) {
7351 pickCount
+= nvg__pickSubPath(ps
, psp
, x
, y
, pp
.evenOddMode
);
7354 return ((pp
.evenOddMode ? pickCount
&1 : pickCount
) != 0);
7357 bool nvg__pickPathStroke (const(NVGpickScene
)* ps
, const(NVGpickPath
)* pp
, float x
, float y
) {
7358 const(NVGpickSubPath
)* psp
= pp
.subPaths
;
7359 while (psp
!is null) {
7360 if (nvg__pickSubPathStroke(ps
, psp
, x
, y
, pp
.strokeWidth
, pp
.lineCap
, pp
.lineJoin
)) return true;
7366 bool nvg__pickPathTestBounds (NVGContext ctx
, const NVGpickScene
* ps
, const NVGpickPath
* pp
, float x
, float y
) {
7367 if (nvg__pointInBounds(x
, y
, pp
.bounds
)) {
7368 //{ import core.stdc.stdio; printf(" (0): in bounds!\n"); }
7369 if (pp
.flags
&NVGPathFlags
.Scissor
) {
7370 const(float)* scissor
= &ps
.points
[pp
.scissor
*2];
7371 // untransform scissor translation
7372 float stx
= void, sty
= void;
7373 ctx
.gpuUntransformPoint(&stx
, &sty
, scissor
[4], scissor
[5]);
7374 immutable float rx
= x
-stx
;
7375 immutable float ry
= y
-sty
;
7376 //{ 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]); }
7377 if (nvg__absf((scissor
[0]*rx
)+(scissor
[1]*ry
)) > scissor
[6] ||
7378 nvg__absf((scissor
[2]*rx
)+(scissor
[3]*ry
)) > scissor
[7])
7380 //{ import core.stdc.stdio; printf(" (1): scissor reject!\n"); }
7389 int nvg__countBitsUsed (uint v
) pure {
7390 pragma(inline
, true);
7391 import core
.bitop
: bsr;
7392 return (v
!= 0 ?
bsr(v
)+1 : 0);
7395 void nvg__pickSceneInsert (NVGpickScene
* ps
, NVGpickPath
* pp
) {
7396 if (ps
is null || pp
is null) return;
7399 int base
= ps
.nlevels
-1;
7405 NVGpickPath
** cell
= null;
7407 // Bit tricks for inserting into an implicit quadtree.
7409 // Calc bounds of path in cells at the lowest level
7410 cellbounds
.ptr
[0] = cast(int)(pp
.bounds
.ptr
[0]/ps
.xdim
);
7411 cellbounds
.ptr
[1] = cast(int)(pp
.bounds
.ptr
[1]/ps
.ydim
);
7412 cellbounds
.ptr
[2] = cast(int)(pp
.bounds
.ptr
[2]/ps
.xdim
);
7413 cellbounds
.ptr
[3] = cast(int)(pp
.bounds
.ptr
[3]/ps
.ydim
);
7415 // Find which bits differ between the min/max x/y coords
7416 cellbounds
.ptr
[0] ^
= cellbounds
.ptr
[2];
7417 cellbounds
.ptr
[1] ^
= cellbounds
.ptr
[3];
7419 // Use the number of bits used (countBitsUsed(x) == sizeof(int) * 8 - clz(x);
7420 // to calculate the level to insert at (the level at which the bounds fit in a single cell)
7421 level
= nvg__min(base
-nvg__countBitsUsed(cellbounds
.ptr
[0]), base
-nvg__countBitsUsed(cellbounds
.ptr
[1]));
7422 if (level
< 0) level
= 0;
7423 //{ 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]); }
7426 // Find the correct cell in the chosen level, clamping to the edges.
7427 levelwidth
= 1<<level
;
7428 levelshift
= (ps
.nlevels
-level
)-1;
7429 levelx
= nvg__clamp(cellbounds
.ptr
[2]>>levelshift
, 0, levelwidth
-1);
7430 levely
= nvg__clamp(cellbounds
.ptr
[3]>>levelshift
, 0, levelwidth
-1);
7432 // Insert the path into the linked list at that cell.
7433 cell
= &ps
.levels
[level
][levely
*levelwidth
+levelx
];
7435 pp
.cellnext
= *cell
;
7438 if (ps
.paths
is null) ps
.lastPath
= pp
;
7442 // Store the order (depth) of the path for picking ops.
7443 pp
.order
= cast(short)ps
.npaths
;
7447 void nvg__pickBeginFrame (NVGContext ctx
, int width
, int height
) {
7448 NVGpickScene
* ps
= nvg__pickSceneGet(ctx
);
7450 //NVG_PICK_DEBUG_NEWFRAME();
7452 // Return all paths & sub paths from last frame to the free list
7453 while (ps
.paths
!is null) {
7454 NVGpickPath
* pp
= ps
.paths
.next
;
7455 nvg__freePickPath(ps
, ps
.paths
);
7462 // Store the screen metrics for the quadtree
7466 immutable float lowestSubDiv
= cast(float)(1<<(ps
.nlevels
-1));
7467 ps
.xdim
= cast(float)width
/lowestSubDiv
;
7468 ps
.ydim
= cast(float)height
/lowestSubDiv
;
7470 // Allocate the quadtree if required.
7471 if (ps
.levels
is null) {
7474 ps
.levels
= cast(NVGpickPath
***)malloc((NVGpickPath
**).sizeof
*ps
.nlevels
);
7475 for (int l
= 0; l
< ps
.nlevels
; ++l
) {
7476 int leveldim
= 1<<l
;
7477 ncells
+= leveldim
*leveldim
;
7480 ps
.levels
[0] = cast(NVGpickPath
**)malloc((NVGpickPath
*).sizeof
*ncells
);
7483 for (int l
= 1; l
< ps
.nlevels
; ++l
) {
7484 ps
.levels
[l
] = &ps
.levels
[0][cell
];
7485 int leveldim
= 1<<l
;
7486 cell
+= leveldim
*leveldim
;
7491 memset(ps
.levels
[0], 0, ps
.ncells
*(NVGpickPath
*).sizeof
);
7493 // Allocate temporary storage for nvgHitTestAll results if required.
7494 if (ps
.picked
is null) {
7496 ps
.picked
= cast(NVGpickPath
**)malloc((NVGpickPath
*).sizeof
*ps
.cpicked
);
7502 } // nothrow @trusted @nogc
7505 /// Return outline of the current path. Returned outline is not flattened.
7507 public NVGPathOutline
getCurrPathOutline (NVGContext ctx
) nothrow @trusted @nogc {
7508 if (ctx
is null ||
!ctx
.contextAlive || ctx
.ncommands
== 0) return NVGPathOutline
.init
;
7510 auto res
= NVGPathOutline
.createNew();
7512 const(float)[] acommands
= ctx
.commands
[0..ctx
.ncommands
];
7513 int ncommands
= cast(int)acommands
.length
;
7514 const(float)* commands
= acommands
.ptr
;
7516 float cx
= 0, cy
= 0;
7517 float[2] start
= void;
7518 float[4] totalBounds
= [float.max
, float.max
, -float.max
, -float.max
];
7519 float[8] bcp
= void; // bezier curve points; used to calculate bounds
7521 void addToBounds (in float x
, in float y
) nothrow @trusted @nogc {
7522 totalBounds
.ptr
[0] = nvg__min(totalBounds
.ptr
[0], x
);
7523 totalBounds
.ptr
[1] = nvg__min(totalBounds
.ptr
[1], y
);
7524 totalBounds
.ptr
[2] = nvg__max(totalBounds
.ptr
[2], x
);
7525 totalBounds
.ptr
[3] = nvg__max(totalBounds
.ptr
[3], y
);
7528 bool hasPoints
= false;
7530 void closeIt () nothrow @trusted @nogc {
7531 if (!hasPoints
) return;
7532 if (cx
!= start
.ptr
[0] || cy
!= start
.ptr
[1]) {
7533 res
.ds.putCommand(NVGPathOutline
.Command
.Kind
.LineTo
);
7534 res
.ds.putArgs(start
[]);
7537 addToBounds(cx
, cy
);
7542 while (i
< ncommands
) {
7543 int cmd
= cast(int)commands
[i
++];
7545 case Command
.MoveTo
: // one coordinate pair
7546 const(float)* tfxy
= commands
+i
;
7549 res
.ds.putCommand(NVGPathOutline
.Command
.Kind
.MoveTo
);
7550 res
.ds.putArgs(tfxy
[0..2]);
7551 // new starting point
7552 start
.ptr
[0..2] = tfxy
[0..2];
7555 addToBounds(cx
, cy
);
7558 case Command
.LineTo
: // one coordinate pair
7559 const(float)* tfxy
= commands
+i
;
7562 res
.ds.putCommand(NVGPathOutline
.Command
.Kind
.LineTo
);
7563 res
.ds.putArgs(tfxy
[0..2]);
7566 addToBounds(cx
, cy
);
7569 case Command
.BezierTo
: // three coordinate pairs
7570 const(float)* tfxy
= commands
+i
;
7573 res
.ds.putCommand(NVGPathOutline
.Command
.Kind
.BezierTo
);
7574 res
.ds.putArgs(tfxy
[0..6]);
7578 bcp
.ptr
[2..8] = tfxy
[0..6];
7579 nvg__bezierBounds(bcp
.ptr
, totalBounds
);
7588 case Command
.Winding
:
7589 //psp.winding = cast(short)cast(int)commands[i];
7597 res
.ds.bounds
[] = totalBounds
[];
7602 // ////////////////////////////////////////////////////////////////////////// //
7605 /** Creates font by loading it from the disk from specified file name.
7606 * Returns handle to the font or FONS_INVALID (aka -1) on error.
7607 * Use "fontname:noaa" as [name] to turn off antialiasing (if font driver supports that).
7609 * On POSIX systems it is possible to use fontconfig font names too.
7610 * `:noaa` in font path is still allowed, but it must be the last option.
7614 public int createFont (NVGContext ctx
, const(char)[] name
, const(char)[] path
) nothrow @trusted {
7615 return ctx
.fs
.addFont(name
, path
, ctx
.params
.fontAA
);
7618 /** Creates font by loading it from the specified memory chunk.
7619 * Returns handle to the font or FONS_INVALID (aka -1) on error.
7620 * Won't free data on error.
7624 public int createFontMem (NVGContext ctx
, const(char)[] name
, ubyte* data
, int ndata
, bool freeData
) nothrow @trusted @nogc {
7625 return ctx
.fs
.addFontMem(name
, data
, ndata
, freeData
, ctx
.params
.fontAA
);
7628 /// Add fonts from another context.
7629 /// This is more effective than reloading fonts, 'cause font data will be shared.
7631 public void addFontsFrom (NVGContext ctx
, NVGContext source
) nothrow @trusted @nogc {
7632 if (ctx
is null || source
is null) return;
7633 ctx
.fs
.addFontsFrom(source
.fs
);
7636 /// Finds a loaded font of specified name, and returns handle to it, or FONS_INVALID (aka -1) if the font is not found.
7638 public int findFont (NVGContext ctx
, const(char)[] name
) nothrow @trusted @nogc {
7639 pragma(inline
, true);
7640 return (name
.length
== 0 ? FONS_INVALID
: ctx
.fs
.getFontByName(name
));
7643 /// Sets the font size of current text style.
7645 public void fontSize (NVGContext ctx
, float size
) nothrow @trusted @nogc {
7646 pragma(inline
, true);
7647 nvg__getState(ctx
).fontSize
= size
;
7650 /// Gets the font size of current text style.
7652 public float fontSize (NVGContext ctx
) nothrow @trusted @nogc {
7653 pragma(inline
, true);
7654 return nvg__getState(ctx
).fontSize
;
7657 /// Sets the blur of current text style.
7659 public void fontBlur (NVGContext ctx
, float blur
) nothrow @trusted @nogc {
7660 pragma(inline
, true);
7661 nvg__getState(ctx
).fontBlur
= blur
;
7664 /// Gets the blur of current text style.
7666 public float fontBlur (NVGContext ctx
) nothrow @trusted @nogc {
7667 pragma(inline
, true);
7668 return nvg__getState(ctx
).fontBlur
;
7671 /// Sets the letter spacing of current text style.
7673 public void textLetterSpacing (NVGContext ctx
, float spacing
) nothrow @trusted @nogc {
7674 pragma(inline
, true);
7675 nvg__getState(ctx
).letterSpacing
= spacing
;
7678 /// Gets the letter spacing of current text style.
7680 public float textLetterSpacing (NVGContext ctx
) nothrow @trusted @nogc {
7681 pragma(inline
, true);
7682 return nvg__getState(ctx
).letterSpacing
;
7685 /// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
7687 public void textLineHeight (NVGContext ctx
, float lineHeight
) nothrow @trusted @nogc {
7688 pragma(inline
, true);
7689 nvg__getState(ctx
).lineHeight
= lineHeight
;
7692 /// Gets the proportional line height of current text style. The line height is specified as multiple of font size.
7694 public float textLineHeight (NVGContext ctx
) nothrow @trusted @nogc {
7695 pragma(inline
, true);
7696 return nvg__getState(ctx
).lineHeight
;
7699 /// Sets the text align of current text style, see [NVGTextAlign] for options.
7701 public void textAlign (NVGContext ctx
, NVGTextAlign talign
) nothrow @trusted @nogc {
7702 pragma(inline
, true);
7703 nvg__getState(ctx
).textAlign
= talign
;
7707 public void textAlign (NVGContext ctx
, NVGTextAlign
.H h
) nothrow @trusted @nogc {
7708 pragma(inline
, true);
7709 nvg__getState(ctx
).textAlign
.horizontal
= h
;
7713 public void textAlign (NVGContext ctx
, NVGTextAlign
.V v
) nothrow @trusted @nogc {
7714 pragma(inline
, true);
7715 nvg__getState(ctx
).textAlign
.vertical
= v
;
7719 public void textAlign (NVGContext ctx
, NVGTextAlign
.H h
, NVGTextAlign
.V v
) nothrow @trusted @nogc {
7720 pragma(inline
, true);
7721 nvg__getState(ctx
).textAlign
.reset(h
, v
);
7725 public void textAlign (NVGContext ctx
, NVGTextAlign
.V v
, NVGTextAlign
.H h
) nothrow @trusted @nogc {
7726 pragma(inline
, true);
7727 nvg__getState(ctx
).textAlign
.reset(h
, v
);
7730 /// Gets the text align of current text style, see [NVGTextAlign] for options.
7732 public NVGTextAlign
textAlign (NVGContext ctx
) nothrow @trusted @nogc {
7733 pragma(inline
, true);
7734 return nvg__getState(ctx
).textAlign
;
7737 /// Sets the font face based on specified id of current text style.
7739 public void fontFaceId (NVGContext ctx
, int font
) nothrow @trusted @nogc {
7740 pragma(inline
, true);
7741 nvg__getState(ctx
).fontId
= font
;
7744 /// Gets the font face based on specified id of current text style.
7746 public int fontFaceId (NVGContext ctx
) nothrow @trusted @nogc {
7747 pragma(inline
, true);
7748 return nvg__getState(ctx
).fontId
;
7751 /** Sets the font face based on specified name of current text style.
7753 * The underlying implementation is using O(1) data structure to lookup
7754 * font names, so you probably should use this function instead of [fontFaceId]
7755 * to make your code more robust and less error-prone.
7759 public void fontFace (NVGContext ctx
, const(char)[] font
) nothrow @trusted @nogc {
7760 pragma(inline
, true);
7761 nvg__getState(ctx
).fontId
= ctx
.fs
.getFontByName(font
);
7764 static if (is(typeof(&fons__nvg__toPath
))) {
7765 public enum NanoVegaHasCharToPath
= true; ///
7767 public enum NanoVegaHasCharToPath
= false; ///
7770 /// Adds glyph outlines to the current path. Vertical 0 is baseline.
7771 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
7772 /// Returns `false` if there is no such glyph, or current font is not scalable.
7774 public bool charToPath (NVGContext ctx
, dchar dch
, float[] bounds
=null) nothrow @trusted @nogc {
7775 NVGstate
* state
= nvg__getState(ctx
);
7776 ctx
.fs
.fontId
= state
.fontId
;
7777 return ctx
.fs
.toPath(ctx
, dch
, bounds
);
7780 static if (is(typeof(&fons__nvg__bounds
))) {
7781 public enum NanoVegaHasCharPathBounds
= true; ///
7783 public enum NanoVegaHasCharPathBounds
= false; ///
7786 /// Returns bounds of the glyph outlines. Vertical 0 is baseline.
7787 /// The glyph is not scaled in any way.
7788 /// Returns `false` if there is no such glyph, or current font is not scalable.
7790 public bool charPathBounds (NVGContext ctx
, dchar dch
, float[] bounds
) nothrow @trusted @nogc {
7791 NVGstate
* state
= nvg__getState(ctx
);
7792 ctx
.fs
.fontId
= state
.fontId
;
7793 return ctx
.fs
.getPathBounds(dch
, bounds
);
7796 /** [charOutline] will return [NVGPathOutline].
7801 float[4] bounds = void;
7803 nvg.scale(0.5, 0.5);
7804 nvg.translate(500, 800);
7808 nvg.charToPath('&', bounds[]);
7809 conwriteln(bounds[]);
7810 nvg.fillPaint(nvg.linearGradient(0, 0, 600, 600, NVGColor("#f70"), NVGColor("#ff0")));
7811 nvg.strokeColor(NVGColor("#0f0"));
7812 nvg.strokeWidth = 3;
7817 nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7818 nvg.strokeColor(NVGColor("#00f"));
7822 nvg.charToPath('g', bounds[]);
7823 conwriteln(bounds[]);
7825 nvg.strokeColor(NVGColor("#0f0"));
7829 nvg.rect(bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
7830 nvg.strokeColor(NVGColor("#00f"));
7836 nvg.strokeColor(NVGColor("#0ff"));
7839 if (auto ol = nvg.charOutline('Q')) {
7840 scope(exit) ol.kill();
7842 conwriteln("==== length: ", ol.length, " ====");
7843 foreach (const ref cmd; ol.commands) {
7844 //conwriteln(" ", cmd.code, ": ", cmd.args[]);
7846 final switch (cmd.code) {
7847 case cmd.Kind.MoveTo: nvg.moveTo(cmd.args[0], cmd.args[1]); break;
7848 case cmd.Kind.LineTo: nvg.lineTo(cmd.args[0], cmd.args[1]); break;
7849 case cmd.Kind.QuadTo: nvg.quadTo(cmd.args[0], cmd.args[1], cmd.args[2], cmd.args[3]); break;
7850 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;
7853 nvg.strokeColor(NVGColor("#f00"));
7860 public struct NVGPathOutline
{
7861 private nothrow @trusted @nogc:
7863 uint rc
; // refcount
7867 uint ccount
; // number of commands
7868 float[4] bounds
= 0; /// outline bounds
7869 nothrow @trusted @nogc:
7870 void putBytes (const(void)[] b
) {
7871 if (b
.length
== 0) return;
7872 if (b
.length
>= int.max
/8) assert(0, "NanoVega: out of memory");
7873 if (int.max
/8-used
< b
.length
) assert(0, "NanoVega: out of memory");
7874 if (used
+cast(uint)b
.length
> size
) {
7875 import core
.stdc
.stdlib
: realloc
;
7877 while (newsz
< used
+cast(uint)b
.length
) newsz
= (newsz
== 0 ?
1024 : newsz
< 32768 ? newsz
*2 : newsz
+8192);
7878 assert(used
+cast(uint)b
.length
<= newsz
);
7879 data
= cast(ubyte*)realloc(data
, newsz
);
7880 if (data
is null) assert(0, "NanoVega: out of memory");
7883 import core
.stdc
.string
: memcpy
;
7884 memcpy(data
+used
, b
.ptr
, b
.length
);
7885 used
+= cast(uint)b
.length
;
7887 void putCommand (ubyte cmd
) { pragma(inline
, true); ++ccount
; putBytes((&cmd
)[0..1]); }
7888 void putArgs (const(float)[] f
...) { pragma(inline
, true); putBytes(f
[]); }
7891 static void incRef (DataStore
* ds) {
7892 pragma(inline
, true);
7895 //{ import core.stdc.stdio; printf("ods(%p): incref: newrc=%u\n", ds, ds.rc); }
7899 static void decRef (DataStore
* ds) {
7900 version(aliced
) pragma(inline
, true);
7902 //{ import core.stdc.stdio; printf("ods(%p): decref: newrc=%u\n", ds, ds.rc-1); }
7904 import core
.stdc
.stdlib
: free
;
7905 import core
.stdc
.string
: memset
;
7906 if (ds.data
!is null) free(ds.data
);
7907 memset(ds, 0, DataStore
.sizeof
); // just in case
7909 //{ import core.stdc.stdio; printf(" ods(%p): killed.\n"); }
7915 static NVGPathOutline
createNew () {
7916 import core
.stdc
.stdlib
: malloc
;
7917 import core
.stdc
.string
: memset
;
7918 auto ds = cast(DataStore
*)malloc(DataStore
.sizeof
);
7919 if (ds is null) assert(0, "NanoVega: out of memory");
7920 memset(ds, 0, DataStore
.sizeof
);
7923 res
.dsaddr
= cast(usize
)ds;
7928 usize dsaddr
; // fool GC
7930 @property inout(DataStore
)* ds () inout pure { pragma(inline
, true); return cast(DataStore
*)dsaddr
; }
7934 static struct Command
{
7941 End
, /// no more commands (this command is not `valid`!)
7945 const(float)[] args
; ///
7946 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (code
>= Kind
.min
&& code
< Kind
.End
&& args
.length
>= 2); } ///
7948 static uint arglen (Kind code
) pure nothrow @safe @nogc {
7949 pragma(inline
, true);
7951 code
== Kind
.MoveTo || code
== Kind
.LineTo ?
2 :
7952 code
== Kind
.QuadTo ?
4 :
7953 code
== Kind
.BezierTo ?
6 :
7957 /// perform NanoVega command with stored data.
7958 void perform (NVGContext ctx
) const nothrow @trusted @nogc {
7959 if (ctx
is null) return;
7960 final switch (code
) {
7961 case Kind
.MoveTo
: if (args
.length
> 1) ctx
.moveTo(args
.ptr
[0..2]); break;
7962 case Kind
.LineTo
: if (args
.length
> 1) ctx
.lineTo(args
.ptr
[0..2]); break;
7963 case Kind
.QuadTo
: if (args
.length
> 3) ctx
.quadTo(args
.ptr
[0..4]); break;
7964 case Kind
.BezierTo
: if (args
.length
> 5) ctx
.bezierTo(args
.ptr
[0..6]); break;
7965 case Kind
.End
: break;
7969 /// perform NanoVega command with stored data, transforming points with [xform] transformation matrix.
7970 void perform() (NVGContext ctx
, in auto ref NVGMatrix xform
) const nothrow @trusted @nogc {
7971 if (ctx
is null ||
!valid
) return;
7972 float[6] pts
= void;
7973 pts
[0..args
.length
] = args
[];
7974 foreach (immutable pidx
; 0..args
.length
/2) xform
.point(pts
.ptr
[pidx
*2+0], pts
.ptr
[pidx
*2+1]);
7975 final switch (code
) {
7976 case Kind
.MoveTo
: if (args
.length
> 1) ctx
.moveTo(pts
.ptr
[0..2]); break;
7977 case Kind
.LineTo
: if (args
.length
> 1) ctx
.lineTo(pts
.ptr
[0..2]); break;
7978 case Kind
.QuadTo
: if (args
.length
> 3) ctx
.quadTo(pts
.ptr
[0..4]); break;
7979 case Kind
.BezierTo
: if (args
.length
> 5) ctx
.bezierTo(pts
.ptr
[0..6]); break;
7980 case Kind
.End
: break;
7986 /// Create new path with quadratic bezier (first command is MoveTo, second command is QuadTo).
7987 static NVGPathOutline
createNewQuad (in float x0
, in float y0
, in float cx
, in float cy
, in float x
, in float y
) {
7988 auto res
= createNew();
7989 res
.ds.putCommand(Command
.Kind
.MoveTo
);
7990 res
.ds.putArgs(x0
, y0
);
7991 res
.ds.putCommand(Command
.Kind
.QuadTo
);
7992 res
.ds.putArgs(cx
, cy
, x
, y
);
7996 /// Create new path with cubic bezier (first command is MoveTo, second command is BezierTo).
7997 static NVGPathOutline
createNewBezier (in float x1
, in float y1
, in float x2
, in float y2
, in float x3
, in float y3
, in float x4
, in float y4
) {
7998 auto res
= createNew();
7999 res
.ds.putCommand(Command
.Kind
.MoveTo
);
8000 res
.ds.putArgs(x1
, y1
);
8001 res
.ds.putCommand(Command
.Kind
.BezierTo
);
8002 res
.ds.putArgs(x2
, y2
, x3
, y3
, x4
, y4
);
8007 this (this) { pragma(inline
, true); incRef(cast(DataStore
*)dsaddr
); }
8008 ~this () { pragma(inline
, true); decRef(cast(DataStore
*)dsaddr
); }
8010 void opAssign() (in auto ref NVGPathOutline a
) {
8011 incRef(cast(DataStore
*)a
.dsaddr
);
8012 decRef(cast(DataStore
*)dsaddr
);
8018 pragma(inline
, true);
8023 /// Is this outline empty?
8024 @property empty () const pure { pragma(inline
, true); return (dsaddr
== 0 ||
ds.ccount
== 0); }
8026 /// Returns number of commands in outline.
8027 @property int length () const pure { pragma(inline
, true); return (dsaddr ?
ds.ccount
: 0); }
8029 /// Returns "flattened" path. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8030 NVGPathOutline
flatten () const { pragma(inline
, true); return flattenInternal(null); }
8032 /// Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8033 NVGPathOutline
flatten() (in auto ref NVGMatrix mt
) const { pragma(inline
, true); return flattenInternal(&mt
); }
8035 // Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
8036 private NVGPathOutline
flattenInternal (scope NVGMatrix
* tfm
) const {
8037 import core
.stdc
.string
: memset
;
8040 if (dsaddr
== 0 ||
ds.ccount
== 0) { res
= this; return res
; } // nothing to do
8042 // check if we need to flatten the path
8044 bool dowork
= false;
8045 foreach (const ref cs
; commands
) {
8046 if (cs
.code
!= Command
.Kind
.MoveTo
&& cs
.code
!= Command
.Kind
.LineTo
) {
8051 if (!dowork
) { res
= this; return res
; } // nothing to do
8054 NVGcontextinternal ctx
;
8055 memset(&ctx
, 0, ctx
.sizeof
);
8056 ctx
.cache
= nvg__allocPathCache();
8058 import core
.stdc
.stdlib
: free
;
8059 nvg__deletePathCache(ctx
.cache
);
8062 ctx
.tessTol
= 0.25f;
8063 ctx
.angleTol
= 0; // 0 -- angle tolerance for McSeem Bezier rasterizer
8064 ctx
.cuspLimit
= 0; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
8065 ctx
.distTol
= 0.01f;
8066 ctx
.tesselatortype
= NVGTesselation
.DeCasteljau
;
8068 nvg__addPath(&ctx
); // we need this for `nvg__addPoint()`
8070 // has some curves or transformations, convert path
8072 float[8] args
= void;
8074 res
.ds.bounds
= [float.max
, float.max
, -float.max
, -float.max
];
8076 float lastX
= float.max
, lastY
= float.max
;
8077 bool lastWasMove
= false;
8079 void addPoint (float x
, float y
, Command
.Kind cmd
=Command
.Kind
.LineTo
) nothrow @trusted @nogc {
8080 if (tfm
!is null) tfm
.point(x
, y
);
8081 bool isMove
= (cmd
== Command
.Kind
.MoveTo
);
8084 if (lastWasMove
&& nvg__ptEquals(lastX
, lastY
, x
, y
, ctx
.distTol
)) return;
8087 if (nvg__ptEquals(lastX
, lastY
, x
, y
, ctx
.distTol
)) return;
8089 lastWasMove
= isMove
;
8092 res
.ds.putCommand(cmd
);
8093 res
.ds.putArgs(x
, y
);
8094 res
.ds.bounds
.ptr
[0] = nvg__min(res
.ds.bounds
.ptr
[0], x
);
8095 res
.ds.bounds
.ptr
[1] = nvg__min(res
.ds.bounds
.ptr
[1], y
);
8096 res
.ds.bounds
.ptr
[2] = nvg__max(res
.ds.bounds
.ptr
[2], x
);
8097 res
.ds.bounds
.ptr
[3] = nvg__max(res
.ds.bounds
.ptr
[3], y
);
8100 // sorry for this pasta
8101 void flattenBezier (in float x1
, in float y1
, in float x2
, in float y2
, in float x3
, in float y3
, in float x4
, in float y4
, in int level
) nothrow @trusted @nogc {
8102 ctx
.cache
.npoints
= 0;
8103 if (ctx
.tesselatortype
== NVGTesselation
.DeCasteljau
) {
8104 nvg__tesselateBezier(&ctx
, x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
, 0, PointFlag
.Corner
);
8105 } else if (ctx
.tesselatortype
== NVGTesselation
.DeCasteljauMcSeem
) {
8106 nvg__tesselateBezierMcSeem(&ctx
, x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
, 0, PointFlag
.Corner
);
8108 nvg__tesselateBezierAFD(&ctx
, x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
, PointFlag
.Corner
);
8110 // add generated points
8111 foreach (const ref pt
; ctx
.cache
.points
[0..ctx
.cache
.npoints
]) addPoint(pt
.x
, pt
.y
);
8114 void flattenQuad (in float x0
, in float y0
, in float cx
, in float cy
, in float x
, in float y
) {
8117 x0
+2.0f/3.0f*(cx
-x0
), y0
+2.0f/3.0f*(cy
-y0
),
8118 x
+2.0f/3.0f*(cx
-x
), y
+2.0f/3.0f*(cy
-y
),
8124 float cx
= 0, cy
= 0;
8125 foreach (const ref cs
; commands
) {
8127 case Command
.Kind
.LineTo
:
8128 case Command
.Kind
.MoveTo
:
8129 addPoint(cs
.args
[0], cs
.args
[1], cs
.code
);
8133 case Command
.Kind
.QuadTo
:
8134 flattenQuad(cx
, cy
, cs
.args
[0], cs
.args
[1], cs
.args
[2], cs
.args
[3]);
8138 case Command
.Kind
.BezierTo
:
8139 flattenBezier(cx
, cy
, cs
.args
[0], cs
.args
[1], cs
.args
[2], cs
.args
[3], cs
.args
[4], cs
.args
[5], 0);
8151 /// Returns forward range with all glyph commands.
8152 auto commands () const nothrow @trusted @nogc {
8153 static struct Range
{
8154 private nothrow @trusted @nogc:
8156 uint cpos
; // current position in data
8157 uint cleft
; // number of commands left
8158 @property const(ubyte)* data () inout pure { pragma(inline
, true); return (dsaddr ?
(cast(DataStore
*)dsaddr
).data
: null); }
8160 this (this) { pragma(inline
, true); incRef(cast(DataStore
*)dsaddr
); }
8161 ~this () { pragma(inline
, true); decRef(cast(DataStore
*)dsaddr
); }
8162 void opAssign() (in auto ref Range a
) {
8163 incRef(cast(DataStore
*)a
.dsaddr
);
8164 decRef(cast(DataStore
*)dsaddr
);
8169 float[4] bounds () const pure { float[4] res
= 0; pragma(inline
, true); if (dsaddr
) res
[] = (cast(DataStore
*)dsaddr
).bounds
[]; return res
; } /// outline bounds
8170 @property bool empty () const pure { pragma(inline
, true); return (cleft
== 0); }
8171 @property int length () const pure { pragma(inline
, true); return cleft
; }
8172 @property Range
save () const { pragma(inline
, true); Range res
= this; return res
; }
8173 @property Command
front () const {
8176 res
.code
= cast(Command
.Kind
)data
[cpos
];
8178 case Command
.Kind
.MoveTo
:
8179 case Command
.Kind
.LineTo
:
8180 res
.args
= (cast(const(float*))(data
+cpos
+1))[0..1*2];
8182 case Command
.Kind
.QuadTo
:
8183 res
.args
= (cast(const(float*))(data
+cpos
+1))[0..2*2];
8185 case Command
.Kind
.BezierTo
:
8186 res
.args
= (cast(const(float*))(data
+cpos
+1))[0..3*2];
8189 res
.code
= Command
.Kind
.End
;
8194 res
.code
= Command
.Kind
.End
;
8200 if (cleft
<= 1) { cleft
= 0; return; } // don't waste time skipping last command
8202 switch (data
[cpos
]) {
8203 case Command
.Kind
.MoveTo
:
8204 case Command
.Kind
.LineTo
:
8205 cpos
+= 1+1*2*cast(uint)float.sizeof
;
8207 case Command
.Kind
.QuadTo
:
8208 cpos
+= 1+2*2*cast(uint)float.sizeof
;
8210 case Command
.Kind
.BezierTo
:
8211 cpos
+= 1+3*2*cast(uint)float.sizeof
;
8220 incRef(cast(DataStore
*)dsaddr
); // range anchors it
8221 return Range(dsaddr
, 0, ds.ccount
);
8228 public alias NVGGlyphOutline
= NVGPathOutline
; /// For backwards compatibility.
8230 /// Destroy glyph outiline and free allocated memory.
8232 public void kill (ref NVGPathOutline ol
) nothrow @trusted @nogc {
8233 pragma(inline
, true);
8237 static if (is(typeof(&fons__nvg__toOutline
))) {
8238 public enum NanoVegaHasCharOutline
= true; ///
8240 public enum NanoVegaHasCharOutline
= false; ///
8243 /// Returns glyph outlines as array of commands. Vertical 0 is baseline.
8244 /// The glyph is not scaled in any way, so you have to use NanoVega transformations instead.
8245 /// Returns `null` if there is no such glyph, or current font is not scalable.
8247 public NVGPathOutline
charOutline (NVGContext ctx
, dchar dch
) nothrow @trusted @nogc {
8248 import core
.stdc
.stdlib
: malloc
;
8249 import core
.stdc
.string
: memcpy
;
8250 NVGstate
* state
= nvg__getState(ctx
);
8251 ctx
.fs
.fontId
= state
.fontId
;
8252 auto oline
= NVGPathOutline
.createNew();
8253 if (!ctx
.fs
.toOutline(dch
, oline
.ds)) oline
.clear();
8258 float nvg__quantize (float a
, float d
) pure nothrow @safe @nogc {
8259 pragma(inline
, true);
8260 return (cast(int)(a
/d
+0.5f))*d
;
8263 float nvg__getFontScale (NVGstate
* state
) /*pure*/ nothrow @safe @nogc {
8264 pragma(inline
, true);
8265 return nvg__min(nvg__quantize(nvg__getAverageScale(state
.xform
), 0.01f), 4.0f);
8268 void nvg__flushTextTexture (NVGContext ctx
) nothrow @trusted @nogc {
8269 int[4] dirty
= void;
8270 if (ctx
.fs
.validateTexture(dirty
.ptr
)) {
8271 auto fontImage
= &ctx
.fontImages
[ctx
.fontImageIdx
];
8273 if (fontImage
.valid
) {
8275 const(ubyte)* data
= ctx
.fs
.getTextureData(&iw
, &ih
);
8278 int w
= dirty
[2]-dirty
[0];
8279 int h
= dirty
[3]-dirty
[1];
8280 ctx
.params
.renderUpdateTexture(ctx
.params
.userPtr
, fontImage
.id
, x
, y
, w
, h
, data
);
8285 bool nvg__allocTextAtlas (NVGContext ctx
) nothrow @trusted @nogc {
8287 nvg__flushTextTexture(ctx
);
8288 if (ctx
.fontImageIdx
>= NVG_MAX_FONTIMAGES
-1) return false;
8289 // if next fontImage already have a texture
8290 if (ctx
.fontImages
[ctx
.fontImageIdx
+1].valid
) {
8291 ctx
.imageSize(ctx
.fontImages
[ctx
.fontImageIdx
+1], iw
, ih
);
8293 // calculate the new font image size and create it
8294 ctx
.imageSize(ctx
.fontImages
[ctx
.fontImageIdx
], iw
, ih
);
8295 if (iw
> ih
) ih
*= 2; else iw
*= 2;
8296 if (iw
> NVG_MAX_FONTIMAGE_SIZE || ih
> NVG_MAX_FONTIMAGE_SIZE
) iw
= ih
= NVG_MAX_FONTIMAGE_SIZE
;
8297 ctx
.fontImages
[ctx
.fontImageIdx
+1].id
= ctx
.params
.renderCreateTexture(ctx
.params
.userPtr
, NVGtexture
.Alpha
, iw
, ih
, (ctx
.params
.fontAA ?
0 : NVGImageFlag
.NoFiltering
), null);
8298 if (ctx
.fontImages
[ctx
.fontImageIdx
+1].id
> 0) {
8299 ctx
.fontImages
[ctx
.fontImageIdx
+1].ctx
= ctx
;
8300 ctx
.nvg__imageIncRef(ctx
.fontImages
[ctx
.fontImageIdx
+1].id
, false); // don't increment driver refcount
8304 ctx
.fs
.resetAtlas(iw
, ih
);
8308 void nvg__renderText (NVGContext ctx
, NVGVertex
* verts
, int nverts
) nothrow @trusted @nogc {
8309 NVGstate
* state
= nvg__getState(ctx
);
8310 NVGPaint paint
= state
.fill
;
8312 // Render triangles.
8313 paint
.image
= ctx
.fontImages
[ctx
.fontImageIdx
];
8315 // Apply global alpha
8316 paint
.innerColor
.a
*= state
.alpha
;
8317 paint
.middleColor
.a
*= state
.alpha
;
8318 paint
.outerColor
.a
*= state
.alpha
;
8320 ctx
.params
.renderTriangles(ctx
.params
.userPtr
, state
.compositeOperation
, NVGClipMode
.None
, &paint
, &state
.scissor
, verts
, nverts
);
8322 ++ctx
.drawCallCount
;
8323 ctx
.textTriCount
+= nverts
/3;
8326 /// Draws text string at specified location. Returns next x position.
8328 public float text(T
) (NVGContext ctx
, float x
, float y
, const(T
)[] str) nothrow @trusted @nogc if (isAnyCharType
!T
) {
8329 NVGstate
* state
= nvg__getState(ctx
);
8330 FONSTextIter
!T iter
, prevIter
;
8333 float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8334 float invscale
= 1.0f/scale
;
8338 if (state
.fontId
== FONS_INVALID
) return x
;
8339 if (str.length
== 0) return x
;
8341 ctx
.fs
.size
= state
.fontSize
*scale
;
8342 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
8343 ctx
.fs
.blur
= state
.fontBlur
*scale
;
8344 ctx
.fs
.textAlign
= state
.textAlign
;
8345 ctx
.fs
.fontId
= state
.fontId
;
8347 cverts
= nvg__max(2, cast(int)(str.length
))*6; // conservative estimate
8348 verts
= nvg__allocTempVerts(ctx
, cverts
);
8349 if (verts
is null) return x
;
8351 if (!iter
.setup(ctx
.fs
, x
*scale
, y
*scale
, str, FONSBitmapFlag
.Required
)) return x
;
8353 while (iter
.next(q
)) {
8354 float[4*2] c
= void;
8355 if (iter
.prevGlyphIndex
< 0) { // can not retrieve glyph?
8357 // TODO: add back-end bit to do this just once per frame
8358 nvg__flushTextTexture(ctx
);
8359 nvg__renderText(ctx
, verts
, nverts
);
8362 if (!nvg__allocTextAtlas(ctx
)) break; // no memory :(
8364 iter
.next(q
); // try again
8365 if (iter
.prevGlyphIndex
< 0) {
8366 // still can not find glyph, try replacement
8368 if (!iter
.getDummyChar(q
)) break;
8372 // transform corners
8373 state
.xform
.point(&c
[0], &c
[1], q
.x0
*invscale
, q
.y0
*invscale
);
8374 state
.xform
.point(&c
[2], &c
[3], q
.x1
*invscale
, q
.y0
*invscale
);
8375 state
.xform
.point(&c
[4], &c
[5], q
.x1
*invscale
, q
.y1
*invscale
);
8376 state
.xform
.point(&c
[6], &c
[7], q
.x0
*invscale
, q
.y1
*invscale
);
8378 if (nverts
+6 <= cverts
) {
8379 nvg__vset(&verts
[nverts
], c
[0], c
[1], q
.s0
, q
.t0
); ++nverts
;
8380 nvg__vset(&verts
[nverts
], c
[4], c
[5], q
.s1
, q
.t1
); ++nverts
;
8381 nvg__vset(&verts
[nverts
], c
[2], c
[3], q
.s1
, q
.t0
); ++nverts
;
8382 nvg__vset(&verts
[nverts
], c
[0], c
[1], q
.s0
, q
.t0
); ++nverts
;
8383 nvg__vset(&verts
[nverts
], c
[6], c
[7], q
.s0
, q
.t1
); ++nverts
;
8384 nvg__vset(&verts
[nverts
], c
[4], c
[5], q
.s1
, q
.t1
); ++nverts
;
8388 // TODO: add back-end bit to do this just once per frame
8390 nvg__flushTextTexture(ctx
);
8391 nvg__renderText(ctx
, verts
, nverts
);
8394 return iter
.nextx
/scale
;
8397 /** Draws multi-line text string at specified location wrapped at the specified width.
8398 * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8399 * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8403 public void textBox(T
) (NVGContext ctx
, float x
, float y
, float breakRowWidth
, const(T
)[] str) nothrow @trusted @nogc if (isAnyCharType
!T
) {
8404 NVGstate
* state
= nvg__getState(ctx
);
8405 if (state
.fontId
== FONS_INVALID
) return;
8407 NVGTextRow
!T
[2] rows
;
8408 auto oldAlign
= state
.textAlign
;
8409 scope(exit
) state
.textAlign
= oldAlign
;
8410 auto halign
= state
.textAlign
.horizontal
;
8413 ctx
.textMetrics(null, null, &lineh
);
8414 state
.textAlign
.horizontal
= NVGTextAlign
.H
.Left
;
8416 auto rres
= ctx
.textBreakLines(str, breakRowWidth
, rows
[]);
8417 //{ import core.stdc.stdio : printf; printf("slen=%u; rlen=%u; bw=%f\n", cast(uint)str.length, cast(uint)rres.length, cast(double)breakRowWidth); }
8418 if (rres
.length
== 0) break;
8419 foreach (ref row
; rres
) {
8420 final switch (halign
) {
8421 case NVGTextAlign
.H
.Left
: ctx
.text(x
, y
, row
.row
); break;
8422 case NVGTextAlign
.H
.Center
: ctx
.text(x
+breakRowWidth
*0.5f-row
.width
*0.5f, y
, row
.row
); break;
8423 case NVGTextAlign
.H
.Right
: ctx
.text(x
+breakRowWidth
-row
.width
, y
, row
.row
); break;
8425 y
+= lineh
*state
.lineHeight
;
8427 str = rres
[$-1].rest
;
8431 private template isGoodPositionDelegate(DG
) {
8433 static if (is(typeof({ NVGGlyphPosition pos
; bool res
= dg(pos
); })) ||
8434 is(typeof({ NVGGlyphPosition pos
; dg(pos
); })))
8435 enum isGoodPositionDelegate
= true;
8437 enum isGoodPositionDelegate
= false;
8440 /** Calculates the glyph x positions of the specified text.
8441 * Measured values are returned in local coordinate space.
8445 public NVGGlyphPosition
[] textGlyphPositions(T
) (NVGContext ctx
, float x
, float y
, const(T
)[] str, NVGGlyphPosition
[] positions
) nothrow @trusted @nogc
8446 if (isAnyCharType
!T
)
8448 if (str.length
== 0 || positions
.length
== 0) return positions
[0..0];
8450 auto len
= ctx
.textGlyphPositions(x
, y
, str, (in ref NVGGlyphPosition pos
) {
8451 positions
.ptr
[posnum
++] = pos
;
8452 return (posnum
< positions
.length
);
8454 return positions
[0..len
];
8458 public int textGlyphPositions(T
, DG
) (NVGContext ctx
, float x
, float y
, const(T
)[] str, scope DG dg
)
8459 if (isAnyCharType
!T
&& isGoodPositionDelegate
!DG
)
8461 import std
.traits
: ReturnType
;
8462 static if (is(ReturnType
!dg
== void)) enum RetBool
= false; else enum RetBool
= true;
8464 NVGstate
* state
= nvg__getState(ctx
);
8465 float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8466 float invscale
= 1.0f/scale
;
8467 FONSTextIter
!T iter
, prevIter
;
8471 if (str.length
== 0) return 0;
8473 ctx
.fs
.size
= state
.fontSize
*scale
;
8474 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
8475 ctx
.fs
.blur
= state
.fontBlur
*scale
;
8476 ctx
.fs
.textAlign
= state
.textAlign
;
8477 ctx
.fs
.fontId
= state
.fontId
;
8479 if (!iter
.setup(ctx
.fs
, x
*scale
, y
*scale
, str, FONSBitmapFlag
.Optional
)) return npos
;
8481 while (iter
.next(q
)) {
8482 if (iter
.prevGlyphIndex
< 0) { // can not retrieve glyph?
8483 if (!nvg__allocTextAtlas(ctx
)) break; // no memory
8485 iter
.next(q
); // try again
8486 if (iter
.prevGlyphIndex
< 0) {
8487 // still can not find glyph, try replacement
8489 if (!iter
.getDummyChar(q
)) break;
8493 NVGGlyphPosition position
= void; //WARNING!
8494 position
.strpos
= cast(usize
)(iter
.stringp
-str.ptr
);
8495 position
.x
= iter
.x
*invscale
;
8496 position
.minx
= nvg__min(iter
.x
, q
.x0
)*invscale
;
8497 position
.maxx
= nvg__max(iter
.nextx
, q
.x1
)*invscale
;
8499 static if (RetBool
) { if (!dg(position
)) return npos
; } else dg(position
);
8505 private template isGoodRowDelegate(CT
, DG
) {
8507 static if (is(typeof({ NVGTextRow
!CT row
; bool res
= dg(row
); })) ||
8508 is(typeof({ NVGTextRow
!CT row
; dg(row
); })))
8509 enum isGoodRowDelegate
= true;
8511 enum isGoodRowDelegate
= false;
8514 /** Breaks the specified text into lines.
8515 * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8516 * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8520 public NVGTextRow
!T
[] textBreakLines(T
) (NVGContext ctx
, const(T
)[] str, float breakRowWidth
, NVGTextRow
!T
[] rows
) nothrow @trusted @nogc
8521 if (isAnyCharType
!T
)
8523 if (rows
.length
== 0) return rows
;
8524 if (rows
.length
> int.max
-1) rows
= rows
[0..int.max
-1];
8526 auto count
= ctx
.textBreakLines(str, breakRowWidth
, (in ref NVGTextRow
!T row
) {
8528 return (nrow
< rows
.length
);
8530 return rows
[0..count
];
8533 /** Breaks the specified text into lines.
8534 * White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
8535 * Words longer than the max width are slit at nearest character (i.e. no hyphenation).
8536 * Returns number of rows.
8540 public int textBreakLines(T
, DG
) (NVGContext ctx
, const(T
)[] str, float breakRowWidth
, scope DG dg
)
8541 if (isAnyCharType
!T
&& isGoodRowDelegate
!(T
, DG
))
8543 import std
.traits
: ReturnType
;
8544 static if (is(ReturnType
!dg
== void)) enum RetBool
= false; else enum RetBool
= true;
8546 enum NVGcodepointType
: int {
8552 NVGstate
* state
= nvg__getState(ctx
);
8553 float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8554 float invscale
= 1.0f/scale
;
8555 FONSTextIter
!T iter
, prevIter
;
8558 float rowStartX
= 0;
8565 float wordStartX
= 0;
8568 float breakWidth
= 0;
8569 float breakMaxX
= 0;
8570 int type
= NVGcodepointType
.Space
, ptype
= NVGcodepointType
.Space
;
8571 uint pcodepoint
= 0;
8573 if (state
.fontId
== FONS_INVALID
) return 0;
8574 if (str.length
== 0 || dg
is null) return 0;
8576 ctx
.fs
.size
= state
.fontSize
*scale
;
8577 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
8578 ctx
.fs
.blur
= state
.fontBlur
*scale
;
8579 ctx
.fs
.textAlign
= state
.textAlign
;
8580 ctx
.fs
.fontId
= state
.fontId
;
8582 breakRowWidth
*= scale
;
8585 Normal
, // searching for breaking point
8586 SkipBlanks
, // skip leading blanks
8588 Phase phase
= Phase
.SkipBlanks
; // don't skip blanks on first line
8590 if (!iter
.setup(ctx
.fs
, 0, 0, str, FONSBitmapFlag
.Optional
)) return 0;
8592 while (iter
.next(q
)) {
8593 if (iter
.prevGlyphIndex
< 0) { // can not retrieve glyph?
8594 if (!nvg__allocTextAtlas(ctx
)) break; // no memory
8596 iter
.next(q
); // try again
8597 if (iter
.prevGlyphIndex
< 0) {
8598 // still can not find glyph, try replacement
8600 if (!iter
.getDummyChar(q
)) break;
8604 switch (iter
.codepoint
) {
8609 case 0x00a0: // NBSP
8610 type
= NVGcodepointType
.Space
;
8613 type
= (pcodepoint
== 13 ? NVGcodepointType
.Space
: NVGcodepointType
.NewLine
);
8616 type
= (pcodepoint
== 10 ? NVGcodepointType
.Space
: NVGcodepointType
.NewLine
);
8619 case 0x2028: // Line Separator
8620 case 0x2029: // Paragraph Separator
8621 type
= NVGcodepointType
.NewLine
;
8624 type
= NVGcodepointType
.Char
;
8627 if (phase
== Phase
.SkipBlanks
) {
8629 rowStart
= cast(int)(iter
.stringp
-str.ptr
);
8632 rowWidth
= iter
.nextx
-rowStartX
; // q.x1-rowStartX;
8633 rowMinX
= q
.x0
-rowStartX
;
8634 rowMaxX
= q
.x1
-rowStartX
;
8635 wordStart
= rowStart
;
8636 wordStartX
= iter
.x
;
8637 wordMinX
= q
.x0
-rowStartX
;
8638 breakEnd
= rowStart
;
8641 if (type
== NVGcodepointType
.Space
) continue;
8642 phase
= Phase
.Normal
;
8645 if (type
== NVGcodepointType
.NewLine
) {
8646 // always handle new lines
8649 row
.start
= rowStart
;
8651 row
.width
= rowWidth
*invscale
;
8652 row
.minx
= rowMinX
*invscale
;
8653 row
.maxx
= rowMaxX
*invscale
;
8655 static if (RetBool
) { if (!dg(row
)) return nrows
; } else dg(row
);
8656 phase
= Phase
.SkipBlanks
;
8658 float nextWidth
= iter
.nextx
-rowStartX
;
8659 // track last non-white space character
8660 if (type
== NVGcodepointType
.Char
) {
8661 rowEnd
= cast(int)(iter
.nextp
-str.ptr
);
8662 rowWidth
= iter
.nextx
-rowStartX
;
8663 rowMaxX
= q
.x1
-rowStartX
;
8665 // track last end of a word
8666 if (ptype
== NVGcodepointType
.Char
&& type
== NVGcodepointType
.Space
) {
8667 breakEnd
= cast(int)(iter
.stringp
-str.ptr
);
8668 breakWidth
= rowWidth
;
8669 breakMaxX
= rowMaxX
;
8671 // track last beginning of a word
8672 if (ptype
== NVGcodepointType
.Space
&& type
== NVGcodepointType
.Char
) {
8673 wordStart
= cast(int)(iter
.stringp
-str.ptr
);
8674 wordStartX
= iter
.x
;
8675 wordMinX
= q
.x0
-rowStartX
;
8677 // break to new line when a character is beyond break width
8678 if (type
== NVGcodepointType
.Char
&& nextWidth
> breakRowWidth
) {
8679 // the run length is too long, need to break to new line
8682 if (breakEnd
== rowStart
) {
8683 // the current word is longer than the row length, just break it from here
8684 row
.start
= rowStart
;
8685 row
.end
= cast(int)(iter
.stringp
-str.ptr
);
8686 row
.width
= rowWidth
*invscale
;
8687 row
.minx
= rowMinX
*invscale
;
8688 row
.maxx
= rowMaxX
*invscale
;
8690 static if (RetBool
) { if (!dg(row
)) return nrows
; } else dg(row
);
8692 rowStart
= cast(int)(iter
.stringp
-str.ptr
);
8693 rowEnd
= cast(int)(iter
.nextp
-str.ptr
);
8694 rowWidth
= iter
.nextx
-rowStartX
;
8695 rowMinX
= q
.x0
-rowStartX
;
8696 rowMaxX
= q
.x1
-rowStartX
;
8697 wordStart
= rowStart
;
8698 wordStartX
= iter
.x
;
8699 wordMinX
= q
.x0
-rowStartX
;
8701 // break the line from the end of the last word, and start new line from the beginning of the new
8702 //{ import core.stdc.stdio : printf; printf("rowStart=%u; rowEnd=%u; breakEnd=%u; len=%u\n", rowStart, rowEnd, breakEnd, cast(uint)str.length); }
8703 row
.start
= rowStart
;
8705 row
.width
= breakWidth
*invscale
;
8706 row
.minx
= rowMinX
*invscale
;
8707 row
.maxx
= breakMaxX
*invscale
;
8709 static if (RetBool
) { if (!dg(row
)) return nrows
; } else dg(row
);
8710 rowStartX
= wordStartX
;
8711 rowStart
= wordStart
;
8712 rowEnd
= cast(int)(iter
.nextp
-str.ptr
);
8713 rowWidth
= iter
.nextx
-rowStartX
;
8715 rowMaxX
= q
.x1
-rowStartX
;
8716 // no change to the word start
8718 // set null break point
8719 breakEnd
= rowStart
;
8725 pcodepoint
= iter
.codepoint
;
8729 // break the line from the end of the last word, and start new line from the beginning of the new
8730 if (phase
!= Phase
.SkipBlanks
&& rowStart
< str.length
) {
8731 //{ import core.stdc.stdio : printf; printf(" rowStart=%u; len=%u\n", rowStart, cast(uint)str.length); }
8734 row
.start
= rowStart
;
8735 row
.end
= cast(int)str.length
;
8736 row
.width
= rowWidth
*invscale
;
8737 row
.minx
= rowMinX
*invscale
;
8738 row
.maxx
= rowMaxX
*invscale
;
8740 static if (RetBool
) { if (!dg(row
)) return nrows
; } else dg(row
);
8746 /** Returns iterator which you can use to calculate text bounds and advancement.
8747 * This is usable when you need to do some text layouting with wrapping, to avoid
8748 * guesswork ("will advancement for this space stay the same?"), and Schlemiel's
8749 * algorithm. Note that you can copy the returned struct to save iterator state.
8751 * You can check if iterator is valid with [valid] property, put new chars with
8752 * [put] method, get current advance with [advance] property, and current
8753 * bounds with `getBounds(ref float[4] bounds)` method.
8755 * $(WARNING Don't change font parameters while iterating! Or use [restoreFont] method.)
8759 public struct TextBoundsIterator
{
8762 FONSTextBoundsIterator fsiter
; // fontstash iterator
8763 float scale
, invscale
, xscaled
, yscaled
;
8765 float fsSize
, fsSpacing
, fsBlur
;
8767 NVGTextAlign fsAlign
;
8770 /// Setups iteration. Takes current font parameters from the given NanoVega context.
8771 this (NVGContext actx
, float ax
=0, float ay
=0) nothrow @trusted @nogc { reset(actx
, ax
, ay
); }
8773 /// Resets iteration. Takes current font parameters from the given NanoVega context.
8774 void reset (NVGContext actx
, float ax
=0, float ay
=0) nothrow @trusted @nogc {
8775 fsiter
= fsiter
.init
;
8777 if (actx
is null) return;
8778 NVGstate
* state
= nvg__getState(actx
);
8779 if (state
is null) return;
8780 if (state
.fontId
== FONS_INVALID
) { ctx
= null; return; }
8783 scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8784 invscale
= 1.0f/scale
;
8786 fsSize
= state
.fontSize
*scale
;
8787 fsSpacing
= state
.letterSpacing
*scale
;
8788 fsBlur
= state
.fontBlur
*scale
;
8789 fsAlign
= state
.textAlign
;
8790 fsFontId
= state
.fontId
;
8795 fsiter
.reset(ctx
.fs
, xscaled
, yscaled
);
8798 /// Restart iteration. Will not restore font.
8799 void restart () nothrow @trusted @nogc {
8800 if (ctx
!is null) fsiter
.reset(ctx
.fs
, xscaled
, yscaled
);
8803 /// Restore font settings for the context.
8804 void restoreFont () nothrow @trusted @nogc {
8806 ctx
.fs
.size
= fsSize
;
8807 ctx
.fs
.spacing
= fsSpacing
;
8808 ctx
.fs
.blur
= fsBlur
;
8809 ctx
.fs
.textAlign
= fsAlign
;
8810 ctx
.fs
.fontId
= fsFontId
;
8814 /// Is this iterator valid?
8815 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (ctx
!is null); }
8818 void put(T
) (const(T
)[] str...) nothrow @trusted @nogc if (isAnyCharType
!T
) { pragma(inline
, true); if (ctx
!is null) fsiter
.put(str[]); }
8820 /// Returns current advance
8821 @property float advance () const pure nothrow @safe @nogc { pragma(inline
, true); return (ctx
!is null ? fsiter
.advance
*invscale
: 0); }
8823 /// Returns current text bounds.
8824 void getBounds (ref float[4] bounds
) nothrow @trusted @nogc {
8826 fsiter
.getBounds(bounds
);
8827 ctx
.fs
.getLineBounds(yscaled
, &bounds
[1], &bounds
[3]);
8828 bounds
[0] *= invscale
;
8829 bounds
[1] *= invscale
;
8830 bounds
[2] *= invscale
;
8831 bounds
[3] *= invscale
;
8837 /// Returns current horizontal text bounds.
8838 void getHBounds (out float xmin
, out float xmax
) nothrow @trusted @nogc {
8840 fsiter
.getHBounds(xmin
, xmax
);
8846 /// Returns current vertical text bounds.
8847 void getVBounds (out float ymin
, out float ymax
) nothrow @trusted @nogc {
8849 //fsiter.getVBounds(ymin, ymax);
8850 ctx
.fs
.getLineBounds(yscaled
, &ymin
, &ymax
);
8857 /// Returns font line height (without line spacing), measured in local coordinate space.
8859 public float textFontHeight (NVGContext ctx
) nothrow @trusted @nogc {
8861 ctx
.textMetrics(null, null, &res
);
8865 /// Returns font ascender (positive), measured in local coordinate space.
8867 public float textFontAscender (NVGContext ctx
) nothrow @trusted @nogc {
8869 ctx
.textMetrics(&res
, null, null);
8873 /// Returns font descender (negative), measured in local coordinate space.
8875 public float textFontDescender (NVGContext ctx
) nothrow @trusted @nogc {
8877 ctx
.textMetrics(null, &res
, null);
8881 /** Measures the specified text string. Returns horizontal and vertical sizes of the measured text.
8882 * Measured values are returned in local coordinate space.
8886 public void textExtents(T
) (NVGContext ctx
, const(T
)[] str, float *w
, float *h
) nothrow @trusted @nogc if (isAnyCharType
!T
) {
8887 float[4] bnd
= void;
8888 ctx
.textBounds(0, 0, str, bnd
[]);
8889 if (!ctx
.fs
.getFontAA(nvg__getState(ctx
).fontId
)) {
8890 if (w
!is null) *w
= nvg__lrintf(bnd
.ptr
[2]-bnd
.ptr
[0]);
8891 if (h
!is null) *h
= nvg__lrintf(bnd
.ptr
[3]-bnd
.ptr
[1]);
8893 if (w
!is null) *w
= bnd
.ptr
[2]-bnd
.ptr
[0];
8894 if (h
!is null) *h
= bnd
.ptr
[3]-bnd
.ptr
[1];
8898 /** Measures the specified text string. Returns horizontal size of the measured text.
8899 * Measured values are returned in local coordinate space.
8903 public float textWidth(T
) (NVGContext ctx
, const(T
)[] str) nothrow @trusted @nogc if (isAnyCharType
!T
) {
8905 ctx
.textExtents(str, &w
, null);
8909 /** Measures the specified text string. Parameter bounds should be a float[4],
8910 * if the bounding box of the text should be returned. The bounds value are [xmin, ymin, xmax, ymax]
8911 * Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
8912 * Measured values are returned in local coordinate space.
8916 public float textBounds(T
) (NVGContext ctx
, float x
, float y
, const(T
)[] str, float[] bounds
) nothrow @trusted @nogc
8917 if (isAnyCharType
!T
)
8919 NVGstate
* state
= nvg__getState(ctx
);
8921 if (state
.fontId
== FONS_INVALID
) {
8926 immutable float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8927 ctx
.fs
.size
= state
.fontSize
*scale
;
8928 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
8929 ctx
.fs
.blur
= state
.fontBlur
*scale
;
8930 ctx
.fs
.textAlign
= state
.textAlign
;
8931 ctx
.fs
.fontId
= state
.fontId
;
8934 immutable float width
= ctx
.fs
.getTextBounds(x
*scale
, y
*scale
, str, b
[]);
8935 immutable float invscale
= 1.0f/scale
;
8936 if (bounds
.length
) {
8937 // use line bounds for height
8938 ctx
.fs
.getLineBounds(y
*scale
, b
.ptr
+1, b
.ptr
+3);
8939 if (bounds
.length
> 0) bounds
.ptr
[0] = b
.ptr
[0]*invscale
;
8940 if (bounds
.length
> 1) bounds
.ptr
[1] = b
.ptr
[1]*invscale
;
8941 if (bounds
.length
> 2) bounds
.ptr
[2] = b
.ptr
[2]*invscale
;
8942 if (bounds
.length
> 3) bounds
.ptr
[3] = b
.ptr
[3]*invscale
;
8944 return width
*invscale
;
8948 public void textBoxBounds(T
) (NVGContext ctx
, float x
, float y
, float breakRowWidth
, const(T
)[] str, float[] bounds
) if (isAnyCharType
!T
) {
8949 NVGstate
* state
= nvg__getState(ctx
);
8950 NVGTextRow
!T
[2] rows
;
8951 float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
8952 float invscale
= 1.0f/scale
;
8953 float lineh
= 0, rminy
= 0, rmaxy
= 0;
8954 float minx
, miny
, maxx
, maxy
;
8956 if (state
.fontId
== FONS_INVALID
) {
8961 auto oldAlign
= state
.textAlign
;
8962 scope(exit
) state
.textAlign
= oldAlign
;
8963 auto halign
= state
.textAlign
.horizontal
;
8965 ctx
.textMetrics(null, null, &lineh
);
8966 state
.textAlign
.horizontal
= NVGTextAlign
.H
.Left
;
8971 ctx
.fs
.size
= state
.fontSize
*scale
;
8972 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
8973 ctx
.fs
.blur
= state
.fontBlur
*scale
;
8974 ctx
.fs
.textAlign
= state
.textAlign
;
8975 ctx
.fs
.fontId
= state
.fontId
;
8976 ctx
.fs
.getLineBounds(0, &rminy
, &rmaxy
);
8981 auto rres
= ctx
.textBreakLines(str, breakRowWidth
, rows
[]);
8982 if (rres
.length
== 0) break;
8983 foreach (ref row
; rres
) {
8984 float rminx
, rmaxx
, dx
= 0;
8985 // horizontal bounds
8986 final switch (halign
) {
8987 case NVGTextAlign
.H
.Left
: dx
= 0; break;
8988 case NVGTextAlign
.H
.Center
: dx
= breakRowWidth
*0.5f-row
.width
*0.5f; break;
8989 case NVGTextAlign
.H
.Right
: dx
= breakRowWidth
-row
.width
; break;
8991 rminx
= x
+row
.minx
+dx
;
8992 rmaxx
= x
+row
.maxx
+dx
;
8993 minx
= nvg__min(minx
, rminx
);
8994 maxx
= nvg__max(maxx
, rmaxx
);
8996 miny
= nvg__min(miny
, y
+rminy
);
8997 maxy
= nvg__max(maxy
, y
+rmaxy
);
8998 y
+= lineh
*state
.lineHeight
;
9000 str = rres
[$-1].rest
;
9003 if (bounds
.length
) {
9004 if (bounds
.length
> 0) bounds
.ptr
[0] = minx
;
9005 if (bounds
.length
> 1) bounds
.ptr
[1] = miny
;
9006 if (bounds
.length
> 2) bounds
.ptr
[2] = maxx
;
9007 if (bounds
.length
> 3) bounds
.ptr
[3] = maxy
;
9011 /// Returns the vertical metrics based on the current text style. Measured values are returned in local coordinate space.
9013 public void textMetrics (NVGContext ctx
, float* ascender
, float* descender
, float* lineh
) nothrow @trusted @nogc {
9014 NVGstate
* state
= nvg__getState(ctx
);
9016 if (state
.fontId
== FONS_INVALID
) {
9017 if (ascender
!is null) *ascender
*= 0;
9018 if (descender
!is null) *descender
*= 0;
9019 if (lineh
!is null) *lineh
*= 0;
9023 immutable float scale
= nvg__getFontScale(state
)*ctx
.devicePxRatio
;
9024 immutable float invscale
= 1.0f/scale
;
9026 ctx
.fs
.size
= state
.fontSize
*scale
;
9027 ctx
.fs
.spacing
= state
.letterSpacing
*scale
;
9028 ctx
.fs
.blur
= state
.fontBlur
*scale
;
9029 ctx
.fs
.textAlign
= state
.textAlign
;
9030 ctx
.fs
.fontId
= state
.fontId
;
9032 ctx
.fs
.getVertMetrics(ascender
, descender
, lineh
);
9033 if (ascender
!is null) *ascender
*= invscale
;
9034 if (descender
!is null) *descender
*= invscale
;
9035 if (lineh
!is null) *lineh
*= invscale
;
9039 // ////////////////////////////////////////////////////////////////////////// //
9041 // ////////////////////////////////////////////////////////////////////////// //
9042 import core
.stdc
.stdlib
: malloc
, realloc
, free
;
9043 import core
.stdc
.string
: memset
, memcpy
, strncpy
, strcmp
, strlen
;
9044 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, fseek
, ftell
, fread
, SEEK_END
, SEEK_SET
;
9047 // welcome to version hell!
9048 version(nanovg_force_stb_ttf
) {
9050 version(nanovg_force_detect
) {} else version(nanovg_use_freetype
) { version = nanovg_use_freetype_ii
; }
9052 version(nanovg_ignore_iv_stb_ttf
) enum nanovg_ignore_iv_stb_ttf
= true; else enum nanovg_ignore_iv_stb_ttf
= false;
9053 //version(nanovg_ignore_mono);
9055 version(nanovg_force_stb_ttf
) {
9056 private enum NanoVegaForceFreeType
= false;
9058 version (nanovg_builtin_freetype_bindings
) {
9060 private enum NanoVegaForceFreeType
= true;
9062 private enum NanoVegaForceFreeType
= false;
9066 private enum NanoVegaForceFreeType
= true;
9068 private enum NanoVegaForceFreeType
= false;
9073 version(nanovg_use_freetype_ii
) {
9074 enum NanoVegaIsUsingSTBTTF
= false;
9075 //pragma(msg, "iv.freetype: forced");
9077 static if (NanoVegaForceFreeType
) {
9078 enum NanoVegaIsUsingSTBTTF
= false;
9080 static if (!nanovg_ignore_iv_stb_ttf
&& __traits(compiles
, { import iv
.stb
.ttf
; })) {
9082 enum NanoVegaIsUsingSTBTTF
= true;
9083 version(nanovg_report_stb_ttf
) pragma(msg
, "iv.stb.ttf");
9084 } else static if (__traits(compiles
, { import arsd
.ttf
; })) {
9086 enum NanoVegaIsUsingSTBTTF
= true;
9087 version(nanovg_report_stb_ttf
) pragma(msg
, "arsd.ttf");
9088 } else static if (__traits(compiles
, { import stb_truetype
; })) {
9089 import stb_truetype
;
9090 enum NanoVegaIsUsingSTBTTF
= true;
9091 version(nanovg_report_stb_ttf
) pragma(msg
, "stb_truetype");
9092 } else static if (__traits(compiles
, { import iv
.freetype
; })) {
9093 version (nanovg_builtin_freetype_bindings
) {
9094 enum NanoVegaIsUsingSTBTTF
= false;
9095 version = nanovg_builtin_freetype_bindings
;
9098 enum NanoVegaIsUsingSTBTTF
= false;
9100 version(nanovg_report_stb_ttf
) pragma(msg
, "freetype");
9102 static assert(0, "no stb_ttf/iv.freetype found!");
9108 // ////////////////////////////////////////////////////////////////////////// //
9109 //version = nanovg_ft_mono;
9112 /// Group: font_stash
9113 public enum FONS_INVALID
= -1;
9115 public enum FONSBitmapFlag
: uint {
9120 public enum FONSError
: int {
9122 AtlasFull
= 1, // Font atlas is full.
9123 StatesOverflow
= 2, // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
9124 StatesUnderflow
= 3, // Trying to pop too many states fonsPopState().
9127 /// Initial parameters for new FontStash.
9128 /// Group: font_stash
9129 public struct FONSParams
{
9131 ZeroTopLeft
= 0U, // default
9132 ZeroBottomLeft
= 1U,
9135 Flag flags
= Flag
.ZeroTopLeft
;
9137 bool function (void* uptr
, int width
, int height
) nothrow @trusted @nogc renderCreate
;
9138 int function (void* uptr
, int width
, int height
) nothrow @trusted @nogc renderResize
;
9139 void function (void* uptr
, int* rect
, const(ubyte)* data
) nothrow @trusted @nogc renderUpdate
;
9140 void function (void* uptr
) nothrow @trusted @nogc renderDelete
;
9141 @property bool isZeroTopLeft () const pure nothrow @trusted @nogc { pragma(inline
, true); return ((flags
&Flag
.ZeroBottomLeft
) == 0); }
9144 //TODO: document this
9145 public struct FONSQuad
{
9146 float x0
=0, y0
=0, s0
=0, t0
=0;
9147 float x1
=0, y1
=0, s1
=0, t1
=0;
9150 //TODO: document this
9151 public struct FONSTextIter(CT
) if (isAnyCharType
!CT
) {
9152 alias CharType
= CT
;
9153 float x
=0, y
=0, nextx
=0, nexty
=0, scale
=0, spacing
=0;
9159 const(CT
)* s
; // string
9160 const(CT
)* n
; // next
9161 const(CT
)* e
; // end
9162 FONSBitmapFlag bitmapOption
;
9163 static if (is(CT
== char)) {
9167 this (FONSContext astash
, float ax
, float ay
, const(CharType
)[] astr
, FONSBitmapFlag abitmapOption
) nothrow @trusted @nogc { setup(astash
, ax
, ay
, astr
, abitmapOption
); }
9168 ~this () nothrow @trusted @nogc { pragma(inline
, true); static if (is(CT
== char)) utf8state
= 0; s
= n
= e
= null; }
9170 @property const(CT
)* stringp () const pure nothrow @trusted @nogc { pragma(inline
, true); return s
; }
9171 @property const(CT
)* nextp () const pure nothrow @trusted @nogc { pragma(inline
, true); return n
; }
9172 @property const(CT
)* endp () const pure nothrow @trusted @nogc { pragma(inline
, true); return e
; }
9174 bool setup (FONSContext astash
, float ax
, float ay
, const(CharType
)[] astr
, FONSBitmapFlag abitmapOption
) nothrow @trusted @nogc {
9175 import core
.stdc
.string
: memset
;
9177 memset(&this, 0, this.sizeof
);
9178 if (astash
is null) return false;
9180 FONSstate
* state
= astash
.getState
;
9182 if (state
.font
< 0 || state
.font
>= astash
.nfonts
) return false;
9183 font
= astash
.fonts
[state
.font
];
9184 if (font
is null || font
.fdata
is null) return false;
9186 isize
= cast(short)(state
.size
*10.0f);
9187 iblur
= cast(short)state
.blur
;
9188 scale
= fons__tt_getPixelHeightScale(&font
.font
, cast(float)isize
/10.0f);
9190 // align horizontally
9191 if (state
.talign
.left
) {
9193 } else if (state
.talign
.right
) {
9194 immutable float width
= astash
.getTextBounds(ax
, ay
, astr
, null);
9196 } else if (state
.talign
.center
) {
9197 immutable float width
= astash
.getTextBounds(ax
, ay
, astr
, null);
9202 ay
+= astash
.getVertAlign(font
, state
.talign
, isize
);
9206 spacing
= state
.spacing
;
9208 if (astr
.ptr
is null) {
9209 static if (is(CharType
== char)) astr
= "";
9210 else static if (is(CharType
== wchar)) astr
= ""w
;
9211 else static if (is(CharType
== dchar)) astr
= ""d
;
9212 else static assert(0, "wtf?!");
9216 e
= astr
.ptr
+astr
.length
;
9219 prevGlyphIndex
= -1;
9220 bitmapOption
= abitmapOption
;
9226 bool getDummyChar (ref FONSQuad quad
) nothrow @trusted @nogc {
9227 if (stash
is null || font
is null) return false;
9228 // get glyph and quad
9231 FONSglyph
* glyph
= stash
.getGlyph(font
, 0xFFFD, isize
, iblur
, bitmapOption
);
9232 if (glyph
!is null) {
9233 stash
.getQuad(font
, prevGlyphIndex
, glyph
, isize
/10.0f, scale
, spacing
, &nextx
, &nexty
, &quad
);
9234 prevGlyphIndex
= glyph
.index
;
9237 prevGlyphIndex
= -1;
9242 bool next (ref FONSQuad quad
) nothrow @trusted @nogc {
9243 if (stash
is null || font
is null) return false;
9244 FONSglyph
* glyph
= null;
9245 static if (is(CharType
== char)) {
9246 const(char)* str = this.n
;
9248 if (str is this.e
) return false;
9249 const(char)* e
= this.e
;
9250 for (; str !is e
; ++str) {
9251 /*if (fons__decutf8(&utf8state, &codepoint, *cast(const(ubyte)*)str)) continue;*/
9252 mixin(DecUtfMixin
!("this.utf8state", "this.codepoint", "*cast(const(ubyte)*)str"));
9253 if (utf8state
) continue;
9254 ++str; // 'cause we'll break anyway
9255 // get glyph and quad
9258 glyph
= stash
.getGlyph(font
, codepoint
, isize
, iblur
, bitmapOption
);
9259 if (glyph
!is null) {
9260 stash
.getQuad(font
, prevGlyphIndex
, glyph
, isize
/10.0f, scale
, spacing
, &nextx
, &nexty
, &quad
);
9261 prevGlyphIndex
= glyph
.index
;
9263 prevGlyphIndex
= -1;
9269 const(CharType
)* str = this.n
;
9271 if (str is this.e
) return false;
9272 codepoint
= cast(uint)(*str++);
9273 if (codepoint
> dchar.max
) codepoint
= 0xFFFD;
9274 // get glyph and quad
9277 glyph
= stash
.getGlyph(font
, codepoint
, isize
, iblur
, bitmapOption
);
9278 if (glyph
!is null) {
9279 stash
.getQuad(font
, prevGlyphIndex
, glyph
, isize
/10.0f, scale
, spacing
, &nextx
, &nexty
, &quad
);
9280 prevGlyphIndex
= glyph
.index
;
9282 prevGlyphIndex
= -1;
9291 // ////////////////////////////////////////////////////////////////////////// //
9292 //static if (!HasAST) version = nanovg_use_freetype_ii_x;
9294 /*version(nanovg_use_freetype_ii_x)*/ static if (!NanoVegaIsUsingSTBTTF
) {
9295 version(nanovg_builtin_freetype_bindings
) {
9296 pragma(lib
, "freetype");
9297 private extern(C
) nothrow @trusted @nogc {
9298 private import core
.stdc
.config
: c_long
, c_ulong
;
9299 alias FT_Pos
= c_long
;
9300 // config/ftconfig.h
9301 alias FT_Int16
= short;
9302 alias FT_UInt16
= ushort;
9303 alias FT_Int32
= int;
9304 alias FT_UInt32
= uint;
9305 alias FT_Fast
= int;
9306 alias FT_UFast
= uint;
9307 alias FT_Int64
= long;
9308 alias FT_Uint64
= ulong;
9310 alias FT_Bool
= ubyte;
9311 alias FT_FWord
= short;
9312 alias FT_UFWord
= ushort;
9313 alias FT_Char
= char;
9314 alias FT_Byte
= ubyte;
9315 alias FT_Bytes
= FT_Byte
*;
9316 alias FT_Tag
= FT_UInt32
;
9317 alias FT_String
= char;
9318 alias FT_Short
= short;
9319 alias FT_UShort
= ushort;
9321 alias FT_UInt
= uint;
9322 alias FT_Long
= c_long
;
9323 alias FT_ULong
= c_ulong
;
9324 alias FT_F2Dot14
= short;
9325 alias FT_F26Dot6
= c_long
;
9326 alias FT_Fixed
= c_long
;
9327 alias FT_Error
= int;
9328 alias FT_Pointer
= void*;
9329 alias FT_Offset
= usize
;
9330 alias FT_PtrDist
= ptrdiff_t
;
9332 struct FT_UnitVector
{
9343 const(FT_Byte
)* pointer
;
9346 alias FT_Face
= FT_FaceRec
*;
9351 FT_Long style_flags
;
9353 FT_String
* family_name
;
9354 FT_String
* style_name
;
9355 FT_Int num_fixed_sizes
;
9356 FT_Bitmap_Size
* available_sizes
;
9357 FT_Int num_charmaps
;
9358 FT_CharMap
* charmaps
;
9361 FT_UShort units_per_EM
;
9365 FT_Short max_advance_width
;
9366 FT_Short max_advance_height
;
9367 FT_Short underline_position
;
9368 FT_Short underline_thickness
;
9375 FT_ListRec sizes_list
;
9376 FT_Generic autohint
;
9378 FT_Face_Internal internal
;
9380 struct FT_Bitmap_Size
{
9387 alias FT_CharMap
= FT_CharMapRec
*;
9388 struct FT_CharMapRec
{
9390 FT_Encoding encoding
;
9391 FT_UShort platform_id
;
9392 FT_UShort encoding_id
;
9394 extern(C
) nothrow @nogc { alias FT_Generic_Finalizer
= void function (void* object
); }
9397 FT_Generic_Finalizer finalizer
;
9407 alias FT_Pixel_Mode
= int;
9409 FT_PIXEL_MODE_NONE
= 0,
9412 FT_PIXEL_MODE_GRAY2
,
9413 FT_PIXEL_MODE_GRAY4
,
9415 FT_PIXEL_MODE_LCD_V
,
9436 alias FT_GlyphSlot
= FT_GlyphSlotRec
*;
9437 struct FT_GlyphSlotRec
{
9443 FT_Glyph_Metrics metrics
;
9444 FT_Fixed linearHoriAdvance
;
9445 FT_Fixed linearVertAdvance
;
9447 FT_Glyph_Format format
;
9452 FT_UInt num_subglyphs
;
9453 FT_SubGlyph subglyphs
;
9459 FT_Slot_Internal internal
;
9461 alias FT_Size
= FT_SizeRec
*;
9465 FT_Size_Metrics metrics
;
9466 FT_Size_Internal internal
;
9468 alias FT_Encoding
= FT_Tag
;
9469 alias FT_Face_Internal
= void*;
9470 alias FT_Driver
= void*;
9471 alias FT_Memory
= void*;
9472 alias FT_Stream
= void*;
9473 alias FT_Library
= void*;
9474 alias FT_SubGlyph
= void*;
9475 alias FT_Slot_Internal
= void*;
9476 alias FT_Size_Internal
= void*;
9477 alias FT_ListNode
= FT_ListNodeRec
*;
9478 alias FT_List
= FT_ListRec
*;
9479 struct FT_ListNodeRec
{
9488 struct FT_Glyph_Metrics
{
9491 FT_Pos horiBearingX
;
9492 FT_Pos horiBearingY
;
9494 FT_Pos vertBearingX
;
9495 FT_Pos vertBearingY
;
9498 alias FT_Glyph_Format
= FT_Tag
;
9499 FT_Tag
FT_MAKE_TAG (char x1
, char x2
, char x3
, char x4
) pure nothrow @safe @nogc {
9500 pragma(inline
, true);
9501 return cast(FT_UInt32
)((x1
<<24)|
(x2
<<16)|
(x3
<<8)|x4
);
9504 FT_GLYPH_FORMAT_NONE
= 0,
9505 FT_GLYPH_FORMAT_COMPOSITE
= FT_MAKE_TAG('c','o','m','p'),
9506 FT_GLYPH_FORMAT_BITMAP
= FT_MAKE_TAG('b','i','t','s'),
9507 FT_GLYPH_FORMAT_OUTLINE
= FT_MAKE_TAG('o','u','t','l'),
9508 FT_GLYPH_FORMAT_PLOTTER
= FT_MAKE_TAG('p','l','o','t'),
9510 struct FT_Size_Metrics
{
9522 enum FT_LOAD_DEFAULT
= 0x0U
;
9523 enum FT_LOAD_NO_SCALE
= 1U<<0;
9524 enum FT_LOAD_NO_HINTING
= 1U<<1;
9525 enum FT_LOAD_RENDER
= 1U<<2;
9526 enum FT_LOAD_NO_BITMAP
= 1U<<3;
9527 enum FT_LOAD_VERTICAL_LAYOUT
= 1U<<4;
9528 enum FT_LOAD_FORCE_AUTOHINT
= 1U<<5;
9529 enum FT_LOAD_CROP_BITMAP
= 1U<<6;
9530 enum FT_LOAD_PEDANTIC
= 1U<<7;
9531 enum FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
= 1U<<9;
9532 enum FT_LOAD_NO_RECURSE
= 1U<<10;
9533 enum FT_LOAD_IGNORE_TRANSFORM
= 1U<<11;
9534 enum FT_LOAD_MONOCHROME
= 1U<<12;
9535 enum FT_LOAD_LINEAR_DESIGN
= 1U<<13;
9536 enum FT_LOAD_NO_AUTOHINT
= 1U<<15;
9537 enum FT_LOAD_COLOR
= 1U<<20;
9538 enum FT_LOAD_COMPUTE_METRICS
= 1U<<21;
9539 enum FT_FACE_FLAG_KERNING
= 1U<<6;
9540 alias FT_Kerning_Mode
= int;
9541 enum /*FT_Kerning_Mode*/ {
9542 FT_KERNING_DEFAULT
= 0,
9543 FT_KERNING_UNFITTED
,
9546 extern(C
) nothrow @nogc {
9547 alias FT_Outline_MoveToFunc
= int function (const(FT_Vector
)*, void*);
9548 alias FT_Outline_LineToFunc
= int function (const(FT_Vector
)*, void*);
9549 alias FT_Outline_ConicToFunc
= int function (const(FT_Vector
)*, const(FT_Vector
)*, void*);
9550 alias FT_Outline_CubicToFunc
= int function (const(FT_Vector
)*, const(FT_Vector
)*, const(FT_Vector
)*, void*);
9552 struct FT_Outline_Funcs
{
9553 FT_Outline_MoveToFunc move_to
;
9554 FT_Outline_LineToFunc line_to
;
9555 FT_Outline_ConicToFunc conic_to
;
9556 FT_Outline_CubicToFunc cubic_to
;
9561 FT_Error
FT_Init_FreeType (FT_Library
*);
9562 FT_Error
FT_New_Memory_Face (FT_Library
, const(FT_Byte
)*, FT_Long
, FT_Long
, FT_Face
*);
9563 FT_UInt
FT_Get_Char_Index (FT_Face
, FT_ULong
);
9564 FT_Error
FT_Set_Pixel_Sizes (FT_Face
, FT_UInt
, FT_UInt
);
9565 FT_Error
FT_Load_Glyph (FT_Face
, FT_UInt
, FT_Int32
);
9566 FT_Error
FT_Get_Advance (FT_Face
, FT_UInt
, FT_Int32
, FT_Fixed
*);
9567 FT_Error
FT_Get_Kerning (FT_Face
, FT_UInt
, FT_UInt
, FT_UInt
, FT_Vector
*);
9568 void FT_Outline_Get_CBox (const(FT_Outline
)*, FT_BBox
*);
9569 FT_Error
FT_Outline_Decompose (FT_Outline
*, const(FT_Outline_Funcs
)*, void*);
9575 struct FONSttFontImpl
{
9577 bool mono
; // no aa?
9580 __gshared FT_Library ftLibrary
;
9582 int fons__tt_init (FONSContext context
) nothrow @trusted @nogc {
9584 //FONS_NOTUSED(context);
9585 ftError
= FT_Init_FreeType(&ftLibrary
);
9586 return (ftError
== 0);
9589 void fons__tt_setMono (FONSContext context
, FONSttFontImpl
* font
, bool v
) nothrow @trusted @nogc {
9593 bool fons__tt_getMono (FONSContext context
, FONSttFontImpl
* font
) nothrow @trusted @nogc {
9597 int fons__tt_loadFont (FONSContext context
, FONSttFontImpl
* font
, ubyte* data
, int dataSize
) nothrow @trusted @nogc {
9599 //font.font.userdata = stash;
9600 ftError
= FT_New_Memory_Face(ftLibrary
, cast(const(FT_Byte
)*)data
, dataSize
, 0, &font
.font
);
9601 return ftError
== 0;
9604 void fons__tt_getFontVMetrics (FONSttFontImpl
* font
, int* ascent
, int* descent
, int* lineGap
) nothrow @trusted @nogc {
9605 *ascent
= font
.font
.ascender
;
9606 *descent
= font
.font
.descender
;
9607 *lineGap
= font
.font
.height
-(*ascent
- *descent
);
9610 float fons__tt_getPixelHeightScale (FONSttFontImpl
* font
, float size
) nothrow @trusted @nogc {
9611 return size
/(font
.font
.ascender
-font
.font
.descender
);
9614 int fons__tt_getGlyphIndex (FONSttFontImpl
* font
, int codepoint
) nothrow @trusted @nogc {
9615 return FT_Get_Char_Index(font
.font
, codepoint
);
9618 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 {
9620 FT_GlyphSlot ftGlyph
;
9621 //version(nanovg_ignore_mono) enum exflags = 0;
9622 //else version(nanovg_ft_mono) enum exflags = FT_LOAD_MONOCHROME; else enum exflags = 0;
9623 uint exflags
= (font
.mono ? FT_LOAD_MONOCHROME
: 0);
9624 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
)));
9625 if (ftError
) return 0;
9626 ftError
= FT_Load_Glyph(font
.font
, glyph
, FT_LOAD_RENDER|
/*FT_LOAD_NO_AUTOHINT|*/exflags
);
9627 if (ftError
) return 0;
9628 ftError
= FT_Get_Advance(font
.font
, glyph
, FT_LOAD_NO_SCALE|
/*FT_LOAD_NO_AUTOHINT|*/exflags
, cast(FT_Fixed
*)advance
);
9629 if (ftError
) return 0;
9630 ftGlyph
= font
.font
.glyph
;
9631 *lsb
= cast(int)ftGlyph
.metrics
.horiBearingX
;
9632 *x0
= ftGlyph
.bitmap_left
;
9633 *x1
= *x0
+ftGlyph
.bitmap
.width
;
9634 *y0
= -ftGlyph
.bitmap_top
;
9635 *y1
= *y0
+ftGlyph
.bitmap
.rows
;
9639 void fons__tt_renderGlyphBitmap (FONSttFontImpl
* font
, ubyte* output
, int outWidth
, int outHeight
, int outStride
, float scaleX
, float scaleY
, int glyph
) nothrow @trusted @nogc {
9640 FT_GlyphSlot ftGlyph
= font
.font
.glyph
;
9641 //FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap
9642 //version(nanovg_ignore_mono) enum RenderAA = true;
9643 //else version(nanovg_ft_mono) enum RenderAA = false;
9644 //else enum RenderAA = true;
9646 auto src
= ftGlyph
.bitmap
.buffer
;
9648 auto spt
= ftGlyph
.bitmap
.pitch
;
9649 if (spt
< 0) spt
= -spt
;
9650 foreach (int y
; 0..ftGlyph
.bitmap
.rows
) {
9651 ubyte count
= 0, b
= 0;
9654 foreach (int x
; 0..ftGlyph
.bitmap
.width
) {
9655 if (count
-- == 0) { count
= 7; b
= *s
++; } else b
<<= 1;
9656 *d
++ = (b
&0x80 ?
255 : 0);
9662 auto src
= ftGlyph
.bitmap
.buffer
;
9664 auto spt
= ftGlyph
.bitmap
.pitch
;
9665 if (spt
< 0) spt
= -spt
;
9666 foreach (int y
; 0..ftGlyph
.bitmap
.rows
) {
9667 import core
.stdc
.string
: memcpy
;
9668 //dst[0..ftGlyph.bitmap.width] = src[0..ftGlyph.bitmap.width];
9669 memcpy(dst
, src
, ftGlyph
.bitmap
.width
);
9676 float fons__tt_getGlyphKernAdvance (FONSttFontImpl
* font
, float size
, int glyph1
, int glyph2
) nothrow @trusted @nogc {
9677 FT_Vector ftKerning
;
9680 FT_Get_Kerning(font
.font
, glyph1
, glyph2
, FT_KERNING_DEFAULT
, &ftKerning
);
9681 //{ import core.stdc.stdio : printf; printf("kern for %u:%u: %d %d\n", glyph1, glyph2, ftKerning.x, ftKerning.y); }
9682 return cast(int)ftKerning
.x
; // round up and convert to integer
9685 //FT_Get_Kerning(font.font, glyph1, glyph2, FT_KERNING_UNFITTED, &ftKerning);
9686 if (glyph1
<= 0 || glyph2
<= 0 ||
(font
.font
.face_flags
&FT_FACE_FLAG_KERNING
) == 0) return 0;
9687 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;
9688 if (FT_Get_Kerning(font
.font
, glyph1
, glyph2
, FT_KERNING_DEFAULT
, &ftKerning
)) return 0;
9691 //{ import core.stdc.stdio : printf; printf("has kerning: %u\n", cast(uint)(font.font.face_flags&FT_FACE_FLAG_KERNING)); }
9692 { import core
.stdc
.stdio
: printf
; printf("kern for %u:%u: %d %d (size=%g)\n", glyph1
, glyph2
, ftKerning
.x
, ftKerning
.y
, cast(double)size
); }
9697 if (FT_Get_Kerning(font
.font
, glyph1
, glyph2
, FT_KERNING_UNSCALED
, &kk
)) assert(0, "wtf?!");
9698 auto kadvfrac
= FT_MulFix(kk
.x
, font
.font
.size
.metrics
.x_scale
); // 1/64 of pixel
9699 //return cast(int)((kadvfrac/*+(kadvfrac < 0 ? -32 : 32)*/)>>6);
9700 //assert(ftKerning.x == kadvfrac);
9701 if (ftKerning
.x || kadvfrac
) {
9702 { 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
); }
9704 //return cast(int)(kadvfrac+(kadvfrac < 0 ? -31 : 32)>>6); // round up and convert to integer
9705 return kadvfrac
/64.0f;
9707 //return cast(int)(ftKerning.x+(ftKerning.x < 0 ? -31 : 32)>>6); // round up and convert to integer
9708 return ftKerning
.x
/64.0f;
9712 extern(C
) nothrow @trusted @nogc {
9713 static struct OutlinerData
{
9714 @disable this (this);
9715 void opAssign() (in auto ref OutlinerData a
) { static assert(0, "no copies!"); }
9717 NVGPathOutline
.DataStore
* ol
;
9718 FT_BBox outlineBBox
;
9719 nothrow @trusted @nogc:
9720 static float transx(T
) (T v
) pure { pragma(inline
, true); return cast(float)v
; }
9721 static float transy(T
) (T v
) pure { pragma(inline
, true); return -cast(float)v
; }
9724 int fons__nvg__moveto_cb (const(FT_Vector
)* to
, void* user
) {
9725 auto odata
= cast(OutlinerData
*)user
;
9726 if (odata
.vg
!is null) odata
.vg
.moveTo(odata
.transx(to
.x
), odata
.transy(to
.y
));
9727 if (odata
.ol
!is null) {
9728 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.MoveTo
);
9729 odata
.ol
.putArgs(odata
.transx(to
.x
));
9730 odata
.ol
.putArgs(odata
.transy(to
.y
));
9735 int fons__nvg__lineto_cb (const(FT_Vector
)* to
, void* user
) {
9736 auto odata
= cast(OutlinerData
*)user
;
9737 if (odata
.vg
!is null) odata
.vg
.lineTo(odata
.transx(to
.x
), odata
.transy(to
.y
));
9738 if (odata
.ol
!is null) {
9739 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.LineTo
);
9740 odata
.ol
.putArgs(odata
.transx(to
.x
));
9741 odata
.ol
.putArgs(odata
.transy(to
.y
));
9746 int fons__nvg__quadto_cb (const(FT_Vector
)* c1
, const(FT_Vector
)* to
, void* user
) {
9747 auto odata
= cast(OutlinerData
*)user
;
9748 if (odata
.vg
!is null) odata
.vg
.quadTo(odata
.transx(c1
.x
), odata
.transy(c1
.y
), odata
.transx(to
.x
), odata
.transy(to
.y
));
9749 if (odata
.ol
!is null) {
9750 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.QuadTo
);
9751 odata
.ol
.putArgs(odata
.transx(c1
.x
));
9752 odata
.ol
.putArgs(odata
.transy(c1
.y
));
9753 odata
.ol
.putArgs(odata
.transx(to
.x
));
9754 odata
.ol
.putArgs(odata
.transy(to
.y
));
9759 int fons__nvg__cubicto_cb (const(FT_Vector
)* c1
, const(FT_Vector
)* c2
, const(FT_Vector
)* to
, void* user
) {
9760 auto odata
= cast(OutlinerData
*)user
;
9761 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
));
9762 if (odata
.ol
!is null) {
9763 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.BezierTo
);
9764 odata
.ol
.putArgs(odata
.transx(c1
.x
));
9765 odata
.ol
.putArgs(odata
.transy(c1
.y
));
9766 odata
.ol
.putArgs(odata
.transx(c2
.x
));
9767 odata
.ol
.putArgs(odata
.transy(c2
.y
));
9768 odata
.ol
.putArgs(odata
.transx(to
.x
));
9769 odata
.ol
.putArgs(odata
.transy(to
.y
));
9775 bool fons__nvg__toPath (NVGContext vg
, FONSttFontImpl
* font
, uint glyphidx
, float[] bounds
=null) nothrow @trusted @nogc {
9776 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
9778 FT_Outline_Funcs funcs
;
9779 funcs
.move_to
= &fons__nvg__moveto_cb
;
9780 funcs
.line_to
= &fons__nvg__lineto_cb
;
9781 funcs
.conic_to
= &fons__nvg__quadto_cb
;
9782 funcs
.cubic_to
= &fons__nvg__cubicto_cb
;
9784 auto err
= FT_Load_Glyph(font
.font
, glyphidx
, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE
);
9785 if (err
) { bounds
[] = 0; return false; }
9786 if (font
.font
.glyph
.format
!= FT_GLYPH_FORMAT_OUTLINE
) { bounds
[] = 0; return false; }
9788 FT_Outline outline
= font
.font
.glyph
.outline
;
9792 FT_Outline_Get_CBox(&outline
, &odata
.outlineBBox
);
9794 err
= FT_Outline_Decompose(&outline
, &funcs
, &odata
);
9795 if (err
) { bounds
[] = 0; return false; }
9796 if (bounds
.length
> 0) bounds
.ptr
[0] = odata
.outlineBBox
.xMin
;
9797 if (bounds
.length
> 1) bounds
.ptr
[1] = -odata
.outlineBBox
.yMax
;
9798 if (bounds
.length
> 2) bounds
.ptr
[2] = odata
.outlineBBox
.xMax
;
9799 if (bounds
.length
> 3) bounds
.ptr
[3] = -odata
.outlineBBox
.yMin
;
9803 bool fons__nvg__toOutline (FONSttFontImpl
* font
, uint glyphidx
, NVGPathOutline
.DataStore
* ol
) nothrow @trusted @nogc {
9804 FT_Outline_Funcs funcs
;
9805 funcs
.move_to
= &fons__nvg__moveto_cb
;
9806 funcs
.line_to
= &fons__nvg__lineto_cb
;
9807 funcs
.conic_to
= &fons__nvg__quadto_cb
;
9808 funcs
.cubic_to
= &fons__nvg__cubicto_cb
;
9810 auto err
= FT_Load_Glyph(font
.font
, glyphidx
, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE
);
9811 if (err
) return false;
9812 if (font
.font
.glyph
.format
!= FT_GLYPH_FORMAT_OUTLINE
) return false;
9814 FT_Outline outline
= font
.font
.glyph
.outline
;
9818 FT_Outline_Get_CBox(&outline
, &odata
.outlineBBox
);
9820 err
= FT_Outline_Decompose(&outline
, &funcs
, &odata
);
9821 if (err
) return false;
9822 ol
.bounds
.ptr
[0] = odata
.outlineBBox
.xMin
;
9823 ol
.bounds
.ptr
[1] = -odata
.outlineBBox
.yMax
;
9824 ol
.bounds
.ptr
[2] = odata
.outlineBBox
.xMax
;
9825 ol
.bounds
.ptr
[3] = -odata
.outlineBBox
.yMin
;
9829 bool fons__nvg__bounds (FONSttFontImpl
* font
, uint glyphidx
, float[] bounds
) nothrow @trusted @nogc {
9830 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
9832 auto err
= FT_Load_Glyph(font
.font
, glyphidx
, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE
);
9833 if (err
) return false;
9834 if (font
.font
.glyph
.format
!= FT_GLYPH_FORMAT_OUTLINE
) { bounds
[] = 0; return false; }
9836 FT_Outline outline
= font
.font
.glyph
.outline
;
9837 FT_BBox outlineBBox
;
9838 FT_Outline_Get_CBox(&outline
, &outlineBBox
);
9839 if (bounds
.length
> 0) bounds
.ptr
[0] = outlineBBox
.xMin
;
9840 if (bounds
.length
> 1) bounds
.ptr
[1] = -outlineBBox
.yMax
;
9841 if (bounds
.length
> 2) bounds
.ptr
[2] = outlineBBox
.xMax
;
9842 if (bounds
.length
> 3) bounds
.ptr
[3] = -outlineBBox
.yMin
;
9848 // ////////////////////////////////////////////////////////////////////////// //
9850 import std
.traits
: isFunctionPointer
, isDelegate
;
9851 private auto assumeNoThrowNoGC(T
) (scope T t
) if (isFunctionPointer
!T || isDelegate
!T
) {
9853 enum attrs
= functionAttributes
!T|FunctionAttribute
.nogc|FunctionAttribute
.nothrow_
;
9854 return cast(SetFunctionAttributes
!(T
, functionLinkage
!T
, attrs
)) t
;
9857 private auto forceNoThrowNoGC(T
) (scope T t
) if (isFunctionPointer
!T || isDelegate
!T
) {
9859 return assumeNoThrowNoGC(t
)();
9860 } catch (Exception e
) {
9865 struct FONSttFontImpl
{
9866 stbtt_fontinfo font
;
9867 bool mono
; // no aa?
9870 int fons__tt_init (FONSContext context
) nothrow @trusted @nogc {
9874 void fons__tt_setMono (FONSContext context
, FONSttFontImpl
* font
, bool v
) nothrow @trusted @nogc {
9878 bool fons__tt_getMono (FONSContext context
, FONSttFontImpl
* font
) nothrow @trusted @nogc {
9882 int fons__tt_loadFont (FONSContext context
, FONSttFontImpl
* font
, ubyte* data
, int dataSize
) nothrow @trusted @nogc {
9884 font
.font
.userdata
= context
;
9885 forceNoThrowNoGC({ stbError
= stbtt_InitFont(&font
.font
, data
, 0); });
9889 void fons__tt_getFontVMetrics (FONSttFontImpl
* font
, int* ascent
, int* descent
, int* lineGap
) nothrow @trusted @nogc {
9890 forceNoThrowNoGC({ stbtt_GetFontVMetrics(&font
.font
, ascent
, descent
, lineGap
); });
9893 float fons__tt_getPixelHeightScale (FONSttFontImpl
* font
, float size
) nothrow @trusted @nogc {
9895 forceNoThrowNoGC({ res
= stbtt_ScaleForPixelHeight(&font
.font
, size
); });
9899 int fons__tt_getGlyphIndex (FONSttFontImpl
* font
, int codepoint
) nothrow @trusted @nogc {
9901 forceNoThrowNoGC({ res
= stbtt_FindGlyphIndex(&font
.font
, codepoint
); });
9905 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 {
9906 forceNoThrowNoGC({ stbtt_GetGlyphHMetrics(&font
.font
, glyph
, advance
, lsb
); });
9907 forceNoThrowNoGC({ stbtt_GetGlyphBitmapBox(&font
.font
, glyph
, scale
, scale
, x0
, y0
, x1
, y1
); });
9911 void fons__tt_renderGlyphBitmap (FONSttFontImpl
* font
, ubyte* output
, int outWidth
, int outHeight
, int outStride
, float scaleX
, float scaleY
, int glyph
) nothrow @trusted @nogc {
9912 forceNoThrowNoGC({ stbtt_MakeGlyphBitmap(&font
.font
, output
, outWidth
, outHeight
, outStride
, scaleX
, scaleY
, glyph
); });
9915 float fons__tt_getGlyphKernAdvance (FONSttFontImpl
* font
, float size
, int glyph1
, int glyph2
) nothrow @trusted @nogc {
9916 // FUnits -> pixels: pointSize * resolution / (72 points per inch * units_per_em)
9917 // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#converting
9920 res
= stbtt_GetGlyphKernAdvance(&font
.font
, glyph1
, glyph2
);
9921 res
*= stbtt_ScaleForPixelHeight(&font
.font
, size
);
9925 { import core.stdc.stdio; printf("fres=%g; size=%g; %g (%g); rv=%g\n", res, size, res*stbtt_ScaleForMappingEmToPixels(&font.font, size), stbtt_ScaleForPixelHeight(&font.font, size*100), res*stbtt_ScaleForPixelHeight(&font.font, size*100)); }
9928 //k8: dunno if this is right; i guess it isn't but...
9932 // old arsd.ttf sux! ;-)
9933 static if (is(typeof(STBTT_vcubic
))) {
9935 static struct OutlinerData
{
9936 @disable this (this);
9937 void opAssign() (in auto ref OutlinerData a
) { static assert(0, "no copies!"); }
9938 NVGPathOutline
.DataStore
* ol
;
9939 nothrow @trusted @nogc:
9940 static float transx(T
) (T v
) pure { pragma(inline
, true); return cast(float)v
; }
9941 static float transy(T
) (T v
) pure { pragma(inline
, true); return -cast(float)v
; }
9945 bool fons__nvg__toPath (NVGContext vg
, FONSttFontImpl
* font
, uint glyphidx
, float[] bounds
=null) nothrow @trusted @nogc {
9946 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
9948 bool okflag
= false;
9952 if (!stbtt_GetGlyphBox(&font
.font
, glyphidx
, &x0
, &y0
, &x1
, &y1
)) {
9957 if (bounds
.length
> 0) bounds
.ptr
[0] = x0
;
9958 if (bounds
.length
> 1) bounds
.ptr
[1] = -y1
;
9959 if (bounds
.length
> 2) bounds
.ptr
[2] = x1
;
9960 if (bounds
.length
> 3) bounds
.ptr
[3] = -y0
;
9962 static float transy(T
) (T v
) pure { pragma(inline
, true); return -cast(float)v
; }
9964 stbtt_vertex
* verts
= null;
9965 scope(exit
) { import core
.stdc
.stdlib
: free
; if (verts
!is null) free(verts
); }
9966 int vcount
= stbtt_GetGlyphShape(&font
.font
, glyphidx
, &verts
);
9967 if (vcount
< 1) return;
9969 foreach (const ref vt
; verts
[0..vcount
]) {
9971 case STBTT_vmove
: vg
.moveTo(vt
.x
, transy(vt
.y
)); break;
9972 case STBTT_vline
: vg
.lineTo(vt
.x
, transy(vt
.y
)); break;
9973 case STBTT_vcurve
: vg
.quadTo(vt
.x
, transy(vt
.y
), vt
.cx
, transy(vt
.cy
)); break;
9974 case STBTT_vcubic
: vg
.bezierTo(vt
.x
, transy(vt
.y
), vt
.cx
, transy(vt
.cy
), vt
.cx1
, transy(vt
.cy1
)); break;
9985 bool fons__nvg__toOutline (FONSttFontImpl
* font
, uint glyphidx
, NVGPathOutline
.DataStore
* ol
) nothrow @trusted @nogc {
9986 bool okflag
= false;
9991 if (!stbtt_GetGlyphBox(&font
.font
, glyphidx
, &x0
, &y0
, &x1
, &y1
)) {
9996 ol
.bounds
.ptr
[0] = x0
;
9997 ol
.bounds
.ptr
[1] = -y1
;
9998 ol
.bounds
.ptr
[2] = x1
;
9999 ol
.bounds
.ptr
[3] = -y0
;
10001 stbtt_vertex
* verts
= null;
10002 scope(exit
) { import core
.stdc
.stdlib
: free
; if (verts
!is null) free(verts
); }
10003 int vcount
= stbtt_GetGlyphShape(&font
.font
, glyphidx
, &verts
);
10004 if (vcount
< 1) return;
10006 OutlinerData odata
;
10009 foreach (const ref vt
; verts
[0..vcount
]) {
10012 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.MoveTo
);
10013 odata
.ol
.putArgs(odata
.transx(vt
.x
));
10014 odata
.ol
.putArgs(odata
.transy(vt
.y
));
10017 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.LineTo
);
10018 odata
.ol
.putArgs(odata
.transx(vt
.x
));
10019 odata
.ol
.putArgs(odata
.transy(vt
.y
));
10022 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.QuadTo
);
10023 odata
.ol
.putArgs(odata
.transx(vt
.x
));
10024 odata
.ol
.putArgs(odata
.transy(vt
.y
));
10025 odata
.ol
.putArgs(odata
.transx(vt
.cx
));
10026 odata
.ol
.putArgs(odata
.transy(vt
.cy
));
10029 odata
.ol
.putCommand(NVGPathOutline
.Command
.Kind
.BezierTo
);
10030 odata
.ol
.putArgs(odata
.transx(vt
.x
));
10031 odata
.ol
.putArgs(odata
.transy(vt
.y
));
10032 odata
.ol
.putArgs(odata
.transx(vt
.cx
));
10033 odata
.ol
.putArgs(odata
.transy(vt
.cy
));
10034 odata
.ol
.putArgs(odata
.transx(vt
.cx1
));
10035 odata
.ol
.putArgs(odata
.transy(vt
.cy1
));
10047 bool fons__nvg__bounds (FONSttFontImpl
* font
, uint glyphidx
, float[] bounds
) nothrow @trusted @nogc {
10048 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
10050 bool okflag
= false;
10053 int x0
, y0
, x1
, y1
;
10054 if (stbtt_GetGlyphBox(&font
.font
, glyphidx
, &x0
, &y0
, &x1
, &y1
)) {
10055 if (bounds
.length
> 0) bounds
.ptr
[0] = x0
;
10056 if (bounds
.length
> 1) bounds
.ptr
[1] = -y1
;
10057 if (bounds
.length
> 2) bounds
.ptr
[2] = x1
;
10058 if (bounds
.length
> 3) bounds
.ptr
[3] = -y0
;
10068 } // check for old stb_ttf
10074 // ////////////////////////////////////////////////////////////////////////// //
10076 enum FONS_HASH_LUT_SIZE
= 256;
10077 enum FONS_INIT_FONTS
= 4;
10078 enum FONS_INIT_GLYPHS
= 256;
10079 enum FONS_INIT_ATLAS_NODES
= 256;
10080 enum FONS_VERTEX_COUNT
= 1024;
10081 enum FONS_MAX_STATES
= 20;
10082 enum FONS_MAX_FALLBACKS
= 20;
10090 short x0
, y0
, x1
, y1
;
10091 short xadv
, xoff
, yoff
;
10095 struct FONSfontData
{
10101 @disable this (this); // no copies
10102 void opAssign() (in auto ref FONSfontData a
) { static assert(0, "no copies!"); }
10105 // won't set rc to 1
10106 FONSfontData
* fons__createFontData (ubyte* adata
, int asize
, bool afree
) nothrow @trusted @nogc {
10107 import core
.stdc
.stdlib
: malloc
;
10108 assert(adata
!is null);
10110 auto res
= cast(FONSfontData
*)malloc(FONSfontData
.sizeof
);
10111 if (res
is null) assert(0, "FONS: out of memory");
10113 res
.dataSize
= asize
;
10114 res
.freeData
= afree
;
10119 void incref (FONSfontData
* fd
) pure nothrow @trusted @nogc {
10120 pragma(inline
, true);
10121 if (fd
!is null) ++fd
.rc
;
10124 void decref (ref FONSfontData
* fd
) nothrow @trusted @nogc {
10126 if (--fd
.rc
== 0) {
10127 import core
.stdc
.stdlib
: free
;
10128 if (fd
.freeData
&& fd
.data
!is null) {
10138 // as creating and destroying fonts is a rare operation, malloc some data
10140 FONSttFontImpl font
;
10141 char* name
; // malloced, strz, always lowercase
10144 char* path
; // malloced, strz
10145 FONSfontData
* fdata
;
10152 int[FONS_HASH_LUT_SIZE
] lut
;
10153 int[FONS_MAX_FALLBACKS
] fallbacks
;
10156 @disable this (this);
10157 void opAssign() (in auto ref FONSfont a
) { static assert(0, "no copies"); }
10159 static uint djbhash (const(void)[] s
) pure nothrow @safe @nogc {
10161 foreach (ubyte b
; cast(const(ubyte)[])s
) {
10162 if (b
>= 'A' && b
<= 'Z') b
+= 32; // poor man's tolower
10163 hash
= ((hash
<<5)+hash
)+b
;
10169 void freeMemory () nothrow @trusted @nogc {
10170 import core
.stdc
.stdlib
: free
;
10171 if (name
!is null) { free(name
); name
= null; }
10172 namelen
= namehash
= 0;
10173 if (path
!is null) { free(path
); path
= null; }
10177 // this also calcs name hash
10178 void setName (const(char)[] aname
) nothrow @trusted @nogc {
10179 //{ import core.stdc.stdio; printf("setname: [%.*s]\n", cast(uint)aname.length, aname.ptr); }
10180 import core
.stdc
.stdlib
: realloc
;
10181 if (aname
.length
> int.max
/32) assert(0, "FONS: invalid font name");
10182 namelen
= cast(uint)aname
.length
;
10183 name
= cast(char*)realloc(name
, namelen
+1);
10184 if (name
is null) assert(0, "FONS: out of memory");
10185 if (aname
.length
) name
[0..aname
.length
] = aname
[];
10188 foreach (ref char ch
; name
[0..namelen
]) if (ch
>= 'A' && ch
<= 'Z') ch
+= 32; // poor man's tolower
10189 namehash
= djbhash(name
[0..namelen
]);
10190 //{ import core.stdc.stdio; printf(" [%s] [%.*s] [0x%08x]\n", name, namelen, name, namehash); }
10193 void setPath (const(char)[] apath
) nothrow @trusted @nogc {
10194 import core
.stdc
.stdlib
: realloc
;
10195 if (apath
.length
> int.max
/32) assert(0, "FONS: invalid font path");
10196 path
= cast(char*)realloc(path
, apath
.length
+1);
10197 if (path
is null) assert(0, "FONS: out of memory");
10198 if (apath
.length
) path
[0..apath
.length
] = apath
[];
10199 path
[apath
.length
] = 0;
10202 // this won't check hash
10203 bool nameEqu (const(char)[] aname
) const pure nothrow @trusted @nogc {
10204 //{ import core.stdc.stdio; printf("nameEqu: aname=[%.*s]; namelen=%u; aslen=%u\n", cast(uint)aname.length, aname.ptr, namelen, cast(uint)aname.length); }
10205 if (namelen
!= aname
.length
) return false;
10206 const(char)* ns
= name
;
10208 foreach (char ch
; aname
) {
10209 if (ch
>= 'A' && ch
<= 'Z') ch
+= 32; // poor man's tolower
10210 if (ch
!= *ns
++) return false;
10212 // done (length was checked earlier)
10216 void clear () nothrow @trusted @nogc {
10217 import core
.stdc
.stdlib
: free
;
10218 import core
.stdc
.string
: memset
;
10219 if (glyphs
!is null) free(glyphs
);
10221 memset(&this, 0, this.sizeof
);
10224 FONSglyph
* allocGlyph () nothrow @trusted @nogc {
10225 if (nglyphs
+1 > cglyphs
) {
10226 import core
.stdc
.stdlib
: realloc
;
10227 cglyphs
= (cglyphs
== 0 ?
8 : cglyphs
*2);
10228 glyphs
= cast(FONSglyph
*)realloc(glyphs
, FONSglyph
.sizeof
*cglyphs
);
10229 if (glyphs
is null) assert(0, "FontStash: out of memory");
10232 return &glyphs
[nglyphs
-1];
10236 void kill (ref FONSfont
* font
) nothrow @trusted @nogc {
10237 if (font
!is null) {
10238 import core
.stdc
.stdlib
: free
;
10246 // ////////////////////////////////////////////////////////////////////////// //
10249 NVGTextAlign talign
;
10256 // ////////////////////////////////////////////////////////////////////////// //
10257 // atlas based on Skyline Bin Packer by Jukka Jylänki
10258 alias FONSAtlas
= FONSatlasInternal
*;
10260 struct FONSatlasInternal
{
10261 static struct Node
{
10270 @disable this (this);
10271 void opAssign() (in auto ref FONSatlasInternal a
) { static assert(0, "no copies"); }
10273 nothrow @trusted @nogc:
10274 static FONSAtlas
create (int w
, int h
, int nnodes
) {
10275 import core
.stdc
.stdlib
: malloc
;
10276 import core
.stdc
.string
: memset
;
10278 FONSAtlas atlas
= cast(FONSAtlas
)malloc(FONSatlasInternal
.sizeof
);
10279 if (atlas
is null) assert(0, "FontStash: out of memory");
10280 memset(atlas
, 0, FONSatlasInternal
.sizeof
);
10285 // allocate space for skyline nodes
10286 atlas
.nodes
= cast(Node
*)malloc(Node
.sizeof
*nnodes
);
10287 if (atlas
.nodes
is null) assert(0, "FontStash: out of memory");
10288 memset(atlas
.nodes
, 0, Node
.sizeof
*nnodes
);
10290 atlas
.cnodes
= nnodes
;
10293 atlas
.nodes
[0].x
= 0;
10294 atlas
.nodes
[0].y
= 0;
10295 atlas
.nodes
[0].width
= cast(short)w
;
10302 import core
.stdc
.stdlib
: free
;
10303 import core
.stdc
.string
: memset
;
10305 if (nodes
!is null) free(nodes
);
10306 memset(&this, 0, this.sizeof
);
10309 void insertNode (int idx
, int x
, int y
, int w
) {
10310 if (nnodes
+1 > cnodes
) {
10311 import core
.stdc
.stdlib
: realloc
;
10312 cnodes
= (cnodes
== 0 ?
8 : cnodes
*2);
10313 nodes
= cast(Node
*)realloc(nodes
, Node
.sizeof
*cnodes
);
10314 if (nodes
is null) assert(0, "FontStash: out of memory");
10316 for (int i
= nnodes
; i
> idx
; --i
) nodes
[i
] = nodes
[i
-1];
10317 nodes
[idx
].x
= cast(short)x
;
10318 nodes
[idx
].y
= cast(short)y
;
10319 nodes
[idx
].width
= cast(short)w
;
10323 void removeNode (int idx
) {
10324 if (nnodes
== 0) return;
10325 foreach (immutable int i
; idx
+1..nnodes
) nodes
[i
-1] = nodes
[i
];
10329 // insert node for empty space
10330 void expand (int w
, int h
) {
10331 if (w
> width
) insertNode(nnodes
, width
, 0, w
-width
);
10336 void reset (int w
, int h
) {
10343 nodes
[0].width
= cast(short)w
;
10347 void addSkylineLevel (int idx
, int x
, int y
, int w
, int h
) {
10348 insertNode(idx
, x
, y
+h
, w
);
10350 // delete skyline segments that fall under the shadow of the new segment
10351 for (int i
= idx
+1; i
< nnodes
; ++i
) {
10352 if (nodes
[i
].x
< nodes
[i
-1].x
+nodes
[i
-1].width
) {
10353 int shrink
= nodes
[i
-1].x
+nodes
[i
-1].width
-nodes
[i
].x
;
10354 nodes
[i
].x
+= cast(short)shrink
;
10355 nodes
[i
].width
-= cast(short)shrink
;
10356 if (nodes
[i
].width
<= 0) {
10367 // Merge same height skyline segments that are next to each other
10368 for (int i
= 0; i
< nnodes
-1; ++i
) {
10369 if (nodes
[i
].y
== nodes
[i
+1].y
) {
10370 nodes
[i
].width
+= nodes
[i
+1].width
;
10377 // checks if there is enough space at the location of skyline span 'i',
10378 // and return the max height of all skyline spans under that at that location,
10379 // (think tetris block being dropped at that position); or -1 if no space found
10380 int rectFits (int i
, int w
, int h
) {
10381 int x
= nodes
[i
].x
;
10382 int y
= nodes
[i
].y
;
10383 if (x
+w
> width
) return -1;
10385 while (spaceLeft
> 0) {
10386 if (i
== nnodes
) return -1;
10387 y
= nvg__max(y
, nodes
[i
].y
);
10388 if (y
+h
> height
) return -1;
10389 spaceLeft
-= nodes
[i
].width
;
10395 bool addRect (int rw
, int rh
, int* rx
, int* ry
) {
10396 int besth
= height
, bestw
= width
, besti
= -1;
10397 int bestx
= -1, besty
= -1;
10399 // Bottom left fit heuristic.
10400 for (int i
= 0; i
< nnodes
; ++i
) {
10401 int y
= rectFits(i
, rw
, rh
);
10403 if (y
+rh
< besth ||
(y
+rh
== besth
&& nodes
[i
].width
< bestw
)) {
10405 bestw
= nodes
[i
].width
;
10407 bestx
= nodes
[i
].x
;
10413 if (besti
== -1) return false;
10415 // perform the actual packing
10416 addSkylineLevel(besti
, bestx
, besty
, rw
, rh
);
10425 void kill (ref FONSAtlas atlas
) nothrow @trusted @nogc {
10426 if (atlas
!is null) {
10427 import core
.stdc
.stdlib
: free
;
10435 // ////////////////////////////////////////////////////////////////////////// //
10436 /// FontStash context (internal definition). Don't use it derectly, it was made public only to generate documentation.
10437 /// Group: font_stash
10438 public struct FONScontextInternal
{
10444 FONSfont
** fonts
; // actually, a simple hash table; can't grow yet
10445 int cfonts
; // allocated
10446 int nfonts
; // used (so we can track hash table stats)
10447 int* hashidx
; // [hsize] items; holds indicies in [fonts] array
10448 int hused
, hsize
;// used items and total items in [hashidx]
10450 FONSstate
[FONS_MAX_STATES
] states
;
10453 void delegate (FONSError error
, int val
) nothrow @trusted @nogc handleError
;
10455 @disable this (this);
10456 void opAssign() (in auto ref FONScontextInternal ctx
) { static assert(0, "FONS copying is not allowed"); }
10459 static bool strequci (const(char)[] s0
, const(char)[] s1
) pure nothrow @trusted @nogc {
10460 if (s0
.length
!= s1
.length
) return false;
10461 const(char)* sp0
= s0
.ptr
;
10462 const(char)* sp1
= s1
.ptr
;
10463 foreach (immutable _
; 0..s0
.length
) {
10467 if (c0
>= 'A' && c0
<= 'Z') c0
+= 32; // poor man tolower
10468 if (c1
>= 'A' && c1
<= 'Z') c1
+= 32; // poor man tolower
10469 if (c0
!= c1
) return false;
10475 inout(FONSstate
)* getState () inout pure nothrow @trusted @nogc {
10476 pragma(inline
, true);
10477 return cast(inout)(&states
[(nstates
> 0 ? nstates
-1 : 0)]);
10480 // simple linear probing; returns [FONS_INVALID] if not found
10481 int findNameInHash (const(char)[] name
) const pure nothrow @trusted @nogc {
10482 if (nfonts
== 0) return FONS_INVALID
;
10483 auto nhash
= FONSfont
.djbhash(name
);
10484 //{ import core.stdc.stdio; printf("findinhash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10485 auto res
= nhash
%hsize
;
10486 // hash will never be 100% full, so this loop is safe
10488 int idx
= hashidx
[res
];
10489 if (idx
== -1) break;
10490 auto font
= fonts
[idx
];
10491 if (font
is null) assert(0, "FONS internal error");
10492 if (font
.namehash
== nhash
&& font
.nameEqu(name
)) return idx
;
10493 //{ import core.stdc.stdio; printf("findinhash chained: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10494 res
= (res
+1)%hsize
;
10496 return FONS_INVALID
;
10499 // should be called $(B before) freeing `fonts[fidx]`
10500 void removeIndexFromHash (int fidx
) nothrow @trusted @nogc {
10501 if (fidx
< 0 || fidx
>= nfonts
) assert(0, "FONS internal error");
10502 if (fonts
[fidx
] is null) assert(0, "FONS internal error");
10503 if (hused
!= nfonts
) assert(0, "FONS internal error");
10504 auto nhash
= fonts
[fidx
].namehash
;
10505 auto res
= nhash
%hsize
;
10506 // hash will never be 100% full, so this loop is safe
10508 int idx
= hashidx
[res
];
10509 if (idx
== -1) assert(0, "FONS INTERNAL ERROR");
10511 // i found her! copy rest here
10512 int nidx
= (res
+1)%hsize
;
10514 if ((hashidx
[res
] = hashidx
[nidx
]) == -1) break; // so it will copy `-1` too
10516 nidx
= (nidx
+1)%hsize
;
10520 res
= (res
+1)%hsize
;
10524 // add font with the given index to hash
10525 // prerequisite: font should not exists in hash
10526 void addIndexToHash (int idx
) nothrow @trusted @nogc {
10527 if (idx
< 0 || idx
>= nfonts
) assert(0, "FONS internal error");
10528 if (fonts
[idx
] is null) assert(0, "FONS internal error");
10529 import core
.stdc
.stdlib
: realloc
;
10530 auto nhash
= fonts
[idx
].namehash
;
10531 //{ import core.stdc.stdio; printf("addtohash: name=[%.*s]; nhash=0x%08x\n", cast(uint)name.length, name.ptr, nhash); }
10532 // allocate new hash table if there was none
10534 enum InitSize
= 256;
10535 auto newlist
= cast(int*)realloc(null, InitSize
*hashidx
[0].sizeof
);
10536 if (newlist
is null) assert(0, "FONS: out of memory");
10537 newlist
[0..InitSize
] = -1;
10542 int res
= cast(int)(nhash
%hsize
);
10543 // need to rehash? we want our hash table 50% full at max
10544 if (hashidx
[res
] != -1 && hused
>= hsize
/2) {
10545 uint nsz
= hsize
*2;
10546 if (nsz
> 1024*1024) assert(0, "FONS: out of memory for fonts");
10547 auto newlist
= cast(int*)realloc(fonts
, nsz
*hashidx
[0].sizeof
);
10548 if (newlist
is null) assert(0, "FONS: out of memory");
10549 newlist
[0..nsz
] = -1;
10552 foreach (immutable fidx
, FONSfont
* ff
; fonts
[0..nfonts
]) {
10553 if (ff
is null) continue;
10554 // find slot for this font (guaranteed to have one)
10555 uint newslot
= ff
.namehash
%nsz
;
10556 while (newlist
[newslot
] != -1) newslot
= (newslot
+1)%nsz
;
10557 newlist
[newslot
] = cast(int)fidx
;
10562 // we added everything, including [idx], so nothing more to do here
10564 // find slot (guaranteed to have one)
10565 while (hashidx
[res
] != -1) res
= (res
+1)%hsize
;
10567 hashidx
[res
] = idx
;
10572 void addWhiteRect (int w
, int h
) nothrow @trusted @nogc {
10576 if (!atlas
.addRect(w
, h
, &gx
, &gy
)) return;
10579 dst
= &texData
[gx
+gy
*params
.width
];
10580 foreach (int y
; 0..h
) {
10581 foreach (int x
; 0..w
) {
10584 dst
+= params
.width
;
10587 dirtyRect
.ptr
[0] = nvg__min(dirtyRect
.ptr
[0], gx
);
10588 dirtyRect
.ptr
[1] = nvg__min(dirtyRect
.ptr
[1], gy
);
10589 dirtyRect
.ptr
[2] = nvg__max(dirtyRect
.ptr
[2], gx
+w
);
10590 dirtyRect
.ptr
[3] = nvg__max(dirtyRect
.ptr
[3], gy
+h
);
10593 // returns fid, not hash slot
10594 int allocFontAt (int atidx
) nothrow @trusted @nogc {
10595 if (atidx
>= 0 && atidx
>= nfonts
) assert(0, "internal NanoVega fontstash error");
10598 if (nfonts
>= cfonts
) {
10599 import core
.stdc
.stdlib
: realloc
;
10600 import core
.stdc
.string
: memset
;
10601 assert(nfonts
== cfonts
);
10602 int newsz
= cfonts
+64;
10603 if (newsz
> 65535) assert(0, "FONS: too many fonts");
10604 auto newlist
= cast(FONSfont
**)realloc(fonts
, newsz
*(FONSfont
*).sizeof
);
10605 if (newlist
is null) assert(0, "FONS: out of memory");
10606 memset(newlist
+cfonts
, 0, (newsz
-cfonts
)*(FONSfont
*).sizeof
);
10610 assert(nfonts
< cfonts
);
10613 FONSfont
* font
= cast(FONSfont
*)malloc(FONSfont
.sizeof
);
10614 if (font
is null) assert(0, "FONS: out of memory");
10615 memset(font
, 0, FONSfont
.sizeof
);
10617 font
.glyphs
= cast(FONSglyph
*)malloc(FONSglyph
.sizeof
*FONS_INIT_GLYPHS
);
10618 if (font
.glyphs
is null) assert(0, "FONS: out of memory");
10619 font
.cglyphs
= FONS_INIT_GLYPHS
;
10623 fonts
[nfonts
] = font
;
10626 fonts
[atidx
] = font
;
10632 int findGlyphForCP (FONSfont
*font
, dchar dch
, FONSfont
** renderfont
) nothrow @trusted @nogc {
10633 if (renderfont
!is null) *renderfont
= font
;
10634 if (font
is null || font
.fdata
is null) return 0;
10635 auto g
= fons__tt_getGlyphIndex(&font
.font
, cast(uint)dch
);
10636 // try to find the glyph in fallback fonts
10638 foreach (immutable i
; 0..font
.nfallbacks
) {
10639 FONSfont
* fallbackFont
= fonts
[font
.fallbacks
.ptr
[i
]];
10640 if (fallbackFont
!is null) {
10641 int fallbackIndex
= fons__tt_getGlyphIndex(&fallbackFont
.font
, cast(uint)dch
);
10642 if (fallbackIndex
!= 0) {
10643 if (renderfont
!is null) *renderfont
= fallbackFont
;
10648 // no char, try to find replacement one
10649 if (dch
!= 0xFFFD) {
10650 g
= fons__tt_getGlyphIndex(&font
.font
, 0xFFFD);
10652 foreach (immutable i
; 0..font
.nfallbacks
) {
10653 FONSfont
* fallbackFont
= fonts
[font
.fallbacks
.ptr
[i
]];
10654 if (fallbackFont
!is null) {
10655 int fallbackIndex
= fons__tt_getGlyphIndex(&fallbackFont
.font
, 0xFFFD);
10656 if (fallbackIndex
!= 0) {
10657 if (renderfont
!is null) *renderfont
= fallbackFont
;
10668 void clear () nothrow @trusted @nogc {
10669 import core
.stdc
.stdlib
: free
;
10671 if (params
.renderDelete
!is null) params
.renderDelete(params
.userPtr
);
10672 foreach (immutable int i
; 0..nfonts
) fonts
[i
].kill();
10674 if (atlas
!is null) atlas
.kill();
10675 if (fonts
!is null) free(fonts
);
10676 if (texData
!is null) free(texData
);
10677 if (hashidx
!is null) free(hashidx
);
10680 // add font from another fontstash
10681 int addCookedFont (FONSfont
* font
) nothrow @trusted @nogc {
10682 if (font
is null || font
.fdata
is null) return FONS_INVALID
;
10683 font
.fdata
.incref();
10684 auto res
= addFontWithData(font
.name
[0..font
.namelen
], font
.fdata
, !font
.font
.mono
);
10685 if (res
== FONS_INVALID
) font
.fdata
.decref(); // oops
10689 // fdata refcount must be already increased; it won't be changed
10690 int addFontWithData (const(char)[] name
, FONSfontData
* fdata
, bool defAA
) nothrow @trusted @nogc {
10691 int i
, ascent
, descent
, fh
, lineGap
;
10693 if (name
.length
== 0 ||
strequci(name
, NoAlias
)) return FONS_INVALID
;
10694 if (name
.length
> 32767) return FONS_INVALID
;
10695 if (fdata
is null) return FONS_INVALID
;
10697 // find a font with the given name
10699 FONSfont
* oldfont
= null;
10700 int oldidx
= findNameInHash(name
);
10701 if (oldidx
!= FONS_INVALID
) {
10702 // replacement font
10703 oldfont
= fonts
[oldidx
];
10706 // new font, allocate new bucket
10710 newidx
= allocFontAt(newidx
);
10711 FONSfont
* font
= fonts
[newidx
];
10712 font
.setName(name
);
10713 font
.lut
.ptr
[0..FONS_HASH_LUT_SIZE
] = -1; // init hash lookup
10714 font
.fdata
= fdata
; // set the font data (don't change reference count)
10715 fons__tt_setMono(&this, &font
.font
, !defAA
);
10718 if (!fons__tt_loadFont(&this, &font
.font
, fdata
.data
, fdata
.dataSize
)) {
10719 // we promised to not free data on error, so just clear the data store (it will be freed by the caller)
10722 if (oldidx
!= FONS_INVALID
) {
10723 assert(oldidx
== newidx
);
10724 fonts
[oldidx
] = oldfont
;
10726 assert(newidx
== nfonts
-1);
10727 fonts
[newidx
] = null;
10730 return FONS_INVALID
;
10732 // free old font data, if any
10733 if (oldfont
!is null) oldfont
.kill();
10736 // add font to name hash
10737 if (oldidx
== FONS_INVALID
) addIndexToHash(newidx
);
10739 // store normalized line height
10740 // the real line height is got by multiplying the lineh by font size
10741 fons__tt_getFontVMetrics(&font
.font
, &ascent
, &descent
, &lineGap
);
10742 fh
= ascent
-descent
;
10743 font
.ascender
= cast(float)ascent
/cast(float)fh
;
10744 font
.descender
= cast(float)descent
/cast(float)fh
;
10745 font
.lineh
= cast(float)(fh
+lineGap
)/cast(float)fh
;
10747 //{ import core.stdc.stdio; printf("created font [%.*s] (idx=%d)...\n", cast(uint)name.length, name.ptr, idx); }
10752 float getVertAlign (FONSfont
* font
, NVGTextAlign talign
, short isize
) pure nothrow @trusted @nogc {
10753 if (params
.isZeroTopLeft
) {
10754 final switch (talign
.vertical
) {
10755 case NVGTextAlign
.V
.Top
: return font
.ascender
*cast(float)isize
/10.0f;
10756 case NVGTextAlign
.V
.Middle
: return (font
.ascender
+font
.descender
)/2.0f*cast(float)isize
/10.0f;
10757 case NVGTextAlign
.V
.Baseline
: return 0.0f;
10758 case NVGTextAlign
.V
.Bottom
: return font
.descender
*cast(float)isize
/10.0f;
10761 final switch (talign
.vertical
) {
10762 case NVGTextAlign
.V
.Top
: return -font
.ascender
*cast(float)isize
/10.0f;
10763 case NVGTextAlign
.V
.Middle
: return -(font
.ascender
+font
.descender
)/2.0f*cast(float)isize
/10.0f;
10764 case NVGTextAlign
.V
.Baseline
: return 0.0f;
10765 case NVGTextAlign
.V
.Bottom
: return -font
.descender
*cast(float)isize
/10.0f;
10772 /** Create new FontStash context. It can be destroyed with `fs.kill()` later.
10774 * Note that if you don't plan to rasterize glyphs (i.e. you will use created
10775 * FontStash only to measure text), you can simply pass `FONSParams.init`).
10777 static FONSContext
create() (in auto ref FONSParams params
) nothrow @trusted @nogc {
10778 import core
.stdc
.string
: memcpy
;
10780 FONSContext stash
= null;
10782 // allocate memory for the font stash
10783 stash
= cast(FONSContext
)malloc(FONScontextInternal
.sizeof
);
10784 if (stash
is null) goto error
;
10785 memset(stash
, 0, FONScontextInternal
.sizeof
);
10787 memcpy(&stash
.params
, ¶ms
, params
.sizeof
);
10788 if (stash
.params
.width
< 1) stash
.params
.width
= 32;
10789 if (stash
.params
.height
< 1) stash
.params
.height
= 32;
10791 // initialize implementation library
10792 if (!fons__tt_init(stash
)) goto error
;
10794 if (stash
.params
.renderCreate
!is null) {
10795 if (!stash
.params
.renderCreate(stash
.params
.userPtr
, stash
.params
.width
, stash
.params
.height
)) goto error
;
10798 stash
.atlas
= FONSAtlas
.create(stash
.params
.width
, stash
.params
.height
, FONS_INIT_ATLAS_NODES
);
10799 if (stash
.atlas
is null) goto error
;
10801 // don't allocate space for fonts: hash manager will do that for us later
10802 //stash.cfonts = 0;
10803 //stash.nfonts = 0;
10805 // create texture for the cache
10806 stash
.itw
= 1.0f/stash
.params
.width
;
10807 stash
.ith
= 1.0f/stash
.params
.height
;
10808 stash
.texData
= cast(ubyte*)malloc(stash
.params
.width
*stash
.params
.height
);
10809 if (stash
.texData
is null) goto error
;
10810 memset(stash
.texData
, 0, stash
.params
.width
*stash
.params
.height
);
10812 stash
.dirtyRect
.ptr
[0] = stash
.params
.width
;
10813 stash
.dirtyRect
.ptr
[1] = stash
.params
.height
;
10814 stash
.dirtyRect
.ptr
[2] = 0;
10815 stash
.dirtyRect
.ptr
[3] = 0;
10817 // add white rect at 0, 0 for debug drawing
10818 stash
.addWhiteRect(2, 2);
10821 stash
.clearState();
10831 /// Add fallback font (FontStash will try to find missing glyph in all fallback fonts before giving up).
10832 bool addFallbackFont (int base
, int fallback
) nothrow @trusted @nogc {
10833 FONSfont
* baseFont
= fonts
[base
];
10834 if (baseFont
!is null && baseFont
.nfallbacks
< FONS_MAX_FALLBACKS
) {
10835 baseFont
.fallbacks
.ptr
[baseFont
.nfallbacks
++] = fallback
;
10841 @property void size (float size
) nothrow @trusted @nogc { pragma(inline
, true); getState
.size
= size
; } /// Set current font size.
10842 @property float size () const pure nothrow @trusted @nogc { pragma(inline
, true); return getState
.size
; } /// Get current font size.
10844 @property void spacing (float spacing
) nothrow @trusted @nogc { pragma(inline
, true); getState
.spacing
= spacing
; } /// Set current letter spacing.
10845 @property float spacing () const pure nothrow @trusted @nogc { pragma(inline
, true); return getState
.spacing
; } /// Get current letter spacing.
10847 @property void blur (float blur
) nothrow @trusted @nogc { pragma(inline
, true); getState
.blur
= blur
; } /// Set current letter blur.
10848 @property float blur () const pure nothrow @trusted @nogc { pragma(inline
, true); return getState
.blur
; } /// Get current letter blur.
10850 @property void textAlign (NVGTextAlign talign
) nothrow @trusted @nogc { pragma(inline
, true); getState
.talign
= talign
; } /// Set current text align.
10851 @property NVGTextAlign
textAlign () const pure nothrow @trusted @nogc { pragma(inline
, true); return getState
.talign
; } /// Get current text align.
10853 @property void fontId (int font
) nothrow @trusted @nogc { pragma(inline
, true); getState
.font
= font
; } /// Set current font id.
10854 @property int fontId () const pure nothrow @trusted @nogc { pragma(inline
, true); return getState
.font
; } /// Get current font id.
10856 @property void fontId (const(char)[] name
) nothrow @trusted @nogc { pragma(inline
, true); getState
.font
= getFontByName(name
); } /// Set current font using its name.
10858 /// Check if FontStash has a font with the given name loaded.
10859 bool hasFont (const(char)[] name
) const pure nothrow @trusted @nogc { pragma(inline
, true); return (getFontByName(name
) >= 0); }
10861 /// Get AA for the current font, or for the specified font.
10862 bool getFontAA (int font
=-1) nothrow @trusted @nogc {
10863 FONSstate
* state
= getState
;
10864 if (font
< 0) font
= state
.font
;
10865 if (font
< 0 || font
>= nfonts
) return false;
10866 FONSfont
* f
= fonts
[font
];
10867 return (f
!is null ?
!f
.font
.mono
: false);
10870 /// Push current state. Returns `false` if state stack overflowed.
10871 bool pushState () nothrow @trusted @nogc {
10872 if (nstates
>= FONS_MAX_STATES
) {
10873 if (handleError
!is null) handleError(FONSError
.StatesOverflow
, 0);
10877 import core
.stdc
.string
: memcpy
;
10878 memcpy(&states
[nstates
], &states
[nstates
-1], FONSstate
.sizeof
);
10884 /// Pop current state. Returns `false` if state stack underflowed.
10885 bool popState () nothrow @trusted @nogc {
10886 if (nstates
<= 1) {
10887 if (handleError
!is null) handleError(FONSError
.StatesUnderflow
, 0);
10894 /// Clear current state (i.e. set it to some sane defaults).
10895 void clearState () nothrow @trusted @nogc {
10896 FONSstate
* state
= getState
;
10897 state
.size
= 12.0f;
10901 state
.talign
.reset
;
10904 private enum NoAlias
= ":noaa";
10906 /** Add font to FontStash.
10908 * Load scalable font from disk, and add it to FontStash. If you will try to load a font
10909 * with same name and path several times, FontStash will load it only once. Also, you can
10910 * load new disk font for any existing logical font.
10913 * name = logical font name, that will be used to select this font later.
10914 * path = path to disk file with your font.
10915 * defAA = should FontStash use antialiased font rasterizer?
10918 * font id or [FONS_INVALID].
10920 int addFont (const(char)[] name
, const(char)[] path
, bool defAA
=false) nothrow @trusted {
10921 if (path
.length
== 0 || name
.length
== 0 ||
strequci(name
, NoAlias
)) return FONS_INVALID
;
10922 if (path
.length
> 32768) return FONS_INVALID
; // arbitrary limit
10924 // if font path ends with ":noaa", turn off antialiasing
10925 if (path
.length
>= NoAlias
.length
&& strequci(path
[$-NoAlias
.length
..$], NoAlias
)) {
10926 path
= path
[0..$-NoAlias
.length
];
10927 if (path
.length
== 0) return FONS_INVALID
;
10931 // if font name ends with ":noaa", turn off antialiasing
10932 if (name
.length
> NoAlias
.length
&& strequci(name
[$-NoAlias
.length
..$], NoAlias
)) {
10933 name
= name
[0..$-NoAlias
.length
];
10937 // find a font with the given name
10938 int fidx
= findNameInHash(name
);
10939 //{ import core.stdc.stdio; printf("loading font '%.*s' [%s] (fidx=%d)...\n", cast(uint)path.length, path.ptr, fontnamebuf.ptr, fidx); }
10941 int loadFontFile (const(char)[] path
) {
10942 // check if existing font (if any) has the same path
10944 import core
.stdc
.string
: strlen
;
10945 auto plen
= (fonts
[fidx
].path
!is null ?
strlen(fonts
[fidx
].path
) : 0);
10947 //{ import core.stdc.stdio; printf("+++ font [%.*s] was loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)fonts[fidx].path.length, fonts[fidx].path.ptr); }
10948 if (plen
== path
.length
&& fonts
[fidx
].path
[0..plen
] == path
) {
10949 //{ import core.stdc.stdio; printf("*** font [%.*s] already loaded from [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)plen, path.ptr); }
10954 if (plen
== path
.length
&& strequci(fonts
[fidx
].path
[0..plen
], path
)) {
10961 // special shitdows check: this will reject fontconfig font names (but still allow things like "c:myfont")
10962 foreach (immutable char ch
; path
[(path
.length
>= 2 && path
[1] == ':' ?
2 : 0)..$]) if (ch
== ':') return FONS_INVALID
;
10964 // either no such font, or different path
10965 //{ import core.stdc.stdio; printf("trying font [%.*s] from file [%.*s]\n", cast(uint)blen, fontnamebuf.ptr, cast(uint)path.length, path.ptr); }
10966 int xres
= FONS_INVALID
;
10968 import core
.stdc
.stdlib
: free
, malloc
;
10969 static if (NanoVegaHasIVVFS
) {
10970 auto fl
= VFile(path
);
10971 auto dataSize
= fl
.size
;
10972 if (dataSize
< 16 || dataSize
> int.max
/32) return FONS_INVALID
;
10973 ubyte* data
= cast(ubyte*)malloc(cast(uint)dataSize
);
10974 if (data
is null) assert(0, "out of memory in NanoVega fontstash");
10975 scope(failure
) free(data
); // oops
10976 fl
.rawReadExact(data
[0..cast(uint)dataSize
]);
10979 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, fread
, ftell
, fseek
;
10980 import std
.internal
.cstring
: tempCString
;
10981 auto fl
= fopen(path
.tempCString
, "rb");
10982 if (fl
is null) return FONS_INVALID
;
10983 scope(exit
) fclose(fl
);
10984 if (fseek(fl
, 0, 2/*SEEK_END*/) != 0) return FONS_INVALID
;
10985 auto dataSize
= ftell(fl
);
10986 if (fseek(fl
, 0, 0/*SEEK_SET*/) != 0) return FONS_INVALID
;
10987 if (dataSize
< 16 || dataSize
> int.max
/32) return FONS_INVALID
;
10988 ubyte* data
= cast(ubyte*)malloc(cast(uint)dataSize
);
10989 if (data
is null) assert(0, "out of memory in NanoVega fontstash");
10990 scope(failure
) free(data
); // oops
10991 ubyte* dptr
= data
;
10992 auto left
= cast(uint)dataSize
;
10994 auto rd
= fread(dptr
, 1, left
, fl
);
10995 if (rd
== 0) { free(data
); return FONS_INVALID
; } // unexpected EOF or reading error, it doesn't matter
11000 scope(failure
) free(data
); // oops
11001 // create font data
11002 FONSfontData
* fdata
= fons__createFontData(data
, cast(int)dataSize
, true); // free data
11004 xres
= addFontWithData(name
, fdata
, defAA
);
11005 if (xres
== FONS_INVALID
) {
11006 fdata
.decref(); // this will free [data] and [fdata]
11009 fonts
[xres
].setPath(path
);
11011 } catch (Exception e
) {
11017 // first try direct path
11018 auto res
= loadFontFile(path
);
11019 // if loading failed, try fontconfig (if fontconfig is available)
11020 static if (NanoVegaHasFontConfig
) {
11021 if (res
== FONS_INVALID
&& fontconfigAvailable
) {
11022 // idiotic fontconfig NEVER fails; let's skip it if `path` looks like a path
11024 if (path
.length
> 4 && (path
[$-4..$] == ".ttf" || path
[$-4..$] == ".ttc")) ok
= false;
11025 if (ok
) { foreach (immutable char ch
; path
) if (ch
== '/') { ok
= false; break; } }
11027 import std
.internal
.cstring
: tempCString
;
11028 FcPattern
* pat
= FcNameParse(path
.tempCString
);
11029 if (pat
!is null) {
11030 scope(exit
) FcPatternDestroy(pat
);
11031 if (FcConfigSubstitute(null, pat
, FcMatchPattern
)) {
11032 FcDefaultSubstitute(pat
);
11035 FcPattern
* font
= FcFontMatch(null, pat
, &result
);
11036 if (font
!is null) {
11037 scope(exit
) FcPatternDestroy(font
);
11039 if (FcPatternGetString(font
, FC_FILE
, 0, &file
) == FcResultMatch
) {
11040 if (file
!is null && file
[0]) {
11041 import core
.stdc
.string
: strlen
;
11042 res
= loadFontFile(file
[0..strlen(file
)]);
11054 /** Add font to FontStash, using data from memory.
11056 * And already loaded font to FontStash. You can replace existing logical fonts.
11057 * But note that you can't remove logical font by passing "empty" data.
11059 * $(WARNING If [FONS_INVALID] returned, `data` won't be freed even if `freeData` is `true`.)
11062 * name = logical font name, that will be used to select this font later.
11063 * data = font data.
11064 * dataSize = font data size.
11065 * freeData = should FontStash take ownership of the font data?
11066 * defAA = should FontStash use antialiased font rasterizer?
11069 * font id or [FONS_INVALID].
11071 int addFontMem (const(char)[] name
, ubyte* data
, int dataSize
, bool freeData
, bool defAA
=false) nothrow @trusted @nogc {
11072 if (data
is null || dataSize
< 16) return FONS_INVALID
;
11073 FONSfontData
* fdata
= fons__createFontData(data
, dataSize
, freeData
);
11075 auto res
= addFontWithData(name
, fdata
, defAA
);
11076 if (res
== FONS_INVALID
) {
11077 // we promised to not free data on error
11078 fdata
.freeData
= false;
11079 fdata
.decref(); // this will free [fdata]
11084 /** Add fonts from another FontStash.
11086 * This is more effective (and faster) than reloading fonts, because internally font data
11087 * is reference counted.
11089 void addFontsFrom (FONSContext source
) nothrow @trusted @nogc {
11090 if (source
is null) return;
11091 foreach (FONSfont
* font
; source
.fonts
[0..source
.nfonts
]) {
11092 if (font
!is null) {
11093 auto newidx
= addCookedFont(font
);
11094 FONSfont
* newfont
= fonts
[newidx
];
11095 assert(newfont
!is null);
11096 assert(newfont
.path
is null);
11098 if (font
.path
!is null && font
.path
[0]) {
11099 import core
.stdc
.stdlib
: malloc
;
11100 import core
.stdc
.string
: strcpy
, strlen
;
11101 newfont
.path
= cast(char*)malloc(strlen(font
.path
)+1);
11102 if (newfont
.path
is null) assert(0, "FONS: out of memory");
11103 strcpy(newfont
.path
, font
.path
);
11109 /// Returns logical font name corresponding to the given font id, or `null`.
11110 /// $(WARNING Copy returned name, as name buffer can be invalidated by next FontStash API call!)
11111 const(char)[] getNameById (int idx
) const pure nothrow @trusted @nogc {
11112 if (idx
< 0 || idx
>= nfonts || fonts
[idx
] is null) return null;
11113 return fonts
[idx
].name
[0..fonts
[idx
].namelen
];
11116 /// Returns font id corresponding to the given logical font name, or [FONS_INVALID].
11117 int getFontByName (const(char)[] name
) const pure nothrow @trusted @nogc {
11118 //{ import core.stdc.stdio; printf("fonsGetFontByName: [%.*s]\n", cast(uint)name.length, name.ptr); }
11119 // remove ":noaa" suffix
11120 if (name
.length
>= NoAlias
.length
&& strequci(name
[$-NoAlias
.length
..$], NoAlias
)) {
11121 name
= name
[0..$-NoAlias
.length
];
11123 if (name
.length
== 0) return FONS_INVALID
;
11124 return findNameInHash(name
);
11127 /** Measures the specified text string. Parameter bounds should be a float[4],
11128 * if the bounding box of the text should be returned. The bounds value are [xmin, ymin, xmax, ymax]
11129 * Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
11131 float getTextBounds(T
) (float x
, float y
, const(T
)[] str, float[] bounds
) nothrow @trusted @nogc if (isAnyCharType
!T
) {
11132 FONSstate
* state
= getState
;
11134 uint utf8state
= 0;
11136 FONSglyph
* glyph
= null;
11137 int prevGlyphIndex
= -1;
11138 short isize
= cast(short)(state
.size
*10.0f);
11139 short iblur
= cast(short)state
.blur
;
11142 if (state
.font
< 0 || state
.font
>= nfonts
) return 0;
11143 font
= fonts
[state
.font
];
11144 if (font
is null || font
.fdata
is null) return 0;
11146 float scale
= fons__tt_getPixelHeightScale(&font
.font
, cast(float)isize
/10.0f);
11148 // Align vertically.
11149 y
+= getVertAlign(font
, state
.talign
, isize
);
11151 float minx
= x
, maxx
= x
;
11152 float miny
= y
, maxy
= y
;
11155 foreach (T ch
; str) {
11156 static if (T
.sizeof
== 1) {
11157 //if (fons__decutf8(&utf8state, &codepoint, *cast(const(ubyte)*)str)) continue;
11158 mixin(DecUtfMixin
!("utf8state", "codepoint", "(cast(ubyte)ch)"));
11159 if (utf8state
) continue;
11161 static if (T
.sizeof
== 4) {
11162 if (ch
> dchar.max
) ch
= 0xFFFD;
11164 codepoint
= cast(uint)ch
;
11166 glyph
= getGlyph(font
, codepoint
, isize
, iblur
, FONSBitmapFlag
.Optional
);
11167 if (glyph
!is null) {
11168 getQuad(font
, prevGlyphIndex
, glyph
, isize
/10.0f, scale
, state
.spacing
, &x
, &y
, &q
);
11169 if (q
.x0
< minx
) minx
= q
.x0
;
11170 if (q
.x1
> maxx
) maxx
= q
.x1
;
11171 if (params
.isZeroTopLeft
) {
11172 if (q
.y0
< miny
) miny
= q
.y0
;
11173 if (q
.y1
> maxy
) maxy
= q
.y1
;
11175 if (q
.y1
< miny
) miny
= q
.y1
;
11176 if (q
.y0
> maxy
) maxy
= q
.y0
;
11178 prevGlyphIndex
= glyph
.index
;
11180 //{ import core.stdc.stdio; printf("NO GLYPH FOR 0x%04x\n", cast(uint)codepoint); }
11181 prevGlyphIndex
= -1;
11185 float advance
= x
-startx
;
11186 //{ import core.stdc.stdio; printf("***: x=%g; startx=%g; advance=%g\n", cast(double)x, cast(double)startx, cast(double)advance); }
11188 // Align horizontally
11189 if (state
.talign
.left
) {
11191 } else if (state
.talign
.right
) {
11194 } else if (state
.talign
.center
) {
11195 minx
-= advance
*0.5f;
11196 maxx
-= advance
*0.5f;
11199 if (bounds
.length
) {
11200 if (bounds
.length
> 0) bounds
.ptr
[0] = minx
;
11201 if (bounds
.length
> 1) bounds
.ptr
[1] = miny
;
11202 if (bounds
.length
> 2) bounds
.ptr
[2] = maxx
;
11203 if (bounds
.length
> 3) bounds
.ptr
[3] = maxy
;
11209 /// Returns various font metrics. Any argument can be `null` if you aren't interested in its value.
11210 void getVertMetrics (float* ascender
, float* descender
, float* lineh
) nothrow @trusted @nogc {
11211 FONSstate
* state
= getState
;
11212 if (state
.font
< 0 || state
.font
>= nfonts
) {
11213 if (ascender
!is null) *ascender
= 0;
11214 if (descender
!is null) *descender
= 0;
11215 if (lineh
!is null) *lineh
= 0;
11217 FONSfont
* font
= fonts
[state
.font
];
11218 if (font
is null || font
.fdata
is null) {
11219 if (ascender
!is null) *ascender
= 0;
11220 if (descender
!is null) *descender
= 0;
11221 if (lineh
!is null) *lineh
= 0;
11223 short isize
= cast(short)(state
.size
*10.0f);
11224 if (ascender
!is null) *ascender
= font
.ascender
*isize
/10.0f;
11225 if (descender
!is null) *descender
= font
.descender
*isize
/10.0f;
11226 if (lineh
!is null) *lineh
= font
.lineh
*isize
/10.0f;
11231 /// Returns line bounds. Any argument can be `null` if you aren't interested in its value.
11232 void getLineBounds (float y
, float* minyp
, float* maxyp
) nothrow @trusted @nogc {
11234 FONSstate
* state
= getState
;
11237 if (minyp
!is null) *minyp
= 0;
11238 if (maxyp
!is null) *maxyp
= 0;
11240 if (state
.font
< 0 || state
.font
>= nfonts
) return;
11241 font
= fonts
[state
.font
];
11242 isize
= cast(short)(state
.size
*10.0f);
11243 if (font
is null || font
.fdata
is null) return;
11245 y
+= getVertAlign(font
, state
.talign
, isize
);
11247 if (params
.isZeroTopLeft
) {
11248 immutable float miny
= y
-font
.ascender
*cast(float)isize
/10.0f;
11249 immutable float maxy
= miny
+font
.lineh
*isize
/10.0f;
11250 if (minyp
!is null) *minyp
= miny
;
11251 if (maxyp
!is null) *maxyp
= maxy
;
11253 immutable float maxy
= y
+font
.descender
*cast(float)isize
/10.0f;
11254 immutable float miny
= maxy
-font
.lineh
*isize
/10.0f;
11255 if (minyp
!is null) *minyp
= miny
;
11256 if (maxyp
!is null) *maxyp
= maxy
;
11260 /// Returns font line height.
11261 float fontHeight () nothrow @trusted @nogc {
11263 getVertMetrics(null, null, &res
);
11267 /// Returns font ascender (positive).
11268 float fontAscender () nothrow @trusted @nogc {
11270 getVertMetrics(&res
, null, null);
11274 /// Returns font descender (negative).
11275 float fontDescender () nothrow @trusted @nogc {
11277 getVertMetrics(null, &res
, null);
11281 //TODO: document this
11282 const(ubyte)* getTextureData (int* width
, int* height
) nothrow @trusted @nogc {
11283 if (width
!is null) *width
= params
.width
;
11284 if (height
!is null) *height
= params
.height
;
11288 //TODO: document this
11289 bool validateTexture (int* dirty
) nothrow @trusted @nogc {
11290 if (dirtyRect
.ptr
[0] < dirtyRect
.ptr
[2] && dirtyRect
.ptr
[1] < dirtyRect
.ptr
[3]) {
11291 dirty
[0] = dirtyRect
.ptr
[0];
11292 dirty
[1] = dirtyRect
.ptr
[1];
11293 dirty
[2] = dirtyRect
.ptr
[2];
11294 dirty
[3] = dirtyRect
.ptr
[3];
11295 // reset dirty rect
11296 dirtyRect
.ptr
[0] = params
.width
;
11297 dirtyRect
.ptr
[1] = params
.height
;
11298 dirtyRect
.ptr
[2] = 0;
11299 dirtyRect
.ptr
[3] = 0;
11305 //TODO: document this
11306 void errorCallback (void delegate (FONSError error
, int val
) nothrow @trusted @nogc callback
) nothrow @trusted @nogc {
11307 handleError
= callback
;
11310 //TODO: document this
11311 void getAtlasSize (int* width
, int* height
) const pure nothrow @trusted @nogc {
11312 if (width
!is null) *width
= params
.width
;
11313 if (height
!is null) *height
= params
.height
;
11316 //TODO: document this
11317 bool expandAtlas (int width
, int height
) nothrow @trusted @nogc {
11318 import core
.stdc
.stdlib
: free
;
11319 import core
.stdc
.string
: memcpy
, memset
;
11322 ubyte* data
= null;
11324 width
= nvg__max(width
, params
.width
);
11325 height
= nvg__max(height
, params
.height
);
11327 if (width
== params
.width
&& height
== params
.height
) return true;
11329 // Flush pending glyphs.
11332 // Create new texture
11333 if (params
.renderResize
!is null) {
11334 if (params
.renderResize(params
.userPtr
, width
, height
) == 0) return false;
11336 // Copy old texture data over.
11337 data
= cast(ubyte*)malloc(width
*height
);
11338 if (data
is null) return 0;
11339 foreach (immutable int i
; 0..params
.height
) {
11340 ubyte* dst
= &data
[i
*width
];
11341 ubyte* src
= &texData
[i
*params
.width
];
11342 memcpy(dst
, src
, params
.width
);
11343 if (width
> params
.width
) memset(dst
+params
.width
, 0, width
-params
.width
);
11345 if (height
> params
.height
) memset(&data
[params
.height
*width
], 0, (height
-params
.height
)*width
);
11350 // Increase atlas size
11351 atlas
.expand(width
, height
);
11353 // Add existing data as dirty.
11354 foreach (immutable int i
; 0..atlas
.nnodes
) maxy
= nvg__max(maxy
, atlas
.nodes
[i
].y
);
11355 dirtyRect
.ptr
[0] = 0;
11356 dirtyRect
.ptr
[1] = 0;
11357 dirtyRect
.ptr
[2] = params
.width
;
11358 dirtyRect
.ptr
[3] = maxy
;
11360 params
.width
= width
;
11361 params
.height
= height
;
11362 itw
= 1.0f/params
.width
;
11363 ith
= 1.0f/params
.height
;
11368 //TODO: document this
11369 bool resetAtlas (int width
, int height
) nothrow @trusted @nogc {
11370 import core
.stdc
.stdlib
: realloc
;
11371 import core
.stdc
.string
: memcpy
, memset
;
11373 // flush pending glyphs
11376 // create new texture
11377 if (params
.renderResize
!is null) {
11378 if (params
.renderResize(params
.userPtr
, width
, height
) == 0) return false;
11382 atlas
.reset(width
, height
);
11384 // clear texture data
11385 texData
= cast(ubyte*)realloc(texData
, width
*height
);
11386 if (texData
is null) assert(0, "FONS: out of memory");
11387 memset(texData
, 0, width
*height
);
11389 // reset dirty rect
11390 dirtyRect
.ptr
[0] = width
;
11391 dirtyRect
.ptr
[1] = height
;
11392 dirtyRect
.ptr
[2] = 0;
11393 dirtyRect
.ptr
[3] = 0;
11395 // Reset cached glyphs
11396 foreach (FONSfont
* font
; fonts
[0..nfonts
]) {
11397 if (font
!is null) {
11399 font
.lut
.ptr
[0..FONS_HASH_LUT_SIZE
] = -1;
11403 params
.width
= width
;
11404 params
.height
= height
;
11405 itw
= 1.0f/params
.width
;
11406 ith
= 1.0f/params
.height
;
11408 // Add white rect at 0, 0 for debug drawing.
11409 addWhiteRect(2, 2);
11414 //TODO: document this
11415 bool getPathBounds (dchar dch
, float[] bounds
) nothrow @trusted @nogc {
11416 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
11417 static if (is(typeof(&fons__nvg__bounds
))) {
11418 FONSstate
* state
= getState
;
11419 if (state
.font
< 0 || state
.font
>= nfonts
) { bounds
[] = 0; return false; }
11421 auto g
= findGlyphForCP(fonts
[state
.font
], dch
, &font
);
11422 if (g
== 0) { bounds
[] = 0; return false; }
11423 assert(font
!is null);
11424 return fons__nvg__bounds(&font
.font
, g
, bounds
);
11431 //TODO: document this
11432 bool toPath() (NVGContext vg
, dchar dch
, float[] bounds
=null) nothrow @trusted @nogc {
11433 if (bounds
.length
> 4) bounds
= bounds
.ptr
[0..4];
11434 static if (is(typeof(&fons__nvg__toPath
))) {
11435 if (vg
is null) { bounds
[] = 0; return false; }
11436 FONSstate
* state
= getState
;
11437 if (state
.font
< 0 || state
.font
>= nfonts
) { bounds
[] = 0; return false; }
11439 auto g
= findGlyphForCP(fonts
[state
.font
], dch
, &font
);
11440 if (g
== 0) { bounds
[] = 0; return false; }
11441 assert(font
!is null);
11442 return fons__nvg__toPath(vg
, &font
.font
, g
, bounds
);
11449 //TODO: document this
11450 bool toOutline (dchar dch
, NVGPathOutline
.DataStore
* ol
) nothrow @trusted @nogc {
11451 if (ol
is null) return false;
11452 static if (is(typeof(&fons__nvg__toOutline
))) {
11453 FONSstate
* state
= getState
;
11454 if (state
.font
< 0 || state
.font
>= nfonts
) return false;
11456 auto g
= findGlyphForCP(fonts
[state
.font
], dch
, &font
);
11457 if (g
== 0) return false;
11458 assert(font
!is null);
11459 return fons__nvg__toOutline(&font
.font
, g
, ol
);
11465 //TODO: document this
11466 FONSglyph
* getGlyph (FONSfont
* font
, uint codepoint
, short isize
, short iblur
, FONSBitmapFlag bitmapOption
) nothrow @trusted @nogc {
11467 static uint fons__hashint() (uint a
) pure nothrow @safe @nogc {
11468 pragma(inline
, true);
11478 // based on Exponential blur, Jani Huhtanen, 2006
11482 static void fons__blurCols (ubyte* dst
, int w
, int h
, int dstStride
, int alpha
) nothrow @trusted @nogc {
11483 foreach (immutable int y
; 0..h
) {
11484 int z
= 0; // force zero border
11485 foreach (int x
; 1..w
) {
11486 z
+= (alpha
*((cast(int)(dst
[x
])<<ZPREC
)-z
))>>APREC
;
11487 dst
[x
] = cast(ubyte)(z
>>ZPREC
);
11489 dst
[w
-1] = 0; // force zero border
11491 for (int x
= w
-2; x
>= 0; --x
) {
11492 z
+= (alpha
*((cast(int)(dst
[x
])<<ZPREC
)-z
))>>APREC
;
11493 dst
[x
] = cast(ubyte)(z
>>ZPREC
);
11495 dst
[0] = 0; // force zero border
11500 static void fons__blurRows (ubyte* dst
, int w
, int h
, int dstStride
, int alpha
) nothrow @trusted @nogc {
11501 foreach (immutable int x
; 0..w
) {
11502 int z
= 0; // force zero border
11503 for (int y
= dstStride
; y
< h
*dstStride
; y
+= dstStride
) {
11504 z
+= (alpha
*((cast(int)(dst
[y
])<<ZPREC
)-z
))>>APREC
;
11505 dst
[y
] = cast(ubyte)(z
>>ZPREC
);
11507 dst
[(h
-1)*dstStride
] = 0; // force zero border
11509 for (int y
= (h
-2)*dstStride
; y
>= 0; y
-= dstStride
) {
11510 z
+= (alpha
*((cast(int)(dst
[y
])<<ZPREC
)-z
))>>APREC
;
11511 dst
[y
] = cast(ubyte)(z
>>ZPREC
);
11513 dst
[0] = 0; // force zero border
11518 static void fons__blur (ubyte* dst
, int w
, int h
, int dstStride
, int blur
) nothrow @trusted @nogc {
11519 import std
.math
: expf
= exp
;
11520 if (blur
< 1) return;
11521 // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
11522 immutable float sigma
= cast(float)blur
*0.57735f; // 1/sqrt(3)
11523 int alpha
= cast(int)((1<<APREC
)*(1.0f-expf(-2.3f/(sigma
+1.0f))));
11524 fons__blurRows(dst
, w
, h
, dstStride
, alpha
);
11525 fons__blurCols(dst
, w
, h
, dstStride
, alpha
);
11526 fons__blurRows(dst
, w
, h
, dstStride
, alpha
);
11527 fons__blurCols(dst
, w
, h
, dstStride
, alpha
);
11528 //fons__blurrows(dst, w, h, dstStride, alpha);
11529 //fons__blurcols(dst, w, h, dstStride, alpha);
11532 int advance
, lsb
, x0
, y0
, x1
, y1
, gx
, gy
;
11533 FONSglyph
* glyph
= null;
11534 float size
= isize
/10.0f;
11535 FONSfont
* renderFont
= font
;
11537 if (isize
< 2) return null;
11538 if (iblur
> 20) iblur
= 20;
11541 // Find code point and size.
11542 uint h
= fons__hashint(codepoint
)&(FONS_HASH_LUT_SIZE
-1);
11543 int i
= font
.lut
.ptr
[h
];
11545 //if (font.glyphs[i].codepoint == codepoint && font.glyphs[i].size == isize && font.glyphs[i].blur == iblur) return &font.glyphs[i];
11546 if (font
.glyphs
[i
].codepoint
== codepoint
&& font
.glyphs
[i
].size
== isize
&& font
.glyphs
[i
].blur
== iblur
) {
11547 glyph
= &font
.glyphs
[i
];
11548 // Negative coordinate indicates there is no bitmap data created.
11549 if (bitmapOption
== FONSBitmapFlag
.Optional ||
(glyph
.x0
>= 0 && glyph
.y0
>= 0)) return glyph
;
11550 // At this point, glyph exists but the bitmap data is not yet created.
11553 i
= font
.glyphs
[i
].next
;
11556 // Create a new glyph or rasterize bitmap data for a cached glyph.
11557 //scale = fons__tt_getPixelHeightScale(&font.font, size);
11558 int g
= findGlyphForCP(font
, cast(dchar)codepoint
, &renderFont
);
11559 // It is possible that we did not find a fallback glyph.
11560 // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
11562 float scale
= fons__tt_getPixelHeightScale(&renderFont
.font
, size
);
11563 fons__tt_buildGlyphBitmap(&renderFont
.font
, g
, size
, scale
, &advance
, &lsb
, &x0
, &y0
, &x1
, &y1
);
11564 int gw
= x1
-x0
+pad
*2;
11565 int gh
= y1
-y0
+pad
*2;
11567 // Determines the spot to draw glyph in the atlas.
11568 if (bitmapOption
== FONSBitmapFlag
.Required
) {
11569 // Find free spot for the rect in the atlas.
11570 bool added
= atlas
.addRect(gw
, gh
, &gx
, &gy
);
11571 if (!added
&& handleError
!is null) {
11572 // Atlas is full, let the user to resize the atlas (or not), and try again.
11573 handleError(FONSError
.AtlasFull
, 0);
11574 added
= atlas
.addRect(gw
, gh
, &gx
, &gy
);
11576 if (!added
) return null;
11578 // Negative coordinate indicates there is no bitmap data created.
11584 if (glyph
is null) {
11585 glyph
= font
.allocGlyph();
11586 glyph
.codepoint
= codepoint
;
11587 glyph
.size
= isize
;
11588 glyph
.blur
= iblur
;
11591 // Insert char to hash lookup.
11592 glyph
.next
= font
.lut
.ptr
[h
];
11593 font
.lut
.ptr
[h
] = font
.nglyphs
-1;
11596 glyph
.x0
= cast(short)gx
;
11597 glyph
.y0
= cast(short)gy
;
11598 glyph
.x1
= cast(short)(glyph
.x0
+gw
);
11599 glyph
.y1
= cast(short)(glyph
.y0
+gh
);
11600 glyph
.xadv
= cast(short)(scale
*advance
*10.0f);
11601 glyph
.xoff
= cast(short)(x0
-pad
);
11602 glyph
.yoff
= cast(short)(y0
-pad
);
11604 if (bitmapOption
== FONSBitmapFlag
.Optional
) return glyph
;
11607 ubyte* dst
= &texData
[(glyph
.x0
+pad
)+(glyph
.y0
+pad
)*params
.width
];
11608 fons__tt_renderGlyphBitmap(&font
.font
, dst
, gw
-pad
*2, gh
-pad
*2, params
.width
, scale
, scale
, g
);
11610 // Make sure there is one pixel empty border.
11611 dst
= &texData
[glyph
.x0
+glyph
.y0
*params
.width
];
11612 foreach (immutable int y
; 0..gh
) {
11613 dst
[y
*params
.width
] = 0;
11614 dst
[gw
-1+y
*params
.width
] = 0;
11616 foreach (immutable int x
; 0..gw
) {
11618 dst
[x
+(gh
-1)*params
.width
] = 0;
11621 // Debug code to color the glyph background
11623 foreach (immutable yy
; 0..gh
) {
11624 foreach (immutable xx
; 0..gw
) {
11625 int a
= cast(int)dst
[xx
+yy
*params
.width
]+42;
11626 if (a
> 255) a
= 255;
11627 dst
[xx
+yy
*params
.width
] = cast(ubyte)a
;
11634 ubyte* bdst
= &texData
[glyph
.x0
+glyph
.y0
*params
.width
];
11635 fons__blur(bdst
, gw
, gh
, params
.width
, iblur
);
11638 dirtyRect
.ptr
[0] = nvg__min(dirtyRect
.ptr
[0], glyph
.x0
);
11639 dirtyRect
.ptr
[1] = nvg__min(dirtyRect
.ptr
[1], glyph
.y0
);
11640 dirtyRect
.ptr
[2] = nvg__max(dirtyRect
.ptr
[2], glyph
.x1
);
11641 dirtyRect
.ptr
[3] = nvg__max(dirtyRect
.ptr
[3], glyph
.y1
);
11646 //TODO: document this
11647 void getQuad (FONSfont
* font
, int prevGlyphIndex
, FONSglyph
* glyph
, float size
, float scale
, float spacing
, float* x
, float* y
, FONSQuad
* q
) nothrow @trusted @nogc {
11648 if (prevGlyphIndex
>= 0) {
11649 immutable float adv
= fons__tt_getGlyphKernAdvance(&font
.font
, size
, prevGlyphIndex
, glyph
.index
)/**scale*/; //k8: do we really need scale here?
11650 //if (adv != 0) { import core.stdc.stdio; printf("adv=%g (scale=%g; spacing=%g)\n", cast(double)adv, cast(double)scale, cast(double)spacing); }
11651 *x
+= cast(int)(adv
+spacing
/*+0.5f*/); //k8: for me, it looks better this way (with non-aa fonts)
11654 // Each glyph has 2px border to allow good interpolation,
11655 // one pixel to prevent leaking, and one to allow good interpolation for rendering.
11656 // Inset the texture region by one pixel for correct interpolation.
11657 immutable float xoff
= cast(short)(glyph
.xoff
+1);
11658 immutable float yoff
= cast(short)(glyph
.yoff
+1);
11659 immutable float x0
= cast(float)(glyph
.x0
+1);
11660 immutable float y0
= cast(float)(glyph
.y0
+1);
11661 immutable float x1
= cast(float)(glyph
.x1
-1);
11662 immutable float y1
= cast(float)(glyph
.y1
-1);
11664 if (params
.isZeroTopLeft
) {
11665 immutable float rx
= cast(float)cast(int)(*x
+xoff
);
11666 immutable float ry
= cast(float)cast(int)(*y
+yoff
);
11678 immutable float rx
= cast(float)cast(int)(*x
+xoff
);
11679 immutable float ry
= cast(float)cast(int)(*y
-yoff
);
11692 *x
+= cast(int)(glyph
.xadv
/10.0f+0.5f);
11695 void flush () nothrow @trusted @nogc {
11697 if (dirtyRect
.ptr
[0] < dirtyRect
.ptr
[2] && dirtyRect
.ptr
[1] < dirtyRect
.ptr
[3]) {
11698 if (params
.renderUpdate
!is null) params
.renderUpdate(params
.userPtr
, dirtyRect
.ptr
, texData
);
11699 // reset dirty rect
11700 dirtyRect
.ptr
[0] = params
.width
;
11701 dirtyRect
.ptr
[1] = params
.height
;
11702 dirtyRect
.ptr
[2] = 0;
11703 dirtyRect
.ptr
[3] = 0;
11708 /// Free all resources used by the `stash`, and `stash` itself.
11709 /// Group: font_stash
11710 public void kill (ref FONSContext stash
) nothrow @trusted @nogc {
11711 import core
.stdc
.stdlib
: free
;
11712 if (stash
is null) return;
11719 // ////////////////////////////////////////////////////////////////////////// //
11720 // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
11721 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
11723 enum FONS_UTF8_ACCEPT
= 0;
11724 enum FONS_UTF8_REJECT
= 12;
11726 static immutable ubyte[364] utf8d
= [
11727 // The first part of the table maps bytes to character classes that
11728 // to reduce the size of the transition table and create bitmasks.
11729 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,
11730 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,
11731 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,
11732 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,
11733 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,
11734 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,
11735 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,
11736 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,
11738 // The second part is a transition table that maps a combination
11739 // of a state of the automaton and a character class to a state.
11740 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
11741 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12,
11742 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12,
11743 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12,
11744 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
11747 private enum DecUtfMixin(string state
, string codep
, string byte_
) =
11749 uint type_ = utf8d.ptr[`~byte_
~`];
11750 `~codep
~` = (`~state
~` != FONS_UTF8_ACCEPT ? (`~byte_
~`&0x3fu)|(`~codep
~`<<6) : (0xff>>type_)&`~byte_
~`);
11751 if ((`~state
~` = utf8d.ptr[256+`~state
~`+type_]) == FONS_UTF8_REJECT) {
11752 `~state
~` = FONS_UTF8_ACCEPT;
11753 `~codep
~` = 0xFFFD;
11758 uint fons__decutf8 (uint* state, uint* codep, uint byte_) {
11759 pragma(inline, true);
11760 uint type = utf8d.ptr[byte_];
11761 *codep = (*state != FONS_UTF8_ACCEPT ? (byte_&0x3fu)|(*codep<<6) : (0xff>>type)&byte_);
11762 *state = utf8d.ptr[256 + *state+type];
11768 // ////////////////////////////////////////////////////////////////////////// //
11769 /// This iterator can be used to do text measurement.
11770 /// $(WARNING Don't add new fonts to stash while you are iterating, or you WILL get segfault!)
11771 /// Group: font_stash
11772 public struct FONSTextBoundsIterator
{
11776 uint codepoint
= 0xFFFD;
11777 uint utf8state
= 0;
11778 int prevGlyphIndex
= -1;
11779 short isize
, iblur
;
11782 float startx
= 0, x
= 0, y
= 0;
11783 float minx
= 0, miny
= 0, maxx
= 0, maxy
= 0;
11786 void clear () nothrow @trusted @nogc {
11787 import core
.stdc
.string
: memset
;
11788 memset(&this, 0, this.sizeof
);
11789 this.prevGlyphIndex
= -1;
11790 this.codepoint
= 0xFFFD;
11794 /// Initialize iterator with the current FontStash state. FontStash state can be changed after initialization without affecting the iterator.
11795 this (FONSContext astash
, float ax
=0, float ay
=0) nothrow @trusted @nogc { reset(astash
, ax
, ay
); }
11797 /// (Re)initialize iterator with the current FontStash state. FontStash state can be changed after initialization without affecting the iterator.
11798 void reset (FONSContext astash
, float ax
=0, float ay
=0) nothrow @trusted @nogc {
11801 if (astash
is null || astash
.nstates
== 0) return;
11804 state
= *stash
.getState
;
11806 if (state
.font
< 0 || state
.font
>= stash
.nfonts
) { clear(); return; }
11807 font
= stash
.fonts
[state
.font
];
11808 if (font
is null || font
.fdata
is null) { clear(); return; }
11812 isize
= cast(short)(state
.size
*10.0f);
11813 iblur
= cast(short)state
.blur
;
11814 scale
= fons__tt_getPixelHeightScale(&font
.font
, cast(float)isize
/10.0f);
11816 // align vertically
11817 y
+= astash
.getVertAlign(font
, state
.talign
, isize
);
11824 /// Can this iterator be used?
11825 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (stash
!is null); }
11827 /// Put some text into iterator, calculate new values.
11828 void put(T
) (const(T
)[] str...) nothrow @trusted @nogc if (isAnyCharType
!T
) {
11829 enum DoCodePointMixin
= q
{
11830 glyph
= stash
.getGlyph(font
, codepoint
, isize
, iblur
, FONSBitmapFlag
.Optional
);
11831 if (glyph
!is null) {
11832 stash
.getQuad(font
, prevGlyphIndex
, glyph
, isize
/10.0f, scale
, state
.spacing
, &x
, &y
, &q
);
11833 if (q
.x0
< minx
) minx
= q
.x0
;
11834 if (q
.x1
> maxx
) maxx
= q
.x1
;
11835 if (stash
.params
.isZeroTopLeft
) {
11836 if (q
.y0
< miny
) miny
= q
.y0
;
11837 if (q
.y1
> maxy
) maxy
= q
.y1
;
11839 if (q
.y1
< miny
) miny
= q
.y1
;
11840 if (q
.y0
> maxy
) maxy
= q
.y0
;
11842 prevGlyphIndex
= glyph
.index
;
11844 prevGlyphIndex
= -1;
11848 if (stash
is null ||
str.length
== 0) return; // alas
11853 static if (is(T
== char)) {
11854 foreach (char ch
; str) {
11855 mixin(DecUtfMixin
!("utf8state", "codepoint", "cast(ubyte)ch"));
11856 if (utf8state
) continue; // full char is not collected yet
11857 mixin(DoCodePointMixin
);
11862 codepoint
= 0xFFFD;
11863 mixin(DoCodePointMixin
);
11865 foreach (T dch
; str) {
11866 static if (is(T
== dchar)) {
11867 if (dch
> dchar.max
) dch
= 0xFFFD;
11869 codepoint
= cast(uint)dch
;
11870 mixin(DoCodePointMixin
);
11875 /// Returns current advance.
11876 @property float advance () const pure nothrow @safe @nogc { pragma(inline
, true); return (stash
!is null ? x
-startx
: 0); }
11878 /// Returns current text bounds.
11879 void getBounds (ref float[4] bounds
) const pure nothrow @safe @nogc {
11880 if (stash
is null) { bounds
[] = 0; return; }
11881 float lminx
= minx
, lmaxx
= maxx
;
11882 // align horizontally
11883 if (state
.talign
.left
) {
11885 } else if (state
.talign
.right
) {
11886 float ca
= advance
;
11889 } else if (state
.talign
.center
) {
11890 float ca
= advance
*0.5f;
11900 /// Returns current horizontal text bounds.
11901 void getHBounds (out float xmin
, out float xmax
) nothrow @trusted @nogc {
11902 if (stash
!is null) {
11903 float lminx
= minx
, lmaxx
= maxx
;
11904 // align horizontally
11905 if (state
.talign
.left
) {
11907 } else if (state
.talign
.right
) {
11908 float ca
= advance
;
11911 } else if (state
.talign
.center
) {
11912 float ca
= advance
*0.5f;
11923 /// Returns current vertical text bounds.
11924 void getVBounds (out float ymin
, out float ymax
) nothrow @trusted @nogc {
11925 pragma(inline
, true);
11926 if (stash
!is null) {
11934 /// Returns font line height.
11935 float lineHeight () nothrow @trusted @nogc {
11936 pragma(inline
, true);
11937 return (stash
!is null ? stash
.fonts
[state
.font
].lineh
*cast(short)(state
.size
*10.0f)/10.0f : 0);
11940 /// Returns font ascender (positive).
11941 float ascender () nothrow @trusted @nogc {
11942 pragma(inline
, true);
11943 return (stash
!is null ? stash
.fonts
[state
.font
].ascender
*cast(short)(state
.size
*10.0f)/10.0f : 0);
11946 /// Returns font descender (negative).
11947 float descender () nothrow @trusted @nogc {
11948 pragma(inline
, true);
11949 return (stash
!is null ? stash
.fonts
[state
.font
].descender
*cast(short)(state
.size
*10.0f)/10.0f : 0);
11954 // ////////////////////////////////////////////////////////////////////////// //
11956 // ////////////////////////////////////////////////////////////////////////// //
11957 import core
.stdc
.stdlib
: malloc
, realloc
, free
;
11958 import core
.stdc
.string
: memcpy
, memset
;
11960 static if (__VERSION__
< 2076) {
11961 private auto DGNoThrowNoGC(T
) (scope T t
) /*if (isFunctionPointer!T || isDelegate!T)*/ {
11963 enum attrs
= functionAttributes
!T|FunctionAttribute
.nogc|FunctionAttribute
.nothrow_
;
11964 return cast(SetFunctionAttributes
!(T
, functionLinkage
!T
, attrs
)) t
;
11969 //import arsd.simpledisplay;
11970 version(nanovg_builtin_opengl_bindings
) { import arsd
.simpledisplay
; } else { import iv
.glbinds
; }
11973 // sdpy is missing that yet
11974 static if (!is(typeof(GL_STENCIL_BUFFER_BIT
))) enum uint GL_STENCIL_BUFFER_BIT
= 0x00000400;
11977 // OpenGL API missing from simpledisplay
11978 private extern(System
) nothrow @nogc {
11979 alias GLvoid
= void;
11980 alias GLboolean
= ubyte;
11981 alias GLuint
= uint;
11982 alias GLenum
= uint;
11983 alias GLchar
= char;
11984 alias GLsizei
= int;
11985 alias GLfloat
= float;
11986 alias GLsizeiptr
= ptrdiff_t
;
11988 enum uint GL_STENCIL_BUFFER_BIT
= 0x00000400;
11990 enum uint GL_INVALID_ENUM
= 0x0500;
11992 enum uint GL_ZERO
= 0;
11993 enum uint GL_ONE
= 1;
11995 enum uint GL_FLOAT
= 0x1406;
11997 enum uint GL_STREAM_DRAW
= 0x88E0;
11999 enum uint GL_CCW
= 0x0901;
12001 enum uint GL_STENCIL_TEST
= 0x0B90;
12002 enum uint GL_SCISSOR_TEST
= 0x0C11;
12004 enum uint GL_EQUAL
= 0x0202;
12005 enum uint GL_NOTEQUAL
= 0x0205;
12007 enum uint GL_ALWAYS
= 0x0207;
12008 enum uint GL_KEEP
= 0x1E00;
12010 enum uint GL_INCR
= 0x1E02;
12012 enum uint GL_INCR_WRAP
= 0x8507;
12013 enum uint GL_DECR_WRAP
= 0x8508;
12015 enum uint GL_CULL_FACE
= 0x0B44;
12016 enum uint GL_BACK
= 0x0405;
12018 enum uint GL_FRAGMENT_SHADER
= 0x8B30;
12019 enum uint GL_VERTEX_SHADER
= 0x8B31;
12021 enum uint GL_COMPILE_STATUS
= 0x8B81;
12022 enum uint GL_LINK_STATUS
= 0x8B82;
12024 enum uint GL_UNPACK_ALIGNMENT
= 0x0CF5;
12025 enum uint GL_UNPACK_ROW_LENGTH
= 0x0CF2;
12026 enum uint GL_UNPACK_SKIP_PIXELS
= 0x0CF4;
12027 enum uint GL_UNPACK_SKIP_ROWS
= 0x0CF3;
12029 enum uint GL_GENERATE_MIPMAP
= 0x8191;
12030 enum uint GL_LINEAR_MIPMAP_LINEAR
= 0x2703;
12032 enum uint GL_RED
= 0x1903;
12034 enum uint GL_TEXTURE0
= 0x84C0U
;
12035 enum uint GL_TEXTURE1
= 0x84C1U
;
12037 enum uint GL_ARRAY_BUFFER
= 0x8892;
12039 enum uint GL_SRC_COLOR
= 0x0300;
12040 enum uint GL_ONE_MINUS_SRC_COLOR
= 0x0301;
12041 enum uint GL_SRC_ALPHA
= 0x0302;
12042 enum uint GL_ONE_MINUS_SRC_ALPHA
= 0x0303;
12043 enum uint GL_DST_ALPHA
= 0x0304;
12044 enum uint GL_ONE_MINUS_DST_ALPHA
= 0x0305;
12045 enum uint GL_DST_COLOR
= 0x0306;
12046 enum uint GL_ONE_MINUS_DST_COLOR
= 0x0307;
12047 enum uint GL_SRC_ALPHA_SATURATE
= 0x0308;
12049 enum uint GL_INVERT
= 0x150AU
;
12051 enum uint GL_DEPTH_STENCIL
= 0x84F9U
;
12052 enum uint GL_UNSIGNED_INT_24_8
= 0x84FAU
;
12054 enum uint GL_FRAMEBUFFER
= 0x8D40U
;
12055 enum uint GL_COLOR_ATTACHMENT0
= 0x8CE0U
;
12056 enum uint GL_DEPTH_STENCIL_ATTACHMENT
= 0x821AU
;
12058 enum uint GL_FRAMEBUFFER_COMPLETE
= 0x8CD5U
;
12059 enum uint GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
= 0x8CD6U
;
12060 enum uint GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
= 0x8CD7U
;
12061 enum uint GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
= 0x8CD9U
;
12062 enum uint GL_FRAMEBUFFER_UNSUPPORTED
= 0x8CDDU
;
12064 enum uint GL_COLOR_LOGIC_OP
= 0x0BF2U
;
12065 enum uint GL_CLEAR
= 0x1500U
;
12066 enum uint GL_COPY
= 0x1503U
;
12067 enum uint GL_XOR
= 0x1506U
;
12069 enum uint GL_FRAMEBUFFER_BINDING
= 0x8CA6U
;
12073 private void* kglLoad (const(char)* name) {
12074 void* res = glGetProcAddress(name);
12076 import core.sys.windows.windef, core.sys.windows.winbase;
12077 static HINSTANCE dll = null;
12079 dll = LoadLibraryA("opengl32.dll");
12080 if (dll is null) return null; // <32, but idc
12081 return GetProcAddress(dll, name);
12086 alias kglLoad = glGetProcAddress;
12090 alias glbfn_glStencilMask
= void function(GLuint
);
12091 __gshared glbfn_glStencilMask glStencilMask_NVGLZ
; alias glStencilMask
= glStencilMask_NVGLZ
;
12092 alias glbfn_glStencilFunc
= void function(GLenum
, GLint
, GLuint
);
12093 __gshared glbfn_glStencilFunc glStencilFunc_NVGLZ
; alias glStencilFunc
= glStencilFunc_NVGLZ
;
12094 alias glbfn_glGetShaderInfoLog
= void function(GLuint
, GLsizei
, GLsizei
*, GLchar
*);
12095 __gshared glbfn_glGetShaderInfoLog glGetShaderInfoLog_NVGLZ
; alias glGetShaderInfoLog
= glGetShaderInfoLog_NVGLZ
;
12096 alias glbfn_glGetProgramInfoLog
= void function(GLuint
, GLsizei
, GLsizei
*, GLchar
*);
12097 __gshared glbfn_glGetProgramInfoLog glGetProgramInfoLog_NVGLZ
; alias glGetProgramInfoLog
= glGetProgramInfoLog_NVGLZ
;
12098 alias glbfn_glCreateProgram
= GLuint
function();
12099 __gshared glbfn_glCreateProgram glCreateProgram_NVGLZ
; alias glCreateProgram
= glCreateProgram_NVGLZ
;
12100 alias glbfn_glCreateShader
= GLuint
function(GLenum
);
12101 __gshared glbfn_glCreateShader glCreateShader_NVGLZ
; alias glCreateShader
= glCreateShader_NVGLZ
;
12102 alias glbfn_glShaderSource
= void function(GLuint
, GLsizei
, const(GLchar
*)*, const(GLint
)*);
12103 __gshared glbfn_glShaderSource glShaderSource_NVGLZ
; alias glShaderSource
= glShaderSource_NVGLZ
;
12104 alias glbfn_glCompileShader
= void function(GLuint
);
12105 __gshared glbfn_glCompileShader glCompileShader_NVGLZ
; alias glCompileShader
= glCompileShader_NVGLZ
;
12106 alias glbfn_glGetShaderiv
= void function(GLuint
, GLenum
, GLint
*);
12107 __gshared glbfn_glGetShaderiv glGetShaderiv_NVGLZ
; alias glGetShaderiv
= glGetShaderiv_NVGLZ
;
12108 alias glbfn_glAttachShader
= void function(GLuint
, GLuint
);
12109 __gshared glbfn_glAttachShader glAttachShader_NVGLZ
; alias glAttachShader
= glAttachShader_NVGLZ
;
12110 alias glbfn_glBindAttribLocation
= void function(GLuint
, GLuint
, const(GLchar
)*);
12111 __gshared glbfn_glBindAttribLocation glBindAttribLocation_NVGLZ
; alias glBindAttribLocation
= glBindAttribLocation_NVGLZ
;
12112 alias glbfn_glLinkProgram
= void function(GLuint
);
12113 __gshared glbfn_glLinkProgram glLinkProgram_NVGLZ
; alias glLinkProgram
= glLinkProgram_NVGLZ
;
12114 alias glbfn_glGetProgramiv
= void function(GLuint
, GLenum
, GLint
*);
12115 __gshared glbfn_glGetProgramiv glGetProgramiv_NVGLZ
; alias glGetProgramiv
= glGetProgramiv_NVGLZ
;
12116 alias glbfn_glDeleteProgram
= void function(GLuint
);
12117 __gshared glbfn_glDeleteProgram glDeleteProgram_NVGLZ
; alias glDeleteProgram
= glDeleteProgram_NVGLZ
;
12118 alias glbfn_glDeleteShader
= void function(GLuint
);
12119 __gshared glbfn_glDeleteShader glDeleteShader_NVGLZ
; alias glDeleteShader
= glDeleteShader_NVGLZ
;
12120 alias glbfn_glGetUniformLocation
= GLint
function(GLuint
, const(GLchar
)*);
12121 __gshared glbfn_glGetUniformLocation glGetUniformLocation_NVGLZ
; alias glGetUniformLocation
= glGetUniformLocation_NVGLZ
;
12122 alias glbfn_glGenBuffers
= void function(GLsizei
, GLuint
*);
12123 __gshared glbfn_glGenBuffers glGenBuffers_NVGLZ
; alias glGenBuffers
= glGenBuffers_NVGLZ
;
12124 alias glbfn_glPixelStorei
= void function(GLenum
, GLint
);
12125 __gshared glbfn_glPixelStorei glPixelStorei_NVGLZ
; alias glPixelStorei
= glPixelStorei_NVGLZ
;
12126 alias glbfn_glUniform4fv
= void function(GLint
, GLsizei
, const(GLfloat
)*);
12127 __gshared glbfn_glUniform4fv glUniform4fv_NVGLZ
; alias glUniform4fv
= glUniform4fv_NVGLZ
;
12128 alias glbfn_glColorMask
= void function(GLboolean
, GLboolean
, GLboolean
, GLboolean
);
12129 __gshared glbfn_glColorMask glColorMask_NVGLZ
; alias glColorMask
= glColorMask_NVGLZ
;
12130 alias glbfn_glStencilOpSeparate
= void function(GLenum
, GLenum
, GLenum
, GLenum
);
12131 __gshared glbfn_glStencilOpSeparate glStencilOpSeparate_NVGLZ
; alias glStencilOpSeparate
= glStencilOpSeparate_NVGLZ
;
12132 alias glbfn_glDrawArrays
= void function(GLenum
, GLint
, GLsizei
);
12133 __gshared glbfn_glDrawArrays glDrawArrays_NVGLZ
; alias glDrawArrays
= glDrawArrays_NVGLZ
;
12134 alias glbfn_glStencilOp
= void function(GLenum
, GLenum
, GLenum
);
12135 __gshared glbfn_glStencilOp glStencilOp_NVGLZ
; alias glStencilOp
= glStencilOp_NVGLZ
;
12136 alias glbfn_glUseProgram
= void function(GLuint
);
12137 __gshared glbfn_glUseProgram glUseProgram_NVGLZ
; alias glUseProgram
= glUseProgram_NVGLZ
;
12138 alias glbfn_glCullFace
= void function(GLenum
);
12139 __gshared glbfn_glCullFace glCullFace_NVGLZ
; alias glCullFace
= glCullFace_NVGLZ
;
12140 alias glbfn_glFrontFace
= void function(GLenum
);
12141 __gshared glbfn_glFrontFace glFrontFace_NVGLZ
; alias glFrontFace
= glFrontFace_NVGLZ
;
12142 alias glbfn_glActiveTexture
= void function(GLenum
);
12143 __gshared glbfn_glActiveTexture glActiveTexture_NVGLZ
; alias glActiveTexture
= glActiveTexture_NVGLZ
;
12144 alias glbfn_glBindBuffer
= void function(GLenum
, GLuint
);
12145 __gshared glbfn_glBindBuffer glBindBuffer_NVGLZ
; alias glBindBuffer
= glBindBuffer_NVGLZ
;
12146 alias glbfn_glBufferData
= void function(GLenum
, GLsizeiptr
, const(void)*, GLenum
);
12147 __gshared glbfn_glBufferData glBufferData_NVGLZ
; alias glBufferData
= glBufferData_NVGLZ
;
12148 alias glbfn_glEnableVertexAttribArray
= void function(GLuint
);
12149 __gshared glbfn_glEnableVertexAttribArray glEnableVertexAttribArray_NVGLZ
; alias glEnableVertexAttribArray
= glEnableVertexAttribArray_NVGLZ
;
12150 alias glbfn_glVertexAttribPointer
= void function(GLuint
, GLint
, GLenum
, GLboolean
, GLsizei
, const(void)*);
12151 __gshared glbfn_glVertexAttribPointer glVertexAttribPointer_NVGLZ
; alias glVertexAttribPointer
= glVertexAttribPointer_NVGLZ
;
12152 alias glbfn_glUniform1i
= void function(GLint
, GLint
);
12153 __gshared glbfn_glUniform1i glUniform1i_NVGLZ
; alias glUniform1i
= glUniform1i_NVGLZ
;
12154 alias glbfn_glUniform2fv
= void function(GLint
, GLsizei
, const(GLfloat
)*);
12155 __gshared glbfn_glUniform2fv glUniform2fv_NVGLZ
; alias glUniform2fv
= glUniform2fv_NVGLZ
;
12156 alias glbfn_glDisableVertexAttribArray
= void function(GLuint
);
12157 __gshared glbfn_glDisableVertexAttribArray glDisableVertexAttribArray_NVGLZ
; alias glDisableVertexAttribArray
= glDisableVertexAttribArray_NVGLZ
;
12158 alias glbfn_glDeleteBuffers
= void function(GLsizei
, const(GLuint
)*);
12159 __gshared glbfn_glDeleteBuffers glDeleteBuffers_NVGLZ
; alias glDeleteBuffers
= glDeleteBuffers_NVGLZ
;
12160 alias glbfn_glBlendFuncSeparate
= void function(GLenum
, GLenum
, GLenum
, GLenum
);
12161 __gshared glbfn_glBlendFuncSeparate glBlendFuncSeparate_NVGLZ
; alias glBlendFuncSeparate
= glBlendFuncSeparate_NVGLZ
;
12163 alias glbfn_glLogicOp
= void function (GLenum opcode
);
12164 __gshared glbfn_glLogicOp glLogicOp_NVGLZ
; alias glLogicOp
= glLogicOp_NVGLZ
;
12165 alias glbfn_glFramebufferTexture2D
= void function (GLenum target
, GLenum attachment
, GLenum textarget
, GLuint texture
, GLint level
);
12166 __gshared glbfn_glFramebufferTexture2D glFramebufferTexture2D_NVGLZ
; alias glFramebufferTexture2D
= glFramebufferTexture2D_NVGLZ
;
12167 alias glbfn_glDeleteFramebuffers
= void function (GLsizei n
, const(GLuint
)* framebuffers
);
12168 __gshared glbfn_glDeleteFramebuffers glDeleteFramebuffers_NVGLZ
; alias glDeleteFramebuffers
= glDeleteFramebuffers_NVGLZ
;
12169 alias glbfn_glGenFramebuffers
= void function (GLsizei n
, GLuint
* framebuffers
);
12170 __gshared glbfn_glGenFramebuffers glGenFramebuffers_NVGLZ
; alias glGenFramebuffers
= glGenFramebuffers_NVGLZ
;
12171 alias glbfn_glCheckFramebufferStatus
= GLenum
function (GLenum target
);
12172 __gshared glbfn_glCheckFramebufferStatus glCheckFramebufferStatus_NVGLZ
; alias glCheckFramebufferStatus
= glCheckFramebufferStatus_NVGLZ
;
12173 alias glbfn_glBindFramebuffer
= void function (GLenum target
, GLuint framebuffer
);
12174 __gshared glbfn_glBindFramebuffer glBindFramebuffer_NVGLZ
; alias glBindFramebuffer
= glBindFramebuffer_NVGLZ
;
12176 alias glbfn_glGetIntegerv
= void function (GLenum pname
, GLint
* data
);
12177 __gshared glbfn_glGetIntegerv glGetIntegerv_NVGLZ
; alias glGetIntegerv
= glGetIntegerv_NVGLZ
;
12179 private void nanovgInitOpenGL () {
12180 __gshared
bool initialized
= false;
12181 if (initialized
) return;
12182 glStencilMask_NVGLZ
= cast(glbfn_glStencilMask
)glbindGetProcAddress(`glStencilMask`);
12183 if (glStencilMask_NVGLZ
is null) assert(0, `OpenGL function 'glStencilMask' not found!`);
12184 glStencilFunc_NVGLZ
= cast(glbfn_glStencilFunc
)glbindGetProcAddress(`glStencilFunc`);
12185 if (glStencilFunc_NVGLZ
is null) assert(0, `OpenGL function 'glStencilFunc' not found!`);
12186 glGetShaderInfoLog_NVGLZ
= cast(glbfn_glGetShaderInfoLog
)glbindGetProcAddress(`glGetShaderInfoLog`);
12187 if (glGetShaderInfoLog_NVGLZ
is null) assert(0, `OpenGL function 'glGetShaderInfoLog' not found!`);
12188 glGetProgramInfoLog_NVGLZ
= cast(glbfn_glGetProgramInfoLog
)glbindGetProcAddress(`glGetProgramInfoLog`);
12189 if (glGetProgramInfoLog_NVGLZ
is null) assert(0, `OpenGL function 'glGetProgramInfoLog' not found!`);
12190 glCreateProgram_NVGLZ
= cast(glbfn_glCreateProgram
)glbindGetProcAddress(`glCreateProgram`);
12191 if (glCreateProgram_NVGLZ
is null) assert(0, `OpenGL function 'glCreateProgram' not found!`);
12192 glCreateShader_NVGLZ
= cast(glbfn_glCreateShader
)glbindGetProcAddress(`glCreateShader`);
12193 if (glCreateShader_NVGLZ
is null) assert(0, `OpenGL function 'glCreateShader' not found!`);
12194 glShaderSource_NVGLZ
= cast(glbfn_glShaderSource
)glbindGetProcAddress(`glShaderSource`);
12195 if (glShaderSource_NVGLZ
is null) assert(0, `OpenGL function 'glShaderSource' not found!`);
12196 glCompileShader_NVGLZ
= cast(glbfn_glCompileShader
)glbindGetProcAddress(`glCompileShader`);
12197 if (glCompileShader_NVGLZ
is null) assert(0, `OpenGL function 'glCompileShader' not found!`);
12198 glGetShaderiv_NVGLZ
= cast(glbfn_glGetShaderiv
)glbindGetProcAddress(`glGetShaderiv`);
12199 if (glGetShaderiv_NVGLZ
is null) assert(0, `OpenGL function 'glGetShaderiv' not found!`);
12200 glAttachShader_NVGLZ
= cast(glbfn_glAttachShader
)glbindGetProcAddress(`glAttachShader`);
12201 if (glAttachShader_NVGLZ
is null) assert(0, `OpenGL function 'glAttachShader' not found!`);
12202 glBindAttribLocation_NVGLZ
= cast(glbfn_glBindAttribLocation
)glbindGetProcAddress(`glBindAttribLocation`);
12203 if (glBindAttribLocation_NVGLZ
is null) assert(0, `OpenGL function 'glBindAttribLocation' not found!`);
12204 glLinkProgram_NVGLZ
= cast(glbfn_glLinkProgram
)glbindGetProcAddress(`glLinkProgram`);
12205 if (glLinkProgram_NVGLZ
is null) assert(0, `OpenGL function 'glLinkProgram' not found!`);
12206 glGetProgramiv_NVGLZ
= cast(glbfn_glGetProgramiv
)glbindGetProcAddress(`glGetProgramiv`);
12207 if (glGetProgramiv_NVGLZ
is null) assert(0, `OpenGL function 'glGetProgramiv' not found!`);
12208 glDeleteProgram_NVGLZ
= cast(glbfn_glDeleteProgram
)glbindGetProcAddress(`glDeleteProgram`);
12209 if (glDeleteProgram_NVGLZ
is null) assert(0, `OpenGL function 'glDeleteProgram' not found!`);
12210 glDeleteShader_NVGLZ
= cast(glbfn_glDeleteShader
)glbindGetProcAddress(`glDeleteShader`);
12211 if (glDeleteShader_NVGLZ
is null) assert(0, `OpenGL function 'glDeleteShader' not found!`);
12212 glGetUniformLocation_NVGLZ
= cast(glbfn_glGetUniformLocation
)glbindGetProcAddress(`glGetUniformLocation`);
12213 if (glGetUniformLocation_NVGLZ
is null) assert(0, `OpenGL function 'glGetUniformLocation' not found!`);
12214 glGenBuffers_NVGLZ
= cast(glbfn_glGenBuffers
)glbindGetProcAddress(`glGenBuffers`);
12215 if (glGenBuffers_NVGLZ
is null) assert(0, `OpenGL function 'glGenBuffers' not found!`);
12216 glPixelStorei_NVGLZ
= cast(glbfn_glPixelStorei
)glbindGetProcAddress(`glPixelStorei`);
12217 if (glPixelStorei_NVGLZ
is null) assert(0, `OpenGL function 'glPixelStorei' not found!`);
12218 glUniform4fv_NVGLZ
= cast(glbfn_glUniform4fv
)glbindGetProcAddress(`glUniform4fv`);
12219 if (glUniform4fv_NVGLZ
is null) assert(0, `OpenGL function 'glUniform4fv' not found!`);
12220 glColorMask_NVGLZ
= cast(glbfn_glColorMask
)glbindGetProcAddress(`glColorMask`);
12221 if (glColorMask_NVGLZ
is null) assert(0, `OpenGL function 'glColorMask' not found!`);
12222 glStencilOpSeparate_NVGLZ
= cast(glbfn_glStencilOpSeparate
)glbindGetProcAddress(`glStencilOpSeparate`);
12223 if (glStencilOpSeparate_NVGLZ
is null) assert(0, `OpenGL function 'glStencilOpSeparate' not found!`);
12224 glDrawArrays_NVGLZ
= cast(glbfn_glDrawArrays
)glbindGetProcAddress(`glDrawArrays`);
12225 if (glDrawArrays_NVGLZ
is null) assert(0, `OpenGL function 'glDrawArrays' not found!`);
12226 glStencilOp_NVGLZ
= cast(glbfn_glStencilOp
)glbindGetProcAddress(`glStencilOp`);
12227 if (glStencilOp_NVGLZ
is null) assert(0, `OpenGL function 'glStencilOp' not found!`);
12228 glUseProgram_NVGLZ
= cast(glbfn_glUseProgram
)glbindGetProcAddress(`glUseProgram`);
12229 if (glUseProgram_NVGLZ
is null) assert(0, `OpenGL function 'glUseProgram' not found!`);
12230 glCullFace_NVGLZ
= cast(glbfn_glCullFace
)glbindGetProcAddress(`glCullFace`);
12231 if (glCullFace_NVGLZ
is null) assert(0, `OpenGL function 'glCullFace' not found!`);
12232 glFrontFace_NVGLZ
= cast(glbfn_glFrontFace
)glbindGetProcAddress(`glFrontFace`);
12233 if (glFrontFace_NVGLZ
is null) assert(0, `OpenGL function 'glFrontFace' not found!`);
12234 glActiveTexture_NVGLZ
= cast(glbfn_glActiveTexture
)glbindGetProcAddress(`glActiveTexture`);
12235 if (glActiveTexture_NVGLZ
is null) assert(0, `OpenGL function 'glActiveTexture' not found!`);
12236 glBindBuffer_NVGLZ
= cast(glbfn_glBindBuffer
)glbindGetProcAddress(`glBindBuffer`);
12237 if (glBindBuffer_NVGLZ
is null) assert(0, `OpenGL function 'glBindBuffer' not found!`);
12238 glBufferData_NVGLZ
= cast(glbfn_glBufferData
)glbindGetProcAddress(`glBufferData`);
12239 if (glBufferData_NVGLZ
is null) assert(0, `OpenGL function 'glBufferData' not found!`);
12240 glEnableVertexAttribArray_NVGLZ
= cast(glbfn_glEnableVertexAttribArray
)glbindGetProcAddress(`glEnableVertexAttribArray`);
12241 if (glEnableVertexAttribArray_NVGLZ
is null) assert(0, `OpenGL function 'glEnableVertexAttribArray' not found!`);
12242 glVertexAttribPointer_NVGLZ
= cast(glbfn_glVertexAttribPointer
)glbindGetProcAddress(`glVertexAttribPointer`);
12243 if (glVertexAttribPointer_NVGLZ
is null) assert(0, `OpenGL function 'glVertexAttribPointer' not found!`);
12244 glUniform1i_NVGLZ
= cast(glbfn_glUniform1i
)glbindGetProcAddress(`glUniform1i`);
12245 if (glUniform1i_NVGLZ
is null) assert(0, `OpenGL function 'glUniform1i' not found!`);
12246 glUniform2fv_NVGLZ
= cast(glbfn_glUniform2fv
)glbindGetProcAddress(`glUniform2fv`);
12247 if (glUniform2fv_NVGLZ
is null) assert(0, `OpenGL function 'glUniform2fv' not found!`);
12248 glDisableVertexAttribArray_NVGLZ
= cast(glbfn_glDisableVertexAttribArray
)glbindGetProcAddress(`glDisableVertexAttribArray`);
12249 if (glDisableVertexAttribArray_NVGLZ
is null) assert(0, `OpenGL function 'glDisableVertexAttribArray' not found!`);
12250 glDeleteBuffers_NVGLZ
= cast(glbfn_glDeleteBuffers
)glbindGetProcAddress(`glDeleteBuffers`);
12251 if (glDeleteBuffers_NVGLZ
is null) assert(0, `OpenGL function 'glDeleteBuffers' not found!`);
12252 glBlendFuncSeparate_NVGLZ
= cast(glbfn_glBlendFuncSeparate
)glbindGetProcAddress(`glBlendFuncSeparate`);
12253 if (glBlendFuncSeparate_NVGLZ
is null) assert(0, `OpenGL function 'glBlendFuncSeparate' not found!`);
12255 glLogicOp_NVGLZ
= cast(glbfn_glLogicOp
)glbindGetProcAddress(`glLogicOp`);
12256 if (glLogicOp_NVGLZ
is null) assert(0, `OpenGL function 'glLogicOp' not found!`);
12257 glFramebufferTexture2D_NVGLZ
= cast(glbfn_glFramebufferTexture2D
)glbindGetProcAddress(`glFramebufferTexture2D`);
12258 if (glFramebufferTexture2D_NVGLZ
is null) assert(0, `OpenGL function 'glFramebufferTexture2D' not found!`);
12259 glDeleteFramebuffers_NVGLZ
= cast(glbfn_glDeleteFramebuffers
)glbindGetProcAddress(`glDeleteFramebuffers`);
12260 if (glDeleteFramebuffers_NVGLZ
is null) assert(0, `OpenGL function 'glDeleteFramebuffers' not found!`);
12261 glGenFramebuffers_NVGLZ
= cast(glbfn_glGenFramebuffers
)glbindGetProcAddress(`glGenFramebuffers`);
12262 if (glGenFramebuffers_NVGLZ
is null) assert(0, `OpenGL function 'glGenFramebuffers' not found!`);
12263 glCheckFramebufferStatus_NVGLZ
= cast(glbfn_glCheckFramebufferStatus
)glbindGetProcAddress(`glCheckFramebufferStatus`);
12264 if (glCheckFramebufferStatus_NVGLZ
is null) assert(0, `OpenGL function 'glCheckFramebufferStatus' not found!`);
12265 glBindFramebuffer_NVGLZ
= cast(glbfn_glBindFramebuffer
)glbindGetProcAddress(`glBindFramebuffer`);
12266 if (glBindFramebuffer_NVGLZ
is null) assert(0, `OpenGL function 'glBindFramebuffer' not found!`);
12268 glGetIntegerv_NVGLZ
= cast(glbfn_glGetIntegerv
)glbindGetProcAddress(`glGetIntegerv`);
12269 if (glGetIntegerv_NVGLZ
is null) assert(0, `OpenGL function 'glGetIntegerv' not found!`);
12271 initialized
= true;
12276 /// Context creation flags.
12277 /// Group: context_management
12278 public enum NVGContextFlag
: int {
12279 /// Nothing special, i.e. empty flag.
12281 /// Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA).
12283 /** Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little
12284 * slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. */
12285 StencilStrokes
= 1U<<1,
12286 /// Flag indicating that additional debug checks are done.
12288 /// Filter (antialias) fonts
12290 /// Don't filter (antialias) fonts
12292 /// You can use this as a substitute for default flags, for cases like this: `nvgCreateContext(NVGContextFlag.Default, NVGContextFlag.Debug);`.
12296 public enum NANOVG_GL_USE_STATE_FILTER
= true;
12298 /// Returns flags for glClear().
12299 /// Group: context_management
12300 public uint glNVGClearFlags () pure nothrow @safe @nogc {
12301 pragma(inline
, true);
12302 return (GL_COLOR_BUFFER_BIT|
/*GL_DEPTH_BUFFER_BIT|*/GL_STENCIL_BUFFER_BIT
);
12306 // ////////////////////////////////////////////////////////////////////////// //
12309 version = nanovega_shared_stencil
;
12310 //version = nanovega_debug_clipping;
12312 enum GLNVGuniformLoc
{
12321 alias GLNVGshaderType
= int;
12322 enum /*GLNVGshaderType*/ {
12323 NSVG_SHADER_FILLCOLOR
,
12324 NSVG_SHADER_FILLGRAD
,
12325 NSVG_SHADER_FILLIMG
,
12326 NSVG_SHADER_SIMPLE
, // also used for clipfill
12330 struct GLNVGshader
{
12334 GLint
[GLNVGuniformLoc
.max
+1] loc
;
12337 struct GLNVGtexture
{
12343 shared int rc
; // this can be 0 with tex != 0 -- postponed deletion
12347 struct GLNVGblend
{
12355 alias GLNVGcallType
= int;
12356 enum /*GLNVGcallType*/ {
12362 GLNVG_AFFINE
, // change affine transformation matrix
12366 GLNVG_CLIP_DDUMP_ON
,
12367 GLNVG_CLIP_DDUMP_OFF
,
12372 int evenOdd
; // for fill
12376 int triangleOffset
;
12380 GLNVGblend blendFunc
;
12381 NVGClipMode clipmode
;
12391 align(1) struct GLNVGfragUniforms
{
12393 enum UNIFORM_ARRAY_SIZE
= 13;
12394 // note: after modifying layout or size of uniform array,
12395 // don't forget to also update the fragment shader source!
12400 float[12] scissorMat
; // matrices are actually 3 vec4s
12401 float[12] paintMat
;
12403 NVGColor middleCol
;
12405 float[2] scissorExt
;
12406 float[2] scissorScale
;
12415 float midp
; // for gradients
12416 float unused2
, unused3
;
12418 float[4][UNIFORM_ARRAY_SIZE
] uniformArray
;
12429 final class GLNVGTextureLocker
{}
12431 struct GLNVGcontext
{
12432 private import core
.thread
: ThreadID
;
12434 GLNVGshader shader
;
12435 GLNVGtexture
* textures
;
12437 int freetexid
; // -1: none
12444 GLuint
[NVG_MAX_STATES
] fbo
;
12445 GLuint
[2][NVG_MAX_STATES
] fboTex
; // FBO textures: [0] is color, [1] is stencil
12446 int fboWidth
, fboHeight
;
12447 GLMaskState
[NVG_MAX_STATES
] maskStack
;
12448 int msp
; // mask stack pointer; starts from `0`; points to next free item; see below for logic description
12449 int lastClipFBO
; // -666: cache invalidated; -1: don't mask
12450 int lastClipUniOfs
;
12451 bool doClipUnion
; // specal mode
12452 GLNVGshader shaderFillFBO
;
12453 GLNVGshader shaderCopyFBO
;
12455 bool inFrame
; // will be `true` if we can perform OpenGL operations (used in texture deletion)
12456 shared bool mustCleanTextures
; // will be `true` if we should delete some textures
12460 // Per frame buffers
12473 NVGMatrix lastAffine
;
12476 static if (NANOVG_GL_USE_STATE_FILTER
) {
12477 GLuint boundTexture
;
12478 GLuint stencilMask
;
12479 GLenum stencilFunc
;
12480 GLint stencilFuncRef
;
12481 GLuint stencilFuncMask
;
12482 GLNVGblend blendFunc
;
12486 int glnvg__maxi() (int a
, int b
) { pragma(inline
, true); return (a
> b ? a
: b
); }
12488 void glnvg__bindTexture (GLNVGcontext
* gl
, GLuint tex
) nothrow @trusted @nogc {
12489 static if (NANOVG_GL_USE_STATE_FILTER
) {
12490 if (gl
.boundTexture
!= tex
) {
12491 gl
.boundTexture
= tex
;
12492 glBindTexture(GL_TEXTURE_2D
, tex
);
12495 glBindTexture(GL_TEXTURE_2D
, tex
);
12499 void glnvg__stencilMask (GLNVGcontext
* gl
, GLuint mask
) nothrow @trusted @nogc {
12500 static if (NANOVG_GL_USE_STATE_FILTER
) {
12501 if (gl
.stencilMask
!= mask
) {
12502 gl
.stencilMask
= mask
;
12503 glStencilMask(mask
);
12506 glStencilMask(mask
);
12510 void glnvg__stencilFunc (GLNVGcontext
* gl
, GLenum func
, GLint ref_
, GLuint mask
) nothrow @trusted @nogc {
12511 static if (NANOVG_GL_USE_STATE_FILTER
) {
12512 if (gl
.stencilFunc
!= func || gl
.stencilFuncRef
!= ref_ || gl
.stencilFuncMask
!= mask
) {
12513 gl
.stencilFunc
= func
;
12514 gl
.stencilFuncRef
= ref_
;
12515 gl
.stencilFuncMask
= mask
;
12516 glStencilFunc(func
, ref_
, mask
);
12519 glStencilFunc(func
, ref_
, mask
);
12523 // texture id is never zero
12524 // sets refcount to one
12525 GLNVGtexture
* glnvg__allocTexture (GLNVGcontext
* gl
) nothrow @trusted @nogc {
12526 GLNVGtexture
* tex
= null;
12528 int tid
= gl
.freetexid
;
12530 if (gl
.ntextures
>= gl
.ctextures
) {
12531 assert(gl
.ntextures
== gl
.ctextures
);
12532 //pragma(msg, GLNVGtexture.sizeof*32);
12533 int ctextures
= (gl
.ctextures
== 0 ?
32 : gl
.ctextures
+gl
.ctextures
/2); // 1.5x overallocate
12534 GLNVGtexture
* textures
= cast(GLNVGtexture
*)realloc(gl
.textures
, GLNVGtexture
.sizeof
*ctextures
);
12535 if (textures
is null) assert(0, "NanoVega: out of memory for textures");
12536 memset(&textures
[gl
.ctextures
], 0, (ctextures
-gl
.ctextures
)*GLNVGtexture
.sizeof
);
12537 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("allocated more textures (n=%d; c=%d; nc=%d)\n", gl
.ntextures
, gl
.ctextures
, ctextures
); }}
12538 gl
.textures
= textures
;
12539 gl
.ctextures
= ctextures
;
12541 assert(gl
.ntextures
+1 <= gl
.ctextures
);
12542 tid
= gl
.ntextures
++;
12543 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf(" got next free texture id %d, ntextures=%d\n", tid
+1, gl
.ntextures
); }}
12545 gl
.freetexid
= gl
.textures
[tid
].nextfree
;
12547 assert(tid
<= gl
.ntextures
);
12549 assert(gl
.textures
[tid
].id
== 0);
12550 tex
= &gl
.textures
[tid
];
12551 memset(tex
, 0, (*tex
).sizeof
);
12556 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("allocated texture with id %d (%d)\n", tex
.id
, tid
+1); }}
12561 GLNVGtexture
* glnvg__findTexture (GLNVGcontext
* gl
, int id
) nothrow @trusted @nogc {
12562 if (id
<= 0 || id
> gl
.ntextures
) return null;
12563 if (gl
.textures
[id
-1].id
== 0) return null; // free one
12564 assert(gl
.textures
[id
-1].id
== id
);
12565 return &gl
.textures
[id
-1];
12568 bool glnvg__deleteTexture (GLNVGcontext
* gl
, ref int id
) nothrow @trusted @nogc {
12569 if (id
<= 0 || id
> gl
.ntextures
) return false;
12570 auto tx
= &gl
.textures
[id
-1];
12571 if (tx
.id
== 0) { id
= 0; return false; } // free one
12572 assert(tx
.id
== id
);
12573 assert(tx
.tex
!= 0);
12574 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("decrefing texture with id %d (%d)\n", tx
.id
, id
); }}
12575 import core
.atomic
: atomicOp
;
12576 if (atomicOp
!"-="(tx
.rc
, 1) == 0) {
12577 import core
.thread
: ThreadID
;
12579 static if (__VERSION__
< 2076) {
12581 import core
.thread
; mytid
= Thread
.getThis
.id
;
12584 try { import core
.thread
; mytid
= Thread
.getThis
.id
; } catch (Exception e
) {}
12586 if (gl
.mainTID
== mytid
&& gl
.inFrame
) {
12587 // can delete it right now
12588 if ((tx
.flags
&NVGImageFlag
.NoDelete
) == 0) glDeleteTextures(1, &tx
.tex
);
12589 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("*** deleted texture with id %d (%d); glid=%u\n", tx
.id
, id
, tx
.tex
); }}
12590 memset(tx
, 0, (*tx
).sizeof
);
12591 //{ import core.stdc.stdio; printf("deleting texture with id %d\n", id); }
12592 tx
.nextfree
= gl
.freetexid
;
12593 gl
.freetexid
= id
-1;
12595 // alas, we aren't doing frame business, so we should postpone deletion
12596 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("*** POSTPONED texture deletion with id %d (%d); glid=%u\n", tx
.id
, id
, tx
.tex
); }}
12598 synchronized(GLNVGTextureLocker
.classinfo
) {
12599 tx
.id
= 0; // mark it as dead
12600 gl
.mustCleanTextures
= true; // set "need cleanup" flag
12604 synchronized(GLNVGTextureLocker
.classinfo
) {
12605 tx
.id
= 0; // mark it as dead
12606 gl
.mustCleanTextures
= true; // set "need cleanup" flag
12608 } catch (Exception e
) {}
12616 void glnvg__dumpShaderError (GLuint shader
, const(char)* name
, const(char)* type
) nothrow @trusted @nogc {
12617 import core
.stdc
.stdio
: fprintf
, stderr
;
12618 GLchar
[512+1] str = 0;
12620 glGetShaderInfoLog(shader
, 512, &len
, str.ptr
);
12621 if (len
> 512) len
= 512;
12623 fprintf(stderr
, "Shader %s/%s error:\n%s\n", name
, type
, str.ptr
);
12626 void glnvg__dumpProgramError (GLuint prog
, const(char)* name
) nothrow @trusted @nogc {
12627 import core
.stdc
.stdio
: fprintf
, stderr
;
12628 GLchar
[512+1] str = 0;
12630 glGetProgramInfoLog(prog
, 512, &len
, str.ptr
);
12631 if (len
> 512) len
= 512;
12633 fprintf(stderr
, "Program %s error:\n%s\n", name
, str.ptr
);
12636 void glnvg__resetError(bool force
=false) (GLNVGcontext
* gl
) nothrow @trusted @nogc {
12637 static if (!force
) {
12638 if ((gl
.flags
&NVGContextFlag
.Debug
) == 0) return;
12643 void glnvg__checkError(bool force
=false) (GLNVGcontext
* gl
, const(char)* str) nothrow @trusted @nogc {
12645 static if (!force
) {
12646 if ((gl
.flags
&NVGContextFlag
.Debug
) == 0) return;
12648 err
= glGetError();
12649 if (err
!= GL_NO_ERROR
) {
12650 import core
.stdc
.stdio
: fprintf
, stderr
;
12651 fprintf(stderr
, "Error %08x after %s\n", err
, str);
12656 bool glnvg__createShader (GLNVGshader
* shader
, const(char)* name
, const(char)* header
, const(char)* opts
, const(char)* vshader
, const(char)* fshader
) nothrow @trusted @nogc {
12658 GLuint prog
, vert
, frag
;
12659 const(char)*[3] str;
12661 memset(shader
, 0, (*shader
).sizeof
);
12663 prog
= glCreateProgram();
12664 vert
= glCreateShader(GL_VERTEX_SHADER
);
12665 frag
= glCreateShader(GL_FRAGMENT_SHADER
);
12667 str[1] = (opts
!is null ? opts
: "");
12669 glShaderSource(vert
, 3, cast(const(char)**)str.ptr
, null);
12671 glCompileShader(vert
);
12672 glGetShaderiv(vert
, GL_COMPILE_STATUS
, &status
);
12673 if (status
!= GL_TRUE
) {
12674 glnvg__dumpShaderError(vert
, name
, "vert");
12679 str[1] = (opts
!is null ? opts
: "");
12681 glShaderSource(frag
, 3, cast(const(char)**)str.ptr
, null);
12683 glCompileShader(frag
);
12684 glGetShaderiv(frag
, GL_COMPILE_STATUS
, &status
);
12685 if (status
!= GL_TRUE
) {
12686 glnvg__dumpShaderError(frag
, name
, "frag");
12690 glAttachShader(prog
, vert
);
12691 glAttachShader(prog
, frag
);
12693 glBindAttribLocation(prog
, 0, "vertex");
12694 glBindAttribLocation(prog
, 1, "tcoord");
12696 glLinkProgram(prog
);
12697 glGetProgramiv(prog
, GL_LINK_STATUS
, &status
);
12698 if (status
!= GL_TRUE
) {
12699 glnvg__dumpProgramError(prog
, name
);
12703 shader
.prog
= prog
;
12704 shader
.vert
= vert
;
12705 shader
.frag
= frag
;
12710 void glnvg__deleteShader (GLNVGshader
* shader
) nothrow @trusted @nogc {
12711 if (shader
.prog
!= 0) glDeleteProgram(shader
.prog
);
12712 if (shader
.vert
!= 0) glDeleteShader(shader
.vert
);
12713 if (shader
.frag
!= 0) glDeleteShader(shader
.frag
);
12716 void glnvg__getUniforms (GLNVGshader
* shader
) nothrow @trusted @nogc {
12717 shader
.loc
[GLNVGuniformLoc
.ViewSize
] = glGetUniformLocation(shader
.prog
, "viewSize");
12718 shader
.loc
[GLNVGuniformLoc
.Tex
] = glGetUniformLocation(shader
.prog
, "tex");
12719 shader
.loc
[GLNVGuniformLoc
.Frag
] = glGetUniformLocation(shader
.prog
, "frag");
12720 shader
.loc
[GLNVGuniformLoc
.TMat
] = glGetUniformLocation(shader
.prog
, "tmat");
12721 shader
.loc
[GLNVGuniformLoc
.TTr
] = glGetUniformLocation(shader
.prog
, "ttr");
12722 shader
.loc
[GLNVGuniformLoc
.ClipTex
] = glGetUniformLocation(shader
.prog
, "clipTex");
12725 void glnvg__killFBOs (GLNVGcontext
* gl
) nothrow @trusted @nogc {
12726 foreach (immutable fidx
, ref GLuint fbo
; gl
.fbo
[]) {
12728 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_TEXTURE_2D
, 0, 0);
12729 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_DEPTH_STENCIL_ATTACHMENT
, GL_TEXTURE_2D
, 0, 0);
12730 foreach (ref GLuint tid
; gl
.fboTex
.ptr
[fidx
][]) if (tid
!= 0) { glDeleteTextures(1, &tid
); tid
= 0; }
12731 glDeleteFramebuffers(1, &fbo
);
12735 gl
.fboWidth
= gl
.fboHeight
= 0;
12738 // returns `true` is new FBO was created
12739 // will not unbind buffer, if it was created
12740 bool glnvg__allocFBO (GLNVGcontext
* gl
, int fidx
, bool doclear
=true) nothrow @trusted @nogc {
12741 assert(fidx
>= 0 && fidx
< gl
.fbo
.length
);
12742 assert(gl
.fboWidth
> 0);
12743 assert(gl
.fboHeight
> 0);
12745 if (gl
.fbo
.ptr
[fidx
] != 0) return false; // nothing to do, this FBO is already initialized
12747 glnvg__resetError(gl
);
12749 // allocate FBO object
12751 glGenFramebuffers(1, &fbo
);
12752 if (fbo
== 0) assert(0, "NanoVega: cannot create FBO");
12753 glnvg__checkError(gl
, "glnvg__allocFBO: glGenFramebuffers");
12754 glBindFramebuffer(GL_FRAMEBUFFER
, fbo
);
12755 //scope(exit) glBindFramebuffer(GL_FRAMEBUFFER, 0);
12757 // attach 2D texture to this FBO
12758 GLuint tidColor
= 0;
12759 glGenTextures(1, &tidColor
);
12760 if (tidColor
== 0) assert(0, "NanoVega: cannot create RGBA texture for FBO");
12761 glBindTexture(GL_TEXTURE_2D
, tidColor
);
12762 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
12763 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
12764 glnvg__checkError(gl
, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_S");
12765 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
12766 glnvg__checkError(gl
, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_WRAP_T");
12767 //FIXME: linear or nearest?
12768 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
12769 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
12770 glnvg__checkError(gl
, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MIN_FILTER");
12771 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
12772 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
12773 glnvg__checkError(gl
, "glnvg__allocFBO: glTexParameterf: GL_TEXTURE_MAG_FILTER");
12775 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
12776 // create texture with only one color channel
12777 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RED
, gl
.fboWidth
, gl
.fboHeight
, 0, GL_RED
, GL_UNSIGNED_BYTE
, null);
12778 //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.fboWidth, gl.fboHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
12779 glnvg__checkError(gl
, "glnvg__allocFBO: glTexImage2D (color)");
12780 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_TEXTURE_2D
, tidColor
, 0);
12781 glnvg__checkError(gl
, "glnvg__allocFBO: glFramebufferTexture2D (color)");
12783 // attach stencil texture to this FBO
12784 GLuint tidStencil
= 0;
12785 version(nanovega_shared_stencil
) {
12786 if (gl
.fboTex
.ptr
[0].ptr
[0] == 0) {
12787 glGenTextures(1, &tidStencil
);
12788 if (tidStencil
== 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
12789 gl
.fboTex
.ptr
[0].ptr
[0] = tidStencil
;
12791 tidStencil
= gl
.fboTex
.ptr
[0].ptr
[0];
12793 if (fidx
!= 0) gl
.fboTex
.ptr
[fidx
].ptr
[1] = 0; // stencil texture is shared among FBOs
12795 glGenTextures(1, &tidStencil
);
12796 if (tidStencil
== 0) assert(0, "NanoVega: cannot create stencil texture for FBO");
12797 gl
.fboTex
.ptr
[0].ptr
[0] = tidStencil
;
12799 glBindTexture(GL_TEXTURE_2D
, tidStencil
);
12800 glTexImage2D(GL_TEXTURE_2D
, 0, GL_DEPTH_STENCIL
, gl
.fboWidth
, gl
.fboHeight
, 0, GL_DEPTH_STENCIL
, GL_UNSIGNED_INT_24_8
, null);
12801 glnvg__checkError(gl
, "glnvg__allocFBO: glTexImage2D (stencil)");
12802 glFramebufferTexture2D(GL_FRAMEBUFFER
, GL_DEPTH_STENCIL_ATTACHMENT
, GL_TEXTURE_2D
, tidStencil
, 0);
12803 glnvg__checkError(gl
, "glnvg__allocFBO: glFramebufferTexture2D (stencil)");
12806 GLenum status
= glCheckFramebufferStatus(GL_FRAMEBUFFER
);
12807 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
12809 import core
.stdc
.stdio
;
12810 if (status
== GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
) printf("fucked attachement\n");
12811 if (status
== GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
) printf("fucked dimensions\n");
12812 if (status
== GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
) printf("missing attachement\n");
12813 if (status
== GL_FRAMEBUFFER_UNSUPPORTED
) printf("unsupported\n");
12815 assert(0, "NanoVega: framebuffer creation failed");
12821 glClearColor(0, 0, 0, 0);
12822 glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT
);
12825 // save texture ids
12826 gl
.fbo
.ptr
[fidx
] = fbo
;
12827 gl
.fboTex
.ptr
[fidx
].ptr
[0] = tidColor
;
12828 version(nanovega_shared_stencil
) {} else {
12829 gl
.fboTex
.ptr
[fidx
].ptr
[1] = tidStencil
;
12832 static if (NANOVG_GL_USE_STATE_FILTER
) glBindTexture(GL_TEXTURE_2D
, gl
.boundTexture
);
12834 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): created with index %d\n", gl
.msp
-1, fidx
); }
12839 // will not unbind buffer
12840 void glnvg__clearFBO (GLNVGcontext
* gl
, int fidx
) nothrow @trusted @nogc {
12841 assert(fidx
>= 0 && fidx
< gl
.fbo
.length
);
12842 assert(gl
.fboWidth
> 0);
12843 assert(gl
.fboHeight
> 0);
12844 assert(gl
.fbo
.ptr
[fidx
] != 0);
12845 glBindFramebuffer(GL_FRAMEBUFFER
, gl
.fbo
.ptr
[fidx
]);
12846 glClearColor(0, 0, 0, 0);
12847 glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT
);
12848 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): cleared with index %d\n", gl
.msp
-1, fidx
); }
12851 // will not unbind buffer
12852 void glnvg__copyFBOToFrom (GLNVGcontext
* gl
, int didx
, int sidx
) nothrow @trusted @nogc {
12853 import core
.stdc
.string
: memset
;
12854 assert(didx
>= 0 && didx
< gl
.fbo
.length
);
12855 assert(sidx
>= 0 && sidx
< gl
.fbo
.length
);
12856 assert(gl
.fboWidth
> 0);
12857 assert(gl
.fboHeight
> 0);
12858 assert(gl
.fbo
.ptr
[didx
] != 0);
12859 assert(gl
.fbo
.ptr
[sidx
] != 0);
12860 if (didx
== sidx
) return;
12862 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): copy FBO: %d -> %d\n", gl
.msp
-1, sidx
, didx
); }
12864 glUseProgram(gl
.shaderCopyFBO
.prog
);
12866 glBindFramebuffer(GL_FRAMEBUFFER
, gl
.fbo
.ptr
[didx
]);
12867 glDisable(GL_CULL_FACE
);
12868 glDisable(GL_BLEND
);
12869 glDisable(GL_SCISSOR_TEST
);
12870 glBindTexture(GL_TEXTURE_2D
, gl
.fboTex
.ptr
[sidx
].ptr
[0]);
12871 // copy texture by drawing full quad
12874 immutable int w
= gl
.fboWidth
;
12875 immutable int h
= gl
.fboHeight
;
12877 glVertex2i(x
, y
); // top-left
12878 glVertex2i(w
, y
); // top-right
12879 glVertex2i(w
, h
); // bottom-right
12880 glVertex2i(x
, h
); // bottom-left
12883 // restore state (but don't unbind FBO)
12884 static if (NANOVG_GL_USE_STATE_FILTER
) glBindTexture(GL_TEXTURE_2D
, gl
.boundTexture
);
12885 glEnable(GL_CULL_FACE
);
12886 glEnable(GL_BLEND
);
12887 glUseProgram(gl
.shader
.prog
);
12890 void glnvg__resetFBOClipTextureCache (GLNVGcontext
* gl
) nothrow @trusted @nogc {
12891 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): texture cache invalidated (%d)\n", gl
.msp
-1, gl
.lastClipFBO
); }
12893 if (gl.lastClipFBO >= 0) {
12894 glActiveTexture(GL_TEXTURE1);
12895 glBindTexture(GL_TEXTURE_2D, 0);
12896 glActiveTexture(GL_TEXTURE0);
12899 gl
.lastClipFBO
= -666;
12902 void glnvg__setFBOClipTexture (GLNVGcontext
* gl
, GLNVGfragUniforms
* frag
) nothrow @trusted @nogc {
12903 //assert(gl.msp > 0 && gl.msp <= gl.maskStack.length);
12904 if (gl
.lastClipFBO
!= -666) {
12906 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): cached (%d)\n", gl
.msp
-1, gl
.lastClipFBO
); }
12907 frag
.doclip
= (gl
.lastClipFBO
>= 0 ?
1 : 0);
12913 mainloop
: foreach_reverse (immutable sp
, GLMaskState mst
; gl
.maskStack
.ptr
[0..gl
.msp
]/*; reverse*/) {
12914 final switch (mst
) {
12915 case GLMaskState
.DontMask
: fboidx
= -1; break mainloop
;
12916 case GLMaskState
.Uninitialized
: break;
12917 case GLMaskState
.Initialized
: fboidx
= cast(int)sp
; break mainloop
;
12918 case GLMaskState
.JustCleared
: assert(0, "NanoVega: `glnvg__setFBOClipTexture()` internal error");
12924 gl
.lastClipFBO
= -1;
12928 assert(gl
.fbo
.ptr
[fboidx
] != 0);
12929 gl
.lastClipFBO
= fboidx
;
12933 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): new cache (new:%d)\n", gl
.msp
-1, gl
.lastClipFBO
); }
12935 if (gl
.lastClipFBO
>= 0) {
12936 assert(gl
.fboTex
.ptr
[gl
.lastClipFBO
].ptr
[0]);
12937 glActiveTexture(GL_TEXTURE1
);
12938 glBindTexture(GL_TEXTURE_2D
, gl
.fboTex
.ptr
[gl
.lastClipFBO
].ptr
[0]);
12939 glActiveTexture(GL_TEXTURE0
);
12943 // returns index in `gl.fbo`, or -1 for "don't mask"
12944 int glnvg__generateFBOClipTexture (GLNVGcontext
* gl
) nothrow @trusted @nogc {
12945 assert(gl
.msp
> 0 && gl
.msp
<= gl
.maskStack
.length
);
12946 // we need initialized FBO, even for "don't mask" case
12947 // for this, look back in stack, and either copy initialized FBO,
12948 // or stop at first uninitialized one, and clear it
12949 if (gl
.maskStack
.ptr
[gl
.msp
-1] == GLMaskState
.Initialized
) {
12951 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); }
12952 glBindFramebuffer(GL_FRAMEBUFFER
, gl
.fbo
.ptr
[gl
.msp
-1]);
12955 foreach_reverse (immutable sp
; 0..gl
.msp
/*; reverse*/) {
12956 final switch (gl
.maskStack
.ptr
[sp
]) {
12957 case GLMaskState
.DontMask
:
12959 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): generating new clean texture\n", gl
.msp
-1); }
12960 if (!glnvg__allocFBO(gl
, gl
.msp
-1)) glnvg__clearFBO(gl
, gl
.msp
-1);
12961 gl
.maskStack
.ptr
[gl
.msp
-1] = GLMaskState
.JustCleared
;
12963 case GLMaskState
.Uninitialized
: break; // do nothing
12964 case GLMaskState
.Initialized
:
12965 // i found her! copy to TOS
12966 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): copying texture from %d\n", gl
.msp
-1, cast(int)sp
); }
12967 glnvg__allocFBO(gl
, gl
.msp
-1, false);
12968 glnvg__copyFBOToFrom(gl
, gl
.msp
-1, sp
);
12969 gl
.maskStack
.ptr
[gl
.msp
-1] = GLMaskState
.Initialized
;
12971 case GLMaskState
.JustCleared
: assert(0, "NanoVega: `glnvg__generateFBOClipTexture()` internal error");
12974 // nothing was initialized, lol
12975 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf("FBO(%d): generating new clean texture (first one)\n", gl
.msp
-1); }
12976 if (!glnvg__allocFBO(gl
, gl
.msp
-1)) glnvg__clearFBO(gl
, gl
.msp
-1);
12977 gl
.maskStack
.ptr
[gl
.msp
-1] = GLMaskState
.JustCleared
;
12981 void glnvg__renderPushClip (void* uptr
) nothrow @trusted @nogc {
12982 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
12983 GLNVGcall
* call = glnvg__allocCall(gl
);
12984 if (call is null) return;
12985 call.type
= GLNVG_PUSHCLIP
;
12988 void glnvg__renderPopClip (void* uptr
) nothrow @trusted @nogc {
12989 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
12990 GLNVGcall
* call = glnvg__allocCall(gl
);
12991 if (call is null) return;
12992 call.type
= GLNVG_POPCLIP
;
12995 void glnvg__renderResetClip (void* uptr
) nothrow @trusted @nogc {
12996 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
12997 GLNVGcall
* call = glnvg__allocCall(gl
);
12998 if (call is null) return;
12999 call.type
= GLNVG_RESETCLIP
;
13002 void glnvg__clipDebugDump (void* uptr
, bool doit
) nothrow @trusted @nogc {
13003 version(nanovega_debug_clipping
) {
13004 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13005 GLNVGcall
* call = glnvg__allocCall(gl
);
13006 call.type
= (doit ? GLNVG_CLIP_DDUMP_ON
: GLNVG_CLIP_DDUMP_OFF
);
13010 bool glnvg__renderCreate (void* uptr
) nothrow @trusted @nogc {
13011 import core
.stdc
.stdio
: snprintf
;
13013 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13016 char[64] shaderHeader
= void;
13017 //enum shaderHeader = "#define UNIFORM_ARRAY_SIZE 12\n";
13018 snprintf(shaderHeader
.ptr
, shaderHeader
.length
, "#define UNIFORM_ARRAY_SIZE %u\n", cast(uint)GLNVGfragUniforms
.UNIFORM_ARRAY_SIZE
);
13020 enum fillVertShader
= q
{
13021 uniform vec2 viewSize
;
13022 attribute vec2 vertex
;
13023 attribute vec2 tcoord
;
13024 varying vec2 ftcoord
;
13026 uniform vec4 tmat
; /* abcd of affine matrix: xyzw */
13027 uniform vec2 ttr
; /* tx and ty of affine matrix */
13029 /* affine transformation */
13030 float nx
= vertex
.x
*tmat
.x
+vertex
.y
*tmat
.z
+ttr
.x
;
13031 float ny
= vertex
.x
*tmat
.y
+vertex
.y
*tmat
.w
+ttr
.y
;
13033 fpos
= vec2(nx
, ny
);
13034 gl_Position
= vec4(2.0*nx
/viewSize
.x
-1.0, 1.0-2.0*ny
/viewSize
.y
, 0, 1);
13038 enum fillFragShader
= q
{
13039 uniform vec4 frag
[UNIFORM_ARRAY_SIZE
];
13040 uniform sampler2D tex
;
13041 uniform sampler2D clipTex
;
13042 uniform vec2 viewSize
;
13043 varying vec2 ftcoord
;
13045 #define scissorMat
mat3(frag
[0].xyz
, frag
[1].xyz
, frag
[2].xyz
)
13046 #define paintMat
mat3(frag
[3].xyz
, frag
[4].xyz
, frag
[5].xyz
)
13047 #define innerCol frag
[6]
13048 #define middleCol frag
[7]
13049 #define outerCol frag
[7+1]
13050 #define scissorExt frag
[8+1].xy
13051 #define scissorScale frag
[8+1].zw
13052 #define extent frag
[9+1].xy
13053 #define radius frag
[9+1].z
13054 #define feather frag
[9+1].w
13055 #define strokeMult frag
[10+1].x
13056 #define strokeThr frag
[10+1].y
13057 #define texType
int(frag
[10+1].z
)
13058 #define type
int(frag
[10+1].w
)
13059 #define doclip
int(frag
[11+1].x
)
13060 #define midp frag
[11+1].y
13062 float sdroundrect (in vec2 pt
, in vec2 ext
, in float rad
) {
13063 vec2 ext2
= ext
-vec2(rad
, rad
);
13064 vec2 d
= abs(pt
)-ext2
;
13065 return min(max(d
.x
, d
.y
), 0.0)+length(max(d
, 0.0))-rad
;
13069 float scissorMask (in vec2 p
) {
13070 vec2 sc
= (abs((scissorMat
*vec3(p
, 1.0)).xy
)-scissorExt
);
13071 sc
= vec2(0.5, 0.5)-sc
*scissorScale
;
13072 return clamp(sc
.x
, 0.0, 1.0)*clamp(sc
.y
, 0.0, 1.0);
13076 // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.
13077 float strokeMask () {
13078 return min(1.0, (1.0-abs(ftcoord
.x
*2.0-1.0))*strokeMult
)*min(1.0, ftcoord
.y
);
13085 /*vec4 clr = texelFetch(clipTex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);*/
13086 vec4 clr
= texture2D(clipTex
, vec2(gl_FragCoord
.x
/viewSize
.x
, gl_FragCoord
.y
/viewSize
.y
));
13087 if (clr
.r
== 0.0) discard
;
13089 float scissor
= scissorMask(fpos
);
13090 if (scissor
<= 0.0) discard
; //k8: is it really faster?
13092 float strokeAlpha
= strokeMask();
13093 if (strokeAlpha
< strokeThr
) discard
;
13095 float strokeAlpha
= 1.0;
13099 if (type
== 0) { /* NSVG_SHADER_FILLCOLOR */
13102 color
*= strokeAlpha
*scissor
;
13103 } else if (type
== 1) { /* NSVG_SHADER_FILLGRAD */
13105 // Calculate gradient color using box gradient
13106 vec2 pt
= (paintMat
*vec3(fpos
, 1.0)).xy
;
13107 float d
= clamp((sdroundrect(pt
, extent
, radius
)+feather
*0.5)/feather
, 0.0, 1.0);
13109 color
= mix(innerCol
, outerCol
, d
);
13111 float gdst
= min(midp
, 1.0);
13113 color
= mix(innerCol
, middleCol
, d
/gdst
);
13115 color
= mix(middleCol
, outerCol
, (d
-gdst
)/gdst
);
13119 color
*= strokeAlpha
*scissor
;
13120 } else if (type
== 2) { /* NSVG_SHADER_FILLIMG */
13122 // Calculate color from texture
13123 vec2 pt
= (paintMat
*vec3(fpos
, 1.0)).xy
/extent
;
13124 color
= texture2D(tex
, pt
);
13125 if (texType
== 1) color
= vec4(color
.xyz
*color
.w
, color
.w
);
13126 if (texType
== 2) color
= vec4(color
.x
);
13127 // Apply color tint and alpha
13130 color
*= strokeAlpha
*scissor
;
13131 } else if (type
== 3) { /* NSVG_SHADER_SIMPLE */
13133 color
= vec4(1, 1, 1, 1);
13134 } else if (type
== 4) { /* NSVG_SHADER_IMG */
13136 color
= texture2D(tex
, ftcoord
);
13137 if (texType
== 1) color
= vec4(color
.xyz
*color
.w
, color
.w
);
13138 if (texType
== 2) color
= vec4(color
.x
);
13140 color
*= innerCol
; // Apply color tint
13142 gl_FragColor
= color
;
13146 enum clipVertShaderFill
= q
{
13147 uniform vec2 viewSize
;
13148 attribute vec2 vertex
;
13149 uniform vec4 tmat
; /* abcd of affine matrix: xyzw */
13150 uniform vec2 ttr
; /* tx and ty of affine matrix */
13152 /* affine transformation */
13153 float nx
= vertex
.x
*tmat
.x
+vertex
.y
*tmat
.z
+ttr
.x
;
13154 float ny
= vertex
.x
*tmat
.y
+vertex
.y
*tmat
.w
+ttr
.y
;
13155 gl_Position
= vec4(2.0*nx
/viewSize
.x
-1.0, 1.0-2.0*ny
/viewSize
.y
, 0, 1);
13159 enum clipFragShaderFill
= q
{
13160 uniform vec2 viewSize
;
13162 gl_FragColor
= vec4(1, 1, 1, 1);
13166 enum clipVertShaderCopy
= q
{
13167 uniform vec2 viewSize
;
13168 attribute vec2 vertex
;
13170 gl_Position
= vec4(2.0*vertex
.x
/viewSize
.x
-1.0, 1.0-2.0*vertex
.y
/viewSize
.y
, 0, 1);
13174 enum clipFragShaderCopy
= q
{
13175 uniform sampler2D tex
;
13176 uniform vec2 viewSize
;
13178 //gl_FragColor = texelFetch(tex, ivec2(int(gl_FragCoord.x), int(gl_FragCoord.y)), 0);
13179 gl_FragColor
= texture2D(tex
, vec2(gl_FragCoord
.x
/viewSize
.x
, gl_FragCoord
.y
/viewSize
.y
));
13183 glnvg__checkError(gl
, "init");
13185 string defines
= (gl
.flags
&NVGContextFlag
.Antialias ?
"#define EDGE_AA 1\n" : null);
13186 if (!glnvg__createShader(&gl
.shader
, "shader", shaderHeader
.ptr
, defines
.ptr
, fillVertShader
, fillFragShader
)) return false;
13187 if (!glnvg__createShader(&gl
.shaderFillFBO
, "shaderFillFBO", shaderHeader
.ptr
, defines
.ptr
, clipVertShaderFill
, clipFragShaderFill
)) return false;
13188 if (!glnvg__createShader(&gl
.shaderCopyFBO
, "shaderCopyFBO", shaderHeader
.ptr
, defines
.ptr
, clipVertShaderCopy
, clipFragShaderCopy
)) return false;
13190 glnvg__checkError(gl
, "uniform locations");
13191 glnvg__getUniforms(&gl
.shader
);
13192 glnvg__getUniforms(&gl
.shaderFillFBO
);
13193 glnvg__getUniforms(&gl
.shaderCopyFBO
);
13195 // Create dynamic vertex array
13196 glGenBuffers(1, &gl
.vertBuf
);
13198 gl
.fragSize
= GLNVGfragUniforms
.sizeof
+align_
-GLNVGfragUniforms
.sizeof
%align_
;
13200 glnvg__checkError(gl
, "create done");
13207 int glnvg__renderCreateTexture (void* uptr
, NVGtexture type
, int w
, int h
, int imageFlags
, const(ubyte)* data
) nothrow @trusted @nogc {
13208 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13209 GLNVGtexture
* tex
= glnvg__allocTexture(gl
);
13211 if (tex
is null) return 0;
13213 glGenTextures(1, &tex
.tex
);
13217 tex
.flags
= imageFlags
;
13218 glnvg__bindTexture(gl
, tex
.tex
);
13220 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("created texture with id %d; glid=%u\n", tex
.id
, tex
.tex
); }}
13222 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
13223 glPixelStorei(GL_UNPACK_ROW_LENGTH
, tex
.width
);
13224 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, 0);
13225 glPixelStorei(GL_UNPACK_SKIP_ROWS
, 0);
13227 // GL 1.4 and later has support for generating mipmaps using a tex parameter.
13228 if ((imageFlags
&(NVGImageFlag
.GenerateMipmaps|NVGImageFlag
.NoFiltering
)) == NVGImageFlag
.GenerateMipmaps
) glTexParameteri(GL_TEXTURE_2D
, GL_GENERATE_MIPMAP
, GL_TRUE
);
13230 immutable ttype
= (type
== NVGtexture
.RGBA ? GL_RGBA
: GL_RED
);
13231 glTexImage2D(GL_TEXTURE_2D
, 0, ttype
, w
, h
, 0, ttype
, GL_UNSIGNED_BYTE
, data
);
13234 (imageFlags
&NVGImageFlag
.NoFiltering ? GL_NEAREST
:
13235 imageFlags
&NVGImageFlag
.GenerateMipmaps ? GL_LINEAR_MIPMAP_LINEAR
:
13237 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, tfmin
);
13238 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, (imageFlags
&NVGImageFlag
.NoFiltering ? GL_NEAREST
: GL_LINEAR
));
13240 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, (imageFlags
&NVGImageFlag
.RepeatX ? GL_REPEAT
: GL_CLAMP_TO_EDGE
));
13241 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, (imageFlags
&NVGImageFlag
.RepeatY ? GL_REPEAT
: GL_CLAMP_TO_EDGE
));
13243 glPixelStorei(GL_UNPACK_ALIGNMENT
, 4);
13244 glPixelStorei(GL_UNPACK_ROW_LENGTH
, 0);
13245 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, 0);
13246 glPixelStorei(GL_UNPACK_SKIP_ROWS
, 0);
13248 glnvg__checkError(gl
, "create tex");
13249 glnvg__bindTexture(gl
, 0);
13254 bool glnvg__renderDeleteTexture (void* uptr
, int image
) nothrow @trusted @nogc {
13255 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13256 return glnvg__deleteTexture(gl
, image
);
13259 bool glnvg__renderTextureIncRef (void* uptr
, int image
) nothrow @trusted @nogc {
13260 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13261 GLNVGtexture
* tex
= glnvg__findTexture(gl
, image
);
13263 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("CANNOT incref texture with id %d\n", image
); }}
13266 import core
.atomic
: atomicOp
;
13267 atomicOp
!"+="(tex
.rc
, 1);
13268 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("texture #%d: incref; newref=%d\n", image
, tex
.rc
); }}
13272 bool glnvg__renderUpdateTexture (void* uptr
, int image
, int x
, int y
, int w
, int h
, const(ubyte)* data
) nothrow @trusted @nogc {
13273 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13274 GLNVGtexture
* tex
= glnvg__findTexture(gl
, image
);
13277 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("CANNOT update texture with id %d\n", image
); }}
13281 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("updated texture with id %d; glid=%u\n", tex
.id
, image
, tex
.tex
); }}
13283 glnvg__bindTexture(gl
, tex
.tex
);
13285 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
13286 glPixelStorei(GL_UNPACK_ROW_LENGTH
, tex
.width
);
13287 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, x
);
13288 glPixelStorei(GL_UNPACK_SKIP_ROWS
, y
);
13290 immutable ttype
= (tex
.type
== NVGtexture
.RGBA ? GL_RGBA
: GL_RED
);
13291 glTexSubImage2D(GL_TEXTURE_2D
, 0, x
, y
, w
, h
, ttype
, GL_UNSIGNED_BYTE
, data
);
13293 glPixelStorei(GL_UNPACK_ALIGNMENT
, 4);
13294 glPixelStorei(GL_UNPACK_ROW_LENGTH
, 0);
13295 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, 0);
13296 glPixelStorei(GL_UNPACK_SKIP_ROWS
, 0);
13298 glnvg__bindTexture(gl
, 0);
13303 bool glnvg__renderGetTextureSize (void* uptr
, int image
, int* w
, int* h
) nothrow @trusted @nogc {
13304 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13305 GLNVGtexture
* tex
= glnvg__findTexture(gl
, image
);
13307 if (w
!is null) *w
= 0;
13308 if (h
!is null) *h
= 0;
13311 if (w
!is null) *w
= tex
.width
;
13312 if (h
!is null) *h
= tex
.height
;
13317 void glnvg__xformToMat3x4 (float[] m3
, const(float)[] t
) nothrow @trusted @nogc {
13318 assert(t
.length
>= 6);
13319 assert(m3
.length
>= 12);
13320 m3
.ptr
[0] = t
.ptr
[0];
13321 m3
.ptr
[1] = t
.ptr
[1];
13324 m3
.ptr
[4] = t
.ptr
[2];
13325 m3
.ptr
[5] = t
.ptr
[3];
13328 m3
.ptr
[8] = t
.ptr
[4];
13329 m3
.ptr
[9] = t
.ptr
[5];
13334 NVGColor
glnvg__premulColor() (in auto ref NVGColor c
) nothrow @trusted @nogc {
13335 //pragma(inline, true);
13336 NVGColor res
= void;
13344 bool glnvg__convertPaint (GLNVGcontext
* gl
, GLNVGfragUniforms
* frag
, NVGPaint
* paint
, NVGscissor
* scissor
, float width
, float fringe
, float strokeThr
) nothrow @trusted @nogc {
13345 import core
.stdc
.math
: sqrtf
;
13346 GLNVGtexture
* tex
= null;
13347 NVGMatrix invxform
= void;
13349 memset(frag
, 0, (*frag
).sizeof
);
13351 frag
.innerCol
= glnvg__premulColor(paint
.innerColor
);
13352 frag
.middleCol
= glnvg__premulColor(paint
.middleColor
);
13353 frag
.outerCol
= glnvg__premulColor(paint
.outerColor
);
13354 frag
.midp
= paint
.midp
;
13356 if (scissor
.extent
.ptr
[0] < -0.5f || scissor
.extent
.ptr
[1] < -0.5f) {
13357 memset(frag
.scissorMat
.ptr
, 0, frag
.scissorMat
.sizeof
);
13358 frag
.scissorExt
.ptr
[0] = 1.0f;
13359 frag
.scissorExt
.ptr
[1] = 1.0f;
13360 frag
.scissorScale
.ptr
[0] = 1.0f;
13361 frag
.scissorScale
.ptr
[1] = 1.0f;
13363 //nvgTransformInverse(invxform[], scissor.xform[]);
13364 invxform
= scissor
.xform
.inverted
;
13365 glnvg__xformToMat3x4(frag
.scissorMat
[], invxform
.mat
[]);
13366 frag
.scissorExt
.ptr
[0] = scissor
.extent
.ptr
[0];
13367 frag
.scissorExt
.ptr
[1] = scissor
.extent
.ptr
[1];
13368 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
;
13369 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
;
13372 memcpy(frag
.extent
.ptr
, paint
.extent
.ptr
, frag
.extent
.sizeof
);
13373 frag
.strokeMult
= (width
*0.5f+fringe
*0.5f)/fringe
;
13374 frag
.strokeThr
= strokeThr
;
13376 if (paint
.image
.valid
) {
13377 tex
= glnvg__findTexture(gl
, paint
.image
.id
);
13378 if (tex
is null) return false;
13379 if ((tex
.flags
&NVGImageFlag
.FlipY
) != 0) {
13382 nvgTransformScale(flipped[], 1.0f, -1.0f);
13383 nvgTransformMultiply(flipped[], paint.xform[]);
13384 nvgTransformInverse(invxform[], flipped[]);
13387 NVGMatrix m1 = void, m2 = void;
13388 nvgTransformTranslate(m1[], 0.0f, frag.extent.ptr[1]*0.5f);
13389 nvgTransformMultiply(m1[], paint.xform[]);
13390 nvgTransformScale(m2[], 1.0f, -1.0f);
13391 nvgTransformMultiply(m2[], m1[]);
13392 nvgTransformTranslate(m1[], 0.0f, -frag.extent.ptr[1]*0.5f);
13393 nvgTransformMultiply(m1[], m2[]);
13394 nvgTransformInverse(invxform[], m1[]);
13396 NVGMatrix m1
= NVGMatrix
.Translated(0.0f, frag
.extent
.ptr
[1]*0.5f);
13397 m1
.mul(paint
.xform
);
13398 NVGMatrix m2
= NVGMatrix
.Scaled(1.0f, -1.0f);
13400 m1
= NVGMatrix
.Translated(0.0f, -frag
.extent
.ptr
[1]*0.5f);
13402 invxform
= m1
.inverted
;
13404 //nvgTransformInverse(invxform[], paint.xform[]);
13405 invxform
= paint
.xform
.inverted
;
13407 frag
.type
= NSVG_SHADER_FILLIMG
;
13409 if (tex
.type
== NVGtexture
.RGBA
) {
13410 frag
.texType
= (tex
.flags
&NVGImageFlag
.Premultiplied ?
0 : 1);
13414 //printf("frag.texType = %d\n", frag.texType);
13416 frag
.type
= (paint
.simpleColor ? NSVG_SHADER_FILLCOLOR
: NSVG_SHADER_FILLGRAD
);
13417 frag
.radius
= paint
.radius
;
13418 frag
.feather
= paint
.feather
;
13419 //nvgTransformInverse(invxform[], paint.xform[]);
13420 invxform
= paint
.xform
.inverted
;
13423 glnvg__xformToMat3x4(frag
.paintMat
[], invxform
.mat
[]);
13428 void glnvg__setUniforms (GLNVGcontext
* gl
, int uniformOffset
, int image
) nothrow @trusted @nogc {
13429 GLNVGfragUniforms
* frag
= nvg__fragUniformPtr(gl
, uniformOffset
);
13430 glnvg__setFBOClipTexture(gl
, frag
);
13431 glUniform4fv(gl
.shader
.loc
[GLNVGuniformLoc
.Frag
], frag
.UNIFORM_ARRAY_SIZE
, &(frag
.uniformArray
.ptr
[0].ptr
[0]));
13432 glnvg__checkError(gl
, "glnvg__setUniforms");
13434 GLNVGtexture
* tex
= glnvg__findTexture(gl
, image
);
13435 glnvg__bindTexture(gl
, (tex
!is null ? tex
.tex
: 0));
13436 glnvg__checkError(gl
, "tex paint tex");
13438 glnvg__bindTexture(gl
, 0);
13442 void glnvg__finishClip (GLNVGcontext
* gl
, NVGClipMode clipmode
) nothrow @trusted @nogc {
13443 assert(clipmode
!= NVGClipMode
.None
);
13445 // fill FBO, clear stencil buffer
13446 //TODO: optimize with bounds?
13448 //glnvg__resetAffine(gl);
13449 //glUseProgram(gl.shaderFillFBO.prog);
13450 glDisable(GL_CULL_FACE
);
13451 glDisable(GL_BLEND
);
13452 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
13453 glEnable(GL_STENCIL_TEST
);
13454 if (gl
.doClipUnion
) {
13455 // for "and" we should clear everything that is NOT stencil-masked
13456 glnvg__stencilFunc(gl
, GL_EQUAL
, 0x00, 0xff);
13457 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
);
13459 glnvg__stencilFunc(gl
, GL_NOTEQUAL
, 0x00, 0xff);
13460 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
);
13464 glVertex2i(0, gl
.fboHeight
);
13465 glVertex2i(gl
.fboWidth
, gl
.fboHeight
);
13466 glVertex2i(gl
.fboWidth
, 0);
13468 //glnvg__restoreAffine(gl);
13471 glBindFramebuffer(GL_FRAMEBUFFER
, gl
.mainFBO
);
13472 glDisable(GL_COLOR_LOGIC_OP
);
13473 //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // done above
13474 glEnable(GL_BLEND
);
13475 glDisable(GL_STENCIL_TEST
);
13476 glEnable(GL_CULL_FACE
);
13477 glUseProgram(gl
.shader
.prog
);
13479 // set current FBO as used one
13480 assert(gl
.msp
> 0 && gl
.fbo
.ptr
[gl
.msp
-1] > 0 && gl
.fboTex
.ptr
[gl
.msp
-1].ptr
[0] > 0);
13481 if (gl
.lastClipFBO
!= gl
.msp
-1) {
13482 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); }
13483 gl
.lastClipFBO
= gl
.msp
-1;
13484 glActiveTexture(GL_TEXTURE1
);
13485 glBindTexture(GL_TEXTURE_2D
, gl
.fboTex
.ptr
[gl
.lastClipFBO
].ptr
[0]);
13486 glActiveTexture(GL_TEXTURE0
);
13490 void glnvg__setClipUniforms (GLNVGcontext
* gl
, int uniformOffset
, NVGClipMode clipmode
) nothrow @trusted @nogc {
13491 assert(clipmode
!= NVGClipMode
.None
);
13492 GLNVGfragUniforms
* frag
= nvg__fragUniformPtr(gl
, uniformOffset
);
13493 // save uniform offset for `glnvg__finishClip()`
13494 gl
.lastClipUniOfs
= uniformOffset
;
13495 // get FBO index, bind this FBO
13496 immutable int clipTexId
= glnvg__generateFBOClipTexture(gl
);
13497 assert(clipTexId
>= 0);
13498 glUseProgram(gl
.shaderFillFBO
.prog
);
13499 glnvg__checkError(gl
, "use");
13500 glBindFramebuffer(GL_FRAMEBUFFER
, gl
.fbo
.ptr
[clipTexId
]);
13501 // set logic op for clip
13502 gl
.doClipUnion
= false;
13503 if (gl
.maskStack
.ptr
[gl
.msp
-1] == GLMaskState
.JustCleared
) {
13504 // it is cleared to zero, we can just draw a path
13505 glDisable(GL_COLOR_LOGIC_OP
);
13506 gl
.maskStack
.ptr
[gl
.msp
-1] = GLMaskState
.Initialized
;
13508 glEnable(GL_COLOR_LOGIC_OP
);
13509 final switch (clipmode
) {
13510 case NVGClipMode
.None
: assert(0, "wtf?!");
13511 case NVGClipMode
.Union
: glLogicOp(GL_CLEAR
); gl
.doClipUnion
= true; break; // use `GL_CLEAR` to avoid adding another shader mode
13512 case NVGClipMode
.Or
: glLogicOp(GL_COPY
); break; // GL_OR
13513 case NVGClipMode
.Xor
: glLogicOp(GL_XOR
); break;
13514 case NVGClipMode
.Sub
: glLogicOp(GL_CLEAR
); break;
13515 case NVGClipMode
.Replace
: glLogicOp(GL_COPY
); break;
13518 // set affine matrix
13519 glUniform4fv(gl
.shaderFillFBO
.loc
[GLNVGuniformLoc
.TMat
], 1, gl
.lastAffine
.mat
.ptr
);
13520 glnvg__checkError(gl
, "affine 0");
13521 glUniform2fv(gl
.shaderFillFBO
.loc
[GLNVGuniformLoc
.TTr
], 1, gl
.lastAffine
.mat
.ptr
+4);
13522 glnvg__checkError(gl
, "affine 1");
13523 // setup common OpenGL parameters
13524 glDisable(GL_BLEND
);
13525 glDisable(GL_CULL_FACE
);
13526 glEnable(GL_STENCIL_TEST
);
13527 glnvg__stencilMask(gl
, 0xff);
13528 glnvg__stencilFunc(gl
, GL_EQUAL
, 0x00, 0xff);
13529 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
13530 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
13533 void glnvg__renderViewport (void* uptr
, int width
, int height
) nothrow @trusted @nogc {
13534 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13536 gl
.view
.ptr
[0] = cast(float)width
;
13537 gl
.view
.ptr
[1] = cast(float)height
;
13538 // kill FBOs if we need to create new ones (flushing will recreate 'em if necessary)
13539 if (width
!= gl
.fboWidth || height
!= gl
.fboHeight
) {
13540 glnvg__killFBOs(gl
);
13541 gl
.fboWidth
= width
;
13542 gl
.fboHeight
= height
;
13545 gl
.maskStack
.ptr
[0] = GLMaskState
.DontMask
;
13547 import core
.atomic
: atomicLoad
;
13548 if (atomicLoad(gl
.mustCleanTextures
)) {
13550 import core
.thread
: Thread
;
13551 static if (__VERSION__
< 2076) {
13553 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13556 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13558 synchronized(GLNVGTextureLocker
.classinfo
) {
13559 gl
.mustCleanTextures
= false;
13560 foreach (immutable tidx
, ref GLNVGtexture tex
; gl
.textures
[0..gl
.ntextures
]) {
13561 // no need to use atomic ops here, as we're locked
13562 if (tex
.rc
== 0 && tex
.tex
!= 0 && tex
.id
== 0) {
13563 version(nanovega_debug_textures
) {{ import core
.stdc
.stdio
; printf("*** cleaned up texture with glid=%u\n", tex
.tex
); }}
13564 import core
.stdc
.string
: memset
;
13565 if ((tex
.flags
&NVGImageFlag
.NoDelete
) == 0) glDeleteTextures(1, &tex
.tex
);
13566 memset(&tex
, 0, tex
.sizeof
);
13567 tex
.nextfree
= gl
.freetexid
;
13568 gl
.freetexid
= cast(int)tidx
;
13572 } catch (Exception e
) {}
13576 void glnvg__fill (GLNVGcontext
* gl
, GLNVGcall
* call) nothrow @trusted @nogc {
13577 GLNVGpath
* paths
= &gl
.paths
[call.pathOffset
];
13578 int npaths
= call.pathCount
;
13580 if (call.clipmode
== NVGClipMode
.None
) {
13582 glEnable(GL_STENCIL_TEST
);
13583 glnvg__stencilMask(gl
, 0xffU
);
13584 glnvg__stencilFunc(gl
, GL_ALWAYS
, 0, 0xffU
);
13586 glnvg__setUniforms(gl
, call.uniformOffset
, 0);
13587 glnvg__checkError(gl
, "fill simple");
13589 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
13590 if (call.evenOdd
) {
13591 //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
13592 //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
13593 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INVERT
);
13595 glStencilOpSeparate(GL_FRONT
, GL_KEEP
, GL_KEEP
, GL_INCR_WRAP
);
13596 glStencilOpSeparate(GL_BACK
, GL_KEEP
, GL_KEEP
, GL_DECR_WRAP
);
13598 glDisable(GL_CULL_FACE
);
13599 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_FAN
, paths
[i
].fillOffset
, paths
[i
].fillCount
);
13600 glEnable(GL_CULL_FACE
);
13602 // Draw anti-aliased pixels
13603 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
13604 glnvg__setUniforms(gl
, call.uniformOffset
+gl
.fragSize
, call.image
);
13605 glnvg__checkError(gl
, "fill fill");
13607 if (gl
.flags
&NVGContextFlag
.Antialias
) {
13608 glnvg__stencilFunc(gl
, GL_EQUAL
, 0x00, 0xffU
);
13609 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
13611 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13615 glnvg__stencilFunc(gl
, GL_NOTEQUAL
, 0x0, 0xffU
);
13616 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
);
13617 if (call.evenOdd
) {
13618 glDisable(GL_CULL_FACE
);
13619 glDrawArrays(GL_TRIANGLE_STRIP
, call.triangleOffset
, call.triangleCount
);
13620 //foreach (int i; 0..npaths) glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
13621 glEnable(GL_CULL_FACE
);
13623 glDrawArrays(GL_TRIANGLE_STRIP
, call.triangleOffset
, call.triangleCount
);
13626 glDisable(GL_STENCIL_TEST
);
13628 glnvg__setClipUniforms(gl
, call.uniformOffset
/*+gl.fragSize*/, call.clipmode
); // this activates our FBO
13629 glnvg__checkError(gl
, "fillclip simple");
13630 glnvg__stencilFunc(gl
, GL_ALWAYS
, 0x00, 0xffU
);
13631 if (call.evenOdd
) {
13632 //glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INVERT);
13633 //glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INVERT);
13634 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INVERT
);
13636 glStencilOpSeparate(GL_FRONT
, GL_KEEP
, GL_KEEP
, GL_INCR_WRAP
);
13637 glStencilOpSeparate(GL_BACK
, GL_KEEP
, GL_KEEP
, GL_DECR_WRAP
);
13639 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_FAN
, paths
[i
].fillOffset
, paths
[i
].fillCount
);
13640 glnvg__finishClip(gl
, call.clipmode
); // deactivate FBO, restore rendering state
13644 void glnvg__convexFill (GLNVGcontext
* gl
, GLNVGcall
* call) nothrow @trusted @nogc {
13645 GLNVGpath
* paths
= &gl
.paths
[call.pathOffset
];
13646 int npaths
= call.pathCount
;
13648 if (call.clipmode
== NVGClipMode
.None
) {
13649 glnvg__setUniforms(gl
, call.uniformOffset
, call.image
);
13650 glnvg__checkError(gl
, "convex fill");
13651 if (call.evenOdd
) glDisable(GL_CULL_FACE
);
13652 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_FAN
, paths
[i
].fillOffset
, paths
[i
].fillCount
);
13653 if (gl
.flags
&NVGContextFlag
.Antialias
) {
13655 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13657 if (call.evenOdd
) glEnable(GL_CULL_FACE
);
13659 glnvg__setClipUniforms(gl
, call.uniformOffset
, call.clipmode
); // this activates our FBO
13660 glnvg__checkError(gl
, "clip convex fill");
13661 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_FAN
, paths
[i
].fillOffset
, paths
[i
].fillCount
);
13662 if (gl
.flags
&NVGContextFlag
.Antialias
) {
13664 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13666 glnvg__finishClip(gl
, call.clipmode
); // deactivate FBO, restore rendering state
13670 void glnvg__stroke (GLNVGcontext
* gl
, GLNVGcall
* call) nothrow @trusted @nogc {
13671 GLNVGpath
* paths
= &gl
.paths
[call.pathOffset
];
13672 int npaths
= call.pathCount
;
13674 if (call.clipmode
== NVGClipMode
.None
) {
13675 if (gl
.flags
&NVGContextFlag
.StencilStrokes
) {
13676 glEnable(GL_STENCIL_TEST
);
13677 glnvg__stencilMask(gl
, 0xff);
13679 // Fill the stroke base without overlap
13680 glnvg__stencilFunc(gl
, GL_EQUAL
, 0x0, 0xff);
13681 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
13682 glnvg__setUniforms(gl
, call.uniformOffset
+gl
.fragSize
, call.image
);
13683 glnvg__checkError(gl
, "stroke fill 0");
13684 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13686 // Draw anti-aliased pixels.
13687 glnvg__setUniforms(gl
, call.uniformOffset
, call.image
);
13688 glnvg__stencilFunc(gl
, GL_EQUAL
, 0x00, 0xff);
13689 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
13690 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13692 // Clear stencil buffer.
13693 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
13694 glnvg__stencilFunc(gl
, GL_ALWAYS
, 0x0, 0xff);
13695 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
);
13696 glnvg__checkError(gl
, "stroke fill 1");
13697 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13698 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
13700 glDisable(GL_STENCIL_TEST
);
13702 //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, strokeWidth, fringe, 1.0f-0.5f/255.0f);
13704 glnvg__setUniforms(gl
, call.uniformOffset
, call.image
);
13705 glnvg__checkError(gl
, "stroke fill");
13707 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13710 glnvg__setClipUniforms(gl
, call.uniformOffset
/*+gl.fragSize*/, call.clipmode
);
13711 glnvg__checkError(gl
, "stroke fill 0");
13712 foreach (int i
; 0..npaths
) glDrawArrays(GL_TRIANGLE_STRIP
, paths
[i
].strokeOffset
, paths
[i
].strokeCount
);
13713 glnvg__finishClip(gl
, call.clipmode
); // deactivate FBO, restore rendering state
13717 void glnvg__triangles (GLNVGcontext
* gl
, GLNVGcall
* call) nothrow @trusted @nogc {
13718 if (call.clipmode
== NVGClipMode
.None
) {
13719 glnvg__setUniforms(gl
, call.uniformOffset
, call.image
);
13720 glnvg__checkError(gl
, "triangles fill");
13721 glDrawArrays(GL_TRIANGLES
, call.triangleOffset
, call.triangleCount
);
13723 //TODO(?): use texture as mask?
13727 void glnvg__affine (GLNVGcontext
* gl
, GLNVGcall
* call) nothrow @trusted @nogc {
13728 glUniform4fv(gl
.shader
.loc
[GLNVGuniformLoc
.TMat
], 1, call.affine
.mat
.ptr
);
13729 glnvg__checkError(gl
, "affine");
13730 glUniform2fv(gl
.shader
.loc
[GLNVGuniformLoc
.TTr
], 1, call.affine
.mat
.ptr
+4);
13731 glnvg__checkError(gl
, "affine");
13732 //glnvg__setUniforms(gl, call.uniformOffset, call.image);
13735 void glnvg__renderCancelInternal (GLNVGcontext
* gl
, bool clearTextures
) nothrow @trusted @nogc {
13736 scope(exit
) gl
.inFrame
= false;
13737 if (clearTextures
&& gl
.inFrame
) {
13739 import core
.thread
: Thread
;
13740 static if (__VERSION__
< 2076) {
13742 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13745 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13747 } catch (Exception e
) {}
13748 foreach (ref GLNVGcall c
; gl
.calls
[0..gl
.ncalls
]) if (c
.image
> 0) glnvg__deleteTexture(gl
, c
.image
);
13755 gl
.maskStack
.ptr
[0] = GLMaskState
.DontMask
;
13758 void glnvg__renderCancel (void* uptr
) nothrow @trusted @nogc {
13759 glnvg__renderCancelInternal(cast(GLNVGcontext
*)uptr
, true);
13762 GLenum
glnvg_convertBlendFuncFactor (NVGBlendFactor factor
) pure nothrow @trusted @nogc {
13763 if (factor
== NVGBlendFactor
.Zero
) return GL_ZERO
;
13764 if (factor
== NVGBlendFactor
.One
) return GL_ONE
;
13765 if (factor
== NVGBlendFactor
.SrcColor
) return GL_SRC_COLOR
;
13766 if (factor
== NVGBlendFactor
.OneMinusSrcColor
) return GL_ONE_MINUS_SRC_COLOR
;
13767 if (factor
== NVGBlendFactor
.DstColor
) return GL_DST_COLOR
;
13768 if (factor
== NVGBlendFactor
.OneMinusDstColor
) return GL_ONE_MINUS_DST_COLOR
;
13769 if (factor
== NVGBlendFactor
.SrcAlpha
) return GL_SRC_ALPHA
;
13770 if (factor
== NVGBlendFactor
.OneMinusSrcAlpha
) return GL_ONE_MINUS_SRC_ALPHA
;
13771 if (factor
== NVGBlendFactor
.DstAlpha
) return GL_DST_ALPHA
;
13772 if (factor
== NVGBlendFactor
.OneMinusDstAlpha
) return GL_ONE_MINUS_DST_ALPHA
;
13773 if (factor
== NVGBlendFactor
.SrcAlphaSaturate
) return GL_SRC_ALPHA_SATURATE
;
13774 return GL_INVALID_ENUM
;
13777 GLNVGblend
glnvg__buildBlendFunc (NVGCompositeOperationState op
) pure nothrow @trusted @nogc {
13779 res
.simple
= op
.simple
;
13780 res
.srcRGB
= glnvg_convertBlendFuncFactor(op
.srcRGB
);
13781 res
.dstRGB
= glnvg_convertBlendFuncFactor(op
.dstRGB
);
13782 res
.srcAlpha
= glnvg_convertBlendFuncFactor(op
.srcAlpha
);
13783 res
.dstAlpha
= glnvg_convertBlendFuncFactor(op
.dstAlpha
);
13785 if (res
.srcAlpha
== GL_INVALID_ENUM || res
.dstAlpha
== GL_INVALID_ENUM
) {
13786 res
.srcRGB
= res
.srcAlpha
= res
.dstRGB
= res
.dstAlpha
= GL_INVALID_ENUM
;
13789 if (res
.srcRGB
== GL_INVALID_ENUM || res
.dstRGB
== GL_INVALID_ENUM || res
.srcAlpha
== GL_INVALID_ENUM || res
.dstAlpha
== GL_INVALID_ENUM
) {
13791 res
.srcRGB
= res
.srcAlpha
= res
.dstRGB
= res
.dstAlpha
= GL_INVALID_ENUM
;
13797 void glnvg__blendCompositeOperation() (GLNVGcontext
* gl
, in auto ref GLNVGblend op
) nothrow @trusted @nogc {
13798 //glBlendFuncSeparate(glnvg_convertBlendFuncFactor(op.srcRGB), glnvg_convertBlendFuncFactor(op.dstRGB), glnvg_convertBlendFuncFactor(op.srcAlpha), glnvg_convertBlendFuncFactor(op.dstAlpha));
13799 static if (NANOVG_GL_USE_STATE_FILTER
) {
13800 if (gl
.blendFunc
.simple
== op
.simple
) {
13802 if (gl
.blendFunc
.srcAlpha
== op
.srcAlpha
&& gl
.blendFunc
.dstAlpha
== op
.dstAlpha
) return;
13804 if (gl
.blendFunc
.srcRGB
== op
.srcRGB
&& gl
.blendFunc
.dstRGB
== op
.dstRGB
&& gl
.blendFunc
.srcAlpha
== op
.srcAlpha
&& gl
.blendFunc
.dstAlpha
== op
.dstAlpha
) return;
13810 if (op
.srcAlpha
== GL_INVALID_ENUM || op
.dstAlpha
== GL_INVALID_ENUM
) {
13811 glBlendFunc(GL_ONE
, GL_ONE_MINUS_SRC_ALPHA
);
13813 glBlendFunc(op
.srcAlpha
, op
.dstAlpha
);
13816 if (op
.srcRGB
== GL_INVALID_ENUM || op
.dstRGB
== GL_INVALID_ENUM || op
.srcAlpha
== GL_INVALID_ENUM || op
.dstAlpha
== GL_INVALID_ENUM
) {
13817 glBlendFunc(GL_ONE
, GL_ONE_MINUS_SRC_ALPHA
);
13819 glBlendFuncSeparate(op
.srcRGB
, op
.dstRGB
, op
.srcAlpha
, op
.dstAlpha
);
13824 void glnvg__renderSetAffine (void* uptr
, in ref NVGMatrix mat
) nothrow @trusted @nogc {
13825 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13827 // if last operation was GLNVG_AFFINE, simply replace the matrix
13828 if (gl
.ncalls
> 0 && gl
.calls
[gl
.ncalls
-1].type
== GLNVG_AFFINE
) {
13829 call = &gl
.calls
[gl
.ncalls
-1];
13831 call = glnvg__allocCall(gl
);
13832 if (call is null) return;
13833 call.type
= GLNVG_AFFINE
;
13835 call.affine
.mat
.ptr
[0..6] = mat
.mat
.ptr
[0..6];
13838 version(nanovega_debug_clipping
) public __gshared
bool nanovegaClipDebugDump
= false;
13840 void glnvg__renderFlush (void* uptr
) nothrow @trusted @nogc {
13841 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
13842 if (!gl
.inFrame
) assert(0, "NanoVega: internal driver error");
13844 import core
.thread
: Thread
;
13845 static if (__VERSION__
< 2076) {
13847 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13850 if (gl
.mainTID
!= Thread
.getThis
.id
) assert(0, "NanoVega: cannot use context in alien thread");
13852 } catch (Exception e
) {}
13853 scope(exit
) gl
.inFrame
= false;
13855 glnvg__resetError
!true(gl
);
13858 glGetIntegerv(GL_FRAMEBUFFER_BINDING
, &vv
);
13859 if (glGetError() || vv
< 0) vv
= 0;
13860 gl
.mainFBO
= cast(uint)vv
;
13863 enum ShaderType
{ None
, Fill
, Clip
}
13864 auto lastShader
= ShaderType
.None
;
13865 if (gl
.ncalls
> 0) {
13867 gl
.maskStack
.ptr
[0] = GLMaskState
.DontMask
;
13869 // Setup require GL state.
13870 glUseProgram(gl
.shader
.prog
);
13872 glActiveTexture(GL_TEXTURE1
);
13873 glBindTexture(GL_TEXTURE_2D
, 0);
13874 glActiveTexture(GL_TEXTURE0
);
13875 glnvg__resetFBOClipTextureCache(gl
);
13877 //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
13878 static if (NANOVG_GL_USE_STATE_FILTER
) {
13879 gl
.blendFunc
.simple
= true;
13880 gl
.blendFunc
.srcRGB
= gl
.blendFunc
.dstRGB
= gl
.blendFunc
.srcAlpha
= gl
.blendFunc
.dstAlpha
= GL_INVALID_ENUM
;
13882 glBlendFunc(GL_ONE
, GL_ONE_MINUS_SRC_ALPHA
); // just in case
13883 glEnable(GL_CULL_FACE
);
13884 glCullFace(GL_BACK
);
13885 glFrontFace(GL_CCW
);
13886 glEnable(GL_BLEND
);
13887 glDisable(GL_DEPTH_TEST
);
13888 glDisable(GL_SCISSOR_TEST
);
13889 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
13890 glStencilMask(0xffffffff);
13891 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
13892 glStencilFunc(GL_ALWAYS
, 0, 0xffffffff);
13893 glActiveTexture(GL_TEXTURE0
);
13894 glBindTexture(GL_TEXTURE_2D
, 0);
13895 static if (NANOVG_GL_USE_STATE_FILTER
) {
13896 gl
.boundTexture
= 0;
13897 gl
.stencilMask
= 0xffffffff;
13898 gl
.stencilFunc
= GL_ALWAYS
;
13899 gl
.stencilFuncRef
= 0;
13900 gl
.stencilFuncMask
= 0xffffffff;
13902 glnvg__checkError(gl
, "OpenGL setup");
13904 // Upload vertex data
13905 glBindBuffer(GL_ARRAY_BUFFER
, gl
.vertBuf
);
13906 glBufferData(GL_ARRAY_BUFFER
, gl
.nverts
*NVGVertex
.sizeof
, gl
.verts
, GL_STREAM_DRAW
);
13907 glEnableVertexAttribArray(0);
13908 glEnableVertexAttribArray(1);
13909 glVertexAttribPointer(0, 2, GL_FLOAT
, GL_FALSE
, NVGVertex
.sizeof
, cast(const(GLvoid
)*)cast(usize
)0);
13910 glVertexAttribPointer(1, 2, GL_FLOAT
, GL_FALSE
, NVGVertex
.sizeof
, cast(const(GLvoid
)*)(0+2*float.sizeof
));
13911 glnvg__checkError(gl
, "vertex data uploading");
13913 // Set view and texture just once per frame.
13914 glUniform1i(gl
.shader
.loc
[GLNVGuniformLoc
.Tex
], 0);
13915 if (gl
.shader
.loc
[GLNVGuniformLoc
.ClipTex
] != -1) {
13916 //{ import core.stdc.stdio; printf("%d\n", gl.shader.loc[GLNVGuniformLoc.ClipTex]); }
13917 glUniform1i(gl
.shader
.loc
[GLNVGuniformLoc
.ClipTex
], 1);
13919 if (gl
.shader
.loc
[GLNVGuniformLoc
.ViewSize
] != -1) glUniform2fv(gl
.shader
.loc
[GLNVGuniformLoc
.ViewSize
], 1, gl
.view
.ptr
);
13920 glnvg__checkError(gl
, "render shader setup");
13922 // Reset affine transformations.
13923 glUniform4fv(gl
.shader
.loc
[GLNVGuniformLoc
.TMat
], 1, NVGMatrix
.IdentityMat
.ptr
);
13924 glUniform2fv(gl
.shader
.loc
[GLNVGuniformLoc
.TTr
], 1, NVGMatrix
.IdentityMat
.ptr
+4);
13925 glnvg__checkError(gl
, "affine setup");
13927 // set clip shaders params
13929 glUseProgram(gl
.shaderFillFBO
.prog
);
13930 glnvg__checkError(gl
, "clip shaders setup (fill 0)");
13931 if (gl
.shaderFillFBO
.loc
[GLNVGuniformLoc
.ViewSize
] != -1) glUniform2fv(gl
.shaderFillFBO
.loc
[GLNVGuniformLoc
.ViewSize
], 1, gl
.view
.ptr
);
13932 glnvg__checkError(gl
, "clip shaders setup (fill 1)");
13934 glUseProgram(gl
.shaderCopyFBO
.prog
);
13935 glnvg__checkError(gl
, "clip shaders setup (copy 0)");
13936 if (gl
.shaderCopyFBO
.loc
[GLNVGuniformLoc
.ViewSize
] != -1) glUniform2fv(gl
.shaderCopyFBO
.loc
[GLNVGuniformLoc
.ViewSize
], 1, gl
.view
.ptr
);
13937 glnvg__checkError(gl
, "clip shaders setup (copy 1)");
13938 //glUniform1i(gl.shaderFillFBO.loc[GLNVGuniformLoc.Tex], 0);
13939 glUniform1i(gl
.shaderCopyFBO
.loc
[GLNVGuniformLoc
.Tex
], 0);
13940 glnvg__checkError(gl
, "clip shaders setup (copy 2)");
13941 // restore render shader
13942 glUseProgram(gl
.shader
.prog
);
13944 //{ 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]); }
13946 gl
.lastAffine
.identity
;
13948 foreach (int i
; 0..gl
.ncalls
) {
13949 GLNVGcall
* call = &gl
.calls
[i
];
13950 switch (call.type
) {
13951 case GLNVG_FILL
: glnvg__blendCompositeOperation(gl
, call.blendFunc
); glnvg__fill(gl
, call); break;
13952 case GLNVG_CONVEXFILL
: glnvg__blendCompositeOperation(gl
, call.blendFunc
); glnvg__convexFill(gl
, call); break;
13953 case GLNVG_STROKE
: glnvg__blendCompositeOperation(gl
, call.blendFunc
); glnvg__stroke(gl
, call); break;
13954 case GLNVG_TRIANGLES
: glnvg__blendCompositeOperation(gl
, call.blendFunc
); glnvg__triangles(gl
, call); break;
13955 case GLNVG_AFFINE
: gl
.lastAffine
= call.affine
; glnvg__affine(gl
, call); break;
13956 // clip region management
13957 case GLNVG_PUSHCLIP
:
13958 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]); }
13959 if (gl
.msp
>= gl
.maskStack
.length
) assert(0, "NanoVega: mask stack overflow in OpenGL backend");
13960 if (gl
.maskStack
.ptr
[gl
.msp
-1] == GLMaskState
.DontMask
) {
13961 gl
.maskStack
.ptr
[gl
.msp
++] = GLMaskState
.DontMask
;
13963 gl
.maskStack
.ptr
[gl
.msp
++] = GLMaskState
.Uninitialized
;
13965 // no need to reset FBO cache here, as nothing was changed
13967 case GLNVG_POPCLIP
:
13968 if (gl
.msp
<= 1) assert(0, "NanoVega: mask stack underflow in OpenGL backend");
13969 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]); }
13971 assert(gl
.msp
> 0);
13972 //{ import core.stdc.stdio; printf("popped; new msp is %d; state is %d\n", gl.msp, gl.maskStack.ptr[gl.msp]); }
13973 // check popped item
13974 final switch (gl
.maskStack
.ptr
[gl
.msp
]) {
13975 case GLMaskState
.DontMask
:
13976 // if last FBO was "don't mask", reset cache if current is not "don't mask"
13977 if (gl
.maskStack
.ptr
[gl
.msp
-1] != GLMaskState
.DontMask
) {
13978 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf(" +++ need to reset FBO cache\n"); }
13979 glnvg__resetFBOClipTextureCache(gl
);
13982 case GLMaskState
.Uninitialized
:
13983 // if last FBO texture was uninitialized, it means that nothing was changed,
13984 // so we can keep using cached FBO
13986 case GLMaskState
.Initialized
:
13987 // if last FBO was initialized, it means that something was definitely changed
13988 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf(" +++ need to reset FBO cache\n"); }
13989 glnvg__resetFBOClipTextureCache(gl
);
13991 case GLMaskState
.JustCleared
: assert(0, "NanoVega: internal FBO stack error");
13994 case GLNVG_RESETCLIP
:
13995 // mark current mask as "don't mask"
13996 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]); }
13998 if (gl
.maskStack
.ptr
[gl
.msp
-1] != GLMaskState
.DontMask
) {
13999 gl
.maskStack
.ptr
[gl
.msp
-1] = GLMaskState
.DontMask
;
14000 version(nanovega_debug_clipping
) if (nanovegaClipDebugDump
) { import core
.stdc
.stdio
; printf(" +++ need to reset FBO cache\n"); }
14001 glnvg__resetFBOClipTextureCache(gl
);
14005 case GLNVG_CLIP_DDUMP_ON
:
14006 version(nanovega_debug_clipping
) nanovegaClipDebugDump
= true;
14008 case GLNVG_CLIP_DDUMP_OFF
:
14009 version(nanovega_debug_clipping
) nanovegaClipDebugDump
= false;
14011 case GLNVG_NONE
: break;
14014 import core
.stdc
.stdio
; stderr
.fprintf("NanoVega FATAL: invalid command in OpenGL backend: %d\n", call.type
);
14016 assert(0, "NanoVega: invalid command in OpenGL backend (fatal internal error)");
14018 // and free texture, why not
14019 glnvg__deleteTexture(gl
, call.image
);
14022 glDisableVertexAttribArray(0);
14023 glDisableVertexAttribArray(1);
14024 glDisable(GL_CULL_FACE
);
14025 glBindBuffer(GL_ARRAY_BUFFER
, 0);
14027 glnvg__bindTexture(gl
, 0);
14030 // this will do all necessary cleanup
14031 glnvg__renderCancelInternal(gl
, false); // no need to clear textures
14034 int glnvg__maxVertCount (const(NVGpath
)* paths
, int npaths
) nothrow @trusted @nogc {
14036 foreach (int i
; 0..npaths
) {
14037 count
+= paths
[i
].nfill
;
14038 count
+= paths
[i
].nstroke
;
14043 GLNVGcall
* glnvg__allocCall (GLNVGcontext
* gl
) nothrow @trusted @nogc {
14044 GLNVGcall
* ret = null;
14045 if (gl
.ncalls
+1 > gl
.ccalls
) {
14047 int ccalls
= glnvg__maxi(gl
.ncalls
+1, 128)+gl
.ccalls
/2; // 1.5x Overallocate
14048 calls
= cast(GLNVGcall
*)realloc(gl
.calls
, GLNVGcall
.sizeof
*ccalls
);
14049 if (calls
is null) return null;
14051 gl
.ccalls
= ccalls
;
14053 ret = &gl
.calls
[gl
.ncalls
++];
14054 memset(ret, 0, GLNVGcall
.sizeof
);
14058 int glnvg__allocPaths (GLNVGcontext
* gl
, int n
) nothrow @trusted @nogc {
14060 if (gl
.npaths
+n
> gl
.cpaths
) {
14062 int cpaths
= glnvg__maxi(gl
.npaths
+n
, 128)+gl
.cpaths
/2; // 1.5x Overallocate
14063 paths
= cast(GLNVGpath
*)realloc(gl
.paths
, GLNVGpath
.sizeof
*cpaths
);
14064 if (paths
is null) return -1;
14066 gl
.cpaths
= cpaths
;
14073 int glnvg__allocVerts (GLNVGcontext
* gl
, int n
) nothrow @trusted @nogc {
14075 if (gl
.nverts
+n
> gl
.cverts
) {
14077 int cverts
= glnvg__maxi(gl
.nverts
+n
, 4096)+gl
.cverts
/2; // 1.5x Overallocate
14078 verts
= cast(NVGVertex
*)realloc(gl
.verts
, NVGVertex
.sizeof
*cverts
);
14079 if (verts
is null) return -1;
14081 gl
.cverts
= cverts
;
14088 int glnvg__allocFragUniforms (GLNVGcontext
* gl
, int n
) nothrow @trusted @nogc {
14089 int ret = 0, structSize
= gl
.fragSize
;
14090 if (gl
.nuniforms
+n
> gl
.cuniforms
) {
14092 int cuniforms
= glnvg__maxi(gl
.nuniforms
+n
, 128)+gl
.cuniforms
/2; // 1.5x Overallocate
14093 uniforms
= cast(ubyte*)realloc(gl
.uniforms
, structSize
*cuniforms
);
14094 if (uniforms
is null) return -1;
14095 gl
.uniforms
= uniforms
;
14096 gl
.cuniforms
= cuniforms
;
14098 ret = gl
.nuniforms
*structSize
;
14103 GLNVGfragUniforms
* nvg__fragUniformPtr (GLNVGcontext
* gl
, int i
) nothrow @trusted @nogc {
14104 return cast(GLNVGfragUniforms
*)&gl
.uniforms
[i
];
14107 void glnvg__vset (NVGVertex
* vtx
, float x
, float y
, float u
, float v
) nothrow @trusted @nogc {
14114 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 {
14115 if (npaths
< 1) return;
14117 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
14118 GLNVGcall
* call = glnvg__allocCall(gl
);
14120 GLNVGfragUniforms
* frag
;
14121 int maxverts
, offset
;
14123 if (call is null) return;
14125 call.type
= GLNVG_FILL
;
14126 call.evenOdd
= evenOdd
;
14127 call.clipmode
= clipmode
;
14128 //if (clipmode != NVGClipMode.None) { import core.stdc.stdio; printf("CLIP!\n"); }
14129 call.blendFunc
= glnvg__buildBlendFunc(compositeOperation
);
14130 call.triangleCount
= 4;
14131 call.pathOffset
= glnvg__allocPaths(gl
, npaths
);
14132 if (call.pathOffset
== -1) goto error
;
14133 call.pathCount
= npaths
;
14134 call.image
= paint
.image
.id
;
14135 if (call.image
> 0) glnvg__renderTextureIncRef(uptr
, call.image
);
14137 if (npaths
== 1 && paths
[0].convex
) {
14138 call.type
= GLNVG_CONVEXFILL
;
14139 call.triangleCount
= 0; // Bounding box fill quad not needed for convex fill
14142 // Allocate vertices for all the paths.
14143 maxverts
= glnvg__maxVertCount(paths
, npaths
)+call.triangleCount
;
14144 offset
= glnvg__allocVerts(gl
, maxverts
);
14145 if (offset
== -1) goto error
;
14147 foreach (int i
; 0..npaths
) {
14148 GLNVGpath
* copy
= &gl
.paths
[call.pathOffset
+i
];
14149 const(NVGpath
)* path
= &paths
[i
];
14150 memset(copy
, 0, GLNVGpath
.sizeof
);
14151 if (path
.nfill
> 0) {
14152 copy
.fillOffset
= offset
;
14153 copy
.fillCount
= path
.nfill
;
14154 memcpy(&gl
.verts
[offset
], path
.fill
, NVGVertex
.sizeof
*path
.nfill
);
14155 offset
+= path
.nfill
;
14157 if (path
.nstroke
> 0) {
14158 copy
.strokeOffset
= offset
;
14159 copy
.strokeCount
= path
.nstroke
;
14160 memcpy(&gl
.verts
[offset
], path
.stroke
, NVGVertex
.sizeof
*path
.nstroke
);
14161 offset
+= path
.nstroke
;
14165 // Setup uniforms for draw calls
14166 if (call.type
== GLNVG_FILL
) {
14167 import core
.stdc
.string
: memcpy
;
14169 call.triangleOffset
= offset
;
14170 quad
= &gl
.verts
[call.triangleOffset
];
14171 glnvg__vset(&quad
[0], bounds
[2], bounds
[3], 0.5f, 1.0f);
14172 glnvg__vset(&quad
[1], bounds
[2], bounds
[1], 0.5f, 1.0f);
14173 glnvg__vset(&quad
[2], bounds
[0], bounds
[3], 0.5f, 1.0f);
14174 glnvg__vset(&quad
[3], bounds
[0], bounds
[1], 0.5f, 1.0f);
14176 call.uniformOffset
= glnvg__allocFragUniforms(gl
, 2);
14177 if (call.uniformOffset
== -1) goto error
;
14178 // Simple shader for stencil
14179 frag
= nvg__fragUniformPtr(gl
, call.uniformOffset
);
14180 memset(frag
, 0, (*frag
).sizeof
);
14181 glnvg__convertPaint(gl
, nvg__fragUniformPtr(gl
, call.uniformOffset
), paint
, scissor
, fringe
, fringe
, -1.0f);
14182 memcpy(nvg__fragUniformPtr(gl
, call.uniformOffset
+gl
.fragSize
), frag
, (*frag
).sizeof
);
14183 frag
.strokeThr
= -1.0f;
14184 frag
.type
= NSVG_SHADER_SIMPLE
;
14186 //glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call.uniformOffset+gl.fragSize), paint, scissor, fringe, fringe, -1.0f);
14188 call.uniformOffset
= glnvg__allocFragUniforms(gl
, 1);
14189 if (call.uniformOffset
== -1) goto error
;
14191 glnvg__convertPaint(gl
, nvg__fragUniformPtr(gl
, call.uniformOffset
), paint
, scissor
, fringe
, fringe
, -1.0f);
14197 // We get here if call alloc was ok, but something else is not.
14198 // Roll back the last call to prevent drawing it.
14199 if (gl
.ncalls
> 0) --gl
.ncalls
;
14202 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 {
14203 if (npaths
< 1) return;
14205 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
14206 GLNVGcall
* call = glnvg__allocCall(gl
);
14207 int maxverts
, offset
;
14209 if (call is null) return;
14211 call.type
= GLNVG_STROKE
;
14212 call.clipmode
= clipmode
;
14213 call.blendFunc
= glnvg__buildBlendFunc(compositeOperation
);
14214 call.pathOffset
= glnvg__allocPaths(gl
, npaths
);
14215 if (call.pathOffset
== -1) goto error
;
14216 call.pathCount
= npaths
;
14217 call.image
= paint
.image
.id
;
14218 if (call.image
> 0) glnvg__renderTextureIncRef(uptr
, call.image
);
14220 // Allocate vertices for all the paths.
14221 maxverts
= glnvg__maxVertCount(paths
, npaths
);
14222 offset
= glnvg__allocVerts(gl
, maxverts
);
14223 if (offset
== -1) goto error
;
14225 foreach (int i
; 0..npaths
) {
14226 GLNVGpath
* copy
= &gl
.paths
[call.pathOffset
+i
];
14227 const(NVGpath
)* path
= &paths
[i
];
14228 memset(copy
, 0, GLNVGpath
.sizeof
);
14229 if (path
.nstroke
) {
14230 copy
.strokeOffset
= offset
;
14231 copy
.strokeCount
= path
.nstroke
;
14232 memcpy(&gl
.verts
[offset
], path
.stroke
, NVGVertex
.sizeof
*path
.nstroke
);
14233 offset
+= path
.nstroke
;
14237 if (gl
.flags
&NVGContextFlag
.StencilStrokes
) {
14239 call.uniformOffset
= glnvg__allocFragUniforms(gl
, 2);
14240 if (call.uniformOffset
== -1) goto error
;
14241 glnvg__convertPaint(gl
, nvg__fragUniformPtr(gl
, call.uniformOffset
), paint
, scissor
, strokeWidth
, fringe
, -1.0f);
14242 glnvg__convertPaint(gl
, nvg__fragUniformPtr(gl
, call.uniformOffset
+gl
.fragSize
), paint
, scissor
, strokeWidth
, fringe
, 1.0f-0.5f/255.0f);
14245 call.uniformOffset
= glnvg__allocFragUniforms(gl
, 1);
14246 if (call.uniformOffset
== -1) goto error
;
14247 glnvg__convertPaint(gl
, nvg__fragUniformPtr(gl
, call.uniformOffset
), paint
, scissor
, strokeWidth
, fringe
, -1.0f);
14253 // We get here if call alloc was ok, but something else is not.
14254 // Roll back the last call to prevent drawing it.
14255 if (gl
.ncalls
> 0) --gl
.ncalls
;
14258 void glnvg__renderTriangles (void* uptr
, NVGCompositeOperationState compositeOperation
, NVGClipMode clipmode
, NVGPaint
* paint
, NVGscissor
* scissor
, const(NVGVertex
)* verts
, int nverts
) nothrow @trusted @nogc {
14259 if (nverts
< 1) return;
14261 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
14262 GLNVGcall
* call = glnvg__allocCall(gl
);
14263 GLNVGfragUniforms
* frag
;
14265 if (call is null) return;
14267 call.type
= GLNVG_TRIANGLES
;
14268 call.clipmode
= clipmode
;
14269 call.blendFunc
= glnvg__buildBlendFunc(compositeOperation
);
14270 call.image
= paint
.image
.id
;
14271 if (call.image
> 0) glnvg__renderTextureIncRef(uptr
, call.image
);
14273 // Allocate vertices for all the paths.
14274 call.triangleOffset
= glnvg__allocVerts(gl
, nverts
);
14275 if (call.triangleOffset
== -1) goto error
;
14276 call.triangleCount
= nverts
;
14278 memcpy(&gl
.verts
[call.triangleOffset
], verts
, NVGVertex
.sizeof
*nverts
);
14281 call.uniformOffset
= glnvg__allocFragUniforms(gl
, 1);
14282 if (call.uniformOffset
== -1) goto error
;
14283 frag
= nvg__fragUniformPtr(gl
, call.uniformOffset
);
14284 glnvg__convertPaint(gl
, frag
, paint
, scissor
, 1.0f, 1.0f, -1.0f);
14285 frag
.type
= NSVG_SHADER_IMG
;
14290 // We get here if call alloc was ok, but something else is not.
14291 // Roll back the last call to prevent drawing it.
14292 if (gl
.ncalls
> 0) --gl
.ncalls
;
14295 void glnvg__renderDelete (void* uptr
) nothrow @trusted @nogc {
14296 GLNVGcontext
* gl
= cast(GLNVGcontext
*)uptr
;
14297 if (gl
is null) return;
14299 glnvg__killFBOs(gl
);
14300 glnvg__deleteShader(&gl
.shader
);
14301 glnvg__deleteShader(&gl
.shaderFillFBO
);
14302 glnvg__deleteShader(&gl
.shaderCopyFBO
);
14304 if (gl
.vertBuf
!= 0) glDeleteBuffers(1, &gl
.vertBuf
);
14306 foreach (ref GLNVGtexture tex
; gl
.textures
[0..gl
.ntextures
]) {
14307 if (tex
.id
!= 0 && (tex
.flags
&NVGImageFlag
.NoDelete
) == 0) {
14308 assert(tex
.tex
!= 0);
14309 glDeleteTextures(1, &tex
.tex
);
14323 /** Creates NanoVega contexts for OpenGL2+.
14325 * Specify creation flags as additional arguments, like this:
14326 * `nvgCreateContext(NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes);`
14328 * If you won't specify any flags, defaults will be used:
14329 * `[NVGContextFlag.Antialias, NVGContextFlag.StencilStrokes]`.
14331 * Group: context_management
14333 public NVGContext
nvgCreateContext (const(NVGContextFlag
)[] flagList
...) nothrow @trusted @nogc {
14335 enum DefaultFlags
= NVGContextFlag
.Antialias|NVGContextFlag
.StencilStrokes|NVGContextFlag
.FontNoAA
;
14337 enum DefaultFlags
= NVGContextFlag
.Antialias|NVGContextFlag
.StencilStrokes
;
14340 if (flagList
.length
!= 0) {
14341 foreach (immutable flg
; flagList
) flags |
= (flg
!= NVGContextFlag
.Default ? flg
: DefaultFlags
);
14343 flags
= DefaultFlags
;
14345 NVGparams params
= void;
14346 NVGContext ctx
= null;
14347 version(nanovg_builtin_opengl_bindings
) nanovgInitOpenGL(); // why not?
14348 GLNVGcontext
* gl
= cast(GLNVGcontext
*)malloc(GLNVGcontext
.sizeof
);
14349 if (gl
is null) goto error
;
14350 memset(gl
, 0, GLNVGcontext
.sizeof
);
14352 memset(¶ms
, 0, params
.sizeof
);
14353 params
.renderCreate
= &glnvg__renderCreate
;
14354 params
.renderCreateTexture
= &glnvg__renderCreateTexture
;
14355 params
.renderTextureIncRef
= &glnvg__renderTextureIncRef
;
14356 params
.renderDeleteTexture
= &glnvg__renderDeleteTexture
;
14357 params
.renderUpdateTexture
= &glnvg__renderUpdateTexture
;
14358 params
.renderGetTextureSize
= &glnvg__renderGetTextureSize
;
14359 params
.renderViewport
= &glnvg__renderViewport
;
14360 params
.renderCancel
= &glnvg__renderCancel
;
14361 params
.renderFlush
= &glnvg__renderFlush
;
14362 params
.renderPushClip
= &glnvg__renderPushClip
;
14363 params
.renderPopClip
= &glnvg__renderPopClip
;
14364 params
.renderResetClip
= &glnvg__renderResetClip
;
14365 params
.renderFill
= &glnvg__renderFill
;
14366 params
.renderStroke
= &glnvg__renderStroke
;
14367 params
.renderTriangles
= &glnvg__renderTriangles
;
14368 params
.renderSetAffine
= &glnvg__renderSetAffine
;
14369 params
.renderDelete
= &glnvg__renderDelete
;
14370 params
.userPtr
= gl
;
14371 params
.edgeAntiAlias
= (flags
&NVGContextFlag
.Antialias ?
true : false);
14372 if (flags
&(NVGContextFlag
.FontAA|NVGContextFlag
.FontNoAA
)) {
14373 params
.fontAA
= (flags
&NVGContextFlag
.FontNoAA ? NVG_INVERT_FONT_AA
: !NVG_INVERT_FONT_AA
);
14375 params
.fontAA
= NVG_INVERT_FONT_AA
;
14381 ctx
= createInternal(¶ms
);
14382 if (ctx
is null) goto error
;
14384 static if (__VERSION__
< 2076) {
14385 DGNoThrowNoGC(() { import core
.thread
; gl
.mainTID
= Thread
.getThis
.id
; })();
14387 try { import core
.thread
; gl
.mainTID
= Thread
.getThis
.id
; } catch (Exception e
) {}
14393 // 'gl' is freed by nvgDeleteInternal.
14394 if (ctx
!is null) ctx
.deleteInternal();
14398 /// Create NanoVega OpenGL image from texture id.
14400 public int glCreateImageFromHandleGL2 (NVGContext ctx
, GLuint textureId
, int w
, int h
, int imageFlags
) nothrow @trusted @nogc {
14401 GLNVGcontext
* gl
= cast(GLNVGcontext
*)ctx
.internalParams().userPtr
;
14402 GLNVGtexture
* tex
= glnvg__allocTexture(gl
);
14404 if (tex
is null) return 0;
14406 tex
.type
= NVGtexture
.RGBA
;
14407 tex
.tex
= textureId
;
14408 tex
.flags
= imageFlags
;
14415 /// Returns OpenGL texture id for NanoVega image.
14417 public GLuint
glImageHandleGL2 (NVGContext ctx
, int image
) nothrow @trusted @nogc {
14418 GLNVGcontext
* gl
= cast(GLNVGcontext
*)ctx
.internalParams().userPtr
;
14419 GLNVGtexture
* tex
= glnvg__findTexture(gl
, image
);
14424 // ////////////////////////////////////////////////////////////////////////// //
14427 static if (NanoVegaHasFontConfig
) {
14428 version(nanovg_builtin_fontconfig_bindings
) {
14429 pragma(lib
, "fontconfig");
14431 private extern(C
) nothrow @trusted @nogc {
14432 enum FC_FILE
= "file"; /* String */
14433 alias FcBool
= int;
14434 alias FcChar8
= char;
14437 alias FcMatchKind
= int;
14438 enum : FcMatchKind
{
14443 alias FcResult
= int;
14447 FcResultTypeMismatch
,
14449 FcResultOutOfMemory
14452 FcBool
FcConfigSubstituteWithPat (FcConfig
* config
, FcPattern
* p
, FcPattern
* p_pat
, FcMatchKind kind
);
14453 void FcDefaultSubstitute (FcPattern
* pattern
);
14454 FcBool
FcConfigSubstitute (FcConfig
* config
, FcPattern
* p
, FcMatchKind kind
);
14455 FcPattern
* FcFontMatch (FcConfig
* config
, FcPattern
* p
, FcResult
* result
);
14456 FcPattern
* FcNameParse (const(FcChar8
)* name
);
14457 void FcPatternDestroy (FcPattern
* p
);
14458 FcResult
FcPatternGetString (const(FcPattern
)* p
, const(char)* object
, int n
, FcChar8
** s
);
14462 __gshared
bool fontconfigAvailable
= false;
14463 // initialize fontconfig
14464 shared static this () {
14466 fontconfigAvailable
= true;
14468 import core
.stdc
.stdio
: stderr
, fprintf
;
14469 stderr
.fprintf("***NanoVega WARNING: cannot init fontconfig!\n");
14475 // ////////////////////////////////////////////////////////////////////////// //
14476 public enum BaphometDims
= 512.0f; // baphomet icon is 512x512 ([0..511])
14478 private static immutable ubyte[7641] baphometPath
= [
14479 0x01,0x04,0x06,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x08,0xa0,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,
14480 0x00,0x80,0xff,0x43,0xa2,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x08,0x00,0x80,0xff,
14481 0x43,0x7a,0x89,0xe5,0x42,0xa0,0x1d,0xc6,0x43,0x00,0x00,0x00,0x00,0x30,0x89,0x7f,0x43,0x00,0x00,0x00,
14482 0x00,0x08,0x7a,0x89,0xe5,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x89,0xe5,0x42,0x00,0x00,
14483 0x00,0x00,0x30,0x89,0x7f,0x43,0x08,0x00,0x00,0x00,0x00,0xa2,0x1d,0xc6,0x43,0x7a,0x89,0xe5,0x42,0x00,
14484 0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x09,0x06,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
14485 0x43,0x08,0x16,0x68,0xb3,0x43,0x72,0x87,0xdd,0x43,0x71,0x87,0xdd,0x43,0x17,0x68,0xb3,0x43,0x71,0x87,
14486 0xdd,0x43,0x30,0x89,0x7f,0x43,0x08,0x71,0x87,0xdd,0x43,0xd2,0x2f,0x18,0x43,0x16,0x68,0xb3,0x43,0x35,
14487 0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x35,0xe2,0x87,0x42,0x08,0xd1,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,
14488 0x35,0xe2,0x87,0x42,0xd2,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x08,0x35,0xe2,0x87,
14489 0x42,0x17,0x68,0xb3,0x43,0xd1,0x2f,0x18,0x43,0x72,0x87,0xdd,0x43,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
14490 0x43,0x09,0x06,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x07,0xa4,0x3f,0x7f,0x43,0x0b,0x86,0xdc,0x43,
14491 0x07,0x6c,0xb9,0xb2,0x43,0xe8,0xd1,0xca,0x42,0x07,0x6e,0x4d,0xa0,0x42,0xa9,0x10,0x9c,0x43,0x07,0xb7,
14492 0x40,0xd7,0x43,0xa9,0x10,0x9c,0x43,0x07,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x09,0x06,0x98,0x42,
14493 0x74,0x43,0xb1,0x8d,0x68,0x43,0x08,0xd7,0x24,0x79,0x43,0xba,0x83,0x6e,0x43,0xa9,0x16,0x7c,0x43,0x56,
14494 0xa1,0x76,0x43,0x74,0x2a,0x7d,0x43,0x44,0x73,0x80,0x43,0x08,0x55,0xd1,0x7e,0x43,0xe3,0xea,0x76,0x43,
14495 0xbc,0x18,0x81,0x43,0x7f,0xa8,0x6e,0x43,0x8f,0x0a,0x84,0x43,0x02,0xfc,0x68,0x43,0x09,0x06,0x92,0x29,
14496 0x8d,0x43,0x73,0xc3,0x67,0x43,0x08,0xa4,0xd9,0x8e,0x43,0xf2,0xa6,0x7a,0x43,0x8f,0x22,0x88,0x43,0x75,
14497 0x2a,0x7d,0x43,0x42,0x7f,0x82,0x43,0x08,0xc8,0x88,0x43,0x09,0x06,0xc1,0x79,0x74,0x43,0x50,0x64,0x89,
14498 0x43,0x08,0x68,0x2d,0x72,0x43,0xee,0x21,0x81,0x43,0xcd,0x97,0x55,0x43,0xe6,0xf1,0x7b,0x43,0x91,0xec,
14499 0x5d,0x43,0xa8,0xc7,0x6a,0x43,0x09,0x06,0xfa,0xa5,0x52,0x43,0x60,0x97,0x7c,0x43,0x08,0x19,0xff,0x50,
14500 0x43,0xe9,0x6e,0x8a,0x43,0xb0,0xbd,0x70,0x43,0x4c,0x51,0x82,0x43,0x04,0xeb,0x69,0x43,0x66,0x0f,0x8e,
14501 0x43,0x09,0x06,0x17,0xbf,0x71,0x43,0x2c,0x58,0x94,0x43,0x08,0x1c,0x96,0x6e,0x43,0x61,0x68,0x99,0x43,
14502 0x2d,0x3a,0x6e,0x43,0xc8,0x81,0x9e,0x43,0xb7,0x9b,0x72,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0x30,0xdb,
14503 0x82,0x43,0xdb,0xe9,0x93,0x43,0x08,0x11,0x82,0x84,0x43,0x61,0x68,0x99,0x43,0xe8,0x4a,0x84,0x43,0x8e,
14504 0xa6,0x9e,0x43,0x42,0x7f,0x82,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0xc4,0x02,0x85,0x43,0xd1,0x0b,0x92,
14505 0x43,0x08,0xd6,0xb2,0x86,0x43,0x34,0x1e,0x92,0x43,0x4f,0x58,0x87,0x43,0xa4,0xf1,0x92,0x43,0x03,0xd9,
14506 0x87,0x43,0x7b,0xc6,0x94,0x43,0x09,0x06,0x87,0x3e,0x64,0x43,0x31,0x3b,0x93,0x43,0x08,0x3b,0xbf,0x64,
14507 0x43,0x6f,0xf9,0x91,0x43,0x96,0x0b,0x67,0x43,0xc5,0x4a,0x91,0x43,0xcf,0xfe,0x6a,0x43,0x31,0x2f,0x91,
14508 0x43,0x09,0x06,0x16,0x74,0xb5,0x43,0x08,0xec,0x8e,0x43,0x08,0x1b,0x4b,0xb2,0x43,0xee,0x5d,0x8b,0x43,
14509 0x48,0x4d,0xad,0x43,0x12,0xa6,0x8a,0x43,0xf3,0xd7,0xa7,0x43,0x74,0xb8,0x8a,0x43,0x08,0x8c,0xb2,0xa0,
14510 0x43,0xcd,0xf8,0x8a,0x43,0x68,0x46,0x9b,0x43,0x79,0x8f,0x87,0x43,0x49,0xc9,0x96,0x43,0xe9,0x3e,0x82,
14511 0x43,0x08,0x60,0x5c,0x97,0x43,0xa1,0xde,0x8b,0x43,0x4e,0xa0,0x93,0x43,0x31,0x3b,0x93,0x43,0x9f,0xea,
14512 0x8d,0x43,0x27,0x8d,0x99,0x43,0x08,0x07,0xe0,0x8c,0x43,0x06,0x34,0x9b,0x43,0x38,0xe9,0x8c,0x43,0x46,
14513 0x0a,0x9e,0x43,0x3d,0xcc,0x8b,0x43,0xb2,0x06,0xa2,0x43,0x08,0xf1,0x40,0x8a,0x43,0xb0,0x12,0xa4,0x43,
14514 0x39,0xd1,0x88,0x43,0x76,0x43,0xa6,0x43,0xfa,0x06,0x88,0x43,0xa4,0x75,0xa9,0x43,0x08,0x19,0x6c,0x88,
14515 0x43,0x9f,0x9e,0xac,0x43,0x66,0xeb,0x87,0x43,0x44,0x76,0xb0,0x43,0x6b,0xce,0x86,0x43,0x3b,0xbc,0xb4,
14516 0x43,0x08,0xa9,0x8c,0x85,0x43,0x06,0xd0,0xb5,0x43,0xfa,0xee,0x83,0x43,0x74,0xa3,0xb6,0x43,0x3d,0x90,
14517 0x81,0x43,0x31,0xf6,0xb6,0x43,0x08,0x9d,0x61,0x7d,0x43,0xee,0x48,0xb7,0x43,0x3b,0x1f,0x75,0x43,0xcf,
14518 0xe3,0xb6,0x43,0xee,0x6f,0x6d,0x43,0x68,0xe2,0xb5,0x43,0x08,0xd4,0xed,0x6b,0x43,0x87,0x2f,0xb2,0x43,
14519 0x0e,0xc9,0x6b,0x43,0xa7,0x7c,0xae,0x43,0x98,0xfa,0x67,0x43,0xab,0x53,0xab,0x43,0x08,0x25,0x2c,0x64,
14520 0x43,0x33,0xa2,0xa8,0x43,0x40,0x96,0x61,0x43,0xc3,0xc2,0xa5,0x43,0x64,0xde,0x60,0x43,0xfa,0xa2,0xa2,
14521 0x43,0x08,0xb0,0x5d,0x60,0x43,0x06,0x4c,0x9f,0x43,0x9a,0xca,0x5f,0x43,0x38,0x3d,0x9b,0x43,0x3b,0x8f,
14522 0x5c,0x43,0x85,0xb0,0x98,0x43,0x08,0x42,0x36,0x51,0x43,0x3d,0xf0,0x91,0x43,0xcd,0x4f,0x49,0x43,0xdb,
14523 0xb9,0x8b,0x43,0xe0,0xdb,0x44,0x43,0x42,0x8b,0x84,0x43,0x08,0x7e,0xc9,0x44,0x43,0x8a,0x57,0x8d,0x43,
14524 0xbc,0x6c,0x0f,0x43,0x23,0x62,0x8e,0x43,0xf5,0x17,0x07,0x43,0xc5,0x3e,0x8f,0x43,0x09,0x06,0xe0,0xea,
14525 0x76,0x43,0xab,0xef,0xc5,0x43,0x08,0x12,0x00,0x79,0x43,0xab,0xcb,0xbf,0x43,0x79,0xb9,0x6d,0x43,0x7e,
14526 0x8d,0xba,0x43,0xee,0x6f,0x6d,0x43,0x98,0xeb,0xb5,0x43,0x08,0xe0,0x02,0x7b,0x43,0x5f,0x1c,0xb8,0x43,
14527 0x85,0x2c,0x82,0x43,0xe9,0x65,0xb8,0x43,0xd6,0xb2,0x86,0x43,0xc6,0x05,0xb5,0x43,0x08,0x03,0xcd,0x85,
14528 0x43,0x5a,0x39,0xb9,0x43,0xe4,0x4f,0x81,0x43,0xdb,0xd4,0xbf,0x43,0xdf,0x6c,0x82,0x43,0xbc,0x93,0xc5,
14529 0x43,0x09,0x06,0xf0,0xd0,0x22,0x43,0x5d,0x19,0x08,0x43,0x08,0xbc,0xab,0x49,0x43,0x4a,0x35,0x29,0x43,
14530 0xcb,0xf7,0x65,0x43,0xce,0x37,0x45,0x43,0x0e,0x99,0x63,0x43,0x67,0xc6,0x5c,0x43,0x09,0x06,0x05,0x94,
14531 0xab,0x43,0xc2,0x13,0x04,0x43,0x08,0x9f,0x26,0x98,0x43,0x11,0x42,0x25,0x43,0x97,0x00,0x8a,0x43,0x32,
14532 0x32,0x41,0x43,0xf5,0x2f,0x8b,0x43,0xc7,0xc0,0x58,0x43,0x09,0x06,0x8f,0x85,0x48,0x43,0xe0,0xa8,0x8c,
14533 0x43,0x08,0x55,0xaa,0x48,0x43,0xe0,0xa8,0x8c,0x43,0x6b,0x3d,0x49,0x43,0xc1,0x43,0x8c,0x43,0x31,0x62,
14534 0x49,0x43,0xc1,0x43,0x8c,0x43,0x08,0x2f,0xe3,0x2f,0x43,0xad,0xe7,0x98,0x43,0xff,0x0d,0x0d,0x43,0xad,
14535 0xf3,0x9a,0x43,0xf0,0xaf,0xcc,0x42,0x74,0x00,0x97,0x43,0x08,0xbb,0xa2,0xf7,0x42,0x93,0x4d,0x93,0x43,
14536 0x5e,0x19,0x08,0x43,0x5a,0x2a,0x87,0x43,0x23,0x6e,0x10,0x43,0x42,0x97,0x86,0x43,0x08,0xca,0xe8,0x33,
14537 0x43,0x1b,0x3c,0x80,0x43,0x80,0xe8,0x4d,0x43,0xda,0xf4,0x70,0x43,0xae,0x0e,0x4f,0x43,0x2b,0x1b,0x65,
14538 0x43,0x08,0x66,0x96,0x54,0x43,0xa3,0xe1,0x3b,0x43,0x4e,0xc4,0x19,0x43,0xa0,0x1a,0x16,0x43,0x10,0xe2,
14539 0x14,0x43,0x26,0x14,0xe0,0x42,0x08,0x5c,0x91,0x1c,0x43,0xcb,0x27,0xee,0x42,0xa9,0x40,0x24,0x43,0x71,
14540 0x3b,0xfc,0x42,0xf3,0xef,0x2b,0x43,0x8b,0x27,0x05,0x43,0x08,0xe2,0x4b,0x2c,0x43,0x48,0x86,0x07,0x43,
14541 0x79,0x62,0x2f,0x43,0x05,0xe5,0x09,0x43,0x55,0x32,0x34,0x43,0xa0,0xd2,0x09,0x43,0x08,0x74,0xa3,0x36,
14542 0x43,0x3a,0xd1,0x08,0x43,0x7e,0x81,0x38,0x43,0x09,0xd4,0x0a,0x43,0x0d,0xba,0x39,0x43,0xa0,0xea,0x0d,
14543 0x43,0x08,0x6f,0xe4,0x3d,0x43,0x43,0xc7,0x0e,0x43,0xd6,0xe5,0x3e,0x43,0xc4,0x4a,0x11,0x43,0x55,0x7a,
14544 0x40,0x43,0x59,0x72,0x13,0x43,0x08,0x55,0x92,0x44,0x43,0xbf,0x73,0x14,0x43,0x23,0x95,0x46,0x43,0xa5,
14545 0x09,0x17,0x43,0xe0,0xf3,0x48,0x43,0xfe,0x55,0x19,0x43,0x08,0xcd,0x4f,0x49,0x43,0xaa,0x10,0x1c,0x43,
14546 0x61,0x77,0x4b,0x43,0xfe,0x6d,0x1d,0x43,0x80,0xe8,0x4d,0x43,0x2b,0x94,0x1e,0x43,0x08,0x58,0xc9,0x51,
14547 0x43,0x41,0x27,0x1f,0x43,0x9b,0x82,0x53,0x43,0x35,0x72,0x20,0x43,0x53,0xf2,0x54,0x43,0x88,0xcf,0x21,
14548 0x43,0x08,0x7b,0x29,0x55,0x43,0xe8,0x0a,0x25,0x43,0xb2,0x2d,0x58,0x43,0xef,0xe8,0x26,0x43,0x9b,0xb2,
14549 0x5b,0x43,0xd0,0x8f,0x28,0x43,0x08,0x5f,0xef,0x5f,0x43,0xeb,0x11,0x2a,0x43,0xfd,0xdc,0x5f,0x43,0x6e,
14550 0x95,0x2c,0x43,0x3b,0xa7,0x60,0x43,0x2b,0xf4,0x2e,0x43,0x08,0x06,0xbb,0x61,0x43,0xfd,0xe5,0x31,0x43,
14551 0xe7,0x61,0x63,0x43,0xef,0x30,0x33,0x43,0x53,0x52,0x65,0x43,0xa3,0xb1,0x33,0x43,0x08,0x12,0xa0,0x68,
14552 0x43,0x7f,0x69,0x34,0x43,0x40,0xc6,0x69,0x43,0x64,0xff,0x36,0x43,0x7e,0x90,0x6a,0x43,0x71,0xcc,0x39,
14553 0x43,0x08,0xbc,0x5a,0x6b,0x43,0x51,0x73,0x3b,0x43,0xc1,0x49,0x6c,0x43,0xa5,0xd0,0x3c,0x43,0xe0,0xba,
14554 0x6e,0x43,0xb8,0x74,0x3c,0x43,0x08,0x6b,0x1c,0x73,0x43,0x13,0xc1,0x3e,0x43,0x40,0xf6,0x71,0x43,0xce,
14555 0x1f,0x41,0x43,0x55,0x89,0x72,0x43,0x8d,0x7e,0x43,0x43,0x08,0x68,0x2d,0x72,0x43,0x89,0xae,0x4b,0x43,
14556 0xc1,0x79,0x74,0x43,0xcb,0x78,0x4c,0x43,0x55,0xa1,0x76,0x43,0x5b,0xb1,0x4d,0x43,0x08,0xa2,0x38,0x7a,
14557 0x43,0xd1,0x56,0x4e,0x43,0x85,0xb6,0x78,0x43,0xb1,0x15,0x54,0x43,0x83,0xc7,0x77,0x43,0x89,0x0e,0x5c,
14558 0x43,0x08,0xcf,0x46,0x77,0x43,0x0f,0x81,0x5f,0x43,0x1a,0xde,0x7a,0x43,0xce,0xc7,0x5d,0x43,0x42,0x73,
14559 0x80,0x43,0x99,0xc3,0x5a,0x43,0x08,0x85,0x2c,0x82,0x43,0xf6,0xe6,0x59,0x43,0x81,0x3d,0x81,0x43,0x16,
14560 0x10,0x50,0x43,0xd6,0x8e,0x80,0x43,0x5b,0x99,0x49,0x43,0x08,0xc4,0xea,0x80,0x43,0x22,0x95,0x46,0x43,
14561 0xfa,0xe2,0x81,0x43,0xda,0xec,0x43,0x43,0x78,0x77,0x83,0x43,0xe4,0xb2,0x41,0x43,0x08,0x8a,0x27,0x85,
14562 0x43,0x86,0x77,0x3e,0x43,0x0c,0x9f,0x85,0x43,0x07,0xf4,0x3b,0x43,0x8f,0x16,0x86,0x43,0xe6,0x82,0x39,
14563 0x43,0x08,0x85,0x44,0x86,0x43,0x37,0xd9,0x35,0x43,0x1e,0x4f,0x87,0x43,0xe1,0x7b,0x34,0x43,0xdf,0x90,
14564 0x88,0x43,0xb6,0x55,0x33,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0xe5,0x31,0x43,0xfa,0x12,0x8a,0x43,0xbf,
14565 0x03,0x2d,0x43,0x19,0x78,0x8a,0x43,0x45,0x5e,0x2c,0x43,0x08,0x03,0xf1,0x8b,0x43,0xac,0x47,0x29,0x43,
14566 0x2f,0x17,0x8d,0x43,0x45,0x46,0x28,0x43,0xc8,0x21,0x8e,0x43,0x30,0xb3,0x27,0x43,0x08,0xa9,0xc8,0x8f,
14567 0x43,0xef,0xe8,0x26,0x43,0xbf,0x5b,0x90,0x43,0x5b,0xc1,0x24,0x43,0x10,0xca,0x90,0x43,0xa0,0x62,0x22,
14568 0x43,0x08,0x26,0x5d,0x91,0x43,0xbb,0xcc,0x1f,0x43,0xf0,0x70,0x92,0x43,0x78,0x13,0x1e,0x43,0x77,0xd7,
14569 0x93,0x43,0x73,0x24,0x1d,0x43,0x08,0x65,0x3f,0x96,0x43,0xce,0x58,0x1b,0x43,0xbe,0x7f,0x96,0x43,0xbf,
14570 0x8b,0x18,0x43,0x60,0x5c,0x97,0x43,0xb6,0xad,0x16,0x43,0x08,0xba,0xa8,0x99,0x43,0x78,0xcb,0x11,0x43,
14571 0x49,0xe1,0x9a,0x43,0x78,0xcb,0x11,0x43,0x01,0x51,0x9c,0x43,0x73,0xdc,0x10,0x43,0x08,0x72,0x24,0x9d,
14572 0x43,0xd2,0xff,0x0f,0x43,0x1c,0xd3,0x9d,0x43,0x07,0xec,0x0e,0x43,0xeb,0xc9,0x9d,0x43,0xe8,0x7a,0x0c,
14573 0x43,0x08,0x60,0x80,0x9d,0x43,0xd7,0xbe,0x08,0x43,0x4d,0xe8,0x9f,0x43,0x86,0x50,0x08,0x43,0x25,0xbd,
14574 0xa1,0x43,0x5b,0x2a,0x07,0x43,0x08,0x99,0x7f,0xa3,0x43,0xc9,0xf1,0x05,0x43,0x48,0x1d,0xa5,0x43,0x86,
14575 0x38,0x04,0x43,0x6c,0x71,0xa6,0x43,0x18,0x59,0x01,0x43,0x08,0x32,0x96,0xa6,0x43,0x6e,0x64,0xff,0x42,
14576 0x48,0x29,0xa7,0x43,0xed,0xcf,0xfd,0x42,0x5f,0xbc,0xa7,0x43,0x71,0x3b,0xfc,0x42,0x08,0xf3,0xe3,0xa9,
14577 0x43,0xf7,0x7d,0xf7,0x42,0xd8,0x6d,0xaa,0x43,0x45,0xe5,0xf2,0x42,0x48,0x41,0xab,0x43,0xcb,0x27,0xee,
14578 0x42,0x08,0x24,0xf9,0xab,0x43,0x52,0x6a,0xe9,0x42,0xee,0x0c,0xad,0x43,0x4c,0x8c,0xe7,0x42,0x1b,0x33,
14579 0xae,0x43,0xcc,0xf7,0xe5,0x42,0x08,0xaa,0x6b,0xaf,0x43,0xe8,0x61,0xe3,0x42,0x90,0xf5,0xaf,0x43,0xc9,
14580 0xf0,0xe0,0x42,0xe0,0x63,0xb0,0x43,0xe5,0x5a,0xde,0x42,0x08,0xaa,0x83,0xb3,0x43,0x29,0x2d,0x09,0x43,
14581 0x6a,0xfe,0x8e,0x43,0xb8,0x74,0x3c,0x43,0xd5,0x06,0x95,0x43,0xe6,0x79,0x67,0x43,0x08,0x2f,0x53,0x97,
14582 0x43,0xe9,0xb0,0x74,0x43,0xa8,0x28,0xa0,0x43,0x43,0xfd,0x76,0x43,0x83,0x28,0xad,0x43,0x17,0x59,0x81,
14583 0x43,0x08,0x3d,0xe7,0xbf,0x43,0x4b,0x8d,0x8c,0x43,0xae,0x96,0xba,0x43,0x66,0x27,0x92,0x43,0x15,0xe0,
14584 0xc7,0x43,0x6f,0x11,0x96,0x43,0x08,0x7e,0x5d,0xb2,0x43,0xdb,0x01,0x98,0x43,0x9e,0x56,0xa0,0x43,0x80,
14585 0xc1,0x97,0x43,0x69,0x2e,0x97,0x43,0x31,0x17,0x8d,0x43,0x09,0x06,0xab,0xa7,0x39,0x43,0x67,0x0f,0x0e,
14586 0x43,0x08,0xdb,0xbc,0x3b,0x43,0xe8,0x92,0x10,0x43,0xb5,0x85,0x3b,0x43,0x97,0x3c,0x14,0x43,0xab,0xa7,
14587 0x39,0x43,0x0c,0x0b,0x18,0x43,0x09,0x06,0xca,0x30,0x40,0x43,0x30,0x3b,0x13,0x43,0x08,0x17,0xc8,0x43,
14588 0x43,0xa5,0x09,0x17,0x43,0x7e,0xc9,0x44,0x43,0x1a,0xd8,0x1a,0x43,0x9d,0x22,0x43,0x43,0x8d,0xa6,0x1e,
14589 0x43,0x09,0x06,0xc8,0x78,0x4c,0x43,0xed,0xc9,0x1d,0x43,0x08,0x0b,0x32,0x4e,0x43,0x22,0xce,0x20,0x43,
14590 0x23,0xc5,0x4e,0x43,0x58,0xd2,0x23,0x43,0x0b,0x32,0x4e,0x43,0x2b,0xc4,0x26,0x43,0x09,0x06,0xec,0x08,
14591 0x58,0x43,0xc7,0xb1,0x26,0x43,0x08,0x02,0x9c,0x58,0x43,0xef,0x00,0x2b,0x43,0xd9,0x64,0x58,0x43,0x02,
14592 0xbd,0x2e,0x43,0x10,0x51,0x57,0x43,0x37,0xc1,0x31,0x43,0x09,0x06,0xcb,0xdf,0x61,0x43,0x4a,0x65,0x31,
14593 0x43,0x08,0xbe,0x2a,0x63,0x43,0xbd,0x33,0x35,0x43,0x32,0xe1,0x62,0x43,0x56,0x4a,0x38,0x43,0xde,0x83,
14594 0x61,0x43,0x3c,0xe0,0x3a,0x43,0x09,0x06,0x1c,0x7e,0x6a,0x43,0x5b,0x39,0x39,0x43,0x08,0x31,0x11,0x6b,
14595 0x43,0x0c,0xd2,0x3d,0x43,0x1c,0x7e,0x6a,0x43,0x13,0xd9,0x42,0x43,0xd9,0xc4,0x68,0x43,0xcb,0x60,0x48,
14596 0x43,0x09,0x06,0xe5,0xc1,0x73,0x43,0x16,0xf8,0x4b,0x43,0x08,0xa6,0xf7,0x72,0x43,0xb1,0xfd,0x4f,0x43,
14597 0x3b,0x07,0x71,0x43,0x4a,0x14,0x53,0x43,0xa2,0xf0,0x6d,0x43,0x7c,0x29,0x55,0x43,0x09,0x06,0x00,0x8d,
14598 0xa6,0x43,0xef,0x21,0x01,0x43,0x08,0x52,0xfb,0xa6,0x43,0xce,0xc8,0x02,0x43,0xe6,0x16,0xa7,0x43,0x51,
14599 0x4c,0x05,0x43,0x3b,0x68,0xa6,0x43,0x4c,0x75,0x08,0x43,0x09,0x06,0xde,0x20,0xa1,0x43,0x86,0x50,0x08,
14600 0x43,0x08,0xd4,0x4e,0xa1,0x43,0xd3,0xe7,0x0b,0x43,0xb5,0xe9,0xa0,0x43,0x59,0x5a,0x0f,0x43,0xba,0xcc,
14601 0x9f,0x43,0x54,0x83,0x12,0x43,0x09,0x06,0x77,0xfb,0x99,0x43,0x6c,0x16,0x13,0x43,0x08,0xde,0xfc,0x9a,
14602 0x43,0x4a,0xbd,0x14,0x43,0x06,0x34,0x9b,0x43,0xfe,0x55,0x19,0x43,0x13,0xe9,0x99,0x43,0x41,0x27,0x1f,
14603 0x43,0x09,0x06,0x46,0xce,0x93,0x43,0x26,0xa5,0x1d,0x43,0x08,0xe7,0xaa,0x94,0x43,0xbb,0xcc,0x1f,0x43,
14604 0x18,0xb4,0x94,0x43,0xa8,0x40,0x24,0x43,0xe2,0xbb,0x93,0x43,0x21,0xfe,0x28,0x43,0x09,0x06,0xb1,0x8e,
14605 0x8d,0x43,0xa8,0x58,0x28,0x43,0x08,0x19,0x90,0x8e,0x43,0x54,0x13,0x2b,0x43,0xa4,0xd9,0x8e,0x43,0x84,
14606 0x40,0x31,0x43,0x46,0xaa,0x8d,0x43,0x29,0x24,0x37,0x43,0x09,0x06,0xd6,0xbe,0x88,0x43,0xef,0x30,0x33,
14607 0x43,0x08,0x0c,0xb7,0x89,0x43,0x0e,0xa2,0x35,0x43,0xc0,0x37,0x8a,0x43,0x7a,0xaa,0x3b,0x43,0xbb,0x48,
14608 0x89,0x43,0xbb,0x7b,0x41,0x43,0x09,0x06,0x3a,0xad,0x82,0x43,0xc4,0x59,0x43,0x43,0x08,0xd2,0xb7,0x83,
14609 0x43,0x2b,0x5b,0x44,0x43,0x35,0xd6,0x85,0x43,0x48,0xf5,0x49,0x43,0x42,0x97,0x86,0x43,0xc4,0xa1,0x4f,
14610 0x43,0x09,0x06,0x9c,0xb3,0x80,0x43,0x48,0x55,0x5a,0x43,0x08,0xff,0xc5,0x80,0x43,0x09,0x73,0x55,0x43,
14611 0x93,0xe1,0x80,0x43,0x0f,0x39,0x53,0x43,0xf1,0xbe,0x7e,0x43,0x18,0xe7,0x4c,0x43,0x09,0x06,0xe0,0x02,
14612 0x7b,0x43,0x92,0xec,0x5d,0x43,0x08,0x09,0x3a,0x7b,0x43,0xf0,0xf7,0x58,0x43,0x09,0x3a,0x7b,0x43,0xe6,
14613 0x31,0x5b,0x43,0xe0,0x02,0x7b,0x43,0xa8,0x4f,0x56,0x43,0x09,0x06,0x39,0x4f,0x7d,0x43,0x3e,0x8f,0x5c,
14614 0x43,0x08,0xe9,0xe0,0x7c,0x43,0x03,0x9c,0x58,0x43,0x1e,0x2b,0x81,0x43,0x7f,0x30,0x5a,0x43,0xff,0x73,
14615 0x7d,0x43,0xf6,0xb6,0x51,0x43,0x09,0x06,0x5c,0xb8,0x52,0x43,0x28,0x21,0x87,0x43,0x08,0xae,0x3e,0x57,
14616 0x43,0x12,0x9a,0x88,0x43,0x23,0xf5,0x56,0x43,0x04,0xf1,0x8b,0x43,0x25,0xfc,0x5b,0x43,0x85,0x74,0x8e,
14617 0x43,0x08,0x2f,0xf2,0x61,0x43,0x8e,0x52,0x90,0x43,0xd9,0xdc,0x6c,0x43,0x85,0x74,0x8e,0x43,0xc6,0x20,
14618 0x69,0x43,0x3d,0xd8,0x8d,0x43,0x08,0x6d,0x8c,0x5a,0x43,0xf5,0x3b,0x8d,0x43,0x3d,0x77,0x58,0x43,0xa1,
14619 0xc6,0x87,0x43,0xf8,0xed,0x5e,0x43,0x5e,0x0d,0x86,0x43,0x09,0x06,0xde,0xcc,0x92,0x43,0xf7,0x17,0x87,
14620 0x43,0x08,0xb6,0x89,0x90,0x43,0xae,0x87,0x88,0x43,0x4a,0xa5,0x90,0x43,0xa1,0xde,0x8b,0x43,0xf9,0x2a,
14621 0x8e,0x43,0x23,0x62,0x8e,0x43,0x08,0xf5,0x2f,0x8b,0x43,0x5c,0x49,0x90,0x43,0x35,0xd6,0x85,0x43,0x8e,
14622 0x46,0x8e,0x43,0x3d,0xb4,0x87,0x43,0x47,0xaa,0x8d,0x43,0x08,0x6a,0xfe,0x8e,0x43,0xff,0x0d,0x8d,0x43,
14623 0xbb,0x6c,0x8f,0x43,0xf7,0x17,0x87,0x43,0x5c,0x31,0x8c,0x43,0xb2,0x5e,0x85,0x43,0x09,0x06,0x60,0x38,
14624 0x91,0x43,0x69,0x5d,0x7a,0x43,0x08,0x34,0x1e,0x92,0x43,0x1e,0x5b,0x89,0x43,0x04,0x63,0x7e,0x43,0x5e,
14625 0x01,0x84,0x43,0x59,0x2a,0x87,0x43,0x0d,0xcf,0x8d,0x43,0x09,0x03,0x04,0x06,0x5a,0x18,0x63,0x43,0x82,
14626 0x79,0x8b,0x43,0x08,0x25,0x2c,0x64,0x43,0x82,0x79,0x8b,0x43,0x2a,0x1b,0x65,0x43,0x9d,0xef,0x8a,0x43,
14627 0x2a,0x1b,0x65,0x43,0xc1,0x37,0x8a,0x43,0x08,0x2a,0x1b,0x65,0x43,0x17,0x89,0x89,0x43,0x25,0x2c,0x64,
14628 0x43,0x31,0xff,0x88,0x43,0x5a,0x18,0x63,0x43,0x31,0xff,0x88,0x43,0x08,0xf3,0x16,0x62,0x43,0x31,0xff,
14629 0x88,0x43,0xee,0x27,0x61,0x43,0x17,0x89,0x89,0x43,0xee,0x27,0x61,0x43,0xc1,0x37,0x8a,0x43,0x08,0xee,
14630 0x27,0x61,0x43,0x9d,0xef,0x8a,0x43,0xf3,0x16,0x62,0x43,0x82,0x79,0x8b,0x43,0x5a,0x18,0x63,0x43,0x82,
14631 0x79,0x8b,0x43,0x09,0x06,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x08,0x34,0xee,0x89,0x43,0x82,0x79,
14632 0x8b,0x43,0x85,0x5c,0x8a,0x43,0x9d,0xef,0x8a,0x43,0x85,0x5c,0x8a,0x43,0xc1,0x37,0x8a,0x43,0x08,0x85,
14633 0x5c,0x8a,0x43,0x17,0x89,0x89,0x43,0x34,0xee,0x89,0x43,0x31,0xff,0x88,0x43,0x4f,0x64,0x89,0x43,0x31,
14634 0xff,0x88,0x43,0x08,0x9c,0xe3,0x88,0x43,0x31,0xff,0x88,0x43,0x19,0x6c,0x88,0x43,0x17,0x89,0x89,0x43,
14635 0x19,0x6c,0x88,0x43,0xc1,0x37,0x8a,0x43,0x08,0x19,0x6c,0x88,0x43,0x9d,0xef,0x8a,0x43,0x9c,0xe3,0x88,
14636 0x43,0x82,0x79,0x8b,0x43,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x09,0x02,0x04,0x06,0x19,0x60,0x86,
14637 0x43,0xec,0xed,0xa3,0x43,0x08,0x35,0xd6,0x85,0x43,0x76,0x43,0xa6,0x43,0x93,0xe1,0x80,0x43,0x57,0x02,
14638 0xac,0x43,0x61,0xd8,0x80,0x43,0x87,0x17,0xae,0x43,0x08,0xa5,0x85,0x80,0x43,0xc3,0xfe,0xaf,0x43,0xce,
14639 0xbc,0x80,0x43,0x83,0x40,0xb1,0x43,0xa5,0x91,0x82,0x43,0x79,0x6e,0xb1,0x43,0x08,0x23,0x26,0x84,0x43,
14640 0x40,0x93,0xb1,0x43,0x30,0xe7,0x84,0x43,0xbe,0x1b,0xb1,0x43,0x11,0x82,0x84,0x43,0xab,0x6b,0xaf,0x43,
14641 0x08,0xb7,0x41,0x84,0x43,0x3b,0x98,0xae,0x43,0xb7,0x41,0x84,0x43,0xc3,0xf2,0xad,0x43,0xa1,0xae,0x83,
14642 0x43,0x83,0x28,0xad,0x43,0x08,0xb2,0x52,0x83,0x43,0x80,0x39,0xac,0x43,0x81,0x49,0x83,0x43,0xf0,0x00,
14643 0xab,0x43,0xe4,0x67,0x85,0x43,0x76,0x4f,0xa8,0x43,0x08,0x9c,0xd7,0x86,0x43,0xd1,0x83,0xa6,0x43,0xec,
14644 0x45,0x87,0x43,0x01,0x75,0xa2,0x43,0x19,0x60,0x86,0x43,0xec,0xed,0xa3,0x43,0x09,0x06,0xd9,0xdc,0x6c,
14645 0x43,0x14,0x25,0xa4,0x43,0x08,0xa2,0xf0,0x6d,0x43,0x9f,0x7a,0xa6,0x43,0x47,0xec,0x77,0x43,0x80,0x39,
14646 0xac,0x43,0xa9,0xfe,0x77,0x43,0xb0,0x4e,0xae,0x43,0x08,0x23,0xa4,0x78,0x43,0xea,0x35,0xb0,0x43,0xd2,
14647 0x35,0x78,0x43,0xab,0x77,0xb1,0x43,0xc1,0x79,0x74,0x43,0xa2,0xa5,0xb1,0x43,0x08,0xc6,0x50,0x71,0x43,
14648 0x68,0xca,0xb1,0x43,0xab,0xce,0x6f,0x43,0xe7,0x52,0xb1,0x43,0xea,0x98,0x70,0x43,0xd4,0xa2,0xaf,0x43,
14649 0x08,0x9d,0x19,0x71,0x43,0x96,0xd8,0xae,0x43,0x9d,0x19,0x71,0x43,0xec,0x29,0xae,0x43,0xca,0x3f,0x72,
14650 0x43,0xab,0x5f,0xad,0x43,0x08,0xa6,0xf7,0x72,0x43,0xa7,0x70,0xac,0x43,0x09,0x0a,0x73,0x43,0x17,0x38,
14651 0xab,0x43,0x44,0xcd,0x6e,0x43,0x9f,0x86,0xa8,0x43,0x08,0xd4,0xed,0x6b,0x43,0xf8,0xba,0xa6,0x43,0x31,
14652 0x11,0x6b,0x43,0x2a,0xac,0xa2,0x43,0xd9,0xdc,0x6c,0x43,0x14,0x25,0xa4,0x43,0x09,0x01,0x05,0x06,0x66,
14653 0x5d,0x7a,0x43,0x74,0xeb,0xc2,0x43,0x08,0x09,0x22,0x77,0x43,0x50,0xbb,0xc7,0x43,0xe9,0xe0,0x7c,0x43,
14654 0xf5,0x86,0xc9,0x43,0x8f,0x94,0x7a,0x43,0xc5,0x95,0xcd,0x43,0x09,0x06,0x08,0x98,0x80,0x43,0x6b,0x19,
14655 0xc3,0x43,0x08,0xb7,0x35,0x82,0x43,0x79,0xf2,0xc7,0x43,0xf1,0xbe,0x7e,0x43,0x1e,0xbe,0xc9,0x43,0x73,
14656 0x7c,0x80,0x43,0xec,0xcc,0xcd,0x43,0x09,0x06,0x28,0xab,0x7d,0x43,0xae,0xde,0xc6,0x43,0x08,0x1e,0xcd,
14657 0x7b,0x43,0x8a,0xa2,0xc9,0x43,0x30,0x89,0x7f,0x43,0x5c,0x94,0xcc,0x43,0x28,0xab,0x7d,0x43,0x42,0x2a,
14658 0xcf,0x43,0x09,0x01,0x05,0x06,0x24,0x14,0xe0,0x42,0xf5,0x77,0x97,0x43,0x08,0xf7,0x1d,0xe7,0x42,0x74,
14659 0x00,0x97,0x43,0x4d,0x93,0xec,0x42,0xdb,0xf5,0x95,0x43,0x29,0x4b,0xed,0x42,0xcd,0x34,0x95,0x43,0x09,
14660 0x06,0x29,0x7b,0xf5,0x42,0x6f,0x1d,0x98,0x43,0x08,0xe4,0xf1,0xfb,0x42,0x61,0x5c,0x97,0x43,0xdb,0x7d,
14661 0x01,0x43,0xb2,0xbe,0x95,0x43,0x55,0x23,0x02,0x43,0xe7,0xaa,0x94,0x43,0x09,0x06,0x98,0xdc,0x03,0x43,
14662 0xbe,0x8b,0x98,0x43,0x08,0x66,0xdf,0x05,0x43,0x47,0xe6,0x97,0x43,0xae,0x87,0x08,0x43,0x98,0x48,0x96,
14663 0x43,0x61,0x08,0x09,0x43,0xd6,0x06,0x95,0x43,0x09,0x06,0x31,0x0b,0x0b,0x43,0x8e,0x82,0x98,0x43,0x08,
14664 0xdb,0xc5,0x0d,0x43,0x80,0xc1,0x97,0x43,0xd6,0xee,0x10,0x43,0xa9,0xec,0x95,0x43,0x79,0xcb,0x11,0x43,
14665 0x55,0x8f,0x94,0x43,0x09,0x06,0xd1,0x2f,0x18,0x43,0xdb,0x01,0x98,0x43,0x08,0xad,0xe7,0x18,0x43,0x38,
14666 0x25,0x97,0x43,0x8a,0x9f,0x19,0x43,0x80,0xb5,0x95,0x43,0xd6,0x1e,0x19,0x43,0xe0,0xd8,0x94,0x43,0x09,
14667 0x06,0x9a,0x5b,0x1d,0x43,0x58,0x8a,0x97,0x43,0x08,0x01,0x5d,0x1e,0x43,0xf1,0x88,0x96,0x43,0x2f,0x83,
14668 0x1f,0x43,0x19,0xb4,0x94,0x43,0x19,0xf0,0x1e,0x43,0x6f,0x05,0x94,0x43,0x09,0x06,0x0b,0x53,0x24,0x43,
14669 0xae,0xdb,0x96,0x43,0x08,0x25,0xd5,0x25,0x43,0x50,0xac,0x95,0x43,0x53,0xfb,0x26,0x43,0x8a,0x7b,0x93,
14670 0x43,0x76,0x43,0x26,0x43,0xb7,0x95,0x92,0x43,0x09,0x06,0x76,0x5b,0x2a,0x43,0x47,0xda,0x95,0x43,0x08,
14671 0xf3,0xef,0x2b,0x43,0x10,0xe2,0x94,0x43,0x6d,0x95,0x2c,0x43,0xae,0xc3,0x92,0x43,0x68,0xa6,0x2b,0x43,
14672 0x47,0xc2,0x91,0x43,0x09,0x06,0x36,0xc1,0x31,0x43,0x2c,0x58,0x94,0x43,0x08,0x8c,0x1e,0x33,0x43,0x31,
14673 0x3b,0x93,0x43,0x79,0x7a,0x33,0x43,0xff,0x25,0x91,0x43,0xd9,0x9d,0x32,0x43,0xc1,0x5b,0x90,0x43,0x09,
14674 0x06,0x25,0x35,0x36,0x43,0x31,0x3b,0x93,0x43,0x08,0x3f,0xb7,0x37,0x43,0xc1,0x67,0x92,0x43,0xe0,0x93,
14675 0x38,0x43,0xae,0xb7,0x90,0x43,0x7e,0x81,0x38,0x43,0x0d,0xdb,0x8f,0x43,0x09,0x06,0xb5,0x85,0x3b,0x43,
14676 0xe4,0xaf,0x91,0x43,0x08,0xcf,0x07,0x3d,0x43,0x9d,0x13,0x91,0x43,0xbc,0x63,0x3d,0x43,0x47,0xb6,0x8f,
14677 0x43,0xe5,0x9a,0x3d,0x43,0x74,0xd0,0x8e,0x43,0x09,0x06,0xae,0xc6,0x42,0x43,0xa4,0xd9,0x8e,0x43,0x08,
14678 0xca,0x48,0x44,0x43,0xfa,0x2a,0x8e,0x43,0xa2,0x11,0x44,0x43,0x9d,0xfb,0x8c,0x43,0x55,0x92,0x44,0x43,
14679 0x0d,0xc3,0x8b,0x43,0x09,0x06,0x39,0x10,0xc3,0x43,0x34,0x36,0x96,0x43,0x08,0x92,0x44,0xc1,0x43,0xe4,
14680 0xc7,0x95,0x43,0x6f,0xf0,0xbf,0x43,0x4b,0xbd,0x94,0x43,0x47,0xb9,0xbf,0x43,0x0b,0xf3,0x93,0x43,0x09,
14681 0x06,0x8f,0x49,0xbe,0x43,0xb7,0xad,0x96,0x43,0x08,0x11,0xb5,0xbc,0x43,0x77,0xe3,0x95,0x43,0x9c,0xf2,
14682 0xba,0x43,0xfa,0x4e,0x94,0x43,0xae,0x96,0xba,0x43,0x31,0x3b,0x93,0x43,0x09,0x06,0xdb,0xb0,0xb9,0x43,
14683 0x10,0xee,0x96,0x43,0x08,0x42,0xa6,0xb8,0x43,0xc8,0x51,0x96,0x43,0x50,0x5b,0xb7,0x43,0x19,0xb4,0x94,
14684 0x43,0xf7,0x1a,0xb7,0x43,0x58,0x72,0x93,0x43,0x09,0x06,0xf2,0x2b,0xb6,0x43,0x10,0xee,0x96,0x43,0x08,
14685 0x9d,0xce,0xb4,0x43,0x04,0x2d,0x96,0x43,0xed,0x30,0xb3,0x43,0x2c,0x58,0x94,0x43,0xce,0xcb,0xb2,0x43,
14686 0xd6,0xfa,0x92,0x43,0x09,0x06,0x5a,0x09,0xb1,0x43,0x19,0xc0,0x96,0x43,0x08,0x6c,0xad,0xb0,0x43,0x77,
14687 0xe3,0x95,0x43,0x7e,0x51,0xb0,0x43,0xc0,0x73,0x94,0x43,0xd8,0x91,0xb0,0x43,0x1e,0x97,0x93,0x43,0x09,
14688 0x06,0x48,0x4d,0xad,0x43,0xbe,0x7f,0x96,0x43,0x08,0x95,0xcc,0xac,0x43,0x58,0x7e,0x95,0x43,0x4d,0x30,
14689 0xac,0x43,0x80,0xa9,0x93,0x43,0xd8,0x79,0xac,0x43,0xd6,0xfa,0x92,0x43,0x09,0x06,0x90,0xd1,0xa9,0x43,
14690 0x14,0xd1,0x95,0x43,0x08,0x83,0x10,0xa9,0x43,0xb7,0xa1,0x94,0x43,0x3b,0x74,0xa8,0x43,0xf1,0x70,0x92,
14691 0x43,0x29,0xd0,0xa8,0x43,0x1e,0x8b,0x91,0x43,0x09,0x06,0x5a,0xcd,0xa6,0x43,0x8a,0x87,0x95,0x43,0x08,
14692 0x1c,0x03,0xa6,0x43,0x23,0x86,0x94,0x43,0x5f,0xb0,0xa5,0x43,0xc1,0x67,0x92,0x43,0xe1,0x27,0xa6,0x43,
14693 0x8a,0x6f,0x91,0x43,0x09,0x06,0xd4,0x5a,0xa3,0x43,0x2c,0x58,0x94,0x43,0x08,0x29,0xac,0xa2,0x43,0x31,
14694 0x3b,0x93,0x43,0x32,0x7e,0xa2,0x43,0xff,0x25,0x91,0x43,0x83,0xec,0xa2,0x43,0x8e,0x52,0x90,0x43,0x09,
14695 0x06,0xf8,0x96,0xa0,0x43,0x1e,0x97,0x93,0x43,0x08,0xeb,0xd5,0x9f,0x43,0x7b,0xba,0x92,0x43,0x99,0x67,
14696 0x9f,0x43,0x9d,0x13,0x91,0x43,0x99,0x67,0x9f,0x43,0xfa,0x36,0x90,0x43,0x09,0x06,0xeb,0xc9,0x9d,0x43,
14697 0xc8,0x39,0x92,0x43,0x08,0xde,0x08,0x9d,0x43,0xb2,0xa6,0x91,0x43,0xe6,0xda,0x9c,0x43,0x2c,0x40,0x90,
14698 0x43,0x52,0xbf,0x9c,0x43,0x5a,0x5a,0x8f,0x43,0x09,0x06,0x37,0x3d,0x9b,0x43,0x85,0x80,0x90,0x43,0x08,
14699 0x2a,0x7c,0x9a,0x43,0xdb,0xd1,0x8f,0x43,0xf0,0xa0,0x9a,0x43,0x7d,0xa2,0x8e,0x43,0x65,0x57,0x9a,0x43,
14700 0xee,0x69,0x8d,0x43,0x09,0x02,0x04,0x06,0x2a,0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x08,0x0d,0x8a,0x31,
14701 0x42,0x9f,0x0e,0x94,0x43,0xf3,0x1f,0x34,0x42,0x3d,0xfc,0x93,0x43,0x63,0xff,0x36,0x42,0xa9,0xe0,0x93,
14702 0x43,0x08,0xb5,0x34,0x5d,0x42,0x0b,0xf3,0x93,0x43,0x6d,0xa4,0x5e,0x42,0x03,0x39,0x98,0x43,0xe7,0x31,
14703 0x5b,0x42,0x93,0x89,0x9d,0x43,0x08,0x02,0x9c,0x58,0x42,0xd4,0x5a,0xa3,0x43,0x38,0x70,0x53,0x42,0x14,
14704 0x49,0xaa,0x43,0xf8,0xed,0x5e,0x42,0x83,0x28,0xad,0x43,0x08,0xea,0x68,0x68,0x42,0x20,0x22,0xaf,0x43,
14705 0x12,0xb8,0x6c,0x42,0xb5,0x49,0xb1,0x43,0x2a,0x4b,0x6d,0x42,0x0d,0x96,0xb3,0x43,0x07,0x2a,0x4b,0x6d,
14706 0x42,0xc6,0x05,0xb5,0x43,0x08,0x87,0x6e,0x6c,0x42,0x68,0xee,0xb7,0x43,0x1c,0x66,0x66,0x42,0x31,0x0e,
14707 0xbb,0x43,0x57,0x11,0x5e,0x42,0x8f,0x49,0xbe,0x43,0x08,0x66,0x96,0x54,0x42,0xb9,0x5c,0xb8,0x43,0x2c,
14708 0x2b,0x3c,0x42,0x68,0xd6,0xb3,0x43,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x07,0x2a,0xf4,0x2e,0x42,
14709 0x61,0xa4,0xa3,0x43,0x08,0x55,0x1a,0x30,0x42,0xf0,0xd0,0xa2,0x43,0xf8,0xf6,0x30,0x42,0xb2,0x06,0xa2,
14710 0x43,0x98,0xd3,0x31,0x42,0xd6,0x4e,0xa1,0x43,0x08,0x1c,0x6f,0x38,0x42,0x2a,0x94,0x9e,0x43,0xc1,0x22,
14711 0x36,0x42,0xf5,0x9b,0x9d,0x43,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x57,
14712 0xa2,0x9b,0x43,0x08,0xab,0x8f,0x35,0x42,0x8a,0xab,0x9b,0x43,0xe9,0x71,0x3a,0x42,0xb2,0xe2,0x9b,0x43,
14713 0xb7,0x74,0x3c,0x42,0x34,0x5a,0x9c,0x43,0x08,0x23,0x7d,0x42,0x42,0x0b,0x2f,0x9e,0x43,0xe5,0x9a,0x3d,
14714 0x42,0x38,0x6d,0xa3,0x43,0x36,0xd9,0x35,0x42,0xf3,0xd7,0xa7,0x43,0x08,0x12,0x61,0x2e,0x42,0xb0,0x42,
14715 0xac,0x43,0x63,0xff,0x36,0x42,0xdd,0x74,0xaf,0x43,0x1e,0xa6,0x45,0x42,0x44,0x82,0xb2,0x43,0x08,0x74,
14716 0x1b,0x4b,0x42,0x79,0x7a,0xb3,0x43,0x10,0x21,0x4f,0x42,0x2a,0x18,0xb5,0x43,0xdb,0x4c,0x54,0x42,0x91,
14717 0x19,0xb6,0x43,0x08,0xee,0x3f,0x65,0x42,0x5f,0x28,0xba,0x43,0xa7,0xaf,0x66,0x42,0xb9,0x50,0xb6,0x43,
14718 0x14,0x58,0x5c,0x42,0xca,0xdc,0xb1,0x43,0x08,0x2c,0x8b,0x4c,0x42,0x4e,0x30,0xac,0x43,0x19,0xcf,0x48,
14719 0x42,0x2a,0xd0,0xa8,0x43,0xbc,0xab,0x49,0x42,0xa9,0x4c,0xa6,0x43,0x08,0x61,0x5f,0x47,0x42,0xfa,0xa2,
14720 0xa2,0x43,0xa7,0xaf,0x66,0x42,0x85,0x98,0x94,0x43,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x07,0x2a,
14721 0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x09,0x06,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x08,0xdc,0xe3,
14722 0xf1,0x41,0xe9,0x9e,0x92,0x43,0xd2,0xe7,0x0b,0x42,0xd6,0x06,0x95,0x43,0x2a,0xf4,0x2e,0x42,0x04,0x21,
14723 0x94,0x43,0x07,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x08,0x87,0x17,0x2e,0x42,0xc3,0x62,0x95,0x43,
14724 0xe7,0x3a,0x2d,0x42,0xf5,0x6b,0x95,0x43,0x44,0x5e,0x2c,0x42,0xf5,0x6b,0x95,0x43,0x08,0xd1,0x47,0x1c,
14725 0x42,0x19,0xc0,0x96,0x43,0x66,0xdf,0x05,0x42,0x38,0x19,0x95,0x43,0x12,0x6a,0x00,0x42,0xb2,0xbe,0x95,
14726 0x43,0x08,0xbb,0x6b,0xea,0x41,0xd6,0x12,0x97,0x43,0x2d,0x82,0xfa,0x41,0x61,0x74,0x9b,0x43,0x7e,0x72,
14727 0x06,0x42,0x8a,0xab,0x9b,0x43,0x08,0xc8,0x39,0x12,0x42,0x4e,0xd0,0x9b,0x43,0x53,0xe3,0x22,0x42,0xc3,
14728 0x86,0x9b,0x43,0x2a,0xf4,0x2e,0x42,0x57,0xa2,0x9b,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,
14729 0x08,0x01,0xa5,0x2a,0x42,0xa4,0x2d,0x9d,0x43,0x96,0x9c,0x24,0x42,0x06,0x40,0x9d,0x43,0x8a,0xb7,0x1d,
14730 0x42,0x9a,0x5b,0x9d,0x43,0x08,0x6b,0x16,0x13,0x42,0xcd,0x64,0x9d,0x43,0x42,0xc7,0x0e,0x42,0x9a,0x5b,
14731 0x9d,0x43,0x23,0x26,0x04,0x42,0xcd,0x64,0x9d,0x43,0x08,0xe6,0x91,0xeb,0x41,0x38,0x49,0x9d,0x43,0x73,
14732 0x7b,0xdb,0x41,0xf5,0x83,0x99,0x43,0x7f,0x60,0xe2,0x41,0x0b,0x0b,0x98,0x43,0x08,0x7f,0x60,0xe2,0x41,
14733 0xec,0x99,0x95,0x43,0xe3,0x5a,0xde,0x41,0xbe,0x7f,0x96,0x43,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,
14734 0x07,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x09,0x06,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x08,
14735 0xd4,0x7e,0x29,0x42,0xab,0x6b,0xaf,0x43,0x4e,0x0c,0x26,0x42,0x44,0x6a,0xae,0x43,0x38,0x79,0x25,0x42,
14736 0xd4,0x96,0xad,0x43,0x08,0x25,0xbd,0x21,0x42,0xe2,0x4b,0xac,0x43,0x49,0x35,0x29,0x42,0x9a,0x97,0xa7,
14737 0x43,0x2a,0xf4,0x2e,0x42,0x61,0xa4,0xa3,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x09,0x06,
14738 0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x08,0x86,0x20,0x80,0x43,0x57,0x41,0xe6,0x43,0x7d,0x4e,0x80,
14739 0x43,0x25,0x38,0xe6,0x43,0xa5,0x85,0x80,0x43,0xf3,0x2e,0xe6,0x43,0x08,0x35,0xca,0x83,0x43,0xd4,0xc9,
14740 0xe5,0x43,0x9c,0xd7,0x86,0x43,0x44,0x91,0xe4,0x43,0xd5,0xca,0x8a,0x43,0x91,0x1c,0xe6,0x43,0x08,0x53,
14741 0x5f,0x8c,0x43,0xf8,0x1d,0xe7,0x43,0x2f,0x17,0x8d,0x43,0x4e,0x7b,0xe8,0x43,0x92,0x29,0x8d,0x43,0x2f,
14742 0x22,0xea,0x43,0x07,0x92,0x29,0x8d,0x43,0x44,0xb5,0xea,0x43,0x08,0xfe,0x0d,0x8d,0x43,0x2a,0x4b,0xed,
14743 0x43,0xe3,0x8b,0x8b,0x43,0x55,0x7d,0xf0,0x43,0xec,0x51,0x89,0x43,0x72,0x0b,0xf4,0x43,0x08,0xcd,0xd4,
14744 0x84,0x43,0x9d,0x55,0xfb,0x43,0xc9,0xe5,0x83,0x43,0x74,0x1e,0xfb,0x43,0x73,0x94,0x84,0x43,0x5a,0x90,
14745 0xf7,0x43,0x08,0xe8,0x62,0x88,0x43,0xfd,0x30,0xee,0x43,0x39,0xc5,0x86,0x43,0xdd,0xbf,0xeb,0x43,0x35,
14746 0xbe,0x81,0x43,0x40,0xde,0xed,0x43,0x08,0x4f,0x34,0x81,0x43,0x36,0x0c,0xee,0x43,0x08,0x98,0x80,0x43,
14747 0xfd,0x30,0xee,0x43,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x40,0xec,
14748 0x43,0x08,0x35,0xbe,0x81,0x43,0x06,0xf7,0xeb,0x43,0x15,0x65,0x83,0x43,0x49,0xa4,0xeb,0x43,0x1e,0x43,
14749 0x85,0x43,0xbe,0x5a,0xeb,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0x18,0xea,0x43,0x42,0x97,0x86,0x43,0x5f,
14750 0x67,0xf4,0x43,0xa9,0x98,0x87,0x43,0xd4,0x1d,0xf4,0x43,0x08,0x5c,0x25,0x8a,0x43,0xcf,0x16,0xef,0x43,
14751 0x46,0xaa,0x8d,0x43,0x5a,0x3c,0xe9,0x43,0x19,0x6c,0x88,0x43,0x53,0x5e,0xe7,0x43,0x08,0xc4,0x02,0x85,
14752 0x43,0x96,0x0b,0xe7,0x43,0x85,0x2c,0x82,0x43,0x83,0x67,0xe7,0x43,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,
14753 0x43,0x07,0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x09,0x06,0xfd,0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,
14754 0x08,0xfa,0x6c,0x78,0x43,0xd1,0xc2,0xe0,0x43,0x25,0x5c,0x6c,0x43,0x25,0x44,0xe8,0x43,0x1d,0xe5,0x7f,
14755 0x43,0x87,0x4a,0xe6,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,0x43,0x08,0xa6,0x27,0x7b,0x43,0x91,
14756 0x28,0xe8,0x43,0xbc,0xa2,0x77,0x43,0xb0,0x8d,0xe8,0x43,0xc6,0x68,0x75,0x43,0x57,0x4d,0xe8,0x43,0x08,
14757 0xe0,0xd2,0x72,0x43,0xab,0x9e,0xe7,0x43,0x50,0x9a,0x71,0x43,0x2a,0x27,0xe7,0x43,0xea,0x98,0x70,0x43,
14758 0x57,0x35,0xe4,0x43,0x08,0x94,0x3b,0x6f,0x43,0x14,0x7c,0xe2,0x43,0xff,0x13,0x6d,0x43,0x06,0xbb,0xe1,
14759 0x43,0xcf,0xfe,0x6a,0x43,0x06,0xbb,0xe1,0x43,0x08,0x44,0x9d,0x66,0x43,0x77,0x8e,0xe2,0x43,0x3b,0xef,
14760 0x6c,0x43,0x91,0x10,0xe4,0x43,0xfd,0x24,0x6c,0x43,0xb0,0x81,0xe6,0x43,0x08,0x96,0x23,0x6b,0x43,0xee,
14761 0x57,0xe9,0x43,0xca,0x0f,0x6a,0x43,0x5f,0x37,0xec,0x43,0x55,0x71,0x6e,0x43,0x9f,0x01,0xed,0x43,0x08,
14762 0xdb,0xfb,0x75,0x43,0x3b,0xef,0xec,0x43,0x09,0x3a,0x7b,0x43,0xb0,0xa5,0xec,0x43,0x1d,0xe5,0x7f,0x43,
14763 0x91,0x40,0xec,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x08,0xa9,0x16,0x7c,0x43,0xb0,0xb1,
14764 0xee,0x43,0x47,0xec,0x77,0x43,0xd9,0xe8,0xee,0x43,0x1e,0x9d,0x73,0x43,0xcf,0x16,0xef,0x43,0x08,0x0e,
14765 0xc9,0x6b,0x43,0xee,0x7b,0xef,0x43,0x7e,0x90,0x6a,0x43,0xfd,0x30,0xee,0x43,0x01,0xfc,0x68,0x43,0x4e,
14766 0x93,0xec,0x43,0x08,0x31,0xf9,0x66,0x43,0x4e,0x87,0xea,0x43,0x31,0x11,0x6b,0x43,0xd4,0xd5,0xe7,0x43,
14767 0xd9,0xc4,0x68,0x43,0xd4,0xc9,0xe5,0x43,0x08,0xe5,0x79,0x67,0x43,0x77,0x9a,0xe4,0x43,0x44,0x9d,0x66,
14768 0x43,0xab,0x86,0xe3,0x43,0x7e,0x78,0x66,0x43,0x0b,0xaa,0xe2,0x43,0x07,0x7e,0x78,0x66,0x43,0x57,0x29,
14769 0xe2,0x43,0x08,0xa7,0xaf,0x66,0x43,0xbe,0x1e,0xe1,0x43,0x87,0x56,0x68,0x43,0x77,0x82,0xe0,0x43,0xfd,
14770 0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,0x09,0x06,0xc4,0x41,0xbf,0x43,0x85,0xc0,0x72,0x42,0x08,0x73,0xdf,
14771 0xc0,0x43,0xf4,0x76,0x72,0x42,0x97,0x33,0xc2,0x43,0x85,0xc0,0x72,0x42,0xb2,0xb5,0xc3,0x43,0x64,0x56,
14772 0x75,0x42,0x08,0x03,0x24,0xc4,0x43,0x5e,0x7f,0x78,0x42,0xfa,0x51,0xc4,0x43,0x01,0x85,0x7c,0x42,0x5c,
14773 0x64,0xc4,0x43,0xa0,0xb3,0x80,0x42,0x07,0x5c,0x64,0xc4,0x43,0x10,0x93,0x83,0x42,0x08,0xc8,0x48,0xc4,
14774 0x43,0x1c,0x78,0x8a,0x42,0x27,0x6c,0xc3,0x43,0xaf,0xcf,0x94,0x42,0x23,0x7d,0xc2,0x43,0x99,0x9c,0xa4,
14775 0x42,0x08,0x3d,0xe7,0xbf,0x43,0xfb,0xfd,0xb5,0x42,0xb3,0x9d,0xbf,0x43,0x88,0x17,0xae,0x42,0xc4,0x41,
14776 0xbf,0x43,0x69,0x76,0xa3,0x42,0x07,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x08,0x4f,0x8b,0xbf,0x43,
14777 0xed,0x81,0x91,0x42,0xe4,0xa6,0xbf,0x43,0x5d,0x61,0x94,0x42,0xfa,0x39,0xc0,0x43,0x3b,0x49,0x9d,0x42,
14778 0x08,0x2b,0x43,0xc0,0x43,0x28,0xed,0xa9,0x42,0x61,0x3b,0xc1,0x43,0x00,0x9e,0xa5,0x42,0xe4,0xb2,0xc1,
14779 0x43,0x5d,0x91,0x9c,0x42,0x08,0x78,0xce,0xc1,0x43,0xfd,0x36,0x90,0x42,0x22,0x89,0xc4,0x43,0x81,0x72,
14780 0x86,0x42,0xae,0xc6,0xc2,0x43,0xa0,0xb3,0x80,0x42,0x08,0x54,0x86,0xc2,0x43,0x58,0xd1,0x7e,0x42,0x30,
14781 0x32,0xc1,0x43,0xce,0x5e,0x7b,0x42,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x07,0xc4,0x41,0xbf,0x43,
14782 0x85,0xc0,0x72,0x42,0x09,0x06,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x08,0x35,0xfd,0xbb,0x43,0xa4,
14783 0xa1,0x5c,0x42,0x5e,0x34,0xbc,0x43,0x9d,0x2a,0x70,0x42,0x5e,0x40,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,
14784 0x4c,0x9c,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,0xef,0xbe,0x43,0x0e,0x0a,0x73,0x42,0xc4,0x41,0xbf,0x43,
14785 0x85,0xc0,0x72,0x42,0x07,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x08,0xcd,0x13,0xbf,0x43,0xe8,0xf1,
14786 0x7b,0x42,0xd6,0xe5,0xbe,0x43,0x71,0x3b,0x7c,0x42,0xdf,0xb7,0xbe,0x43,0x71,0x3b,0x7c,0x42,0x08,0x08,
14787 0xe3,0xbc,0x43,0xa4,0x61,0x7d,0x42,0x28,0x3c,0xbb,0x43,0x91,0x45,0x69,0x42,0x28,0x3c,0xbb,0x43,0x58,
14788 0x71,0x6e,0x42,0x08,0xce,0xfb,0xba,0x43,0xd5,0x35,0x78,0x42,0x59,0x45,0xbb,0x43,0x58,0x23,0x82,0x42,
14789 0xa1,0xe1,0xbb,0x43,0xd7,0xbe,0x88,0x42,0x08,0xc9,0x18,0xbc,0x43,0xaf,0x9f,0x8c,0x42,0x1e,0x76,0xbd,
14790 0x43,0x51,0x7c,0x8d,0x42,0xd6,0xe5,0xbe,0x43,0xf4,0x58,0x8e,0x42,0x08,0x9c,0x0a,0xbf,0x43,0x45,0xc7,
14791 0x8e,0x42,0x30,0x26,0xbf,0x43,0x96,0x35,0x8f,0x42,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x07,0xc4,
14792 0x41,0xbf,0x43,0x69,0x76,0xa3,0x42,0x08,0x08,0xef,0xbe,0x43,0xb1,0xd6,0x99,0x42,0xe8,0x89,0xbe,0x43,
14793 0xde,0xc5,0x8d,0x42,0xc0,0x46,0xbc,0x43,0xc2,0x5b,0x90,0x42,0x08,0x9c,0xf2,0xba,0x43,0x86,0x80,0x90,
14794 0x42,0xf2,0x43,0xba,0x43,0xe8,0x73,0x87,0x42,0x8f,0x31,0xba,0x43,0xb6,0xf4,0x7d,0x42,0x07,0x8f,0x31,
14795 0xba,0x43,0x21,0xc6,0x76,0x42,0x08,0xc0,0x3a,0xba,0x43,0x5f,0x48,0x6b,0x42,0xae,0x96,0xba,0x43,0xe3,
14796 0x83,0x61,0x42,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x09,0x06,0xea,0x74,0xea,0x43,0x61,0x44,0x93,
14797 0x43,0x08,0x24,0x5c,0xec,0x43,0x31,0x3b,0x93,0x43,0xfb,0x30,0xee,0x43,0x93,0x4d,0x93,0x43,0x0d,0xe1,
14798 0xef,0x43,0x80,0xa9,0x93,0x43,0x08,0x8f,0x58,0xf0,0x43,0xd1,0x17,0x94,0x43,0xb7,0x8f,0xf0,0x43,0x10,
14799 0xe2,0x94,0x43,0xea,0x98,0xf0,0x43,0xa9,0xec,0x95,0x43,0x07,0xea,0x98,0xf0,0x43,0x38,0x25,0x97,0x43,
14800 0x08,0x23,0x74,0xf0,0x43,0x9f,0x32,0x9a,0x43,0x5a,0x60,0xef,0x43,0x53,0xcb,0x9e,0x43,0x2d,0x3a,0xee,
14801 0x43,0xfd,0x91,0xa3,0x43,0x08,0xa2,0xf0,0xed,0x43,0xdd,0x38,0xa5,0x43,0x17,0xa7,0xed,0x43,0xbe,0xdf,
14802 0xa6,0x43,0x5a,0x54,0xed,0x43,0x9f,0x86,0xa8,0x43,0x08,0xfc,0x24,0xec,0x43,0xca,0xc4,0xad,0x43,0x48,
14803 0xa4,0xeb,0x43,0x40,0x6f,0xab,0x43,0x28,0x3f,0xeb,0x43,0x1c,0x0f,0xa8,0x43,0x08,0x1f,0x6d,0xeb,0x43,
14804 0x72,0x48,0xa3,0x43,0x67,0x09,0xec,0x43,0xd1,0x53,0x9e,0x43,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,0x43,
14805 0x07,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x08,0x7e,0x90,0xea,0x43,0x8a,0x9f,0x99,0x43,0x12,0xac,
14806 0xea,0x43,0xbc,0xa8,0x99,0x43,0xa7,0xc7,0xea,0x43,0xbc,0xa8,0x99,0x43,0x08,0x51,0x76,0xeb,0x43,0x9f,
14807 0x32,0x9a,0x43,0x5e,0x37,0xec,0x43,0x49,0xed,0x9c,0x43,0xb0,0xa5,0xec,0x43,0x2a,0xa0,0xa0,0x43,0x08,
14808 0x09,0xe6,0xec,0x43,0xd1,0x77,0xa4,0x43,0x28,0x4b,0xed,0x43,0x61,0xa4,0xa3,0x43,0xab,0xc2,0xed,0x43,
14809 0x8e,0xb2,0xa0,0x43,0x08,0x70,0xe7,0xed,0x43,0xde,0x08,0x9d,0x43,0x87,0x86,0xf0,0x43,0x2f,0x53,0x97,
14810 0x43,0x87,0x7a,0xee,0x43,0xec,0x99,0x95,0x43,0x08,0xca,0x27,0xee,0x43,0xff,0x3d,0x95,0x43,0x74,0xca,
14811 0xec,0x43,0x55,0x8f,0x94,0x43,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x07,0xea,0x74,0xea,0x43,0x61,
14812 0x44,0x93,0x43,0x09,0x06,0x05,0xd3,0xe5,0x43,0x19,0x9c,0x90,0x43,0x08,0x09,0xc2,0xe6,0x43,0xd1,0xff,
14813 0x8f,0x43,0x4d,0x6f,0xe6,0x43,0x74,0xe8,0x92,0x43,0x3b,0xd7,0xe8,0x43,0xc3,0x56,0x93,0x43,0x08,0x1f,
14814 0x61,0xe9,0x43,0x93,0x4d,0x93,0x43,0x05,0xeb,0xe9,0x43,0x93,0x4d,0x93,0x43,0xea,0x74,0xea,0x43,0x61,
14815 0x44,0x93,0x43,0x07,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x08,0x24,0x50,0xea,0x43,0xe7,0xaa,0x94,
14816 0x43,0x2d,0x22,0xea,0x43,0xe7,0xaa,0x94,0x43,0x36,0xf4,0xe9,0x43,0xe7,0xaa,0x94,0x43,0x08,0xa2,0xcc,
14817 0xe7,0x43,0xe0,0xd8,0x94,0x43,0xd4,0xc9,0xe5,0x43,0x19,0xa8,0x92,0x43,0xd4,0xc9,0xe5,0x43,0x27,0x69,
14818 0x93,0x43,0x08,0x17,0x77,0xe5,0x43,0xe0,0xd8,0x94,0x43,0x67,0xe5,0xe5,0x43,0x47,0xda,0x95,0x43,0x43,
14819 0x9d,0xe6,0x43,0xe2,0xd3,0x97,0x43,0x08,0x9d,0xdd,0xe6,0x43,0xad,0xe7,0x98,0x43,0x09,0xce,0xe8,0x43,
14820 0xff,0x55,0x99,0x43,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x07,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,
14821 0x43,0x08,0x71,0xcf,0xe9,0x43,0x53,0xb3,0x9a,0x43,0xa7,0xbb,0xe8,0x43,0xdb,0x0d,0x9a,0x43,0xc6,0x14,
14822 0xe7,0x43,0xdb,0x0d,0x9a,0x43,0x08,0x48,0x80,0xe5,0x43,0xdb,0x0d,0x9a,0x43,0x0a,0xb6,0xe4,0x43,0xc3,
14823 0x6e,0x97,0x43,0x76,0x9a,0xe4,0x43,0x74,0xf4,0x94,0x43,0x07,0x76,0x9a,0xe4,0x43,0x79,0xd7,0x93,0x43,
14824 0x08,0xd8,0xac,0xe4,0x43,0x66,0x27,0x92,0x43,0x29,0x1b,0xe5,0x43,0xe0,0xc0,0x90,0x43,0x05,0xd3,0xe5,
14825 0x43,0x19,0x9c,0x90,0x43,0x09,0x06,0x1b,0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x08,0x71,0x0b,0xf4,0x42,
14826 0x00,0x0e,0x8d,0x42,0x8c,0x0f,0x01,0x43,0x3e,0xc0,0x89,0x42,0xf3,0x28,0x06,0x43,0x48,0x9e,0x8b,0x42,
14827 0x08,0x15,0x89,0x09,0x43,0x00,0x0e,0x8d,0x42,0xe0,0x9c,0x0a,0x43,0xc1,0x8b,0x98,0x42,0xa6,0xc1,0x0a,
14828 0x43,0x02,0xa5,0xaa,0x42,0x07,0xa6,0xc1,0x0a,0x43,0xf9,0xf6,0xb0,0x42,0x08,0xa6,0xc1,0x0a,0x43,0x47,
14829 0x8e,0xb4,0x42,0x42,0xaf,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0xe0,0x9c,0x0a,0x43,0xba,0x74,0xbc,0x42,0x08,
14830 0xa1,0xd2,0x09,0x43,0x40,0x47,0xd0,0x42,0x0d,0xab,0x07,0x43,0x91,0xb5,0xd0,0x42,0x3b,0xb9,0x04,0x43,
14831 0xec,0x71,0xba,0x42,0x08,0xe5,0x5b,0x03,0x43,0xe3,0x33,0xa8,0x42,0x63,0xd8,0x00,0x43,0xce,0x70,0x9f,
14832 0x42,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x07,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,0x42,0x08,0xed,
14833 0x6f,0xed,0x42,0x73,0x24,0x9d,0x42,0xd8,0x0c,0xf5,0x42,0x99,0x6c,0x9c,0x42,0x27,0xab,0xfd,0x42,0xea,
14834 0xda,0x9c,0x42,0x08,0x36,0xca,0x03,0x43,0x2b,0x94,0x9e,0x42,0x68,0xc7,0x01,0x43,0x8f,0xbe,0xa2,0x42,
14835 0xfa,0x06,0x08,0x43,0x73,0xb4,0xb5,0x42,0x08,0x8e,0x2e,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0x9d,0xe3,0x08,
14836 0x43,0xd7,0x1e,0x99,0x42,0x28,0x15,0x05,0x43,0x32,0x3b,0x93,0x42,0x08,0x63,0xf0,0x04,0x43,0x70,0xed,
14837 0x8f,0x42,0x71,0x0b,0xf4,0x42,0x32,0x3b,0x93,0x42,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x07,0x1b,
14838 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x09,0x06,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,0x08,0x8e,0x55,
14839 0xc0,0x42,0xb8,0x4d,0x86,0x42,0x60,0xbf,0xd7,0x42,0x3e,0xf0,0x91,0x42,0x63,0xf6,0xe4,0x42,0x70,0xed,
14840 0x8f,0x42,0x08,0x7a,0x89,0xe5,0x42,0xac,0xc8,0x8f,0x42,0xcc,0xf7,0xe5,0x42,0xac,0xc8,0x8f,0x42,0x1b,
14841 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x07,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x08,0x63,0xf6,0xe4,
14842 0x42,0x3b,0x19,0x95,0x42,0xe6,0x61,0xe3,0x42,0x00,0x3e,0x95,0x42,0xf4,0x16,0xe2,0x42,0xc4,0x62,0x95,
14843 0x42,0x08,0x6e,0x74,0xd6,0x42,0x15,0xd1,0x95,0x42,0x97,0x63,0xca,0x42,0xaf,0xcf,0x94,0x42,0xfb,0x2d,
14844 0xbe,0x42,0x86,0x80,0x90,0x42,0x08,0x97,0x03,0xba,0x42,0xce,0x10,0x8f,0x42,0x5e,0x28,0xba,0x42,0x3e,
14845 0xf0,0x91,0x42,0xf2,0x4f,0xbc,0x42,0x45,0xf7,0x96,0x42,0x08,0x27,0x54,0xbf,0x42,0x73,0x24,0x9d,0x42,
14846 0xa5,0xe8,0xc0,0x42,0x86,0xe0,0xa0,0x42,0xe4,0xca,0xc5,0x42,0xed,0x11,0xaa,0x42,0x08,0x54,0xaa,0xc8,
14847 0x42,0x86,0x40,0xb1,0x42,0x59,0x81,0xc5,0x42,0xa1,0x11,0xc4,0x42,0x3e,0xe7,0xbf,0x42,0xfb,0x8d,0xce,
14848 0x42,0x08,0xb4,0x6d,0xb7,0x42,0x30,0xc2,0xd9,0x42,0x46,0xf5,0xc9,0x42,0xdf,0x53,0xd9,0x42,0x38,0x40,
14849 0xcb,0x42,0x62,0x8f,0xcf,0x42,0x08,0x7d,0xf9,0xcc,0x42,0xec,0xa1,0xc2,0x42,0x07,0x43,0xcd,0x42,0x6c,
14850 0xdd,0xb8,0x42,0x2b,0x8b,0xcc,0x42,0x92,0xf5,0xaf,0x42,0x08,0xf9,0x8d,0xce,0x42,0x41,0x57,0xa7,0x42,
14851 0x5b,0xb8,0xd2,0x42,0xae,0x2f,0xa5,0x42,0x18,0x2f,0xd9,0x42,0x13,0x2a,0xa1,0x42,0x08,0x41,0x7e,0xdd,
14852 0x42,0xe3,0x03,0xa0,0x42,0x2e,0xf2,0xe1,0x42,0x7c,0x02,0x9f,0x42,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,
14853 0x42,0x07,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x08,0x4d,0x63,0xe4,0x42,0x00,0x9e,0xa5,0x42,0xf4,
14854 0x16,0xe2,0x42,0x15,0x31,0xa6,0x42,0x99,0xca,0xdf,0x42,0x2b,0xc4,0xa6,0x42,0x08,0xc0,0x82,0xc6,0x42,
14855 0xc4,0xc2,0xa5,0x42,0x57,0xe1,0xd5,0x42,0x91,0xb5,0xd0,0x42,0x54,0xda,0xd0,0x42,0x97,0x93,0xd2,0x42,
14856 0x08,0x9c,0x3a,0xc7,0x42,0x17,0x58,0xdc,0x42,0x9c,0x0a,0xbf,0x42,0x6e,0xa4,0xde,0x42,0x90,0x25,0xb8,
14857 0x42,0xdf,0x53,0xd9,0x42,0x08,0x59,0x21,0xb5,0x42,0xf2,0xdf,0xd4,0x42,0x51,0x43,0xb3,0x42,0x91,0xb5,
14858 0xd0,0x42,0xc5,0x29,0xbb,0x42,0x0e,0x1a,0xca,0x42,0x08,0x65,0x36,0xc4,0x42,0xd0,0x07,0xbd,0x42,0x3e,
14859 0xe7,0xbf,0x42,0x37,0x09,0xbe,0x42,0x0c,0xea,0xc1,0x42,0xcd,0xd0,0xaf,0x42,0x08,0x2b,0x5b,0xc4,0x42,
14860 0x18,0x08,0xa3,0x42,0x67,0xa6,0xab,0x42,0x99,0x3c,0x94,0x42,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,
14863 private struct ThePath
{
14866 Bounds
, // always first, has 4 args (x0, y0, x1, y1)
14874 CubicTo
, // cubic bezier
14879 const(ubyte)[] path
;
14883 this (const(void)[] apath
) pure nothrow @trusted @nogc {
14884 path
= cast(const(ubyte)[])apath
;
14887 @property bool empty () const pure nothrow @safe @nogc { pragma(inline
, true); return (ppos
>= path
.length
); }
14889 Command
getCommand () nothrow @trusted @nogc {
14890 pragma(inline
, true);
14891 if (ppos
>= cast(uint)path
.length
) assert(0, "invalid path");
14892 return cast(Command
)(path
.ptr
[ppos
++]);
14895 // number of (x,y) pairs for this command
14896 static int argCount (in Command cmd
) nothrow @safe @nogc {
14897 version(aliced
) pragma(inline
, true);
14898 if (cmd
== Command
.Bounds
) return 2;
14899 else if (cmd
== Command
.MoveTo || cmd
== Command
.LineTo
) return 1;
14900 else if (cmd
== Command
.CubicTo
) return 3;
14904 void skipArgs (int argc
) nothrow @trusted @nogc {
14905 pragma(inline
, true);
14906 ppos
+= cast(uint)(float.sizeof
*2*argc
);
14909 float getFloat () nothrow @trusted @nogc {
14910 pragma(inline
, true);
14911 if (ppos
>= cast(uint)path
.length ||
cast(uint)path
.length
-ppos
< float.sizeof
) assert(0, "invalid path");
14912 version(LittleEndian
) {
14913 float res
= *cast(const(float)*)(&path
.ptr
[ppos
]);
14914 ppos
+= cast(uint)float.sizeof
;
14917 static assert(float.sizeof
== 4);
14918 uint xp
= path
.ptr
[ppos
]|
(path
.ptr
[ppos
+1]<<8)|
(path
.ptr
[ppos
+2]<<16)|
(path
.ptr
[ppos
+3]<<24);
14919 ppos
+= cast(uint)float.sizeof
;
14920 return *cast(const(float)*)(&xp
);
14925 // this will add baphomet's background path to the current NanoVega path, so you can fill it.
14926 public void addBaphometBack (NVGContext nvg
, float ofsx
=0, float ofsy
=0, float scalex
=1, float scaley
=1) nothrow @trusted @nogc {
14927 if (nvg
is null) return;
14929 auto path
= ThePath(baphometPath
);
14931 float getScaledX () nothrow @trusted @nogc { pragma(inline
, true); return (ofsx
+path
.getFloat()*scalex
); }
14932 float getScaledY () nothrow @trusted @nogc { pragma(inline
, true); return (ofsy
+path
.getFloat()*scaley
); }
14934 bool inPath
= false;
14935 while (!path
.empty
) {
14936 auto cmd
= path
.getCommand();
14938 case ThePath
.Command
.MoveTo
:
14940 immutable float ex
= getScaledX();
14941 immutable float ey
= getScaledY();
14942 nvg
.moveTo(ex
, ey
);
14944 case ThePath
.Command
.LineTo
:
14946 immutable float ex
= getScaledX();
14947 immutable float ey
= getScaledY();
14948 nvg
.lineTo(ex
, ey
);
14950 case ThePath
.Command
.CubicTo
: // cubic bezier
14952 immutable float x1
= getScaledX();
14953 immutable float y1
= getScaledY();
14954 immutable float x2
= getScaledX();
14955 immutable float y2
= getScaledY();
14956 immutable float ex
= getScaledX();
14957 immutable float ey
= getScaledY();
14958 nvg
.bezierTo(x1
, y1
, x2
, y2
, ex
, ey
);
14960 case ThePath
.Command
.EndPath
:
14961 if (inPath
) return;
14964 path
.skipArgs(path
.argCount(cmd
));
14970 // this will add baphomet's pupil paths to the current NanoVega path, so you can fill it.
14971 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 {
14972 // pupils starts with "fill-and-stroke" mode
14973 if (nvg
is null) return;
14975 auto path
= ThePath(baphometPath
);
14977 float getScaledX () nothrow @trusted @nogc { pragma(inline
, true); return (ofsx
+path
.getFloat()*scalex
); }
14978 float getScaledY () nothrow @trusted @nogc { pragma(inline
, true); return (ofsy
+path
.getFloat()*scaley
); }
14980 bool inPath
= false;
14981 bool pupLeft
= true;
14982 while (!path
.empty
) {
14983 auto cmd
= path
.getCommand();
14985 case ThePath
.Command
.StrokeFillMode
: inPath
= true; break;
14986 case ThePath
.Command
.MoveTo
:
14987 if (!inPath
) goto default;
14988 static if (!left
) { if (pupLeft
) goto default; }
14989 static if (!right
) { if (!pupLeft
) goto default; }
14990 immutable float ex
= getScaledX();
14991 immutable float ey
= getScaledY();
14992 nvg
.moveTo(ex
, ey
);
14994 case ThePath
.Command
.LineTo
:
14995 if (!inPath
) goto default;
14996 static if (!left
) { if (pupLeft
) goto default; }
14997 static if (!right
) { if (!pupLeft
) goto default; }
14998 immutable float ex
= getScaledX();
14999 immutable float ey
= getScaledY();
15000 nvg
.lineTo(ex
, ey
);
15002 case ThePath
.Command
.CubicTo
: // cubic bezier
15003 if (!inPath
) goto default;
15004 static if (!left
) { if (pupLeft
) goto default; }
15005 static if (!right
) { if (!pupLeft
) goto default; }
15006 immutable float x1
= getScaledX();
15007 immutable float y1
= getScaledY();
15008 immutable float x2
= getScaledX();
15009 immutable float y2
= getScaledY();
15010 immutable float ex
= getScaledX();
15011 immutable float ey
= getScaledY();
15012 nvg
.bezierTo(x1
, y1
, x2
, y2
, ex
, ey
);
15014 case ThePath
.Command
.EndPath
:
15016 if (pupLeft
) pupLeft
= false; else return;
15020 path
.skipArgs(path
.argCount(cmd
));
15026 // mode: 'f' to allow fills; 's' to allow strokes; 'w' to allow stroke widths; 'c' to replace fills with strokes
15027 public void renderBaphomet(string mode
="fs") (NVGContext nvg
, float ofsx
=0, float ofsy
=0, float scalex
=1, float scaley
=1) nothrow @trusted @nogc {
15028 template hasChar(char ch
, string s
) {
15029 static if (s
.length
== 0) enum hasChar
= false;
15030 else static if (s
[0] == ch
) enum hasChar
= true;
15031 else enum hasChar
= hasChar
!(ch
, s
[1..$]);
15033 enum AllowStroke
= hasChar
!('s', mode
);
15034 enum AllowFill
= hasChar
!('f', mode
);
15035 enum AllowWidth
= hasChar
!('w', mode
);
15036 enum Contour
= hasChar
!('c', mode
);
15037 //static assert(AllowWidth || AllowFill);
15039 if (nvg
is null) return;
15041 auto path
= ThePath(baphometPath
);
15043 float getScaledX () nothrow @trusted @nogc { pragma(inline
, true); return (ofsx
+path
.getFloat()*scalex
); }
15044 float getScaledY () nothrow @trusted @nogc { pragma(inline
, true); return (ofsy
+path
.getFloat()*scaley
); }
15047 int sw
= ThePath
.Command
.NormalStroke
;
15049 while (!path
.empty
) {
15050 auto cmd
= path
.getCommand();
15052 case ThePath
.Command
.StrokeMode
: mode
= ThePath
.Command
.StrokeMode
; break;
15053 case ThePath
.Command
.FillMode
: mode
= ThePath
.Command
.FillMode
; break;
15054 case ThePath
.Command
.StrokeFillMode
: mode
= ThePath
.Command
.StrokeFillMode
; break;
15055 case ThePath
.Command
.NormalStroke
: sw
= ThePath
.Command
.NormalStroke
; break;
15056 case ThePath
.Command
.ThinStroke
: sw
= ThePath
.Command
.ThinStroke
; break;
15057 case ThePath
.Command
.MoveTo
:
15058 immutable float ex
= getScaledX();
15059 immutable float ey
= getScaledY();
15060 nvg
.moveTo(ex
, ey
);
15062 case ThePath
.Command
.LineTo
:
15063 immutable float ex
= getScaledX();
15064 immutable float ey
= getScaledY();
15065 nvg
.lineTo(ex
, ey
);
15067 case ThePath
.Command
.CubicTo
: // cubic bezier
15068 immutable float x1
= getScaledX();
15069 immutable float y1
= getScaledY();
15070 immutable float x2
= getScaledX();
15071 immutable float y2
= getScaledY();
15072 immutable float ex
= getScaledX();
15073 immutable float ey
= getScaledY();
15074 nvg
.bezierTo(x1
, y1
, x2
, y2
, ex
, ey
);
15076 case ThePath
.Command
.EndPath
:
15077 if (mode
== ThePath
.Command
.FillMode || mode
== ThePath
.Command
.StrokeFillMode
) {
15078 static if (AllowFill || Contour
) {
15079 static if (Contour
) {
15080 if (mode
== ThePath
.Command
.FillMode
) { nvg
.strokeWidth
= 1; nvg
.stroke(); }
15086 if (mode
== ThePath
.Command
.StrokeMode || mode
== ThePath
.Command
.StrokeFillMode
) {
15087 static if (AllowStroke || Contour
) {
15088 static if (AllowWidth
) {
15089 if (sw
== ThePath
.Command
.NormalStroke
) nvg
.strokeWidth
= 1;
15090 else if (sw
== ThePath
.Command
.ThinStroke
) nvg
.strokeWidth
= 0.5;
15091 else assert(0, "wtf?!");
15099 path
.skipArgs(path
.argCount(cmd
));