#pragma once #include #include #include #include #include #include "nanovg.h" #include "nanovg/dk_renderer.hpp" #ifdef __cplusplus extern "C" { #endif static int dknvg__maxi(int a, int b) { return a > b ? a : b; } static const DKNVGtextureDescriptor* dknvg__findTexture(DKNVGcontext* dk, int id) { return dk->renderer->GetTextureDescriptor(*dk, id); } static int dknvg__renderCreate(void* uptr) { DKNVGcontext *dk = (DKNVGcontext*)uptr; return dk->renderer->Create(*dk); } static int dknvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) { DKNVGcontext *dk = (DKNVGcontext*)uptr; return dk->renderer->CreateTexture(*dk, type, w, h, imageFlags, data); } static int dknvg__renderDeleteTexture(void* uptr, int image) { DKNVGcontext *dk = (DKNVGcontext*)uptr; return dk->renderer->DeleteTexture(*dk, image); } static int dknvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) { DKNVGcontext *dk = (DKNVGcontext*)uptr; return dk->renderer->UpdateTexture(*dk, image, x, y, w, h, data); } static int dknvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) { DKNVGcontext *dk = (DKNVGcontext*)uptr; return dk->renderer->GetTextureSize(*dk, image, w, h); } static void dknvg__xformToMat3x4(float* m3, float* t) { m3[0] = t[0]; m3[1] = t[1]; m3[2] = 0.0f; m3[3] = 0.0f; m3[4] = t[2]; m3[5] = t[3]; m3[6] = 0.0f; m3[7] = 0.0f; m3[8] = t[4]; m3[9] = t[5]; m3[10] = 1.0f; m3[11] = 0.0f; } static NVGcolor dknvg__premulColor(NVGcolor c) { c.r *= c.a; c.g *= c.a; c.b *= c.a; return c; } static int dknvg__convertPaint(DKNVGcontext* dk, DKNVGfragUniforms* frag, NVGpaint* paint, NVGscissor* scissor, float width, float fringe, float strokeThr) { const DKNVGtextureDescriptor *tex = NULL; float invxform[6]; memset(frag, 0, sizeof(*frag)); frag->innerCol = dknvg__premulColor(paint->innerColor); frag->outerCol = dknvg__premulColor(paint->outerColor); if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); frag->scissorExt[0] = 1.0f; frag->scissorExt[1] = 1.0f; frag->scissorScale[0] = 1.0f; frag->scissorScale[1] = 1.0f; } else { nvgTransformInverse(invxform, scissor->xform); dknvg__xformToMat3x4(frag->scissorMat, invxform); frag->scissorExt[0] = scissor->extent[0]; frag->scissorExt[1] = scissor->extent[1]; 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; frag->strokeThr = strokeThr; if (paint->image != 0) { tex = dknvg__findTexture(dk, paint->image); if (tex == NULL) return 0; if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { float m1[6], m2[6]; nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); nvgTransformMultiply(m1, paint->xform); nvgTransformScale(m2, 1.0f, -1.0f); nvgTransformMultiply(m2, m1); nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); nvgTransformMultiply(m1, m2); nvgTransformInverse(invxform, m1); } else { nvgTransformInverse(invxform, paint->xform); } frag->type = NSVG_SHADER_FILLIMG; if (tex->type == NVG_TEXTURE_RGBA) frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; else frag->texType = 2; // printf("frag->texType = %d\n", frag->texType); } else { frag->type = NSVG_SHADER_FILLGRAD; frag->radius = paint->radius; frag->feather = paint->feather; nvgTransformInverse(invxform, paint->xform); } dknvg__xformToMat3x4(frag->paintMat, invxform); return 1; } static DKNVGfragUniforms* nvg__fragUniformPtr(DKNVGcontext* dk, int i); static void dknvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) { NVG_NOTUSED(devicePixelRatio); DKNVGcontext* dk = (DKNVGcontext*)uptr; dk->view[0] = width; dk->view[1] = height; } static void dknvg__renderCancel(void* uptr) { DKNVGcontext* dk = (DKNVGcontext*)uptr; dk->nverts = 0; dk->npaths = 0; dk->ncalls = 0; dk->nuniforms = 0; } static int dknvg_convertBlendFuncFactor(int factor) { switch (factor) { case NVG_ZERO: return DkBlendFactor_Zero; case NVG_ONE: return DkBlendFactor_One; case NVG_SRC_COLOR: return DkBlendFactor_SrcColor; case NVG_ONE_MINUS_SRC_COLOR: return DkBlendFactor_InvSrcColor; case NVG_DST_COLOR: return DkBlendFactor_DstColor; case NVG_ONE_MINUS_DST_COLOR: return DkBlendFactor_InvDstColor; case NVG_SRC_ALPHA: return DkBlendFactor_SrcAlpha; case NVG_ONE_MINUS_SRC_ALPHA: return DkBlendFactor_InvSrcAlpha; case NVG_DST_ALPHA: return DkBlendFactor_DstAlpha; case NVG_ONE_MINUS_DST_ALPHA: return DkBlendFactor_InvDstAlpha; case NVG_SRC_ALPHA_SATURATE: return DkBlendFactor_SrcAlphaSaturate; default: return -1; } } static DKNVGblend dknvg__blendCompositeOperation(NVGcompositeOperationState op) { DKNVGblend blend; blend.srcRGB = dknvg_convertBlendFuncFactor(op.srcRGB); blend.dstRGB = dknvg_convertBlendFuncFactor(op.dstRGB); blend.srcAlpha = dknvg_convertBlendFuncFactor(op.srcAlpha); blend.dstAlpha = dknvg_convertBlendFuncFactor(op.dstAlpha); if (blend.srcRGB == -1 || blend.dstRGB == -1 || blend.srcAlpha == -1 || blend.dstAlpha == -1) { blend.srcRGB = DkBlendFactor_One; blend.dstRGB = DkBlendFactor_InvSrcAlpha; blend.srcAlpha = DkBlendFactor_One; blend.dstAlpha = DkBlendFactor_InvSrcAlpha; } return blend; } static void dknvg__renderFlush(void* uptr) { DKNVGcontext *dk = (DKNVGcontext*)uptr; dk->renderer->Flush(*dk); } static int dknvg__maxVertCount(const NVGpath* paths, int npaths) { int i, count = 0; for (i = 0; i < npaths; i++) { count += paths[i].nfill; count += paths[i].nstroke; } return count; } static DKNVGcall* dknvg__allocCall(DKNVGcontext* dk) { DKNVGcall* ret = NULL; if (dk->ncalls+1 > dk->ccalls) { DKNVGcall* calls; int ccalls = dknvg__maxi(dk->ncalls+1, 128) + dk->ccalls/2; // 1.5x Overallocate calls = (DKNVGcall*)realloc(dk->calls, sizeof(DKNVGcall) * ccalls); if (calls == NULL) return NULL; dk->calls = calls; dk->ccalls = ccalls; } ret = &dk->calls[dk->ncalls++]; memset(ret, 0, sizeof(DKNVGcall)); return ret; } static int dknvg__allocPaths(DKNVGcontext* dk, int n) { int ret = 0; if (dk->npaths+n > dk->cpaths) { DKNVGpath* paths; int cpaths = dknvg__maxi(dk->npaths + n, 128) + dk->cpaths/2; // 1.5x Overallocate paths = (DKNVGpath*)realloc(dk->paths, sizeof(DKNVGpath) * cpaths); if (paths == NULL) return -1; dk->paths = paths; dk->cpaths = cpaths; } ret = dk->npaths; dk->npaths += n; return ret; } static int dknvg__allocVerts(DKNVGcontext* dk, int n) { int ret = 0; if (dk->nverts+n > dk->cverts) { NVGvertex* verts; int cverts = dknvg__maxi(dk->nverts + n, 4096) + dk->cverts/2; // 1.5x Overallocate verts = (NVGvertex*)realloc(dk->verts, sizeof(NVGvertex) * cverts); if (verts == NULL) return -1; dk->verts = verts; dk->cverts = cverts; } ret = dk->nverts; dk->nverts += n; return ret; } static int dknvg__allocFragUniforms(DKNVGcontext* dk, int n) { int ret = 0, structSize = dk->fragSize; if (dk->nuniforms+n > dk->cuniforms) { unsigned char* uniforms; int cuniforms = dknvg__maxi(dk->nuniforms+n, 128) + dk->cuniforms/2; // 1.5x Overallocate uniforms = (unsigned char*)realloc(dk->uniforms, structSize * cuniforms); if (uniforms == NULL) return -1; dk->uniforms = uniforms; dk->cuniforms = cuniforms; } ret = dk->nuniforms * structSize; dk->nuniforms += n; return ret; } static DKNVGfragUniforms* nvg__fragUniformPtr(DKNVGcontext* dk, int i) { return (DKNVGfragUniforms*)&dk->uniforms[i]; } static void dknvg__vset(NVGvertex* vtx, float x, float y, float u, float v) { vtx->x = x; vtx->y = y; vtx->u = u; vtx->v = v; } static void dknvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths) { DKNVGcontext* dk = (DKNVGcontext*)uptr; DKNVGcall* call = dknvg__allocCall(dk); NVGvertex* quad; DKNVGfragUniforms* frag; int i, maxverts, offset; if (call == NULL) return; call->type = DKNVG_FILL; call->triangleCount = 4; call->pathOffset = dknvg__allocPaths(dk, npaths); if (call->pathOffset == -1) goto error; call->pathCount = npaths; call->image = paint->image; call->blendFunc = dknvg__blendCompositeOperation(compositeOperation); if (npaths == 1 && paths[0].convex) { call->type = DKNVG_CONVEXFILL; call->triangleCount = 0; // Bounding box fill quad not needed for convex fill } // Allocate vertices for all the paths. maxverts = dknvg__maxVertCount(paths, npaths) + call->triangleCount; offset = dknvg__allocVerts(dk, maxverts); if (offset == -1) goto error; for (i = 0; i < npaths; i++) { DKNVGpath* copy = &dk->paths[call->pathOffset + i]; const NVGpath* path = &paths[i]; memset(copy, 0, sizeof(DKNVGpath)); if (path->nfill > 0) { copy->fillOffset = offset; copy->fillCount = path->nfill; memcpy(&dk->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); offset += path->nfill; } if (path->nstroke > 0) { copy->strokeOffset = offset; copy->strokeCount = path->nstroke; memcpy(&dk->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); offset += path->nstroke; } } // Setup uniforms for draw calls if (call->type == DKNVG_FILL) { // Quad call->triangleOffset = offset; quad = &dk->verts[call->triangleOffset]; dknvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); dknvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); dknvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); dknvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); call->uniformOffset = dknvg__allocFragUniforms(dk, 2); if (call->uniformOffset == -1) goto error; // Simple shader for stencil frag = nvg__fragUniformPtr(dk, call->uniformOffset); memset(frag, 0, sizeof(*frag)); frag->strokeThr = -1.0f; frag->type = NSVG_SHADER_SIMPLE; // Fill shader dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset + dk->fragSize), paint, scissor, fringe, fringe, -1.0f); } else { call->uniformOffset = dknvg__allocFragUniforms(dk, 1); if (call->uniformOffset == -1) goto error; // Fill shader dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); } return; error: // We get here if call alloc was ok, but something else is not. // Roll back the last call to prevent drawing it. if (dk->ncalls > 0) dk->ncalls--; } static void dknvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths) { DKNVGcontext* dk = (DKNVGcontext*)uptr; DKNVGcall* call = dknvg__allocCall(dk); int i, maxverts, offset; if (call == NULL) { return; } call->type = DKNVG_STROKE; call->pathOffset = dknvg__allocPaths(dk, npaths); if (call->pathOffset == -1) goto error; call->pathCount = npaths; call->image = paint->image; call->blendFunc = dknvg__blendCompositeOperation(compositeOperation); // Allocate vertices for all the paths. maxverts = dknvg__maxVertCount(paths, npaths); offset = dknvg__allocVerts(dk, maxverts); if (offset == -1) goto error; for (i = 0; i < npaths; i++) { DKNVGpath* copy = &dk->paths[call->pathOffset + i]; const NVGpath* path = &paths[i]; memset(copy, 0, sizeof(DKNVGpath)); if (path->nstroke) { copy->strokeOffset = offset; copy->strokeCount = path->nstroke; memcpy(&dk->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); offset += path->nstroke; } } if (dk->flags & NVG_STENCIL_STROKES) { // Fill shader call->uniformOffset = dknvg__allocFragUniforms(dk, 2); if (call->uniformOffset == -1) goto error; dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset + dk->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); } else { // Fill shader call->uniformOffset = dknvg__allocFragUniforms(dk, 1); if (call->uniformOffset == -1) goto error; dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); } return; error: // We get here if call alloc was ok, but something else is not. // Roll back the last call to prevent drawing it. if (dk->ncalls > 0) dk->ncalls--; } static void dknvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe) { DKNVGcontext* dk = (DKNVGcontext*)uptr; DKNVGcall* call = dknvg__allocCall(dk); DKNVGfragUniforms* frag; if (call == NULL) return; call->type = DKNVG_TRIANGLES; call->image = paint->image; call->blendFunc = dknvg__blendCompositeOperation(compositeOperation); // Allocate vertices for all the paths. call->triangleOffset = dknvg__allocVerts(dk, nverts); if (call->triangleOffset == -1) goto error; call->triangleCount = nverts; memcpy(&dk->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); // Fill shader call->uniformOffset = dknvg__allocFragUniforms(dk, 1); if (call->uniformOffset == -1) goto error; frag = nvg__fragUniformPtr(dk, call->uniformOffset); dknvg__convertPaint(dk, frag, paint, scissor, 1.0f, fringe, -1.0f); frag->type = NSVG_SHADER_IMG; return; error: // We get here if call alloc was ok, but something else is not. // Roll back the last call to prevent drawing it. if (dk->ncalls > 0) dk->ncalls--; } static void dknvg__renderDelete(void* uptr) { DKNVGcontext* dk = (DKNVGcontext*)uptr; if (dk == NULL) return; free(dk->paths); free(dk->verts); free(dk->uniforms); free(dk->calls); free(dk); } NVGcontext* nvgCreateDk(nvg::DkRenderer *renderer, int flags) { NVGparams params; NVGcontext* ctx = NULL; DKNVGcontext* dk = (DKNVGcontext*)malloc(sizeof(DKNVGcontext)); if (dk == NULL) goto error; memset(dk, 0, sizeof(DKNVGcontext)); memset(¶ms, 0, sizeof(params)); params.renderCreate = dknvg__renderCreate; params.renderCreateTexture = dknvg__renderCreateTexture; params.renderDeleteTexture = dknvg__renderDeleteTexture; params.renderUpdateTexture = dknvg__renderUpdateTexture; params.renderGetTextureSize = dknvg__renderGetTextureSize; params.renderViewport = dknvg__renderViewport; params.renderCancel = dknvg__renderCancel; params.renderFlush = dknvg__renderFlush; params.renderFill = dknvg__renderFill; params.renderStroke = dknvg__renderStroke; params.renderTriangles = dknvg__renderTriangles; params.renderDelete = dknvg__renderDelete; params.userPtr = dk; params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; dk->renderer = renderer; dk->flags = flags; ctx = nvgCreateInternal(¶ms); if (ctx == NULL) goto error; return ctx; error: // 'dk' is freed by nvgDeleteInternal. if (ctx != NULL) nvgDeleteInternal(ctx); return NULL; } void nvgDeleteDk(NVGcontext* ctx) { nvgDeleteInternal(ctx); } #ifdef __cplusplus } #endif