- fixed off-by-one pixels from demo - added device-pixel-ratio support (retina support) - added nvgEndFrame()shared-context
@@ -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); | |||
@@ -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); | |||
@@ -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); | |||
@@ -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); | |||
@@ -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); | |||
@@ -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; | |||
@@ -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); | |||
@@ -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; | |||
@@ -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; | |||