Browse Source

Fix for issue #92

- added (optional) 3-pass stencil trick to stroke rendering, reduces
overdraw artifacts, a little slower
- fixed some text bounds issues
shared-context
Mikko Mononen 11 years ago
parent
commit
003561e721
6 changed files with 101 additions and 35 deletions
  1. +2
    -2
      example/example_gl2.c
  2. +2
    -2
      example/example_gl3.c
  3. +1
    -1
      example/example_gles2.c
  4. +1
    -1
      example/example_gles3.c
  5. +3
    -1
      src/nanovg.c
  6. +92
    -28
      src/nanovg_gl.h

+ 2
- 2
example/example_gl2.c View File

@@ -94,9 +94,9 @@ int main()
#endif #endif


#ifdef DEMO_MSAA #ifdef DEMO_MSAA
vg = nvgCreateGL2(512, 512, 0);
vg = nvgCreateGL2(512, 512, NVG_STENCIL_STROKES);
#else #else
vg = nvgCreateGL2(512, 512, NVG_ANTIALIAS);
vg = nvgCreateGL2(512, 512, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
#endif #endif
if (vg == NULL) { if (vg == NULL) {
printf("Could not init nanovg.\n"); printf("Could not init nanovg.\n");


+ 2
- 2
example/example_gl3.c View File

@@ -105,9 +105,9 @@ int main()
#endif #endif


#ifdef DEMO_MSAA #ifdef DEMO_MSAA
vg = nvgCreateGL3(512, 512, 0);
vg = nvgCreateGL3(512, 512, NVG_STENCIL_STROKES);
#else #else
vg = nvgCreateGL3(512, 512, NVG_ANTIALIAS);
vg = nvgCreateGL3(512, 512, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
#endif #endif
if (vg == NULL) { if (vg == NULL) {
printf("Could not init nanovg.\n"); printf("Could not init nanovg.\n");


+ 1
- 1
example/example_gles2.c View File

@@ -82,7 +82,7 @@ int main()


glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);


vg = nvgCreateGLES2(512, 512, NVG_ANTIALIAS);
vg = nvgCreateGLES2(512, 512, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
if (vg == NULL) { if (vg == NULL) {
printf("Could not init nanovg.\n"); printf("Could not init nanovg.\n");
return -1; return -1;


+ 1
- 1
example/example_gles3.c View File

@@ -82,7 +82,7 @@ int main()


glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);


vg = nvgCreateGLES3(512, 512, NVG_ANTIALIAS);
vg = nvgCreateGLES3(512, 512, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
if (vg == NULL) { if (vg == NULL) {
printf("Could not init nanovg.\n"); printf("Could not init nanovg.\n");
return -1; return -1;


+ 3
- 1
src/nanovg.c View File

@@ -2484,8 +2484,10 @@ float nvgTextBounds(struct NVGcontext* ctx, float x, float y, const char* string
fonsSetAlign(ctx->fs, state->textAlign); fonsSetAlign(ctx->fs, state->textAlign);
fonsSetFont(ctx->fs, state->fontId); fonsSetFont(ctx->fs, state->fontId);


width = fonsTextBounds(ctx->fs, x, y, string, end, bounds);
width = fonsTextBounds(ctx->fs, x*scale, y*scale, string, end, bounds);
if (bounds != NULL) { if (bounds != NULL) {
// Use line bounds for height.
fonsLineBounds(ctx->fs, y*scale, &bounds[1], &bounds[3]);
bounds[0] *= invscale; bounds[0] *= invscale;
bounds[1] *= invscale; bounds[1] *= invscale;
bounds[2] *= invscale; bounds[2] *= invscale;


+ 92
- 28
src/nanovg_gl.h View File

@@ -22,7 +22,14 @@
extern "C" { extern "C" {
#endif #endif


#define NVG_ANTIALIAS 1
// Create flags

// Flag indicating if geoemtry based anti-aliasing is used (may not be needed when using MSAA).
#define NVG_ANTIALIAS 1

// Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little
// slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once.
#define NVG_STENCIL_STROKES 2


#if defined NANOVG_GL2_IMPLEMENTATION #if defined NANOVG_GL2_IMPLEMENTATION
# define NANOVG_GL2 1 # define NANOVG_GL2 1
@@ -40,14 +47,17 @@ extern "C" {
#endif #endif




// Creates NanoVG contexts for different OpenGL (ES) versions.
// Flags should be combination of the create flags above.

#if defined NANOVG_GL2 #if defined NANOVG_GL2


struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa);
struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int flags);
void nvgDeleteGL2(struct NVGcontext* ctx); void nvgDeleteGL2(struct NVGcontext* ctx);


#elif defined NANOVG_GL3 #elif defined NANOVG_GL3


struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa);
struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int flags);
void nvgDeleteGL3(struct NVGcontext* ctx); void nvgDeleteGL3(struct NVGcontext* ctx);


#elif defined NANOVG_GLES2 #elif defined NANOVG_GLES2
@@ -102,6 +112,7 @@ enum GLNVGuniformLoc {
GLNVG_LOC_INNERCOL, GLNVG_LOC_INNERCOL,
GLNVG_LOC_OUTERCOL, GLNVG_LOC_OUTERCOL,
GLNVG_LOC_STROKEMULT, GLNVG_LOC_STROKEMULT,
GLNVG_LOC_STROKETHR,
GLNVG_LOC_TEXTYPE, GLNVG_LOC_TEXTYPE,
GLNVG_LOC_TYPE, GLNVG_LOC_TYPE,
#endif #endif
@@ -172,6 +183,7 @@ struct GLNVGfragUniforms {
float radius; float radius;
float feather; float feather;
float strokeMult; float strokeMult;
float strokeThr;
int texType; int texType;
int type; int type;
}; };
@@ -191,7 +203,7 @@ struct GLNVGcontext {
GLuint fragBuf; GLuint fragBuf;
#endif #endif
int fragSize; int fragSize;
int edgeAntiAlias;
int flags;


// Per frame buffers // Per frame buffers
struct GLNVGcall* calls; struct GLNVGcall* calls;
@@ -372,6 +384,7 @@ static void glnvg__getUniforms(struct GLNVGshader* shader)
shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol"); shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol");
shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol"); shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol");
shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult"); shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult");
shader->loc[GLNVG_LOC_STROKETHR] = glGetUniformLocation(shader->prog, "strokeThr");
shader->loc[GLNVG_LOC_TEXTYPE] = glGetUniformLocation(shader->prog, "texType"); shader->loc[GLNVG_LOC_TEXTYPE] = glGetUniformLocation(shader->prog, "texType");
shader->loc[GLNVG_LOC_TYPE] = glGetUniformLocation(shader->prog, "type"); shader->loc[GLNVG_LOC_TYPE] = glGetUniformLocation(shader->prog, "type");
#endif #endif
@@ -441,6 +454,7 @@ static int glnvg__renderCreate(void* uptr)
" float radius;\n" " float radius;\n"
" float feather;\n" " float feather;\n"
" float strokeMult;\n" " float strokeMult;\n"
" float strokeThr;\n"
" int texType;\n" " int texType;\n"
" int type;\n" " int type;\n"
" };\n" " };\n"
@@ -455,6 +469,7 @@ static int glnvg__renderCreate(void* uptr)
" uniform float radius;\n" " uniform float radius;\n"
" uniform float feather;\n" " uniform float feather;\n"
" uniform float strokeMult;\n" " uniform float strokeMult;\n"
" uniform float strokeThr;\n"
" uniform int texType;\n" " uniform int texType;\n"
" uniform int type;\n" " uniform int type;\n"
"#endif\n" "#endif\n"
@@ -473,6 +488,7 @@ static int glnvg__renderCreate(void* uptr)
" uniform float radius;\n" " uniform float radius;\n"
" uniform float feather;\n" " uniform float feather;\n"
" uniform float strokeMult;\n" " uniform float strokeMult;\n"
" uniform float strokeThr;\n"
" uniform int texType;\n" " uniform int texType;\n"
" uniform int type;\n" " uniform int type;\n"
" uniform sampler2D tex;\n" " uniform sampler2D tex;\n"
@@ -504,6 +520,7 @@ static int glnvg__renderCreate(void* uptr)
" float scissor = scissorMask(fpos);\n" " float scissor = scissorMask(fpos);\n"
"#ifdef EDGE_AA\n" "#ifdef EDGE_AA\n"
" float strokeAlpha = strokeMask();\n" " float strokeAlpha = strokeMask();\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#else\n" "#else\n"
" float strokeAlpha = 1.0;\n" " float strokeAlpha = 1.0;\n"
"#endif\n" "#endif\n"
@@ -550,7 +567,7 @@ static int glnvg__renderCreate(void* uptr)


glnvg__checkError("init"); glnvg__checkError("init");


if (gl->edgeAntiAlias) {
if (gl->flags & NVG_ANTIALIAS) {
if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0) if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0)
return 0; return 0;
} else { } else {
@@ -710,7 +727,7 @@ static void glnvg__xformToMat3x4(float* m3, float* t)
} }


static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms* frag, struct NVGpaint* paint, static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms* frag, struct NVGpaint* paint,
struct NVGscissor* scissor, float width, float fringe)
struct NVGscissor* scissor, float width, float fringe, float strokeThr)
{ {
struct GLNVGtexture* tex = NULL; struct GLNVGtexture* tex = NULL;
float invxform[6]; float invxform[6];
@@ -737,6 +754,7 @@ static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms


memcpy(frag->extent, paint->extent, sizeof(frag->extent)); memcpy(frag->extent, paint->extent, sizeof(frag->extent));
frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe;
frag->strokeThr = strokeThr;


if (paint->image != 0) { if (paint->image != 0) {
tex = glnvg__findTexture(gl, paint->image); tex = glnvg__findTexture(gl, paint->image);
@@ -801,6 +819,7 @@ static void glnvg__setUniforms(struct GLNVGcontext* gl, int uniformOffset, int i
glUniform1f(gl->shader.loc[GLNVG_LOC_RADIUS], frag->radius); glUniform1f(gl->shader.loc[GLNVG_LOC_RADIUS], frag->radius);
glUniform1f(gl->shader.loc[GLNVG_LOC_FEATHER], frag->feather); glUniform1f(gl->shader.loc[GLNVG_LOC_FEATHER], frag->feather);
glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], frag->strokeMult); glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], frag->strokeMult);
glUniform1f(gl->shader.loc[GLNVG_LOC_STROKETHR], frag->strokeThr);
glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], frag->texType); glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], frag->texType);
glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], frag->type); glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], frag->type);
#endif #endif
@@ -830,7 +849,7 @@ static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call)
// Draw shapes // Draw shapes
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glStencilMask(0xff); glStencilMask(0xff);
glStencilFunc(GL_ALWAYS, 0, ~0L);
glStencilFunc(GL_ALWAYS, 0, 0xff);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);


