From c4b865796db4b09390ee426ce014b32802f398fc Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Tue, 9 Aug 2016 16:12:08 +0800 Subject: [PATCH] Implements composite operation for blending rendering between frames. This commit implements the `nvgGlobalCompositeOperation()` function to support blending between frames. All operations defined in HTML5 canvas API are supported. Also, it is possible to create custom composite operation by calling `nvgBlendFunc()` or `nvgBlendFuncSeparate()` functions. --- src/nanovg.c | 19 ++++++++++++++++++ src/nanovg.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nanovg_gl.h | 42 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/nanovg.c b/src/nanovg.c index 43371ec..2ac3503 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -338,6 +338,25 @@ void nvgEndFrame(NVGcontext* ctx) } } +NVGcompositeOperation nvgBlendFunc(int sfactor, int dfactor) +{ + return nvgBlendFuncSeparate(sfactor, dfactor, sfactor, dfactor); +} + +NVGcompositeOperation nvgBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) +{ + NVGcompositeOperation operation; + operation.srcRGB = srcRGB; + operation.dstRGB = dstRGB; + operation.srcAlpha = srcAlpha; + operation.dstAlpha = dstAlpha; + return operation; +} + +void nvgGlobalCompositeOperation(NVGcontext* ctx, NVGcompositeOperation op) { + ctx->params.renderCompositeOperation(ctx->params.userPtr, op); +} + NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b) { return nvgRGBA(r,g,b,255); diff --git a/src/nanovg.h b/src/nanovg.h index 603b8e3..3c2c4a4 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -83,6 +83,41 @@ enum NVGalign { NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. }; +enum NVGblendFactor { + NVG_ZERO = 1<<0, + NVG_ONE = 1<<1, + NVG_SRC_COLOR = 1<<2, + NVG_ONE_MINUS_SRC_COLOR = 1<<3, + NVG_DST_COLOR = 1<<4, + NVG_ONE_MINUS_DST_COLOR = 1<<5, + NVG_SRC_ALPHA = 1<<6, + NVG_ONE_MINUS_SRC_ALPHA = 1<<7, + NVG_DST_ALPHA = 1<<8, + NVG_ONE_MINUS_DST_ALPHA = 1<<9, + NVG_SRC_ALPHA_SATURATE = 1<<10, +}; + +struct NVGcompositeOperation { + int srcRGB; + int dstRGB; + int srcAlpha; + int dstAlpha; +}; +typedef struct NVGcompositeOperation NVGcompositeOperation; + +// Predefined composite operations. +#define NVG_SOURCE_OVER nvgBlendFunc(NVG_ONE, NVG_ONE_MINUS_SRC_ALPHA) +#define NVG_SOURCE_IN nvgBlendFunc(NVG_DST_ALPHA, NVG_ZERO) +#define NVG_SOURCE_OUT nvgBlendFunc(NVG_ONE_MINUS_DST_ALPHA, NVG_ZERO) +#define NVG_ATOP nvgBlendFunc(NVG_DST_ALPHA, NVG_ONE_MINUS_SRC_ALPHA) +#define NVG_DESTINATION_OVER nvgBlendFunc(NVG_ONE_MINUS_DST_ALPHA, NVG_ONE) +#define NVG_DESTINATION_IN nvgBlendFunc(NVG_ZERO, NVG_SRC_ALPHA) +#define NVG_DESTINATION_OUT nvgBlendFunc(NVG_ZERO, NVG_ONE_MINUS_SRC_ALPHA) +#define NVG_DESTINATION_ATOP nvgBlendFunc(NVG_ONE_MINUS_DST_ALPHA, NVG_SRC_ALPHA) +#define NVG_LIGHTER nvgBlendFunc(NVG_ONE, NVG_ONE) +#define NVG_COPY nvgBlendFunc(NVG_ONE, NVG_ZERO) +#define NVG_XOR nvgBlendFunc(NVG_ONE_MINUS_DST_ALPHA NVG_ONE_MINUS_SRC_ALPHA) + struct NVGglyphPosition { const char* str; // Position of the glyph in the input string. float x; // The x-coordinate of the logical glyph position. @@ -123,6 +158,22 @@ void nvgCancelFrame(NVGcontext* ctx); // Ends drawing flushing remaining render state. void nvgEndFrame(NVGcontext* ctx); +// +// Composite operation +// +// Composite operation in NanoVG works between frames. The default composite +// operation of NanoVG is NVG_SOURCE_OVER, and the value is reset whenever +// calling nvgBeginFrame(). + +// Creates a composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. +NVGcompositeOperation nvgBlendFunc(int sfactor, int dfactor); + +// Creates a composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. +NVGcompositeOperation nvgBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); + +// Sets the composite operation for the current frame. This function should be called between nvgBeginFrame() and nvgEndFrame(). The default composite operation of NanoVG is NVG_SOURCE_OVER. +void nvgGlobalCompositeOperation(NVGcontext* ctx, NVGcompositeOperation op); + // // Color utils // @@ -590,6 +641,7 @@ struct NVGparams { int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); void (*renderViewport)(void* uptr, int width, int height, float devicePixelRatio); void (*renderCancel)(void* uptr); + void (*renderCompositeOperation)(void* uptr, NVGcompositeOperation op); void (*renderFlush)(void* uptr); void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 7580af1..b368319 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -219,6 +219,7 @@ struct GLNVGfragUniforms { typedef struct GLNVGfragUniforms GLNVGfragUniforms; struct GLNVGcontext { + NVGcompositeOperation compositeOperation; GLNVGshader shader; GLNVGtexture* textures; float view[2]; @@ -944,6 +945,7 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) static void glnvg__renderViewport(void* uptr, int width, int height, float devicePixelRatio) { GLNVGcontext* gl = (GLNVGcontext*)uptr; + gl->compositeOperation = NVG_SOURCE_OVER; // resets composition gl->view[0] = (float)width; gl->view[1] = (float)height; } @@ -1072,6 +1074,43 @@ static void glnvg__renderCancel(void* uptr) { gl->nuniforms = 0; } +static GLenum glnvg_convertBlendFuncFactor(int factor) +{ + if (factor == NVG_ZERO) + return GL_ZERO; + if (factor == NVG_ONE) + return GL_ONE; + if (factor == NVG_SRC_COLOR) + return GL_SRC_COLOR; + if (factor == NVG_ONE_MINUS_SRC_COLOR) + return GL_ONE_MINUS_SRC_COLOR; + if (factor == NVG_DST_COLOR) + return GL_DST_COLOR; + if (factor == NVG_ONE_MINUS_DST_COLOR) + return GL_ONE_MINUS_DST_COLOR; + if (factor == NVG_SRC_ALPHA) + return GL_SRC_ALPHA; + if (factor == NVG_ONE_MINUS_SRC_ALPHA) + return GL_ONE_MINUS_SRC_ALPHA; + if (factor == NVG_DST_ALPHA) + return GL_DST_ALPHA; + if (factor == NVG_ONE_MINUS_DST_ALPHA) + return GL_ONE_MINUS_DST_ALPHA; + if (factor == NVG_SRC_ALPHA_SATURATE) + return GL_SRC_ALPHA_SATURATE; +} + +static void glnvg__blendCompositeOperation(NVGcompositeOperation operation) +{ + glBlendFuncSeparate(glnvg_convertBlendFuncFactor(operation.srcRGB), glnvg_convertBlendFuncFactor(operation.dstRGB), glnvg_convertBlendFuncFactor(operation.srcAlpha), glnvg_convertBlendFuncFactor(operation.dstAlpha)); +} + +static void glnvg__renderCompositeOperation(void* uptr, NVGcompositeOperation op) +{ + GLNVGcontext* gl = (GLNVGcontext*)uptr; + gl->compositeOperation = op; +} + static void glnvg__renderFlush(void* uptr) { GLNVGcontext* gl = (GLNVGcontext*)uptr; @@ -1082,7 +1121,7 @@ static void glnvg__renderFlush(void* uptr) // Setup require GL state. glUseProgram(gl->shader.prog); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glnvg__blendCompositeOperation(gl->compositeOperation); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); @@ -1473,6 +1512,7 @@ NVGcontext* nvgCreateGLES3(int flags) params.renderGetTextureSize = glnvg__renderGetTextureSize; params.renderViewport = glnvg__renderViewport; params.renderCancel = glnvg__renderCancel; + params.renderCompositeOperation = glnvg__renderCompositeOperation; params.renderFlush = glnvg__renderFlush; params.renderFill = glnvg__renderFill; params.renderStroke = glnvg__renderStroke;