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.shared-context
@@ -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) | NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b) | ||||
{ | { | ||||
return nvgRGBA(r,g,b,255); | return nvgRGBA(r,g,b,255); | ||||
@@ -83,6 +83,41 @@ enum NVGalign { | |||||
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | 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 { | struct NVGglyphPosition { | ||||
const char* str; // Position of the glyph in the input string. | const char* str; // Position of the glyph in the input string. | ||||
float x; // The x-coordinate of the logical glyph position. | float x; // The x-coordinate of the logical glyph position. | ||||
@@ -123,6 +158,22 @@ void nvgCancelFrame(NVGcontext* ctx); | |||||
// Ends drawing flushing remaining render state. | // Ends drawing flushing remaining render state. | ||||
void nvgEndFrame(NVGcontext* ctx); | 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 | // Color utils | ||||
// | // | ||||
@@ -590,6 +641,7 @@ struct NVGparams { | |||||
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | ||||
void (*renderViewport)(void* uptr, int width, int height, float devicePixelRatio); | void (*renderViewport)(void* uptr, int width, int height, float devicePixelRatio); | ||||
void (*renderCancel)(void* uptr); | void (*renderCancel)(void* uptr); | ||||
void (*renderCompositeOperation)(void* uptr, NVGcompositeOperation op); | |||||
void (*renderFlush)(void* uptr); | void (*renderFlush)(void* uptr); | ||||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | 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); | void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | ||||
@@ -219,6 +219,7 @@ struct GLNVGfragUniforms { | |||||
typedef struct GLNVGfragUniforms GLNVGfragUniforms; | typedef struct GLNVGfragUniforms GLNVGfragUniforms; | ||||
struct GLNVGcontext { | struct GLNVGcontext { | ||||
NVGcompositeOperation compositeOperation; | |||||
GLNVGshader shader; | GLNVGshader shader; | ||||
GLNVGtexture* textures; | GLNVGtexture* textures; | ||||
float view[2]; | 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) | static void glnvg__renderViewport(void* uptr, int width, int height, float devicePixelRatio) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
gl->compositeOperation = NVG_SOURCE_OVER; // resets composition | |||||
gl->view[0] = (float)width; | gl->view[0] = (float)width; | ||||
gl->view[1] = (float)height; | gl->view[1] = (float)height; | ||||
} | } | ||||
@@ -1072,6 +1074,43 @@ static void glnvg__renderCancel(void* uptr) { | |||||
gl->nuniforms = 0; | 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) | static void glnvg__renderFlush(void* uptr) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
@@ -1082,7 +1121,7 @@ static void glnvg__renderFlush(void* uptr) | |||||
// Setup require GL state. | // Setup require GL state. | ||||
glUseProgram(gl->shader.prog); | glUseProgram(gl->shader.prog); | ||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||||
glnvg__blendCompositeOperation(gl->compositeOperation); | |||||
glEnable(GL_CULL_FACE); | glEnable(GL_CULL_FACE); | ||||
glCullFace(GL_BACK); | glCullFace(GL_BACK); | ||||
glFrontFace(GL_CCW); | glFrontFace(GL_CCW); | ||||
@@ -1473,6 +1512,7 @@ NVGcontext* nvgCreateGLES3(int flags) | |||||
params.renderGetTextureSize = glnvg__renderGetTextureSize; | params.renderGetTextureSize = glnvg__renderGetTextureSize; | ||||
params.renderViewport = glnvg__renderViewport; | params.renderViewport = glnvg__renderViewport; | ||||
params.renderCancel = glnvg__renderCancel; | params.renderCancel = glnvg__renderCancel; | ||||
params.renderCompositeOperation = glnvg__renderCompositeOperation; | |||||
params.renderFlush = glnvg__renderFlush; | params.renderFlush = glnvg__renderFlush; | ||||
params.renderFill = glnvg__renderFill; | params.renderFill = glnvg__renderFill; | ||||
params.renderStroke = glnvg__renderStroke; | params.renderStroke = glnvg__renderStroke; | ||||