1 module nanovg.gl3; 2 3 // 4 // NanoVG-d: 5 // Copyright (c) 2015 S.Percentage 6 // 7 // Original Source(NanoVG): 8 // Copyright (c) 2013 Mikko Mononen memon@inside.org 9 // 10 // This software is provided 'as-is', without any express or implied 11 // warranty. In no event will the authors be held liable for any damages 12 // arising from the use of this software. 13 // Permission is granted to anyone to use this software for any purpose, 14 // including commercial applications, and to alter it and redistribute it 15 // freely, subject to the following restrictions: 16 // 1. The origin of this software must not be misrepresented; you must not 17 // claim that you wrote the original software. If you use this software 18 // in a product, an acknowledgment in the product documentation would be 19 // appreciated but is not required. 20 // 2. Altered source versions must be plainly marked as such, and must not be 21 // misrepresented as being the original software. 22 // 3. This notice may not be removed or altered from any source distribution. 23 // 24 25 // NanoVG OpenGL3 renderer Implementation 26 // (Rewriting Original Version to Dlang) 27 28 import nanovg.h; 29 import derelict.opengl3.gl3; 30 import std.string, std.range, std.algorithm, std.math; 31 import core.memory; 32 import std.experimental.logger; 33 34 public import nanovg.glInterface; 35 36 enum CommandType 37 { 38 Fill, ConvexFill, Stroke, Triangles 39 } 40 enum ImageType 41 { 42 Single, RGBA 43 } 44 enum ShaderType : int 45 { 46 Gradient, Textured, StencilFilling, TexturedTris 47 } 48 enum TexturePostProcess : int 49 { 50 None, Multiply, Colorize 51 } 52 53 auto premultiplied(NVGcolor color) 54 { 55 return NVGcolor(color.r * color.a, color.g * color.a, color.b * color.a, color.a); 56 } 57 auto asFloat4(NVGcolor color) 58 { 59 return [color.r, color.g, color.b, color.a]; 60 } 61 auto asMatrix3x4(float[6] xform) 62 { 63 return 64 [ 65 xform[0], xform[1], 0.0f, 0.0f, 66 xform[2], xform[3], 0.0f, 0.0f, 67 xform[4], xform[5], 1.0f, 0.0f 68 ]; 69 } 70 auto maxVertCount(const(NVGpath)[] paths) 71 { 72 return paths.map!(a => a.nfill + a.nstroke).sum; 73 } 74 75 class Texture 76 { 77 GLuint texture, sampler; 78 Size size; 79 ImageType type; 80 int flags; 81 82 public this(int w, int h, int type, int imageFlags, const(byte)* pData) 83 { 84 info(false, "CreateTexture: ", w, "/", h); 85 this.size = Size(w, h); 86 this.flags = imageFlags; 87 88 glGenTextures(1, &this.texture); 89 GLContext.Texture2D = this.texture; 90 scope(exit) GLContext.Texture2D = NullTexture; 91 this.setPixelStoreState(); 92 93 GLTexture2D.Wrap.S = imageFlags.raised!NVG_IMAGE_REPEATX ? GL_REPEAT : GL_CLAMP_TO_EDGE; 94 GLTexture2D.Wrap.T = imageFlags.raised!NVG_IMAGE_REPEATY ? GL_REPEAT : GL_CLAMP_TO_EDGE; 95 GLTexture2D.Filter.Min = imageFlags.raised!NVG_IMAGE_GENERATE_MIPMAPS ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; 96 GLTexture2D.Filter.Mag = GL_LINEAR; 97 switch(type) 98 { 99 case NVG_TEXTURE_RGBA: 100 this.type = ImageType.RGBA; 101 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pData); 102 break; 103 default: 104 this.type = ImageType.Single; 105 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, pData); 106 } 107 glCheckError(); 108 this.revertPixelStoreState(); 109 if(imageFlags.raised!NVG_IMAGE_GENERATE_MIPMAPS) 110 { 111 glGenerateMipmap(GL_TEXTURE_2D); 112 glCheckError(); 113 } 114 } 115 public ~this() 116 { 117 glDeleteSamplers(1, &this.sampler); 118 glDeleteTextures(1, &this.texture); 119 } 120 private void setPixelStoreState() 121 { 122 GLPixelStore.Alignment = 1; 123 GLPixelStore.RowLength = this.size.width; 124 GLPixelStore.SkipPixels = 0; 125 GLPixelStore.SkipRows = 0; 126 } 127 private void revertPixelStoreState() 128 { 129 GLPixelStore.SkipRows = 0; 130 GLPixelStore.SkipPixels = 0; 131 GLPixelStore.RowLength = 0; 132 GLPixelStore.Alignment = 4; 133 } 134 135 bool update(int x, int y, int w, int h, const(byte)* data) 136 { 137 info(false, "UpdateTexture: ", x, "/", y, "/", w, "/", h); 138 info(false, "UpdateData: \n", data[0 .. w * h].map!(a => format("%02x", a)).chunks(16).enumerate.map!(a => format("+%04x: ", a[0] * 0x10) ~ a[1].join(" ")).join("\n")); 139 GLContext.Texture2D = this.texture; 140 this.setPixelStoreState(); 141 GLPixelStore.SkipPixels = x; 142 GLPixelStore.SkipRows = y; 143 144 final switch(this.type) 145 { 146 case ImageType.RGBA: 147 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data); 148 break; 149 case ImageType.Single: 150 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RED, GL_UNSIGNED_BYTE, data); 151 } 152 glCheckError(); 153 154 this.revertPixelStoreState(); 155 GLContext.Texture2D = NullTexture; 156 return true; 157 } 158 bool getTextureSize(int* w, int* h) 159 { 160 *w = this.size.width; 161 *h = this.size.height; 162 return true; 163 } 164 } 165 166 class RenderProgram 167 { 168 GLuint vsh, fsh, program; 169 170 public this() 171 { 172 this.vsh = glCreateShader(GL_VERTEX_SHADER); 173 this.fsh = glCreateShader(GL_FRAGMENT_SHADER); 174 this.program = glCreateProgram(); 175 176 this.vsh.glShaderSource(1, [import("vertex.glsl").toStringz].ptr, null); 177 this.fsh.glShaderSource(1, [import("fragment.glsl").toStringz].ptr, null); 178 this.buildShaders(); 179 this.uniformBlocks = new UniformBlockIndexAccessor(); 180 this.uniforms = new UniformLocationAccessor(); 181 this.inputs = new AttributeLocationAccessor(); 182 } 183 private void buildShaders() 184 { 185 GLint status; 186 187 this.vsh.glCompileShader(); 188 this.vsh.glGetShaderiv(GLShaderInfo.CompileStatus, &status); 189 if(status != GL_TRUE) throw new GLShaderCompileError(this.vsh, "Vertex"); 190 this.fsh.glCompileShader(); 191 this.fsh.glGetShaderiv(GLShaderInfo.CompileStatus, &status); 192 if(status != GL_TRUE) throw new GLShaderCompileError(this.fsh, "Fragment"); 193 this.program.glAttachShader(this.vsh); 194 this.program.glAttachShader(this.fsh); 195 this.program.glLinkProgram(); 196 this.program.glGetProgramiv(GLProgramInfo.LinkStatus, &status); 197 if(status != GL_TRUE) throw new GLProgramLinkError(this.program); 198 } 199 200 class UniformBlockIndexAccessor 201 { 202 GLint[string] cache; 203 204 public this() { this.userIndexes = new UserIndexAccessor(); } 205 GLint opDispatch(string op)() { return this[op]; } 206 GLint opIndex(string op) 207 { 208 if(op in cache) return cache[op]; 209 cache[op] = this.outer.program.glGetUniformBlockIndex(op.toStringz); 210 return cache[op]; 211 } 212 class UserIndexAccessor 213 { 214 void opIndexAssign(GLint idx, string vn) 215 { 216 info(false, "UniformBlock Binding: ", this.outer[vn], " <=> ", idx); 217 glUniformBlockBinding(this.outer.outer.program, this.outer[vn], idx); 218 glCheckError(); 219 } 220 } 221 UserIndexAccessor userIndexes; 222 } 223 UniformBlockIndexAccessor uniformBlocks; 224 class UniformLocationAccessor 225 { 226 GLint[string] cache; 227 228 GLint opDispatch(string op)() 229 { 230 if(op in cache) return cache[op]; 231 cache[op] = this.outer.program.glGetUniformLocation(op.toStringz); 232 return cache[op]; 233 } 234 } 235 UniformLocationAccessor uniforms; 236 class AttributeLocationAccessor 237 { 238 GLint[string] cache; 239 240 GLint opDispatch(string op)() 241 { 242 if(op in cache) return cache[op]; 243 cache[op] = this.outer.program.glGetAttribLocation(op.toStringz); 244 return cache[op]; 245 } 246 } 247 AttributeLocationAccessor inputs; 248 } 249 struct FragUniformBuffer 250 { 251 float[3*4] scissorMatr, paintMatr; 252 float[4] innerColor, outerColor; 253 float[2] scissorExt, scissorScale; 254 float[2] extent; 255 float radius, feather; 256 float strokeMult, strokeThr; 257 int texType, type; 258 } 259 struct InternalDrawCall 260 { 261 CommandType type; 262 int image; 263 size_t pathOffset, pathCount; 264 size_t triangleOffset, triangleCount; 265 size_t uniformOffset; 266 } 267 struct Path 268 { 269 size_t fillOffset, fillCount; 270 size_t strokeOffset, strokeCount; 271 } 272 273 class Context 274 { 275 const FUBUserIndex = 1; 276 277 RenderProgram program; 278 UniformBufferObject fragUniformObject; 279 VertexArrayObject varray; 280 ArrayBufferObject vbuffer; 281 Texture[] textureList; 282 Path[] pathList; 283 InternalDrawCall[] callList; 284 NVGvertex[] vertexList; 285 FragUniformBuffer[] uniformList; 286 Size vport; 287 size_t ubHardwareSize, ubHardwarePadding; 288 289 GLint ublocFrag; 290 GLint ulocTexImage, ulocViewSize; 291 GLint alocVertex, alocTexcoord; 292 293 void init() 294 { 295 this.program = new RenderProgram(); 296 this.ublocFrag = this.program.uniformBlocks.frag; 297 this.ulocTexImage = this.program.uniforms.texImage; 298 this.ulocViewSize = this.program.uniforms.viewSize; 299 this.alocVertex = this.program.inputs.vertex; 300 this.alocTexcoord = this.program.inputs.texcoord; 301 302 info(false, "viewSize Location: ", this.ulocViewSize); 303 info(false, "texImage Location: ", this.ulocTexImage); 304 305 this.program.uniformBlocks.userIndexes["frag"] = FUBUserIndex; 306 this.fragUniformObject = new UniformBufferObject(); 307 this.varray = new VertexArrayObject(); 308 this.vbuffer = new ArrayBufferObject(); 309 int ub_align = GLUniformBuffer.OffsetAlignment; 310 this.ubHardwareSize = (cast(int)((FragUniformBuffer.sizeof - 1) / ub_align) + 1) * ub_align; 311 this.ubHardwarePadding = this.ubHardwareSize - FragUniformBuffer.sizeof; 312 info(false, "GL UniformBlockAlign: ", ub_align); 313 info(false, "HardwareUniformBlockSize: ", this.ubHardwareSize); 314 info(false, "UniformBlockPadding: ", this.ubHardwarePadding); 315 316 glFinish(); 317 } 318 void terminate() {} 319 int createTexture(int type, int w, int h, int imageFlags, const(byte)* data) 320 { 321 auto emptyIter = this.textureList.enumerate.filter!(a => a.value is null); 322 if(!emptyIter.empty) 323 { 324 emptyIter.front.value = new Texture(w, h, type, imageFlags, data); 325 return cast(int)emptyIter.front.index + 1; 326 } 327 else 328 { 329 this.textureList ~= new Texture(w, h, type, imageFlags, data); 330 return cast(int)this.textureList.length; 331 } 332 } 333 void deleteTexture(int image_id) 334 { 335 this.textureList[image_id - 1] = null; 336 } 337 Texture findTexture(int image_id) 338 { 339 return this.textureList[image_id - 1]; 340 } 341 342 void cancelRender() 343 { 344 this.pathList = null; 345 this.callList = null; 346 this.vertexList = null; 347 this.uniformList = null; 348 } 349 void flush() 350 { 351 this.processCallList(); 352 this.cancelRender(); 353 } 354 private void processCallList() 355 { 356 if(this.callList.empty) return; 357 358 // render init 359 GLContext.RenderProgram = this.program.program; 360 GLContext.ColorMask = [true, true, true, true]; 361 // blend 362 GLContext.Blend.Enable = true; 363 GLContext.Blend.Func = GLBlendPresets.PremultipliedBlending; 364 // cullface 365 GLContext.CullFace.Enable = true; 366 GLContext.CullFace.Direction = GLFaceDirection.Back; 367 GLContext.CullFace.FrontFace = GLPathDirection.CounterClockwise; 368 // stencil 369 GLContext.Stencil.Mask = GLuint.max; 370 GLContext.Stencil.Operations = GLStencilOpPresets.Keep; 371 GLContext.Stencil.Func = GLStencilFuncParams(GL_ALWAYS, 0, GLuint.max); 372 // switch 373 GLContext.DepthTest.Enable = false; 374 GLContext.ScissorTest.Enable = false; 375 // activate shader resource 376 GLContext.ActiveTexture = 0; 377 GLContext.Texture2D = NullTexture; 378 // uniform setup 379 ubyte[] uniformBytes; 380 foreach(i, ref ub; this.uniformList) 381 { 382 auto pBytePtr = cast(ubyte*)&ub; 383 uniformBytes ~= pBytePtr[0 .. FragUniformBuffer.sizeof]; 384 if(this.ubHardwarePadding > 0) uniformBytes.length += this.ubHardwarePadding; 385 } 386 info(false, "uniforms: ", this.uniformList); 387 info(false, "uniformBufferData: \n", uniformBytes.map!(a => format("%02x", a)).chunks(16).enumerate.map!(a => format("+%04x: ", a[0] * 0x10) ~ a[1].join(" ")).join("\n")); 388 GLContext.UniformBuffer = this.fragUniformObject; 389 GLUniformBuffer.ArrayData = uniformBytes; 390 GLProgram.Uniform[this.ulocTexImage] = 0; 391 GLProgram.Uniform[this.ulocViewSize] = [this.vport.width, this.vport.height]; 392 // vertex array setup 393 GLContext.VertexArray = this.varray; 394 GLContext.ArrayBuffer = this.vbuffer; 395 GLArrayBuffer.ArrayData = this.vertexList; 396 GLArrayBuffer.AttribPointer[this.alocVertex] = PlainFloatPointer(2, NVGvertex.sizeof, NVGvertex.x.offsetof); 397 GLArrayBuffer.AttribPointer[this.alocTexcoord] = PlainFloatPointer(2, NVGvertex.sizeof, NVGvertex.u.offsetof); 398 399 foreach(call; this.callList) 400 { 401 info(false, "callType: ", call.type); 402 final switch(call.type) 403 { 404 case CommandType.Fill: this.processFill(call); break; 405 case CommandType.ConvexFill: this.processConvexFill(call); break; 406 case CommandType.Stroke: this.processStroke(call); break; 407 case CommandType.Triangles: this.processTriangles(call); 408 } 409 } 410 411 GLArrayBuffer.AttribPointer[this.alocTexcoord] = DisablePointer(); 412 GLArrayBuffer.AttribPointer[this.alocVertex] = DisablePointer(); 413 GLContext.ArrayBuffer = cast(ArrayBufferObject)null; 414 GLContext.VertexArray = cast(VertexArrayObject)null; 415 GLContext.CullFace.Enable = false; 416 GLContext.Texture2D = NullTexture; 417 GLContext.RenderProgram = DisableProgram; 418 } 419 const FillFunc = (Path a) { glDrawArrays(GL_TRIANGLE_FAN, cast(int)a.fillOffset, cast(int)a.fillCount); }; 420 const DrawStrokeFunc = (Path a) { glDrawArrays(GL_TRIANGLE_STRIP, cast(int)a.strokeOffset, cast(int)a.strokeCount); }; 421 private void processFill(InternalDrawCall call) 422 { 423 auto processList = this.pathList[call.pathOffset .. call.pathOffset + call.pathCount]; 424 425 // Draw shapes 426 GLContext.Stencil.EnableTest = true; 427 GLContext.Stencil.Mask = 0xff; 428 GLContext.Stencil.Func = GLStencilFuncParams(GL_ALWAYS, 0, 0xff); 429 GLContext.ColorMask = [false, false, false, false]; 430 this.setUniformAndTexture(call.uniformOffset, 0); 431 GLContext.Stencil.Operations[GLFaceDirection.Front] = GLStencilOpSet(GL_KEEP, GL_KEEP, GL_INCR_WRAP); 432 GLContext.Stencil.Operations[GLFaceDirection.Back] = GLStencilOpSet(GL_KEEP, GL_KEEP, GL_DECR_WRAP); 433 GLContext.CullFace.Enable = false; 434 processList.each!FillFunc; 435 GLContext.CullFace.Enable = true; 436 437 // Draw anti-aliased pixels 438 GLContext.ColorMask = [true, true, true, true]; 439 this.setUniformAndTexture(call.uniformOffset + 1, call.image); 440 GLContext.Stencil.Func = GLStencilFuncParams(GL_EQUAL, 0, 0xff); 441 GLContext.Stencil.Operations = GLStencilOpPresets.Keep; 442 processList.each!DrawStrokeFunc; 443 444 // Draw fill 445 GLContext.Stencil.Func = GLStencilFuncParams(GL_NOTEQUAL, 0, 0xff); 446 GLContext.Stencil.Operations = GLStencilOpPresets.Zero; 447 glDrawArrays(GL_TRIANGLES, cast(int)call.triangleOffset, cast(int)call.triangleCount); 448 449 GLContext.Stencil.EnableTest = false; 450 } 451 private void processConvexFill(InternalDrawCall call) 452 { 453 auto processList = this.pathList[call.pathOffset .. call.pathOffset + call.pathCount]; 454 455 this.setUniformAndTexture(call.uniformOffset, call.image); 456 processList.each!FillFunc; 457 processList.each!DrawStrokeFunc; 458 } 459 private void processStroke(InternalDrawCall call) 460 { 461 auto processList = this.pathList[call.pathOffset .. call.pathOffset + call.pathCount]; 462 463 // Uses Stencil Stroke 464 GLContext.Stencil.EnableTest = true; 465 GLContext.Stencil.Mask = 0xff; 466 467 // Fill the stroke base without overlap 468 GLContext.Stencil.Func = GLStencilFuncParams(GL_EQUAL, 0, 0xff); 469 GLContext.Stencil.Operations = GLStencilOpPresets.IncrementOnSucceeded; 470 this.setUniformAndTexture(call.uniformOffset + 1, call.image); 471 processList.each!DrawStrokeFunc; 472 473 // Draw anti-aliased pixels 474 this.setUniformAndTexture(call.uniformOffset, call.image); 475 GLContext.Stencil.Func = GLStencilFuncParams(GL_EQUAL, 0, 0xff); 476 GLContext.Stencil.Operations = GLStencilOpPresets.Keep; 477 processList.each!DrawStrokeFunc; 478 479 // Clear stencil buffer 480 GLContext.ColorMask = [false, false, false, false]; 481 GLContext.Stencil.Func = GLStencilFuncParams(GL_ALWAYS, 0, 0xff); 482 GLContext.Stencil.Operations = GLStencilOpPresets.Zero; 483 processList.each!DrawStrokeFunc; 484 GLContext.ColorMask = [true, true, true, true]; 485 486 GLContext.Stencil.EnableTest = false; 487 } 488 private void processTriangles(InternalDrawCall call) 489 { 490 this.setUniformAndTexture(call.uniformOffset, call.image); 491 glDrawArrays(GL_TRIANGLES, cast(int)call.triangleOffset, cast(int)call.triangleCount); 492 } 493 private void allocatePathList(CommandType T)(const(NVGpath)[] paths) 494 { 495 foreach(path; paths) 496 { 497 Path internal; 498 499 static if(T == CommandType.Fill) if(path.nfill > 0) 500 { 501 internal.fillOffset = this.vertexList.length; 502 internal.fillCount = path.nfill; 503 this.vertexList ~= path.fill[0 .. path.nfill]; 504 } 505 if(path.nstroke > 0) 506 { 507 internal.strokeOffset = this.vertexList.length; 508 internal.strokeCount = path.nstroke; 509 this.vertexList ~= path.stroke[0 .. path.nstroke]; 510 } 511 this.pathList ~= internal; 512 } 513 } 514 void pushCommand(CommandType T, Param)(NVGpaint* paint, NVGscissor* scissor, float fringe, const(NVGpath)[] paths, Param param) 515 { 516 InternalDrawCall call; 517 with(call) 518 { 519 type = T; 520 pathOffset = this.pathList.length; 521 pathCount = paths.length; 522 image = paint.image; 523 uniformOffset = this.uniformList.length; 524 } 525 this.allocatePathList!T(paths); 526 527 // Depended by CommandType 528 static if(T == CommandType.Fill) 529 { 530 if(paths.length == 1 && paths.front.convex) call.type = CommandType.ConvexFill; 531 532 // quad 533 auto quad = [ 534 NVGvertex(param[0], param[3], 0.5f, 1.0f), 535 NVGvertex(param[2], param[3], 0.5f, 1.0f), 536 NVGvertex(param[2], param[1], 0.5f, 1.0f), 537 NVGvertex(param[0], param[3], 0.5f, 1.0f), 538 NVGvertex(param[2], param[1], 0.5f, 1.0f), 539 NVGvertex(param[0], param[1], 0.5f, 1.0f) 540 ]; 541 call.triangleOffset = this.vertexList.length; 542 call.triangleCount = 6; 543 this.vertexList ~= quad; 544 545 // Set UniformBuffer 546 if(call.type == CommandType.Fill) 547 { 548 FragUniformBuffer ub_stencil; 549 ub_stencil.strokeThr = -1.0f; 550 ub_stencil.type = ShaderType.StencilFilling; 551 this.uniformList ~= ub_stencil; 552 } 553 this.uniformList ~= this.createUniformBufferFromPaint(*paint, *scissor, fringe, fringe, -1.0f); 554 } 555 else static if(T == CommandType.Stroke) 556 { 557 this.uniformList ~= this.createUniformBufferFromPaint(*paint, *scissor, param, fringe, -1.0f); 558 this.uniformList ~= this.createUniformBufferFromPaint(*paint, *scissor, param, fringe, 1.0f - 0.5f / 255.0f); 559 } 560 else static assert(false, "Invalid CommandType for pushCommand"); 561 562 this.callList ~= call; 563 } 564 void pushTrianglesCommand(NVGpaint* paint, NVGscissor* scissor, const(NVGvertex)[] verts) 565 { 566 InternalDrawCall call; 567 with(call) 568 { 569 type = CommandType.Triangles; 570 image = paint.image; 571 triangleOffset = this.vertexList.length; 572 triangleCount = verts.length; 573 uniformOffset = this.uniformList.length; 574 } 575 this.vertexList ~= verts; 576 577 this.uniformList ~= this.createUniformBufferFromPaint(*paint, *scissor, 1.0f, 1.0f, -1.0f); 578 this.uniformList.back.type = ShaderType.TexturedTris; 579 580 this.callList ~= call; 581 } 582 583 private auto createUniformBufferFromPaint(NVGpaint paint, NVGscissor scissor, float width, float fringe, float strokeThr) 584 { 585 FragUniformBuffer ub; 586 float[6] invxform; 587 588 ub.innerColor = paint.innerColor.premultiplied.asFloat4; 589 ub.outerColor = paint.outerColor.premultiplied.asFloat4; 590 591 if(scissor.extent[0] < -0.5f || scissor.extent[1] < -0.5f) 592 { 593 ub.scissorMatr[] = 0.0; 594 ub.scissorExt = [1.0f, 1.0f]; 595 ub.scissorScale = [1.0f, 1.0f]; 596 } 597 else 598 { 599 nvgTransformInverse(invxform.ptr, scissor.xform.ptr); 600 ub.scissorMatr = invxform.asMatrix3x4; 601 ub.scissorExt = scissor.extent; 602 ub.scissorScale[0] = sqrt(scissor.xform[0] ^^ 2.0f + scissor.xform[2] ^^ 2.0f); 603 ub.scissorScale[1] = sqrt(scissor.xform[1] ^^ 2.0f + scissor.xform[3] ^^ 2.0f); 604 } 605 606 ub.extent = paint.extent; 607 ub.strokeMult = (width * 0.5f + fringe * 0.5f) / fringe; 608 ub.strokeThr = strokeThr; 609 610 if(paint.image != 0) 611 { 612 auto texture = this.findTexture(paint.image); 613 if(texture is null) throw new Exception("Texture not found"); 614 if(texture.flags.raised!NVG_IMAGE_FLIPY) 615 { 616 float[6] flipped; 617 nvgTransformScale(flipped.ptr, 1.0f, -1.0f); 618 nvgTransformMultiply(flipped.ptr, paint.xform.ptr); 619 nvgTransformInverse(invxform.ptr, flipped.ptr); 620 } 621 else nvgTransformInverse(invxform.ptr, paint.xform.ptr); 622 ub.type = ShaderType.Textured; 623 624 if(texture.type == ImageType.RGBA) 625 { 626 if(texture.flags.raised!NVG_IMAGE_PREMULTIPLIED) 627 { 628 ub.texType = TexturePostProcess.None; 629 } 630 else ub.texType = TexturePostProcess.Multiply; 631 } 632 else ub.texType = TexturePostProcess.Colorize; 633 } 634 else 635 { 636 ub.type = ShaderType.Gradient; 637 ub.radius = paint.radius; 638 ub.feather = paint.feather; 639 nvgTransformInverse(invxform.ptr, paint.xform.ptr); 640 } 641 ub.paintMatr = invxform.asMatrix3x4; 642 return ub; 643 } 644 private void setUniformAndTexture(size_t uniformIndex, int image_id) 645 { 646 info(false, "Setting UniformOffset: ", uniformIndex * this.ubHardwareSize, "(", uniformIndex, ")"); 647 info(false, "UniformBuffer TextureType: ", this.uniformList[uniformIndex].texType); 648 info(false, "UniformBuffer RenderType: ", this.uniformList[uniformIndex].type); 649 GLUniformBuffer.BindRange[this.fragUniformObject.id, FUBUserIndex] = ByteRange(uniformIndex * this.ubHardwareSize, FragUniformBuffer.sizeof); 650 if(image_id == 0) GLContext.Texture2D = NullTexture; 651 else 652 { 653 const auto texture = this.findTexture(image_id); 654 GLContext.Texture2D = texture is null ? NullTexture : texture.texture; 655 info(false, "Texture: ", texture.texture); 656 } 657 } 658 659 @property viewport(Size sz) 660 { 661 this.vport = sz; 662 } 663 } 664 auto asContext(void* p) { return cast(Context)p; } 665 666 extern(C) 667 { 668 // Initialize/Terminate 669 int initContext(void* uptr) 670 { 671 uptr.asContext.init(); 672 return 1; 673 } 674 void deleteContext(void* uptr) 675 { 676 uptr.asContext.terminate(); 677 GC.removeRoot(uptr); 678 } 679 // Textures 680 int createTexture(void* uptr, int type, int w, int h, int imageFlags, const(byte)* data) 681 { 682 return uptr.asContext.createTexture(type, w, h, imageFlags, data); 683 } 684 int deleteTexture(void* uptr, int image) 685 { 686 uptr.asContext.deleteTexture(image); 687 return 1; 688 } 689 int updateTexture(void* uptr, int image, int x, int y, int w, int h, const(byte)* data) 690 { 691 return uptr.asContext.findTexture(image).update(x, y, w, h, data); 692 } 693 int getTextureSize(void* uptr, int image, int* w, int* h) 694 { 695 return uptr.asContext.findTexture(image).getTextureSize(w, h); 696 } 697 // Viewport 698 void setViewport(void* uptr, int w, int h) 699 { 700 uptr.asContext.viewport = Size(w, h); 701 } 702 // Render Control 703 void cancel(void* uptr) { uptr.asContext.cancelRender(); } 704 void flush(void* uptr) { uptr.asContext.flush(); } 705 // Internal Commands 706 void pushFillCommand(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const(float)* bounds, const(NVGpath)* pPaths, int nPaths) 707 { 708 uptr.asContext.pushCommand!(CommandType.Fill)(paint, scissor, fringe, pPaths[0 .. nPaths], bounds[0 .. 4]); 709 } 710 void pushStrokeCommand(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const(NVGpath)* pPaths, int nPaths) 711 { 712 uptr.asContext.pushCommand!(CommandType.Stroke)(paint, scissor, fringe, pPaths[0 .. nPaths], strokeWidth); 713 } 714 void pushTrianglesCommand(void* uptr, NVGpaint* paint, NVGscissor* scissor, const(NVGvertex)* verts, int nVerts) 715 { 716 uptr.asContext.pushTrianglesCommand(paint, scissor, verts[0 .. nVerts]); 717 } 718 } 719 720 // NanoVG Export 721 NVGcontext* nvgCreateGL3() 722 { 723 auto pContext = new Context(); 724 725 GC.addRoot(cast(void*)pContext); 726 GC.setAttr(cast(void*)pContext, GC.BlkAttr.NO_MOVE); 727 728 NVGparams params; 729 with(params) 730 { 731 userPtr = cast(void*)pContext; 732 edgeAntiAlias = 1; 733 renderCreate = &initContext; 734 renderCreateTexture = &createTexture; 735 renderDeleteTexture = &deleteTexture; 736 renderUpdateTexture = &updateTexture; 737 renderGetTextureSize = &getTextureSize; 738 renderViewport = &setViewport; 739 renderCancel = &cancel; 740 renderFlush = &flush; 741 renderFill = &pushFillCommand; 742 renderStroke = &pushStrokeCommand; 743 renderTriangles = &pushTrianglesCommand; 744 renderDelete = &deleteContext; 745 } 746 747 return nvgCreateInternal(¶ms); 748 } 749 alias nvgDeleteGL3 = nvgDeleteInternal;