Mercurial > hg > pub > prymula > com
diff DPF-Prymula-audioplugins/dpf/dgl/src/NanoVG.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DPF-Prymula-audioplugins/dpf/dgl/src/NanoVG.cpp Mon Oct 16 21:53:34 2023 +0200 @@ -0,0 +1,1208 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef _MSC_VER +// instantiated template classes whose methods are defined elsewhere +# pragma warning(disable:4661) +#endif + +#include "../NanoVG.hpp" +#include "SubWidgetPrivateData.hpp" + +#ifndef DGL_NO_SHARED_RESOURCES +# include "Resources.hpp" +#endif + +// ----------------------------------------------------------------------- + +#if defined(DISTRHO_OS_WINDOWS) +# include <windows.h> +# define DGL_EXT(PROC, func) static PROC func; +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLSTENCILOPSEPARATEPROC, glStencilOpSeparate) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) +# ifdef DGL_USE_NANOVG_FBO +DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) +DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) +DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) +DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) +DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) +DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) +DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) +DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) +DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) +DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) +# endif +# ifdef DGL_USE_OPENGL3 +DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) +DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) +DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays) +DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap) +DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex) +DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) +DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) +# endif +# undef DGL_EXT +#endif + +// ----------------------------------------------------------------------- +// Include NanoVG OpenGL implementation + +//#define STB_IMAGE_STATIC +#if defined(DGL_USE_GLES2) +# define NANOVG_GLES2_IMPLEMENTATION +#elif defined(DGL_USE_OPENGL3) +# define NANOVG_GL3_IMPLEMENTATION +#else +# define NANOVG_GL2_IMPLEMENTATION +#endif + +#if defined(DISTRHO_OS_MAC) && defined(NANOVG_GL2_IMPLEMENTATION) +# define glBindVertexArray glBindVertexArrayAPPLE +# define glDeleteVertexArrays glDeleteVertexArraysAPPLE +# define glGenVertexArrays glGenVertexArraysAPPLE +#endif + +#include "nanovg/nanovg_gl.h" + +#ifdef DGL_USE_NANOVG_FBO +# define NANOVG_FBO_VALID 1 +# include "nanovg/nanovg_gl_utils.h" +#endif + +#if defined(NANOVG_GL2) +# define nvgCreateGLfn nvgCreateGL2 +# define nvgDeleteGL nvgDeleteGL2 +# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2 +# define nvglImageHandle nvglImageHandleGL2 +#elif defined(NANOVG_GL3) +# define nvgCreateGLfn nvgCreateGL3 +# define nvgDeleteGL nvgDeleteGL3 +# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3 +# define nvglImageHandle nvglImageHandleGL3 +#elif defined(NANOVG_GLES2) +# define nvgCreateGLfn nvgCreateGLES2 +# define nvgDeleteGL nvgDeleteGLES2 +# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2 +# define nvglImageHandle nvglImageHandleGLES2 +#elif defined(NANOVG_GLES3) +# define nvgCreateGLfn nvgCreateGLES3 +# define nvgDeleteGL nvgDeleteGLES3 +# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3 +# define nvglImageHandle nvglImageHandleGLES3 +#endif + +// ----------------------------------------------------------------------- + +START_NAMESPACE_DGL + +NVGcontext* nvgCreateGL(int flags) +{ +#if defined(DISTRHO_OS_WINDOWS) +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +# endif + static bool needsInit = true; +# define DGL_EXT(PROC, func) \ + if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ + DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); +# define DGL_EXT2(PROC, func, fallback) \ + if (needsInit) { \ + func = (PROC) wglGetProcAddress ( #func ); \ + if (func == nullptr) func = (PROC) wglGetProcAddress ( #fallback ); \ + } DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLSTENCILOPSEPARATEPROC, glStencilOpSeparate) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) +# ifdef DGL_USE_NANOVG_FBO +DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) +DGL_EXT2(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer, glBindFramebufferEXT) +DGL_EXT2(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer, glBindRenderbufferEXT) +DGL_EXT2(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers, glDeleteFramebuffersEXT) +DGL_EXT2(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers, glDeleteRenderbuffersEXT) +DGL_EXT2(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D, glFramebufferTexture2DEXT) +DGL_EXT2(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer, glFramebufferRenderbufferEXT) +DGL_EXT2(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers, glGenFramebuffersEXT) +DGL_EXT2(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers, glGenRenderbuffersEXT) +DGL_EXT2(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage, glRenderbufferStorageEXT) +# endif +# ifdef DGL_USE_OPENGL3 +DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) +DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) +DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays) +DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap) +DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex) +DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) +DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) +# endif +# undef DGL_EXT +# undef DGL_EXT2 + needsInit = false; +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic pop +# endif +#endif + return nvgCreateGLfn(flags); +} + +// ----------------------------------------------------------------------- +// DGL Color class conversion + +Color::Color(const NVGcolor& c) noexcept + : red(c.r), green(c.g), blue(c.b), alpha(c.a) +{ + fixBounds(); +} + +Color::operator NVGcolor() const noexcept +{ + NVGcolor nc; + nc.r = red; + nc.g = green; + nc.b = blue; + nc.a = alpha; + return nc; +} + +// ----------------------------------------------------------------------- +// NanoImage + +NanoImage::NanoImage() + : fHandle(), + fSize() {} + +NanoImage::NanoImage(const Handle& handle) + : fHandle(handle), + fSize() +{ + DISTRHO_SAFE_ASSERT_RETURN(fHandle.context != nullptr && fHandle.imageId != 0,); + + _updateSize(); +} + +NanoImage::~NanoImage() +{ + if (fHandle.context != nullptr && fHandle.imageId != 0) + nvgDeleteImage(fHandle.context, fHandle.imageId); +} + +NanoImage& NanoImage::operator=(const Handle& handle) +{ + if (fHandle.context != nullptr && fHandle.imageId != 0) + nvgDeleteImage(fHandle.context, fHandle.imageId); + + fHandle.context = handle.context; + fHandle.imageId = handle.imageId; + _updateSize(); + + return *this; +} + +bool NanoImage::isValid() const noexcept +{ + return (fHandle.context != nullptr && fHandle.imageId != 0); +} + +Size<uint> NanoImage::getSize() const noexcept +{ + return fSize; +} + +GLuint NanoImage::getTextureHandle() const +{ + DISTRHO_SAFE_ASSERT_RETURN(fHandle.context != nullptr && fHandle.imageId != 0, 0); + + return nvglImageHandle(fHandle.context, fHandle.imageId); +} + +void NanoImage::_updateSize() +{ + int w=0, h=0; + + nvgImageSize(fHandle.context, fHandle.imageId, &w, &h); + + if (w < 0) w = 0; + if (h < 0) h = 0; + + fSize.setSize(static_cast<uint>(w), static_cast<uint>(h)); +} + +// ----------------------------------------------------------------------- +// Paint + +NanoVG::Paint::Paint() noexcept + : radius(0.0f), feather(0.0f), innerColor(), outerColor(), imageId(0) +{ + std::memset(xform, 0, sizeof(float)*6); + std::memset(extent, 0, sizeof(float)*2); +} + +NanoVG::Paint::Paint(const NVGpaint& p) noexcept + : radius(p.radius), feather(p.feather), innerColor(p.innerColor), outerColor(p.outerColor), imageId(p.image) +{ + std::memcpy(xform, p.xform, sizeof(float)*6); + std::memcpy(extent, p.extent, sizeof(float)*2); +} + +NanoVG::Paint::operator NVGpaint() const noexcept +{ + NVGpaint p; + p.radius = radius; + p.feather = feather; + p.innerColor = innerColor; + p.outerColor = outerColor; + p.image = imageId; + std::memcpy(p.xform, xform, sizeof(float)*6); + std::memcpy(p.extent, extent, sizeof(float)*2); + return p; +} + +// ----------------------------------------------------------------------- +// NanoVG + +NanoVG::NanoVG(int flags) + : fContext(nvgCreateGL(flags)), + fInFrame(false), + fIsSubWidget(false) +{ + DISTRHO_CUSTOM_SAFE_ASSERT("Failed to create NanoVG context, expect a black screen", fContext != nullptr); +} + +NanoVG::NanoVG(NVGcontext* const context) + : fContext(context), + fInFrame(false), + fIsSubWidget(true) +{ + DISTRHO_CUSTOM_SAFE_ASSERT("Failed to create NanoVG context, expect a black screen", fContext != nullptr); +} + +NanoVG::~NanoVG() +{ + DISTRHO_CUSTOM_SAFE_ASSERT("Destroying NanoVG context with still active frame", ! fInFrame); + + if (fContext != nullptr && ! fIsSubWidget) + nvgDeleteGL(fContext); +} + +// ----------------------------------------------------------------------- + +void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor) +{ + DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); + DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); + fInFrame = true; + + if (fContext != nullptr) + nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); +} + +void NanoVG::beginFrame(Widget* const widget) +{ + DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); + fInFrame = true; + + if (fContext == nullptr) + return; + + if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) + nvgBeginFrame(fContext, + static_cast<int>(tlw->getWidth()), + static_cast<int>(tlw->getHeight()), + tlw->getScaleFactor()); +} + +void NanoVG::cancelFrame() +{ + DISTRHO_SAFE_ASSERT_RETURN(fInFrame,); + + if (fContext != nullptr) + nvgCancelFrame(fContext); + + fInFrame = false; +} + +void NanoVG::endFrame() +{ + DISTRHO_SAFE_ASSERT_RETURN(fInFrame,); + + // Save current blend state + GLboolean blendEnabled; + GLint blendSrc, blendDst; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrc); + glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDst); + + if (fContext != nullptr) + nvgEndFrame(fContext); + + // Restore blend state + if (blendEnabled) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + glBlendFunc(blendSrc, blendDst); + + fInFrame = false; +} + +// ----------------------------------------------------------------------- +// State Handling + +void NanoVG::save() +{ + if (fContext != nullptr) + nvgSave(fContext); +} + +void NanoVG::restore() +{ + if (fContext != nullptr) + nvgRestore(fContext); +} + +void NanoVG::reset() +{ + if (fContext != nullptr) + nvgReset(fContext); +} + +// ----------------------------------------------------------------------- +// Render styles + +void NanoVG::strokeColor(const Color& color) +{ + if (fContext != nullptr) + nvgStrokeColor(fContext, color); +} + +void NanoVG::strokeColor(const int red, const int green, const int blue, const int alpha) +{ + if (fContext != nullptr) + { + DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,); + + nvgStrokeColor(fContext, nvgRGBA(static_cast<uchar>(red), + static_cast<uchar>(green), + static_cast<uchar>(blue), + static_cast<uchar>(alpha))); + } +} + +void NanoVG::strokeColor(const float red, const float green, const float blue, const float alpha) +{ + if (fContext != nullptr) + nvgStrokeColor(fContext, nvgRGBAf(red, green, blue, alpha)); +} + +void NanoVG::strokePaint(const Paint& paint) +{ + if (fContext != nullptr) + nvgStrokePaint(fContext, paint); +} + +void NanoVG::fillColor(const Color& color) +{ + if (fContext != nullptr) + nvgFillColor(fContext, color); +} + +void NanoVG::fillColor(const int red, const int green, const int blue, const int alpha) +{ + if (fContext != nullptr) + { + DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,); + DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,); + + nvgFillColor(fContext, nvgRGBA(static_cast<uchar>(red), + static_cast<uchar>(green), + static_cast<uchar>(blue), + static_cast<uchar>(alpha))); + } +} + +void NanoVG::fillColor(const float red, const float green, const float blue, const float alpha) +{ + if (fContext != nullptr) + nvgFillColor(fContext, nvgRGBAf(red, green, blue, alpha)); +} + +void NanoVG::fillPaint(const Paint& paint) +{ + if (fContext != nullptr) + nvgFillPaint(fContext, paint); +} + +void NanoVG::miterLimit(float limit) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(limit > 0.0f,); + + nvgMiterLimit(fContext, limit); +} + +void NanoVG::strokeWidth(float size) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(size > 0.0f,); + + nvgStrokeWidth(fContext, size); +} + +void NanoVG::lineCap(NanoVG::LineCap cap) +{ + if (fContext != nullptr) + nvgLineCap(fContext, cap); +} + +void NanoVG::lineJoin(NanoVG::LineCap join) +{ + if (fContext != nullptr) + nvgLineJoin(fContext, join); +} + +void NanoVG::globalAlpha(float alpha) +{ + if (fContext != nullptr) + nvgGlobalAlpha(fContext, alpha); +} + +void NanoVG::globalTint(Color tint) +{ + if (fContext != nullptr) + nvgGlobalTint(fContext, tint); +} + +// ----------------------------------------------------------------------- +// Transforms + +void NanoVG::resetTransform() +{ + if (fContext != nullptr) + nvgResetTransform(fContext); +} + +void NanoVG::transform(float a, float b, float c, float d, float e, float f) +{ + if (fContext != nullptr) + nvgTransform(fContext, a, b, c, d, e, f); +} + +void NanoVG::translate(float x, float y) +{ + if (fContext != nullptr) + nvgTranslate(fContext, x, y); +} + +void NanoVG::rotate(float angle) +{ + if (fContext != nullptr) + nvgRotate(fContext, angle); +} + +void NanoVG::skewX(float angle) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); + + nvgSkewX(fContext, angle); +} + +void NanoVG::skewY(float angle) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); + + nvgSkewY(fContext, angle); +} + +void NanoVG::scale(float x, float y) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(d_isNotZero(x),); + DISTRHO_SAFE_ASSERT_RETURN(d_isNotZero(y),); + + nvgScale(fContext, x, y); +} + +void NanoVG::currentTransform(float xform[6]) +{ + if (fContext != nullptr) + nvgCurrentTransform(fContext, xform); +} + +void NanoVG::transformIdentity(float dst[6]) +{ + nvgTransformIdentity(dst); +} + +void NanoVG::transformTranslate(float dst[6], float tx, float ty) +{ + nvgTransformTranslate(dst, tx, ty); +} + +void NanoVG::transformScale(float dst[6], float sx, float sy) +{ + nvgTransformScale(dst, sx, sy); +} + +void NanoVG::transformRotate(float dst[6], float a) +{ + nvgTransformRotate(dst, a); +} + +void NanoVG::transformSkewX(float dst[6], float a) +{ + nvgTransformSkewX(dst, a); +} + +void NanoVG::transformSkewY(float dst[6], float a) +{ + nvgTransformSkewY(dst, a); +} + +void NanoVG::transformMultiply(float dst[6], const float src[6]) +{ + nvgTransformMultiply(dst, src); +} + +void NanoVG::transformPremultiply(float dst[6], const float src[6]) +{ + nvgTransformPremultiply(dst, src); +} + +int NanoVG::transformInverse(float dst[6], const float src[6]) +{ + return nvgTransformInverse(dst, src); +} + +void NanoVG::transformPoint(float& dstx, float& dsty, const float xform[6], float srcx, float srcy) +{ + nvgTransformPoint(&dstx, &dsty, xform, srcx, srcy); +} + +float NanoVG::degToRad(float deg) +{ + return nvgDegToRad(deg); +} + +float NanoVG::radToDeg(float rad) +{ + return nvgRadToDeg(rad); +} + +// ----------------------------------------------------------------------- +// Images + +NanoImage::Handle NanoVG::createImageFromFile(const char* filename, ImageFlags imageFlags) +{ + return createImageFromFile(filename, static_cast<int>(imageFlags)); +} + +NanoImage::Handle NanoVG::createImageFromFile(const char* filename, int imageFlags) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', NanoImage::Handle()); + + return NanoImage::Handle(fContext, nvgCreateImage(fContext, filename, imageFlags)); +} + +NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags) +{ + return createImageFromMemory(data, dataSize, static_cast<int>(imageFlags)); +} + +NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, int imageFlags) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); + DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle()); + + return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data, static_cast<int>(dataSize))); +} + +NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, + ImageFlags imageFlags, ImageFormat format) +{ + return createImageFromRawMemory(w, h, data, static_cast<int>(imageFlags), format); +} + +NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, + int imageFlags, ImageFormat format) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); + + NVGtexture nvgformat; + switch (format) + { + case kImageFormatGrayscale: + nvgformat = NVG_TEXTURE_ALPHA; + break; + case kImageFormatBGR: + nvgformat = NVG_TEXTURE_BGR; + break; + case kImageFormatBGRA: + nvgformat = NVG_TEXTURE_BGRA; + break; + case kImageFormatRGB: + nvgformat = NVG_TEXTURE_RGB; + break; + case kImageFormatRGBA: + nvgformat = NVG_TEXTURE_RGBA; + break; + default: + return NanoImage::Handle(); + } + + return NanoImage::Handle(fContext, nvgCreateImageRaw(fContext, + static_cast<int>(w), + static_cast<int>(h), imageFlags, nvgformat, data)); +} + +NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags) +{ + return createImageFromRGBA(w, h, data, static_cast<int>(imageFlags)); +} + +NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, int imageFlags) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); + + return NanoImage::Handle(fContext, nvgCreateImageRGBA(fContext, + static_cast<int>(w), + static_cast<int>(h), imageFlags, data)); +} + +NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, + ImageFlags imageFlags, bool deleteTexture) +{ + return createImageFromTextureHandle(textureId, w, h, static_cast<int>(imageFlags), deleteTexture); +} + +NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, + int imageFlags, bool deleteTexture) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(textureId != 0, NanoImage::Handle()); + + if (! deleteTexture) + imageFlags |= NVG_IMAGE_NODELETE; + + return NanoImage::Handle(fContext, nvglCreateImageFromHandle(fContext, + textureId, + static_cast<int>(w), + static_cast<int>(h), imageFlags)); +} + +// ----------------------------------------------------------------------- +// Paints + +NanoVG::Paint NanoVG::linearGradient(float sx, float sy, float ex, float ey, const Color& icol, const Color& ocol) +{ + if (fContext == nullptr) return Paint(); + return nvgLinearGradient(fContext, sx, sy, ex, ey, icol, ocol); +} + +NanoVG::Paint NanoVG::boxGradient(float x, float y, float w, float h, float r, float f, const Color& icol, const Color& ocol) +{ + if (fContext == nullptr) return Paint(); + return nvgBoxGradient(fContext, x, y, w, h, r, f, icol, ocol); +} + +NanoVG::Paint NanoVG::radialGradient(float cx, float cy, float inr, float outr, const Color& icol, const Color& ocol) +{ + if (fContext == nullptr) return Paint(); + return nvgRadialGradient(fContext, cx, cy, inr, outr, icol, ocol); +} + +NanoVG::Paint NanoVG::imagePattern(float ox, float oy, float ex, float ey, float angle, const NanoImage& image, float alpha) +{ + if (fContext == nullptr) return Paint(); + + const int imageId(image.fHandle.imageId); + DISTRHO_SAFE_ASSERT_RETURN(imageId != 0, Paint()); + + return nvgImagePattern(fContext, ox, oy, ex, ey, angle, imageId, alpha); +} + +// ----------------------------------------------------------------------- +// Scissoring + +void NanoVG::scissor(float x, float y, float w, float h) +{ + if (fContext != nullptr) + nvgScissor(fContext, x, y, w, h); +} + +void NanoVG::intersectScissor(float x, float y, float w, float h) +{ + if (fContext != nullptr) + nvgIntersectScissor(fContext, x, y, w, h); +} + +void NanoVG::resetScissor() +{ + if (fContext != nullptr) + nvgResetScissor(fContext); +} + +// ----------------------------------------------------------------------- +// Paths + +void NanoVG::beginPath() +{ + if (fContext != nullptr) + nvgBeginPath(fContext); +} + +void NanoVG::moveTo(float x, float y) +{ + if (fContext != nullptr) + nvgMoveTo(fContext, x, y); +} + +void NanoVG::lineTo(float x, float y) +{ + if (fContext != nullptr) + nvgLineTo(fContext, x, y); +} + +void NanoVG::bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y) +{ + if (fContext != nullptr) + nvgBezierTo(fContext, c1x, c1y, c2x, c2y, x, y); +} + +void NanoVG::quadTo(float cx, float cy, float x, float y) +{ + if (fContext != nullptr) + nvgQuadTo(fContext, cx, cy, x, y); +} + +void NanoVG::arcTo(float x1, float y1, float x2, float y2, float radius) +{ + if (fContext != nullptr) + nvgArcTo(fContext, x1, y1, x2, y2, radius); +} + +void NanoVG::closePath() +{ + if (fContext != nullptr) + nvgClosePath(fContext); +} + +void NanoVG::pathWinding(NanoVG::Winding dir) +{ + if (fContext != nullptr) + nvgPathWinding(fContext, dir); +} + +void NanoVG::arc(float cx, float cy, float r, float a0, float a1, NanoVG::Winding dir) +{ + if (fContext != nullptr) + nvgArc(fContext, cx, cy, r, a0, a1, dir); +} + +void NanoVG::rect(float x, float y, float w, float h) +{ + if (fContext != nullptr) + nvgRect(fContext, x, y, w, h); +} + +void NanoVG::roundedRect(float x, float y, float w, float h, float r) +{ + if (fContext != nullptr) + nvgRoundedRect(fContext, x, y, w, h, r); +} + +void NanoVG::ellipse(float cx, float cy, float rx, float ry) +{ + if (fContext != nullptr) + nvgEllipse(fContext, cx, cy, rx, ry); +} + +void NanoVG::circle(float cx, float cy, float r) +{ + if (fContext != nullptr) + nvgCircle(fContext, cx, cy, r); +} + +void NanoVG::fill() +{ + if (fContext != nullptr) + nvgFill(fContext); +} + +void NanoVG::stroke() +{ + if (fContext != nullptr) + nvgStroke(fContext); +} + +// ----------------------------------------------------------------------- +// Text + +NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) +{ + DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); + + return nvgCreateFont(fContext, name, filename); +} + +NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) +{ + DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); + + return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData); +} + +NanoVG::FontId NanoVG::findFont(const char* name) +{ + DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); + + return nvgFindFont(fContext, name); +} + +void NanoVG::fontSize(float size) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(size > 0.0f,); + + nvgFontSize(fContext, size); +} + +void NanoVG::fontBlur(float blur) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(blur >= 0.0f,); + + nvgFontBlur(fContext, blur); +} + +void NanoVG::textLetterSpacing(float spacing) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(spacing >= 0.0f,); + + nvgTextLetterSpacing(fContext, spacing); +} + +void NanoVG::textLineHeight(float lineHeight) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(lineHeight > 0.0f,); + + nvgTextLineHeight(fContext, lineHeight); +} + +void NanoVG::textAlign(NanoVG::Align align) +{ + if (fContext != nullptr) + nvgTextAlign(fContext, align); +} + +void NanoVG::textAlign(int align) +{ + if (fContext != nullptr) + nvgTextAlign(fContext, align); +} + +void NanoVG::fontFaceId(FontId font) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(font >= 0,); + + nvgFontFaceId(fContext, font); +} + +void NanoVG::fontFace(const char* font) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(font != nullptr && font[0] != '\0',); + + nvgFontFace(fContext, font); +} + +float NanoVG::text(float x, float y, const char* string, const char* end) +{ + if (fContext == nullptr) return 0.0f; + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0.0f); + + return nvgText(fContext, x, y, string, end); +} + +void NanoVG::textBox(float x, float y, float breakRowWidth, const char* string, const char* end) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0',); + + nvgTextBox(fContext, x, y, breakRowWidth, string, end); +} + +float NanoVG::textBounds(float x, float y, const char* string, const char* end, Rectangle<float>& bounds) +{ + if (fContext == nullptr) return 0.0f; + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0.0f); + + float b[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const float ret = nvgTextBounds(fContext, x, y, string, end, b); + bounds = Rectangle<float>(b[0], b[1], b[2] - b[0], b[3] - b[1]); + return ret; +} + +void NanoVG::textBoxBounds(float x, float y, float breakRowWidth, const char* string, const char* end, float bounds[4]) +{ + if (fContext == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0',); + + nvgTextBoxBounds(fContext, x, y, breakRowWidth, string, end, bounds); +} + +int NanoVG::textGlyphPositions(float x, float y, const char* string, const char* end, NanoVG::GlyphPosition& positions, int maxPositions) +{ + if (fContext == nullptr) return 0; + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0); + + return nvgTextGlyphPositions(fContext, x, y, string, end, (NVGglyphPosition*)&positions, maxPositions); +} + +void NanoVG::textMetrics(float* ascender, float* descender, float* lineh) +{ + if (fContext != nullptr) + nvgTextMetrics(fContext, ascender, descender, lineh); +} + +int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWidth, NanoVG::TextRow& rows, int maxRows) +{ + if (fContext != nullptr) + return nvgTextBreakLines(fContext, string, end, breakRowWidth, (NVGtextRow*)&rows, maxRows); + return 0; +} + +#ifndef DGL_NO_SHARED_RESOURCES +bool NanoVG::loadSharedResources() +{ + if (fContext == nullptr) return false; + + if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) + return true; + + using namespace dpf_resources; + + return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; +} +#endif + +// ----------------------------------------------------------------------- + +template <class BaseWidget> +void NanoBaseWidget<BaseWidget>::displayChildren() +{ + std::list<SubWidget*> children(BaseWidget::getChildren()); + + for (std::list<SubWidget*>::iterator it = children.begin(); it != children.end(); ++it) + { + if (NanoSubWidget* const subwidget = dynamic_cast<NanoSubWidget*>(*it)) + { + if (subwidget->fUsingParentContext && subwidget->isVisible()) + subwidget->onDisplay(); + } + } +} + +// ----------------------------------------------------------------------- +// NanoSubWidget + +template <> +NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parentWidget, int flags) + : SubWidget(parentWidget), + NanoVG(flags), + fUsingParentContext(false) +{ + setNeedsViewportScaling(); +} + +template <> +NanoBaseWidget<SubWidget>::NanoBaseWidget(NanoSubWidget* const parentWidget) + : SubWidget(parentWidget), + NanoVG(parentWidget->getContext()), + fUsingParentContext(true) +{ + setSkipDrawing(); +} + +template <> +NanoBaseWidget<SubWidget>::NanoBaseWidget(NanoTopLevelWidget* const parentWidget) + : SubWidget(parentWidget), + NanoVG(parentWidget->getContext()), + fUsingParentContext(true) +{ + setSkipDrawing(); +} + +template <> +inline void NanoBaseWidget<SubWidget>::onDisplay() +{ + if (fUsingParentContext) + { + NanoVG::save(); + translate(SubWidget::getAbsoluteX(), SubWidget::getAbsoluteY()); + onNanoDisplay(); + NanoVG::restore(); + displayChildren(); + } + else + { + NanoVG::beginFrame(SubWidget::getWidth(), SubWidget::getHeight()); + onNanoDisplay(); + displayChildren(); + NanoVG::endFrame(); + } +} + +template class NanoBaseWidget<SubWidget>; + +// ----------------------------------------------------------------------- +// NanoTopLevelWidget + +template <> +NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags) + : TopLevelWidget(windowToMapTo), + NanoVG(flags), + fUsingParentContext(false) {} + +template <> +inline void NanoBaseWidget<TopLevelWidget>::onDisplay() +{ + NanoVG::beginFrame(TopLevelWidget::getWidth(), TopLevelWidget::getHeight()); + onNanoDisplay(); + displayChildren(); + NanoVG::endFrame(); +} + +template class NanoBaseWidget<TopLevelWidget>; + +// ----------------------------------------------------------------------- +// NanoStandaloneWindow + +template <> +NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags) + : StandaloneWindow(app), + NanoVG(flags), + fUsingParentContext(false) {} + +template <> +NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags) + : StandaloneWindow(app, parentWindow), + NanoVG(flags), + fUsingParentContext(false) {} + +template <> +inline void NanoBaseWidget<StandaloneWindow>::onDisplay() +{ + NanoVG::beginFrame(Window::getWidth(), Window::getHeight()); + onNanoDisplay(); + displayChildren(); + NanoVG::endFrame(); +} + +template class NanoBaseWidget<StandaloneWindow>; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#undef final + +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmisleading-indentation" +# pragma GCC diagnostic ignored "-Wshift-negative-value" +#endif + +extern "C" { +#include "nanovg/nanovg.c" +} + +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic pop +#endif + +// -----------------------------------------------------------------------