// set bindpoint for solid loc // set bindpoint for solid loc
@@ -844,13 +863,13 @@ static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call)
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);


// Draw aliased off-pixels
// Draw anti-aliased pixels
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);


glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image);
glnvg__checkError("fill fill"); glnvg__checkError("fill fill");


if (gl->edgeAntiAlias) {
if (gl->flags & NVG_ANTIALIAS) {
glStencilFunc(GL_EQUAL, 0x00, 0xff); glStencilFunc(GL_EQUAL, 0x00, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Draw fringes // Draw fringes
@@ -876,7 +895,7 @@ static void glnvg__convexFill(struct GLNVGcontext* gl, struct GLNVGcall* call)


for (i = 0; i < npaths; i++) for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
if (gl->edgeAntiAlias) {
if (gl->flags & NVG_ANTIALIAS) {
// Draw fringes // Draw fringes
for (i = 0; i < npaths; i++) for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
@@ -888,12 +907,46 @@ static void glnvg__stroke(struct GLNVGcontext* gl, struct GLNVGcall* call)
struct GLNVGpath* paths = &gl->paths[call->pathOffset]; struct GLNVGpath* paths = &gl->paths[call->pathOffset];
int npaths = call->pathCount, i; int npaths = call->pathCount, i;


glnvg__setUniforms(gl, call->uniformOffset, call->image);
glnvg__checkError("stroke fill");
if (gl->flags & NVG_STENCIL_STROKES) {


// Draw Strokes
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
glEnable(GL_STENCIL_TEST);
glStencilMask(0xff);

// Fill the stroke base without overlap
glStencilFunc(GL_EQUAL, 0x0, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image);
glnvg__checkError("stroke fill 0");
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);

// Draw anti-aliased pixels.
glnvg__setUniforms(gl, call->uniformOffset, call->image);
glStencilFunc(GL_EQUAL, 0x00, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);

// Clear stencil buffer.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
glnvg__checkError("stroke fill 1");
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

glDisable(GL_STENCIL_TEST);

// glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f);

} else {
glnvg__setUniforms(gl, call->uniformOffset, call->image);
glnvg__checkError("stroke fill");
// Draw Strokes
for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
}
} }


static void glnvg__triangles(struct GLNVGcontext* gl, struct GLNVGcall* call) static void glnvg__triangles(struct GLNVGcontext* gl, struct GLNVGcall* call)
@@ -1132,14 +1185,15 @@ static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscis
// Simple shader for stencil // Simple shader for stencil
frag = nvg__fragUniformPtr(gl, call->uniformOffset); frag = nvg__fragUniformPtr(gl, call->uniformOffset);
memset(frag, 0, sizeof(*frag)); memset(frag, 0, sizeof(*frag));
frag->strokeThr = -1.0f;
frag->type = NSVG_SHADER_SIMPLE; frag->type = NSVG_SHADER_SIMPLE;
// Fill shader // Fill shader
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe);
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f);
} else { } else {
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
// Fill shader // Fill shader
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe);
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f);
} }


