diff --git a/example/example_gl2.c b/example/example_gl2.c index 89e2538..7500255 100644 --- a/example/example_gl2.c +++ b/example/example_gl2.c @@ -24,6 +24,7 @@ #include "nanovg.h" #define NANOVG_GL2_IMPLEMENTATION #include "nanovg_gl.h" +#include "nanovg_gl_utils.h" #include "demo.h" #include "perf.h" @@ -56,8 +57,10 @@ int main() GLFWwindow* window; struct DemoData data; struct NVGcontext* vg = NULL; + struct NVGLUframebuffer fb; struct PerfGraph fps; double prevt = 0; + int hasFBO; if (!glfwInit()) { printf("Failed to init GLFW."); @@ -74,7 +77,7 @@ int main() glfwWindowHint(GLFW_SAMPLES, 4); #endif - window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); + window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); if (!window) { glfwTerminate(); @@ -109,6 +112,8 @@ int main() glfwSetTime(0); prevt = glfwGetTime(); + hasFBO = nvgluCreateFramebuffer(vg, &fb, 600, 600); + while (!glfwWindowShouldClose(window)) { double mx, my, t, dt; @@ -116,6 +121,20 @@ int main() int fbWidth, fbHeight; float pxRatio; + if (hasFBO) { + int fboWidth, fboHeight; + nvgImageSize(vg, fb.image, &fboWidth, &fboHeight); + // Draw some stull to an FBO as a test + glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); + glViewport(0, 0, fboWidth, fboHeight); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); + nvgBeginFrame(vg, fboWidth, fboHeight, pxRatio, NVG_PREMULTIPLIED_ALPHA); + renderDemo(vg, mx, my, fboWidth, fboHeight, t, blowup, &data); + nvgEndFrame(vg); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + t = glfwGetTime(); dt = t - prevt; prevt = t; @@ -141,6 +160,15 @@ int main() renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); renderGraph(vg, 5,5, &fps); + if (hasFBO) { + struct NVGpaint img = nvgImagePattern(vg, 0, 0, 150, 150, 0, fb.image, 0); + nvgBeginPath(vg); + nvgTranslate(vg, 540, 300); + nvgRect(vg, 0, 0, 150, 150); + nvgFillPaint(vg, img); + nvgFill(vg); + } + nvgEndFrame(vg); if (screenshot) { @@ -154,6 +182,7 @@ int main() freeDemoData(vg, &data); + nvgluDeleteFramebuffer(vg, &fb); nvgDeleteGL2(vg); glfwTerminate(); diff --git a/src/nanovg.c b/src/nanovg.c index 1e667a6..0b87101 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -236,6 +236,11 @@ error: return 0; } +struct NVGparams* nvgInternalParams(struct NVGcontext* ctx) +{ + return &ctx->params; +} + void nvgDeleteInternal(struct NVGcontext* ctx) { if (ctx == NULL) return; diff --git a/src/nanovg.h b/src/nanovg.h index 436ea54..03e09ae 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -580,6 +580,8 @@ struct NVGparams { struct NVGcontext* nvgCreateInternal(struct NVGparams* params); void nvgDeleteInternal(struct NVGcontext* ctx); +struct NVGparams* nvgInternalParams(struct NVGcontext* ctx); + // Debug function to dump cached path data. void nvgDebugDumpPathCache(struct NVGcontext* ctx); diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index dddc696..0f9e2de 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -15,8 +15,8 @@ // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // -#ifndef NANOVG_GL3_H -#define NANOVG_GL3_H +#ifndef NANOVG_GL_H +#define NANOVG_GL_H #ifdef __cplusplus extern "C" { @@ -38,7 +38,7 @@ extern "C" { # define NANOVG_GLES3 1 # define NANOVG_GL_IMPLEMENTATION 1 #endif - + #if defined NANOVG_GL2 @@ -62,11 +62,21 @@ void nvgDeleteGLES3(struct NVGcontext* ctx); #endif +enum NVGLtextureflags { + NVGL_TEXTURE_FLIP_Y = 0x01, + NVGL_TEXTURE_NODELETE = 0x02, +}; + +int nvglCreateImageFromHandle(struct NVGcontext* ctx, GLuint textureId, int flags); +GLuint nvglImageHandle(struct NVGcontext* ctx, int image); +void nvglImageFlags(struct NVGcontext* ctx, int image, int flags); + + #ifdef __cplusplus } #endif -#endif +#endif /* NANOVG_GL_H */ #ifdef NANOVG_GL_IMPLEMENTATION @@ -123,6 +133,7 @@ struct GLNVGtexture { GLuint tex; int width, height; int type; + int flags; }; enum GLNVGcallType { @@ -242,7 +253,7 @@ static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id) int i; for (i = 0; i < gl->ntextures; i++) { if (gl->textures[i].id == id) { - if (gl->textures[i].tex != 0) + if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVGL_TEXTURE_NODELETE) == 0) glDeleteTextures(1, &gl->textures[i].tex); memset(&gl->textures[i], 0, sizeof(gl->textures[i])); return 1; @@ -707,9 +718,6 @@ static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms frag->innerCol = paint->innerColor; frag->outerCol = paint->outerColor; - nvgTransformInverse(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; @@ -724,19 +732,32 @@ static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms 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; if (paint->image != 0) { tex = glnvg__findTexture(gl, paint->image); if (tex == NULL) return 0; + if ((tex->flags & NVGL_TEXTURE_FLIP_Y) != 0) { + float flipped[6]; + nvgTransformScale(flipped, 1.0f, -1.0f); + nvgTransformMultiply(flipped, paint->xform); + nvgTransformInverse(invxform, flipped); + } else { + nvgTransformInverse(invxform, paint->xform); + } frag->type = NSVG_SHADER_FILLIMG; frag->texType = tex->type == NVG_TEXTURE_RGBA ? 0 : 1; } else { frag->type = NSVG_SHADER_FILLGRAD; frag->radius = paint->radius; frag->feather = paint->feather; + nvgTransformInverse(invxform, paint->xform); } + + glnvg__xformToMat3x4(frag->paintMat, invxform); + return 1; } @@ -1226,7 +1247,7 @@ static void glnvg__renderDelete(void* uptr) glDeleteBuffers(1, &gl->vertBuf); for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].tex != 0) + if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVGL_TEXTURE_NODELETE) == 0) glDeleteTextures(1, &gl->textures[i].tex); } free(gl->textures); @@ -1294,4 +1315,36 @@ void nvgDeleteGLES3(struct NVGcontext* ctx) nvgDeleteInternal(ctx); } -#endif +int nvglCreateImageFromHandle(struct NVGcontext* ctx, GLuint textureId, int flags) +{ + struct GLNVGcontext* gl = (struct GLNVGcontext*)nvgInternalParams(ctx)->userPtr; + struct GLNVGtexture* tex = glnvg__allocTexture(gl); + + if (tex == NULL) return 0; + + tex->tex = textureId; + tex->type = NVG_TEXTURE_RGBA; + tex->flags = 0; + glBindTexture(GL_TEXTURE_2D, tex->tex); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex->width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex->height); + glBindTexture(GL_TEXTURE_2D, 0); + + return tex->id; +} + +GLuint nvglImageHandle(struct NVGcontext* ctx, int image) +{ + struct GLNVGcontext* gl = (struct GLNVGcontext*)nvgInternalParams(ctx)->userPtr; + struct GLNVGtexture* tex = glnvg__findTexture(gl, image); + return tex->tex; +} + +void nvglImageFlags(struct NVGcontext* ctx, int image, int flags) +{ + struct GLNVGcontext* gl = (struct GLNVGcontext*)nvgInternalParams(ctx)->userPtr; + struct GLNVGtexture* tex = glnvg__findTexture(gl, image); + tex->flags = flags; +} + +#endif /* NANOVG_GL_IMPLEMENTATION */ diff --git a/src/nanovg_gl_utils.h b/src/nanovg_gl_utils.h new file mode 100644 index 0000000..e2b6b5f --- /dev/null +++ b/src/nanovg_gl_utils.h @@ -0,0 +1,61 @@ +#ifndef gl_utils_h +#define gl_utils_h + + +struct NVGLUframebuffer { + struct NVGcontext* ctx; + GLuint fbo; + GLuint rbo; + GLuint texture; + int image; +}; + +int nvgluCreateFramebuffer(struct NVGcontext* ctx, struct NVGLUframebuffer* fb, int w, int h); +void nvgluDeleteFramebuffer(struct NVGcontext* ctx, struct NVGLUframebuffer* fb); + + +#endif /* gl_utils_h */ + +#ifdef NANOVG_GL_IMPLEMENTATION + +int nvgluCreateFramebuffer(struct NVGcontext* ctx, struct NVGLUframebuffer* fb, int w, int h) { + fb->image = nvgCreateImageRGBA(ctx, w, h, NULL); + fb->texture = nvglImageHandle(ctx, fb->image); + nvglImageFlags(ctx, fb->image, NVGL_TEXTURE_FLIP_Y); + + /* frame buffer object */ + glGenFramebuffers(1, &fb->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); + + /* render buffer object */ + glGenRenderbuffers(1, &fb->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + + /* combine all */ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + nvgluDeleteFramebuffer(ctx, fb); + return 0; + } + return 1; +} + +void nvgluDeleteFramebuffer(struct NVGcontext* ctx, struct NVGLUframebuffer* fb) { + if (fb->fbo != 0) + glDeleteFramebuffers(1, &fb->fbo); + if (fb->rbo != 0) + glDeleteRenderbuffers(1, &fb->rbo); + if (fb->image >= 0) + nvgDeleteImage(ctx, fb->image); + fb->fbo = 0; + fb->rbo = 0; + fb->texture = 0; + fb->image = -1; +} + + +#endif /* NANOVG_GL_IMPLEMENTATION */ +/* vim: set ft=c nu noet ts=4 sw=4 : */