diff --git a/example/demo.c b/example/demo.c index d8d3d4c..436b646 100644 --- a/example/demo.c +++ b/example/demo.c @@ -623,8 +623,8 @@ void drawColorwheel(struct NVGcontext* vg, float x, float y, float w, float h, f } nvgBeginPath(vg); - nvgCircle(vg, cx,cy, r0-1); - nvgCircle(vg, cx,cy, r1+1); + nvgCircle(vg, cx,cy, r0-0.5f); + nvgCircle(vg, cx,cy, r1+0.5f); nvgStrokeColor(vg, nvgRGBA(0,0,0,64)); nvgStrokeWidth(vg, 1.0f); nvgStroke(vg); @@ -643,8 +643,8 @@ void drawColorwheel(struct NVGcontext* vg, float x, float y, float w, float h, f paint = nvgBoxGradient(vg, r0-3,-5,r1-r0+6,10, 2,4, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0)); nvgBeginPath(vg); - nvgRect(vg, r0-3-10,-5-10,r1-r0+6+20,10+20); - nvgRect(vg, r0-3,-5,r1-r0+6,10); + nvgRect(vg, r0-2-10,-4-10,r1-r0+4+20,8+20); + nvgRect(vg, r0-2,-4,r1-r0+4,8); nvgPathWinding(vg, NVG_HOLE); nvgFillPaint(vg, paint); nvgFill(vg); diff --git a/example/example_gl2.c b/example/example_gl2.c index 30ce52f..2d915ae 100644 --- a/example/example_gl2.c +++ b/example/example_gl2.c @@ -103,7 +103,9 @@ int main() while (!glfwWindowShouldClose(window)) { double mx, my, t, dt; - int width, height; + int winWidth, winHeight; + int fbWidth, fbHeight; + float pxRatio; t = glfwGetTime(); dt = t - prevt; @@ -111,25 +113,29 @@ int main() updateFPS(&fps, dt); glfwGetCursorPos(window, &mx, &my); - glfwGetFramebufferSize(window, &width, &height); + glfwGetWindowSize(window, &winWidth, &winHeight); + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + + // Calculate pixel ration for hi-dpi devices. + pxRatio = (float)fbWidth / (float)winWidth; // Update and render - glViewport(0, 0, width, height); + glViewport(0, 0, fbWidth, fbHeight); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); - glColor4ub(255,255,255,255); - nvgBeginFrame(vg, width, height); + nvgBeginFrame(vg, winWidth, winHeight, pxRatio); - renderDemo(vg, mx,my, width,height, t, blowup, &data); + renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); renderFPS(vg, 5,5, &fps); + nvgEndFrame(vg); + glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); diff --git a/example/example_gl3.c b/example/example_gl3.c index 8a14927..5218764 100644 --- a/example/example_gl3.c +++ b/example/example_gl3.c @@ -108,7 +108,9 @@ int main() while (!glfwWindowShouldClose(window)) { double mx, my, t, dt; - int width, height; + int winWidth, winHeight; + int fbWidth, fbHeight; + float pxRatio; t = glfwGetTime(); dt = t - prevt; @@ -116,22 +118,28 @@ int main() updateFPS(&fps, dt); glfwGetCursorPos(window, &mx, &my); - glfwGetFramebufferSize(window, &width, &height); + glfwGetWindowSize(window, &winWidth, &winHeight); + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + // Calculate pixel ration for hi-dpi devices. + pxRatio = (float)fbWidth / (float)winWidth; // Update and render - glViewport(0, 0, width, height); + glViewport(0, 0, fbWidth, fbHeight); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - nvgBeginFrame(vg, width, height); + nvgBeginFrame(vg, winWidth, winHeight, pxRatio); - renderDemo(vg, mx,my, width,height, t, blowup, &data); + renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); renderFPS(vg, 5,5, &fps); + nvgEndFrame(vg); + glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); diff --git a/example/example_gles2.c b/example/example_gles2.c index 00e929b..f204237 100644 --- a/example/example_gles2.c +++ b/example/example_gles2.c @@ -90,7 +90,9 @@ int main() while (!glfwWindowShouldClose(window)) { double mx, my, t, dt; - int width, height; + int winWidth, winHeight; + int fbWidth, fbHeight; + float pxRatio; t = glfwGetTime(); dt = t - prevt; @@ -98,22 +100,28 @@ int main() updateFPS(&fps, dt); glfwGetCursorPos(window, &mx, &my); - glfwGetFramebufferSize(window, &width, &height); + glfwGetWindowSize(window, &winWidth, &winHeight); + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + // Calculate pixel ration for hi-dpi devices. + pxRatio = (float)fbWidth / (float)winWidth; // Update and render - glViewport(0, 0, width, height); + glViewport(0, 0, fbWidth, fbHeight); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - nvgBeginFrame(vg, width, height); + nvgBeginFrame(vg, winWidth, winHeight, pxRatio); - renderDemo(vg, mx,my, width,height, t, blowup, &data); + renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); renderFPS(vg, 5,5, &fps); + nvgEndFrame(vg); + glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); diff --git a/example/example_gles3.c b/example/example_gles3.c index 8d29a39..c070dd5 100644 --- a/example/example_gles3.c +++ b/example/example_gles3.c @@ -90,7 +90,9 @@ int main() while (!glfwWindowShouldClose(window)) { double mx, my, t, dt; - int width, height; + int winWidth, winHeight; + int fbWidth, fbHeight; + float pxRatio; t = glfwGetTime(); dt = t - prevt; @@ -98,22 +100,28 @@ int main() updateFPS(&fps, dt); glfwGetCursorPos(window, &mx, &my); - glfwGetFramebufferSize(window, &width, &height); + glfwGetWindowSize(window, &winWidth, &winHeight); + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + // Calculate pixel ration for hi-dpi devices. + pxRatio = (float)fbWidth / (float)winWidth; // Update and render - glViewport(0, 0, width, height); + glViewport(0, 0, fbWidth, fbHeight); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - nvgBeginFrame(vg, width, height); + nvgBeginFrame(vg, winWidth, winHeight, pxRatio); - renderDemo(vg, mx,my, width,height, t, blowup, &data); + renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); renderFPS(vg, 5,5, &fps); + nvgEndFrame(vg); + glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); diff --git a/src/nanovg.c b/src/nanovg.c index f41f05b..a0362e2 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -28,7 +28,6 @@ #define NVG_INIT_PATH_SIZE 256 #define NVG_MAX_STATES 32 -#define NVG_AA 1.0f #define NVG_KAPPA90 0.5522847493f // Lenght proportional to radius of a cubic bezier handle for 90deg arcs. #define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr])) @@ -101,6 +100,8 @@ struct NVGcontext { struct NVGpathCache* cache; float tessTol; float distTol; + float fringeWidth; + float devicePxRatio; struct FONScontext* fs; int fontImage; int drawCallCount; @@ -173,6 +174,13 @@ error: return NULL; } +static void nvg__setDevicePixelRatio(struct NVGcontext* ctx, float ratio) +{ + ctx->tessTol = 0.3f * 4.0f / ratio; + ctx->distTol = 0.01f / ratio; + ctx->fringeWidth = 1.0f / ratio; + ctx->devicePxRatio = ratio; +} struct NVGcontext* nvgCreateInternal(struct NVGparams* params) { @@ -194,8 +202,7 @@ struct NVGcontext* nvgCreateInternal(struct NVGparams* params) nvgSave(ctx); nvgReset(ctx); - ctx->tessTol = 0.3f * 4.0f; - ctx->distTol = 0.01f; + nvg__setDevicePixelRatio(ctx, 1.0f); if (ctx->params.renderCreate(ctx->params.userPtr) == 0) goto error; @@ -238,13 +245,19 @@ void nvgDeleteInternal(struct NVGcontext* ctx) free(ctx); } -void nvgBeginFrame(struct NVGcontext* ctx, int width, int height) +void nvgBeginFrame(struct NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio) { /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/ + + ctx->nstates = 0; + nvgSave(ctx); + nvgReset(ctx); + + nvg__setDevicePixelRatio(ctx, devicePixelRatio); - ctx->params.renderViewport(ctx->params.userPtr, width, height); + ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight); ctx->drawCallCount = 0; ctx->fillTriCount = 0; @@ -252,6 +265,11 @@ void nvgBeginFrame(struct NVGcontext* ctx, int width, int height) ctx->textTriCount = 0; } +void nvgEndFrame(struct NVGcontext* ctx) +{ + ctx->params.renderFlush(ctx->params.userPtr); +} + unsigned int nvgRGB(unsigned char r, unsigned char g, unsigned char b) { return nvgRGBA(r,g,b,255); @@ -1084,6 +1102,7 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w) int convex = 0; int i, j, s, e; float wo = 0; + float aa = ctx->fringeWidth; // Calculate max vertex usage. cverts = 0; @@ -1110,7 +1129,7 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w) // Calculate shape vertices. if (feats & NVG_FILL) { - wo = 0.5f; + wo = 0.5f*aa; dst = verts; path->fill = dst; @@ -1196,8 +1215,8 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w) nvg__normalize(&dx, &dy); dlx = dy; dly = -dx; - nvg__vset(dst, p0->x + dlx*rw - dx*NVG_AA, p0->y + dly*rw - dy*NVG_AA, u0,0); dst++; - nvg__vset(dst, p0->x - dlx*lw - dx*NVG_AA, p0->y - dly*lw - dy*NVG_AA, u1,0); dst++; + nvg__vset(dst, p0->x + dlx*rw - dx*aa, p0->y + dly*rw - dy*aa, u0,0); dst++; + nvg__vset(dst, p0->x - dlx*lw - dx*aa, p0->y - dly*lw - dy*aa, u1,0); dst++; nvg__vset(dst, p0->x + dlx*rw, p0->y + dly * rw, u0,1); dst++; nvg__vset(dst, p0->x - dlx*lw, p0->y - dly * lw, u1,1); dst++; } @@ -1252,8 +1271,8 @@ static int nvg__expandStrokeAndFill(struct NVGcontext* ctx, int feats, float w) dly = -dx; nvg__vset(dst, p1->x + dlx*rw, p1->y + dly * rw, u0,1); dst++; nvg__vset(dst, p1->x - dlx*lw, p1->y - dly * lw, u1,1); dst++; - nvg__vset(dst, p1->x + dlx*rw + dx*NVG_AA, p1->y + dly*rw + dy*NVG_AA, u0,0); dst++; - nvg__vset(dst, p1->x - dlx*lw + dx*NVG_AA, p1->y - dly*lw + dy*NVG_AA, u1,0); dst++; + nvg__vset(dst, p1->x + dlx*rw + dx*aa, p1->y + dly*rw + dy*aa, u0,0); dst++; + nvg__vset(dst, p1->x - dlx*lw + dx*aa, p1->y - dly*lw + dy*aa, u1,0); dst++; } path->nstroke = (int)(dst - verts); @@ -1488,11 +1507,11 @@ void nvgFill(struct NVGcontext* ctx) nvg__flattenPaths(ctx, state->miterLimit); if (ctx->params.edgeAntiAlias) - nvg__expandStrokeAndFill(ctx, NVG_FILL|NVG_STROKE, NVG_AA); + nvg__expandStrokeAndFill(ctx, NVG_FILL|NVG_STROKE, ctx->fringeWidth); else nvg__expandStrokeAndFill(ctx, NVG_FILL, 0.0f); - ctx->params.renderFill(ctx->params.userPtr, &state->fill, &state->scissor, NVG_AA, + ctx->params.renderFill(ctx->params.userPtr, &state->fill, &state->scissor, 1.0f, ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); // Count triangles @@ -1514,11 +1533,11 @@ void nvgStroke(struct NVGcontext* ctx) nvg__flattenPaths(ctx, state->miterLimit); if (ctx->params.edgeAntiAlias) - nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f + NVG_AA/2.0f); + nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f + ctx->fringeWidth/2.0f); else nvg__expandStrokeAndFill(ctx, NVG_STROKE|NVG_CAPS, strokeWidth*0.5f); - ctx->params.renderStroke(ctx->params.userPtr, &state->stroke, &state->scissor, NVG_AA, + ctx->params.renderStroke(ctx->params.userPtr, &state->stroke, &state->scissor, 1.0f, strokeWidth, ctx->cache->paths, ctx->cache->npaths); // Count triangles @@ -1599,7 +1618,7 @@ float nvgText(struct NVGcontext* ctx, float x, float y, const char* string, cons struct FONStextIter iter; struct FONSquad q; struct NVGvertex* verts; - float scale = nvg__getFontScale(state); + float scale = nvg__getFontScale(state) * ctx->devicePxRatio; float invscale = 1.0f / scale; int dirty[4]; int cverts = 0; diff --git a/src/nanovg.h b/src/nanovg.h index 06b68b6..c63807b 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -66,9 +66,18 @@ enum NVGaling { }; -// Called at the beginning of a frame. -// -void nvgBeginFrame(struct NVGcontext* ctx, int width, int height); +// Begin drawing a new frame +// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() +// nvgBeginFrame() defines the size of the window to render to in relation currently +// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to +// control the rendering on Hi-DPI devices. +// For example, GLFW returns two dimension for an opened window: window size and +// frame buffer size. In that case you would set windowWidth/Height to the window size +// devicePixelRatio to: frameBufferWidth / windowWidth. +void nvgBeginFrame(struct NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); + +// Ends drawing flushing remaining render state. +void nvgEndFrame(struct NVGcontext* ctx); // // Color utils @@ -405,6 +414,7 @@ struct NVGparams { int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); void (*renderViewport)(void* uptr, int width, int height); + void (*renderFlush)(void* uptr); void (*renderFill)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float aasize, const float* bounds, const struct NVGpath* paths, int npaths); void (*renderStroke)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float aasize, float strokeWidth, const struct NVGpath* paths, int npaths); void (*renderTriangles)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, int image, const struct NVGvertex* verts, int nverts); diff --git a/src/nanovg_gl2.h b/src/nanovg_gl2.h index 942ec93..a0e5397 100644 --- a/src/nanovg_gl2.h +++ b/src/nanovg_gl2.h @@ -609,6 +609,12 @@ static void glnvg__renderViewport(void* uptr, int width, int height) gl->viewHeight = height; } +static void glnvg__renderFlush(void* uptr) +{ +// struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; + // empty +} + static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) { int i, count = 0; @@ -883,6 +889,7 @@ struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa) params.renderUpdateTexture = glnvg__renderUpdateTexture; params.renderGetTextureSize = glnvg__renderGetTextureSize; params.renderViewport = glnvg__renderViewport; + params.renderFlush = glnvg__renderFlush; params.renderFill = glnvg__renderFill; params.renderStroke = glnvg__renderStroke; params.renderTriangles = glnvg__renderTriangles; diff --git a/src/nanovg_gl3.h b/src/nanovg_gl3.h index e5e7952..34d6ee1 100644 --- a/src/nanovg_gl3.h +++ b/src/nanovg_gl3.h @@ -611,6 +611,12 @@ static void glnvg__renderViewport(void* uptr, int width, int height) gl->viewHeight = height; } +static void glnvg__renderFlush(void* uptr) +{ +// struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; + // empty +} + static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) { int i, count = 0; @@ -889,6 +895,7 @@ struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa) params.renderUpdateTexture = glnvg__renderUpdateTexture; params.renderGetTextureSize = glnvg__renderGetTextureSize; params.renderViewport = glnvg__renderViewport; + params.renderFlush = glnvg__renderFlush; params.renderFill = glnvg__renderFill; params.renderStroke = glnvg__renderStroke; params.renderTriangles = glnvg__renderTriangles;