return; return;
@@ -1182,10 +1236,20 @@ static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGsc
} }
} }


// Fill shader
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error;
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe);
if (gl->flags & NVG_STENCIL_STROKES) {
// Fill shader
call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
if (call->uniformOffset == -1) goto error;

glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f);

} else {
// Fill shader
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error;
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
}


return; return;


@@ -1218,7 +1282,7 @@ static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NV
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
frag = nvg__fragUniformPtr(gl, call->uniformOffset); frag = nvg__fragUniformPtr(gl, call->uniformOffset);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
frag->type = NSVG_SHADER_IMG; frag->type = NSVG_SHADER_IMG;


return; return;
@@ -1259,13 +1323,13 @@ static void glnvg__renderDelete(void* uptr)




#if defined NANOVG_GL2 #if defined NANOVG_GL2
struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa)
struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int flags)
#elif defined NANOVG_GL3 #elif defined NANOVG_GL3
struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa)
struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int flags)
#elif defined NANOVG_GLES2 #elif defined NANOVG_GLES2
struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int edgeaa)
struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int flags)
#elif defined NANOVG_GLES3 #elif defined NANOVG_GLES3
struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa)
struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int flags)
#endif #endif
{ {
struct NVGparams params; struct NVGparams params;
@@ -1289,9 +1353,9 @@ struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa)
params.userPtr = gl; params.userPtr = gl;
params.atlasWidth = atlasw; params.atlasWidth = atlasw;
params.atlasHeight = atlash; params.atlasHeight = atlash;
params.edgeAntiAlias = edgeaa;
params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0;


gl->edgeAntiAlias = edgeaa;
gl->flags = flags;


ctx = nvgCreateInternal(&params); ctx = nvgCreateInternal(&params);
if (ctx == NULL) goto error; if (ctx == NULL) goto error;


Loading…
Cancel
Save