1069 lines
28 KiB
C++
1069 lines
28 KiB
C++
/*
|
|
* Copyright 2011-2015 Branimir Karadzic. All rights reserved.
|
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
|
*/
|
|
|
|
//
|
|
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
//
|
|
#define NVG_ANTIALIAS 1
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "nanovg.h"
|
|
|
|
#include <bgfx.h>
|
|
|
|
#include <bx/bx.h>
|
|
|
|
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4244); // warning C4244: '=' : conversion from '' to '', possible loss of data
|
|
|
|
namespace
|
|
{
|
|
#include "vs_nanovg_fill.bin.h"
|
|
#include "fs_nanovg_fill.bin.h"
|
|
|
|
static bgfx::VertexDecl s_nvgDecl;
|
|
|
|
enum GLNVGshaderType
|
|
{
|
|
NSVG_SHADER_FILLGRAD,
|
|
NSVG_SHADER_FILLIMG,
|
|
NSVG_SHADER_SIMPLE,
|
|
NSVG_SHADER_IMG
|
|
};
|
|
|
|
// These are additional flags on top of NVGimageFlags.
|
|
enum NVGimageFlagsGL {
|
|
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.
|
|
};
|
|
|
|
struct GLNVGtexture
|
|
{
|
|
bgfx::TextureHandle id;
|
|
int width, height;
|
|
int type;
|
|
int flags;
|
|
};
|
|
|
|
enum GLNVGcallType
|
|
{
|
|
GLNVG_FILL,
|
|
GLNVG_CONVEXFILL,
|
|
GLNVG_STROKE,
|
|
GLNVG_TRIANGLES,
|
|
};
|
|
|
|
struct GLNVGcall
|
|
{
|
|
int type;
|
|
int image;
|
|
int pathOffset;
|
|
int pathCount;
|
|
int vertexOffset;
|
|
int vertexCount;
|
|
int uniformOffset;
|
|
};
|
|
|
|
struct GLNVGpath
|
|
{
|
|
int fillOffset;
|
|
int fillCount;
|
|
int strokeOffset;
|
|
int strokeCount;
|
|
};
|
|
|
|
struct GLNVGfragUniforms
|
|
{
|
|
float scissorMat[12]; // matrices are actually 3 vec4s
|
|
float paintMat[12];
|
|
NVGcolor innerCol;
|
|
NVGcolor outerCol;
|
|
|
|
// u_scissorExtScale
|
|
float scissorExt[2];
|
|
float scissorScale[2];
|
|
|
|
// u_extentRadius
|
|
float extent[2];
|
|
float radius;
|
|
|
|
// u_params
|
|
float feather;
|
|
float strokeMult;
|
|
float texType;
|
|
float type;
|
|
};
|
|
|
|
struct GLNVGcontext
|
|
{
|
|
bgfx::ProgramHandle prog;
|
|
bgfx::UniformHandle u_scissorMat;
|
|
bgfx::UniformHandle u_paintMat;
|
|
bgfx::UniformHandle u_innerCol;
|
|
bgfx::UniformHandle u_outerCol;
|
|
bgfx::UniformHandle u_viewSize;
|
|
bgfx::UniformHandle u_scissorExtScale;
|
|
bgfx::UniformHandle u_extentRadius;
|
|
bgfx::UniformHandle u_params;
|
|
bgfx::UniformHandle u_halfTexel;
|
|
|
|
bgfx::UniformHandle s_tex;
|
|
|
|
uint64_t state;
|
|
bgfx::TextureHandle th;
|
|
|
|
bgfx::TransientVertexBuffer tvb;
|
|
uint8_t viewid;
|
|
|
|
struct GLNVGtexture* textures;
|
|
float view[2];
|
|
float surface[2];
|
|
int ntextures;
|
|
int ctextures;
|
|
int textureId;
|
|
int vertBuf;
|
|
int fragSize;
|
|
int edgeAntiAlias;
|
|
|
|
// Per frame buffers
|
|
struct GLNVGcall* calls;
|
|
int ccalls;
|
|
int ncalls;
|
|
struct GLNVGpath* paths;
|
|
int cpaths;
|
|
int npaths;
|
|
struct NVGvertex* verts;
|
|
int cverts;
|
|
int nverts;
|
|
unsigned char* uniforms;
|
|
int cuniforms;
|
|
int nuniforms;
|
|
};
|
|
|
|
static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl)
|
|
{
|
|
struct GLNVGtexture* tex = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < gl->ntextures; i++)
|
|
{
|
|
if (gl->textures[i].id.idx == bgfx::invalidHandle)
|
|
{
|
|
tex = &gl->textures[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tex == NULL)
|
|
{
|
|
if (gl->ntextures+1 > gl->ctextures)
|
|
{
|
|
int old = gl->ctextures;
|
|
gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2;
|
|
gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures);
|
|
memset(&gl->textures[old], 0xff, (gl->ctextures-old)*sizeof(struct GLNVGtexture) );
|
|
|
|
if (gl->textures == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
tex = &gl->textures[gl->ntextures++];
|
|
}
|
|
|
|
memset(tex, 0, sizeof(*tex) );
|
|
|
|
return tex;
|
|
}
|
|
|
|
static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id)
|
|
{
|
|
int i;
|
|
for (i = 0; i < gl->ntextures; i++)
|
|
{
|
|
if (gl->textures[i].id.idx == id)
|
|
{
|
|
return &gl->textures[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id)
|
|
{
|
|
for (int ii = 0; ii < gl->ntextures; ii++)
|
|
{
|
|
if (gl->textures[ii].id.idx == id)
|
|
{
|
|
if (bgfx::isValid(gl->textures[ii].id)
|
|
&& (gl->textures[ii].flags & NVG_IMAGE_NODELETE) == 0)
|
|
{
|
|
bgfx::destroyTexture(gl->textures[ii].id);
|
|
}
|
|
memset(&gl->textures[ii], 0, sizeof(gl->textures[ii]) );
|
|
gl->textures[ii].id.idx = bgfx::invalidHandle;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nvgRenderCreate(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
const bgfx::Memory* vs_nanovg_fill;
|
|
const bgfx::Memory* fs_nanovg_fill;
|
|
|
|
switch (bgfx::getRendererType() )
|
|
{
|
|
case bgfx::RendererType::Direct3D9:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_dx9, sizeof(vs_nanovg_fill_dx9) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_dx9, sizeof(fs_nanovg_fill_dx9) );
|
|
break;
|
|
|
|
case bgfx::RendererType::Direct3D11:
|
|
case bgfx::RendererType::Direct3D12:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_dx11, sizeof(vs_nanovg_fill_dx11) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_dx11, sizeof(fs_nanovg_fill_dx11) );
|
|
break;
|
|
|
|
default:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_glsl, sizeof(vs_nanovg_fill_glsl) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_glsl, sizeof(fs_nanovg_fill_glsl) );
|
|
break;
|
|
}
|
|
|
|
gl->prog = bgfx::createProgram(
|
|
bgfx::createShader(vs_nanovg_fill)
|
|
, bgfx::createShader(fs_nanovg_fill)
|
|
, true
|
|
);
|
|
|
|
gl->u_scissorMat = bgfx::createUniform("u_scissorMat", bgfx::UniformType::Mat3);
|
|
gl->u_paintMat = bgfx::createUniform("u_paintMat", bgfx::UniformType::Mat3);
|
|
gl->u_innerCol = bgfx::createUniform("u_innerCol", bgfx::UniformType::Vec4);
|
|
gl->u_outerCol = bgfx::createUniform("u_outerCol", bgfx::UniformType::Vec4);
|
|
gl->u_viewSize = bgfx::createUniform("u_viewSize", bgfx::UniformType::Vec4);
|
|
gl->u_scissorExtScale = bgfx::createUniform("u_scissorExtScale", bgfx::UniformType::Vec4);
|
|
gl->u_extentRadius = bgfx::createUniform("u_extentRadius", bgfx::UniformType::Vec4);
|
|
gl->u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4);
|
|
gl->s_tex = bgfx::createUniform("s_tex", bgfx::UniformType::Int1);
|
|
|
|
if (bgfx::getRendererType() == bgfx::RendererType::Direct3D9)
|
|
{
|
|
gl->u_halfTexel = bgfx::createUniform("u_halfTexel", bgfx::UniformType::Vec4);
|
|
}
|
|
else
|
|
{
|
|
gl->u_halfTexel.idx = bgfx::invalidHandle;
|
|
}
|
|
|
|
s_nvgDecl
|
|
.begin()
|
|
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
|
|
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
|
|
.end();
|
|
|
|
int align = 16;
|
|
gl->fragSize = sizeof(struct GLNVGfragUniforms) + align - sizeof(struct GLNVGfragUniforms) % align;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int nvgRenderCreateTexture(void* _userPtr, int _type, int _width, int _height, int _flags, const unsigned char* _rgba)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__allocTexture(gl);
|
|
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
tex->width = _width;
|
|
tex->height = _height;
|
|
tex->type = _type;
|
|
tex->flags = _flags;
|
|
|
|
uint32_t bytesPerPixel = NVG_TEXTURE_RGBA == tex->type ? 4 : 1;
|
|
uint32_t pitch = tex->width * bytesPerPixel;
|
|
|
|
const bgfx::Memory* mem = NULL;
|
|
if (NULL != _rgba)
|
|
{
|
|
mem = bgfx::copy(_rgba, tex->height * pitch);
|
|
}
|
|
|
|
tex->id = bgfx::createTexture2D(tex->width
|
|
, tex->height
|
|
, 1
|
|
, NVG_TEXTURE_RGBA == _type ? bgfx::TextureFormat::RGBA8 : bgfx::TextureFormat::R8
|
|
, BGFX_TEXTURE_NONE
|
|
, mem
|
|
);
|
|
|
|
return tex->id.idx;
|
|
}
|
|
|
|
static int nvgRenderDeleteTexture(void* _userPtr, int image)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
return glnvg__deleteTexture(gl, image);
|
|
}
|
|
|
|
static int nvgRenderUpdateTexture(void* _userPtr, int image, int x, int y, int w, int h, const unsigned char* data)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32_t bytesPerPixel = NVG_TEXTURE_RGBA == tex->type ? 4 : 1;
|
|
uint32_t pitch = tex->width * bytesPerPixel;
|
|
|
|
bgfx::updateTexture2D(tex->id, 0, x, y, w, h
|
|
, bgfx::makeRef(data + y*pitch + x*bytesPerPixel, h*pitch)
|
|
, pitch
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int nvgRenderGetTextureSize(void* _userPtr, int image, int* w, int* h)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
|
|
if (!bgfx::isValid(tex->id) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
*w = tex->width;
|
|
*h = tex->height;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void glnvg__xformIdentity(float* t)
|
|
{
|
|
t[0] = 1.0f; t[1] = 0.0f;
|
|
t[2] = 0.0f; t[3] = 1.0f;
|
|
t[4] = 0.0f; t[5] = 0.0f;
|
|
}
|
|
|
|
static void glnvg__xformInverse(float* inv, float* t)
|
|
{
|
|
double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
|
|
if (det > -1e-6 && det < 1e-6) {
|
|
glnvg__xformIdentity(t);
|
|
return;
|
|
}
|
|
invdet = 1.0 / det;
|
|
inv[0] = (float)(t[3] * invdet);
|
|
inv[2] = (float)(-t[2] * invdet);
|
|
inv[4] = (float)( ((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
|
|
inv[1] = (float)(-t[1] * invdet);
|
|
inv[3] = (float)(t[0] * invdet);
|
|
inv[5] = (float)( ((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
|
|
}
|
|
|
|
static void glnvg__xformToMat3x4(float* m3, float* t)
|
|
{
|
|
m3[0] = t[0];
|
|
m3[1] = t[1];
|
|
m3[2] = 0.0f;
|
|
m3[3] = 0.0f;
|
|
m3[4] = t[2];
|
|
m3[5] = t[3];
|
|
m3[6] = 0.0f;
|
|
m3[7] = 0.0f;
|
|
m3[8] = t[4];
|
|
m3[9] = t[5];
|
|
m3[10] = 1.0f;
|
|
m3[11] = 0.0f;
|
|
}
|
|
|
|
static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms* frag, struct NVGpaint* paint,
|
|
struct NVGscissor* scissor, float width, float fringe)
|
|
{
|
|
struct GLNVGtexture* tex = NULL;
|
|
float invxform[6] = {};
|
|
|
|
memset(frag, 0, sizeof(*frag) );
|
|
|
|
frag->innerCol = paint->innerColor;
|
|
frag->outerCol = paint->outerColor;
|
|
|
|
glnvg__xformInverse(invxform, paint->xform);
|
|
glnvg__xformToMat3x4(frag->paintMat, invxform);
|
|
|
|
if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f)
|
|
{
|
|
memset(frag->scissorMat, 0, sizeof(frag->scissorMat) );
|
|
frag->scissorExt[0] = 1.0f;
|
|
frag->scissorExt[1] = 1.0f;
|
|
frag->scissorScale[0] = 1.0f;
|
|
frag->scissorScale[1] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
glnvg__xformInverse(invxform, scissor->xform);
|
|
glnvg__xformToMat3x4(frag->scissorMat, invxform);
|
|
frag->scissorExt[0] = scissor->extent[0];
|
|
frag->scissorExt[1] = scissor->extent[1];
|
|
frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe;
|
|
frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe;
|
|
}
|
|
memcpy(frag->extent, paint->extent, sizeof(frag->extent) );
|
|
frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe;
|
|
|
|
bgfx::TextureHandle invalid = BGFX_INVALID_HANDLE;
|
|
gl->th = invalid;
|
|
if (paint->image != 0)
|
|
{
|
|
tex = glnvg__findTexture(gl, paint->image);
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
frag->type = NSVG_SHADER_FILLIMG;
|
|
frag->texType = tex->type == NVG_TEXTURE_RGBA ? 0.0f : 1.0f;
|
|
gl->th = tex->id;
|
|
}
|
|
else
|
|
{
|
|
frag->type = NSVG_SHADER_FILLGRAD;
|
|
frag->radius = paint->radius;
|
|
frag->feather = paint->feather;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void glnvg__mat3(float* dst, float* src)
|
|
{
|
|
dst[0] = src[0];
|
|
dst[1] = src[1];
|
|
dst[2] = src[2];
|
|
|
|
dst[3] = src[4];
|
|
dst[4] = src[5];
|
|
dst[5] = src[6];
|
|
|
|
dst[6] = src[8];
|
|
dst[7] = src[9];
|
|
dst[8] = src[10];
|
|
}
|
|
|
|
static struct GLNVGfragUniforms* nvg__fragUniformPtr(struct GLNVGcontext* gl, int i)
|
|
{
|
|
return (struct GLNVGfragUniforms*)&gl->uniforms[i];
|
|
}
|
|
|
|
static void nvgRenderSetUniforms(struct GLNVGcontext* gl, int uniformOffset, int image)
|
|
{
|
|
struct GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
|
|
float tmp[9]; // Maybe there's a way to get rid of this...
|
|
glnvg__mat3(tmp, frag->scissorMat);
|
|
bgfx::setUniform(gl->u_scissorMat, tmp);
|
|
glnvg__mat3(tmp, frag->paintMat);
|
|
bgfx::setUniform(gl->u_paintMat, tmp);
|
|
|
|
bgfx::setUniform(gl->u_innerCol, frag->innerCol.rgba);
|
|
bgfx::setUniform(gl->u_outerCol, frag->outerCol.rgba);
|
|
bgfx::setUniform(gl->u_scissorExtScale, &frag->scissorExt[0]);
|
|
bgfx::setUniform(gl->u_extentRadius, &frag->extent[0]);
|
|
bgfx::setUniform(gl->u_params, &frag->feather);
|
|
|
|
bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
|
|
|
|
if (image != 0)
|
|
{
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
if (tex != NULL)
|
|
{
|
|
handle = tex->id;
|
|
|
|
if (bgfx::isValid(gl->u_halfTexel) )
|
|
{
|
|
float halfTexel[4] = { 0.5f / tex->width, 0.5f / tex->height };
|
|
bgfx::setUniform(gl->u_halfTexel, halfTexel);
|
|
}
|
|
}
|
|
}
|
|
|
|
gl->th = handle;
|
|
}
|
|
|
|
static void nvgRenderViewport(void* _userPtr, int width, int height, int surfaceWidth, int surfaceHeight)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
gl->view[0] = (float)width;
|
|
gl->view[1] = (float)height;
|
|
gl->surface[0] = (float)surfaceWidth;
|
|
gl->surface[1] = (float)surfaceHeight;
|
|
bgfx::setViewRect(gl->viewid, 0, 0, width, height);
|
|
}
|
|
|
|
static void fan(uint32_t _start, uint32_t _count)
|
|
{
|
|
uint32_t numTris = _count-2;
|
|
bgfx::TransientIndexBuffer tib;
|
|
bgfx::allocTransientIndexBuffer(&tib, numTris*3);
|
|
uint16_t* data = (uint16_t*)tib.data;
|
|
for (uint32_t ii = 0; ii < numTris; ++ii)
|
|
{
|
|
data[ii*3+0] = _start;
|
|
data[ii*3+1] = _start + ii + 1;
|
|
data[ii*3+2] = _start + ii + 2;
|
|
}
|
|
|
|
bgfx::setIndexBuffer(&tib);
|
|
}
|
|
|
|
static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int i, npaths = call->pathCount;
|
|
|
|
// set bindpoint for solid loc
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, 0);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
if (2 < paths[i].fillCount)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(0);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_ALWAYS
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_INCR
|
|
, 0
|
|
| BGFX_STENCIL_TEST_ALWAYS
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_DECR
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
fan(paths[i].fillOffset, paths[i].fillCount);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
// Draw aliased off-pixels
|
|
nvgRenderSetUniforms(gl, call->uniformOffset + gl->fragSize, call->image);
|
|
|
|
if (gl->edgeAntiAlias)
|
|
{
|
|
// Draw fringes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_EQUAL
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_KEEP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
// Draw fill
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb, call->vertexOffset, call->vertexCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_NOTEQUAL
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_ZERO
|
|
| BGFX_STENCIL_OP_FAIL_Z_ZERO
|
|
| BGFX_STENCIL_OP_PASS_Z_ZERO
|
|
);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
|
|
static void glnvg__convexFill(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int i, npaths = call->pathCount;
|
|
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
if (paths[i].fillCount == 0) continue;
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
fan(paths[i].fillOffset, paths[i].fillCount);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
|
|
if (gl->edgeAntiAlias)
|
|
{
|
|
// Draw fringes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void glnvg__stroke(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int npaths = call->pathCount, i;
|
|
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
// Draw Strokes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
static void glnvg__triangles(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
if (3 <= call->vertexCount)
|
|
{
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb, call->vertexOffset, call->vertexCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
static void nvgRenderFlush(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
if (gl->ncalls > 0)
|
|
{
|
|
bgfx::allocTransientVertexBuffer(&gl->tvb, gl->nverts, s_nvgDecl);
|
|
|
|
int allocated = gl->tvb.size/gl->tvb.stride;
|
|
|
|
if (allocated < gl->nverts) {
|
|
gl->nverts = allocated;
|
|
BX_WARN(true, "Vertex number truncated due to transient vertex buffer overflow");
|
|
}
|
|
|
|
|
|
memcpy(gl->tvb.data, gl->verts, gl->nverts * sizeof(struct NVGvertex) );
|
|
|
|
gl->state = 0
|
|
| BGFX_STATE_RGB_WRITE
|
|
| BGFX_STATE_ALPHA_WRITE
|
|
;
|
|
|
|
// if (alphaBlend == NVG_PREMULTIPLIED_ALPHA)
|
|
// {
|
|
// gl->state |= BGFX_STATE_BLEND_FUNC_SEPARATE(
|
|
// BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
// , BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
// );
|
|
// }
|
|
// else
|
|
{
|
|
gl->state |= BGFX_STATE_BLEND_FUNC(
|
|
BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
);
|
|
}
|
|
|
|
bgfx::setUniform(gl->u_viewSize, gl->surface);
|
|
|
|
for (uint32_t ii = 0, num = gl->ncalls; ii < num; ++ii)
|
|
{
|
|
struct GLNVGcall* call = &gl->calls[ii];
|
|
switch (call->type)
|
|
{
|
|
case GLNVG_FILL:
|
|
glnvg__fill(gl, call);
|
|
break;
|
|
|
|
case GLNVG_CONVEXFILL:
|
|
glnvg__convexFill(gl, call);
|
|
break;
|
|
|
|
case GLNVG_STROKE:
|
|
glnvg__stroke(gl, call);
|
|
break;
|
|
|
|
case GLNVG_TRIANGLES:
|
|
glnvg__triangles(gl, call);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset calls
|
|
gl->nverts = 0;
|
|
gl->npaths = 0;
|
|
gl->ncalls = 0;
|
|
gl->nuniforms = 0;
|
|
}
|
|
|
|
static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths)
|
|
{
|
|
int i, count = 0;
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
count += paths[i].nfill;
|
|
count += paths[i].nstroke;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int glnvg__maxi(int a, int b) { return a > b ? a : b; }
|
|
|
|
static struct GLNVGcall* glnvg__allocCall(struct GLNVGcontext* gl)
|
|
{
|
|
struct GLNVGcall* ret = NULL;
|
|
if (gl->ncalls+1 > gl->ccalls)
|
|
{
|
|
gl->ccalls = gl->ccalls == 0 ? 32 : gl->ccalls * 2;
|
|
gl->calls = (struct GLNVGcall*)realloc(gl->calls, sizeof(struct GLNVGcall) * gl->ccalls);
|
|
}
|
|
ret = &gl->calls[gl->ncalls++];
|
|
memset(ret, 0, sizeof(struct GLNVGcall) );
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocPaths(struct GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0;
|
|
if (gl->npaths + n > gl->cpaths) {
|
|
GLNVGpath* paths;
|
|
int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths / 2; // 1.5x Overallocate
|
|
paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths);
|
|
if (paths == NULL) return -1;
|
|
gl->paths = paths;
|
|
gl->cpaths = cpaths;
|
|
}
|
|
ret = gl->npaths;
|
|
gl->npaths += n;
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocVerts(GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0;
|
|
if (gl->nverts+n > gl->cverts)
|
|
{
|
|
NVGvertex* verts;
|
|
int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate
|
|
verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts);
|
|
if (verts == NULL) return -1;
|
|
gl->verts = verts;
|
|
gl->cverts = cverts;
|
|
}
|
|
ret = gl->nverts;
|
|
gl->nverts += n;
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocFragUniforms(struct GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0, structSize = gl->fragSize;
|
|
if (gl->nuniforms+n > gl->cuniforms)
|
|
{
|
|
gl->cuniforms = gl->cuniforms == 0 ? glnvg__maxi(n, 32) : gl->cuniforms * 2;
|
|
gl->uniforms = (unsigned char*)realloc(gl->uniforms, gl->cuniforms * structSize);
|
|
}
|
|
ret = gl->nuniforms * structSize;
|
|
gl->nuniforms += n;
|
|
return ret;
|
|
}
|
|
|
|
static void glnvg__vset(struct NVGvertex* vtx, float x, float y, float u, float v)
|
|
{
|
|
vtx->x = x;
|
|
vtx->y = y;
|
|
vtx->u = u;
|
|
vtx->v = v;
|
|
}
|
|
|
|
static void nvgRenderFill(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe,
|
|
const float* bounds, const struct NVGpath* paths, int npaths)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
struct NVGvertex* quad;
|
|
struct GLNVGfragUniforms* frag;
|
|
int i, maxverts, offset;
|
|
|
|
call->type = GLNVG_FILL;
|
|
call->pathOffset = glnvg__allocPaths(gl, npaths);
|
|
call->pathCount = npaths;
|
|
call->image = paint->image;
|
|
|
|
if (npaths == 1 && paths[0].convex)
|
|
{
|
|
call->type = GLNVG_CONVEXFILL;
|
|
}
|
|
|
|
// Allocate vertices for all the paths.
|
|
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
|
|
offset = glnvg__allocVerts(gl, maxverts);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
struct GLNVGpath* copy = &gl->paths[call->pathOffset + i];
|
|
const struct NVGpath* path = &paths[i];
|
|
memset(copy, 0, sizeof(struct GLNVGpath) );
|
|
if (path->nfill > 0)
|
|
{
|
|
copy->fillOffset = offset;
|
|
copy->fillCount = path->nfill;
|
|
memcpy(&gl->verts[offset], path->fill, sizeof(struct NVGvertex) * path->nfill);
|
|
offset += path->nfill;
|
|
}
|
|
|
|
if (path->nstroke > 0)
|
|
{
|
|
copy->strokeOffset = offset;
|
|
copy->strokeCount = path->nstroke;
|
|
memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke);
|
|
offset += path->nstroke;
|
|
}
|
|
}
|
|
|
|
// Quad
|
|
call->vertexOffset = offset;
|
|
call->vertexCount = 6;
|
|
quad = &gl->verts[call->vertexOffset];
|
|
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);
|
|
|
|
glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);
|
|
|
|
// Setup uniforms for draw calls
|
|
if (call->type == GLNVG_FILL)
|
|
{
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
|
|
// Simple shader for stencil
|
|
frag = nvg__fragUniformPtr(gl, call->uniformOffset);
|
|
memset(frag, 0, sizeof(*frag) );
|
|
frag->type = NSVG_SHADER_SIMPLE;
|
|
// Fill shader
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe);
|
|
}
|
|
else
|
|
{
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
// Fill shader
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe);
|
|
}
|
|
}
|
|
|
|
static void nvgRenderStroke(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe,
|
|
float strokeWidth, const struct NVGpath* paths, int npaths)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
int i, maxverts, offset;
|
|
|
|
call->type = GLNVG_STROKE;
|
|
call->pathOffset = glnvg__allocPaths(gl, npaths);
|
|
call->pathCount = npaths;
|
|
call->image = paint->image;
|
|
|
|
// Allocate vertices for all the paths.
|
|
maxverts = glnvg__maxVertCount(paths, npaths);
|
|
offset = glnvg__allocVerts(gl, maxverts);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
struct GLNVGpath* copy = &gl->paths[call->pathOffset + i];
|
|
const struct NVGpath* path = &paths[i];
|
|
memset(copy, 0, sizeof(struct GLNVGpath) );
|
|
if (path->nstroke)
|
|
{
|
|
copy->strokeOffset = offset;
|
|
copy->strokeCount = path->nstroke;
|
|
memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke);
|
|
offset += path->nstroke;
|
|
}
|
|
}
|
|
|
|
// Fill shader
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe);
|
|
}
|
|
|
|
static void nvgRenderTriangles(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor,
|
|
const struct NVGvertex* verts, int nverts)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
struct GLNVGfragUniforms* frag;
|
|
|
|
call->type = GLNVG_TRIANGLES;
|
|
call->image = paint->image;
|
|
|
|
// Allocate vertices for all the paths.
|
|
call->vertexOffset = glnvg__allocVerts(gl, nverts);
|
|
call->vertexCount = nverts;
|
|
memcpy(&gl->verts[call->vertexOffset], verts, sizeof(struct NVGvertex) * nverts);
|
|
|
|
// Fill shader
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
frag = nvg__fragUniformPtr(gl, call->uniformOffset);
|
|
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f);
|
|
frag->type = NSVG_SHADER_IMG;
|
|
}
|
|
|
|
static void nvgRenderDelete(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
if (gl == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bgfx::destroyProgram(gl->prog);
|
|
|
|
bgfx::destroyUniform(gl->u_scissorMat);
|
|
bgfx::destroyUniform(gl->u_paintMat);
|
|
bgfx::destroyUniform(gl->u_innerCol);
|
|
bgfx::destroyUniform(gl->u_outerCol);
|
|
bgfx::destroyUniform(gl->u_viewSize);
|
|
bgfx::destroyUniform(gl->u_scissorExtScale);
|
|
bgfx::destroyUniform(gl->u_extentRadius);
|
|
bgfx::destroyUniform(gl->u_params);
|
|
bgfx::destroyUniform(gl->s_tex);
|
|
|
|
if (bgfx::isValid(gl->u_halfTexel) )
|
|
{
|
|
bgfx::destroyUniform(gl->u_halfTexel);
|
|
}
|
|
|
|
for (uint32_t ii = 0, num = gl->ntextures; ii < num; ++ii)
|
|
{
|
|
if (bgfx::isValid(gl->textures[ii].id)
|
|
&& (gl->textures[ii].flags & NVG_IMAGE_NODELETE) == 0)
|
|
{
|
|
bgfx::destroyTexture(gl->textures[ii].id);
|
|
}
|
|
}
|
|
|
|
free(gl->textures);
|
|
|
|
free(gl);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
NVGcontext* nvgCreate(int edgeaa, unsigned char viewid)
|
|
{
|
|
struct NVGparams params;
|
|
struct NVGcontext* ctx = NULL;
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext) );
|
|
if (gl == NULL) goto error;
|
|
memset(gl, 0, sizeof(struct GLNVGcontext) );
|
|
|
|
memset(¶ms, 0, sizeof(params) );
|
|
params.renderCreate = nvgRenderCreate;
|
|
params.renderCreateTexture = nvgRenderCreateTexture;
|
|
params.renderDeleteTexture = nvgRenderDeleteTexture;
|
|
params.renderUpdateTexture = nvgRenderUpdateTexture;
|
|
params.renderGetTextureSize = nvgRenderGetTextureSize;
|
|
params.renderViewport = nvgRenderViewport;
|
|
params.renderFlush = nvgRenderFlush;
|
|
params.renderFill = nvgRenderFill;
|
|
params.renderStroke = nvgRenderStroke;
|
|
params.renderTriangles = nvgRenderTriangles;
|
|
params.renderDelete = nvgRenderDelete;
|
|
params.userPtr = gl;
|
|
params.edgeAntiAlias = edgeaa;
|
|
|
|
gl->edgeAntiAlias = edgeaa;
|
|
gl->viewid = uint8_t(viewid);
|
|
|
|
ctx = nvgCreateInternal(¶ms);
|
|
if (ctx == NULL) goto error;
|
|
|
|
return ctx;
|
|
|
|
error:
|
|
// 'gl' is freed by nvgDeleteInternal.
|
|
if (ctx != NULL)
|
|
{
|
|
nvgDeleteInternal(ctx);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void nvgViewId(struct NVGcontext* ctx, unsigned char viewid)
|
|
{
|
|
struct NVGparams* params = nvgInternalParams(ctx);
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)params->userPtr;
|
|
gl->viewid = uint8_t(viewid);
|
|
}
|
|
|
|
void nvgDelete(struct NVGcontext* ctx)
|
|
{
|
|
nvgDeleteInternal(ctx);
|
|
}
|