| @@ -1,8 +1,5 @@ | |||||
| os: | |||||
| - linux | |||||
| sudo: required | |||||
| dist: trusty | |||||
| os: linux | |||||
| dist: bionic | |||||
| languages: c++ | languages: c++ | ||||
| compiler: gcc | compiler: gcc | ||||
| @@ -13,6 +10,6 @@ install: | |||||
| - sh ${TRAVIS_BUILD_DIR}/.travis/install.sh | - sh ${TRAVIS_BUILD_DIR}/.travis/install.sh | ||||
| script: | script: | ||||
| - sh ${TRAVIS_BUILD_DIR}/.travis/script-linux.sh | - sh ${TRAVIS_BUILD_DIR}/.travis/script-linux.sh | ||||
| - sh ${TRAVIS_BUILD_DIR}/.travis/script-macos.sh | |||||
| #- sh ${TRAVIS_BUILD_DIR}/.travis/script-macos.sh | |||||
| - sh ${TRAVIS_BUILD_DIR}/.travis/script-win32.sh | - sh ${TRAVIS_BUILD_DIR}/.travis/script-win32.sh | ||||
| - sh ${TRAVIS_BUILD_DIR}/.travis/script-win64.sh | - sh ${TRAVIS_BUILD_DIR}/.travis/script-win64.sh | ||||
| @@ -2,9 +2,11 @@ | |||||
| set -e | set -e | ||||
| sudo add-apt-repository ppa:kxstudio-debian/kxstudio -y | |||||
| sudo add-apt-repository ppa:kxstudio-debian/mingw -y | |||||
| sudo add-apt-repository ppa:kxstudio-debian/toolchain -y | |||||
| sudo add-apt-repository -y ppa:kxstudio-debian/kxstudio | |||||
| sudo add-apt-repository -y ppa:kxstudio-debian/mingw | |||||
| sudo add-apt-repository -y ppa:kxstudio-debian/toolchain | |||||
| sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test | |||||
| sudo apt-get update -qq | sudo apt-get update -qq | ||||
| sudo apt-get install kxstudio-repos | sudo apt-get install kxstudio-repos | ||||
| sudo apt-get update -qq | sudo apt-get update -qq | ||||
| @@ -10,8 +10,10 @@ sudo apt-get install -y \ | |||||
| liblo-dev \ | liblo-dev \ | ||||
| libgl1-mesa-dev \ | libgl1-mesa-dev \ | ||||
| libx11-dev \ | libx11-dev \ | ||||
| apple-x86-setup \ | |||||
| mingw32-x-gcc \ | |||||
| mingw32-x-pkgconfig \ | |||||
| mingw64-x-gcc \ | |||||
| mingw64-x-pkgconfig | |||||
| mingw-w64 \ | |||||
| binutils-mingw-w64-i686 \ | |||||
| binutils-mingw-w64-x86-64 \ | |||||
| g++-mingw-w64-i686 \ | |||||
| g++-mingw-w64-x86-64 \ | |||||
| # apple-x86-setup | |||||
| @@ -157,7 +157,7 @@ endif | |||||
| endif | endif | ||||
| ifeq ($(NOOPT),true) | ifeq ($(NOOPT),true) | ||||
| # No CPU-specific optimization flags | |||||
| # Non-CPU-specific optimization flags | |||||
| BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | ||||
| endif | endif | ||||
| @@ -167,9 +167,6 @@ ifeq ($(WINDOWS),true) | |||||
| BASE_OPTS += -fno-rerun-cse-after-loop | BASE_OPTS += -fno-rerun-cse-after-loop | ||||
| # See https://github.com/falkTX/Carla/issues/855 | # See https://github.com/falkTX/Carla/issues/855 | ||||
| BASE_OPTS += -mstackrealign | BASE_OPTS += -mstackrealign | ||||
| ifeq ($(BUILDING_FOR_WINDOWS),true) | |||||
| BASE_FLAGS += -DBUILDING_CARLA_FOR_WINDOWS | |||||
| endif | |||||
| else | else | ||||
| # Not needed for Windows | # Not needed for Windows | ||||
| BASE_FLAGS += -fPIC -DPIC | BASE_FLAGS += -fPIC -DPIC | ||||
| @@ -229,12 +226,14 @@ endif | |||||
| HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | ||||
| ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
| ifeq ($(MACOS_OR_WINDOWS),true) | |||||
| HAVE_OPENGL = true | HAVE_OPENGL = true | ||||
| else | else | ||||
| HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | ||||
| ifneq ($(HAIKU),true) | |||||
| HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | ||||
| endif | endif | ||||
| endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Check for optional libraries | # Check for optional libraries | ||||
| @@ -308,6 +307,19 @@ HAVE_CAIRO_OR_OPENGL = true | |||||
| endif | endif | ||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Set optional libraries specific stuff | |||||
| ifeq ($(HAVE_JACK),true) | |||||
| JACK_FLAGS = $(shell $(PKG_CONFIG) --cflags jack) | |||||
| JACK_LIBS = $(shell $(PKG_CONFIG) --libs jack) | |||||
| endif | |||||
| ifeq ($(HAVE_LIBLO),true) | |||||
| LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo) | |||||
| LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo) | |||||
| endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Set app extension | # Set app extension | ||||
| @@ -7,31 +7,40 @@ | |||||
| # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | ||||
| ifeq ($(DPF_CUSTOM_PATH),) | |||||
| ifeq (,$(wildcard ../../Makefile.base.mk)) | ifeq (,$(wildcard ../../Makefile.base.mk)) | ||||
| DPF_PATH=../../dpf | DPF_PATH=../../dpf | ||||
| else | else | ||||
| DPF_PATH=../.. | DPF_PATH=../.. | ||||
| endif | endif | ||||
| else | |||||
| DPF_PATH = $(DPF_CUSTOM_PATH) | |||||
| endif | |||||
| include $(DPF_PATH)/Makefile.base.mk | include $(DPF_PATH)/Makefile.base.mk | ||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Basic setup | # Basic setup | ||||
| ifeq ($(DPF_CUSTOM_PATH),) | |||||
| TARGET_DIR = ../../bin | TARGET_DIR = ../../bin | ||||
| BUILD_DIR = ../../build/$(NAME) | BUILD_DIR = ../../build/$(NAME) | ||||
| else | |||||
| ifeq ($(DPF_CUSTOM_TARGET_DIR),) | |||||
| $(error DPF_CUSTOM_TARGET_DIR is not set) | |||||
| else | |||||
| TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR) | |||||
| endif | |||||
| ifeq ($(DPF_CUSTOM_BUILD_DIR),) | |||||
| $(error DPF_CUSTOM_BUILD_DIR is not set) | |||||
| else | |||||
| BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR) | |||||
| endif | |||||
| endif | |||||
| BUILD_C_FLAGS += -I. | BUILD_C_FLAGS += -I. | ||||
| BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | ||||
| ifeq ($(HAVE_CAIRO),true) | |||||
| DGL_FLAGS += -DHAVE_CAIRO | |||||
| endif | |||||
| ifeq ($(HAVE_OPENGL),true) | |||||
| DGL_FLAGS += -DHAVE_OPENGL | |||||
| endif | |||||
| ifeq ($(HAVE_JACK),true) | ifeq ($(HAVE_JACK),true) | ||||
| BASE_FLAGS += -DHAVE_JACK | BASE_FLAGS += -DHAVE_JACK | ||||
| endif | endif | ||||
| @@ -70,8 +79,8 @@ UI_TYPE = opengl | |||||
| endif | endif | ||||
| ifeq ($(UI_TYPE),cairo) | ifeq ($(UI_TYPE),cairo) | ||||
| DGL_FLAGS += -DDGL_CAIRO | |||||
| ifeq ($(HAVE_CAIRO),true) | ifeq ($(HAVE_CAIRO),true) | ||||
| DGL_FLAGS += -DDGL_CAIRO | |||||
| DGL_FLAGS += $(CAIRO_FLAGS) | DGL_FLAGS += $(CAIRO_FLAGS) | ||||
| DGL_LIBS += $(CAIRO_LIBS) | DGL_LIBS += $(CAIRO_LIBS) | ||||
| DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a | DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a | ||||
| @@ -82,8 +91,8 @@ endif | |||||
| endif | endif | ||||
| ifeq ($(UI_TYPE),opengl) | ifeq ($(UI_TYPE),opengl) | ||||
| DGL_FLAGS += -DDGL_OPENGL | |||||
| ifeq ($(HAVE_OPENGL),true) | ifeq ($(HAVE_OPENGL),true) | ||||
| DGL_FLAGS += -DDGL_OPENGL | |||||
| DGL_FLAGS += $(OPENGL_FLAGS) | DGL_FLAGS += $(OPENGL_FLAGS) | ||||
| DGL_LIBS += $(OPENGL_LIBS) | DGL_LIBS += $(OPENGL_LIBS) | ||||
| DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a | DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a | ||||
| @@ -107,6 +116,10 @@ DGL_LIBS = | |||||
| OBJS_UI = | OBJS_UI = | ||||
| endif | endif | ||||
| ifneq ($(HAVE_LIBLO),true) | |||||
| dssi_ui = | |||||
| endif | |||||
| # TODO split dsp and ui object build flags | # TODO split dsp and ui object build flags | ||||
| BASE_FLAGS += $(DGL_FLAGS) | BASE_FLAGS += $(DGL_FLAGS) | ||||
| @@ -152,12 +165,12 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | |||||
| $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | ||||
| -@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
| @echo "Compiling DistrhoPluginMain.cpp (JACK)" | @echo "Compiling DistrhoPluginMain.cpp (JACK)" | ||||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags jack) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@ | |||||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(JACK_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@ | |||||
| $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | ||||
| -@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
| @echo "Compiling DistrhoUIMain.cpp (DSSI)" | @echo "Compiling DistrhoUIMain.cpp (DSSI)" | ||||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags liblo) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ | |||||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # JACK | # JACK | ||||
| @@ -171,7 +184,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o | |||||
| endif | endif | ||||
| -@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
| @echo "Creating JACK standalone for $(NAME)" | @echo "Creating JACK standalone for $(NAME)" | ||||
| $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs jack) -o $@ | |||||
| $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(JACK_LIBS) -o $@ | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # LADSPA | # LADSPA | ||||
| @@ -198,7 +211,7 @@ $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o | |||||
| $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) | $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) | ||||
| -@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
| @echo "Creating DSSI UI for $(NAME)" | @echo "Creating DSSI UI for $(NAME)" | ||||
| $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs liblo) -o $@ | |||||
| $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@ | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # LV2 | # LV2 | ||||
| @@ -239,7 +252,7 @@ endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| -include $(OBJS_DSP:%.o=%.d) | -include $(OBJS_DSP:%.o=%.d) | ||||
| ifeq ($(HAVE_DGL),true) | |||||
| ifneq ($(UI_TYPE),) | |||||
| -include $(OBJS_UI:%.o=%.d) | -include $(OBJS_UI:%.o=%.d) | ||||
| endif | endif | ||||
| @@ -45,12 +45,14 @@ Online help and discussion about DPF happens in the [KXStudio chat DPF room](htt | |||||
| - [Stone Phaser](https://github.com/jpcima/stone-phaser) | - [Stone Phaser](https://github.com/jpcima/stone-phaser) | ||||
| - [String-machine](https://github.com/jpcima/string-machine) | - [String-machine](https://github.com/jpcima/string-machine) | ||||
| - [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins) | - [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins) | ||||
| - [VL1-emulator](https://github.com/linuxmao-org/VL1-emulator) | |||||
| - [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper) | - [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper) | ||||
| - [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | - [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | ||||
| - [YK Chorus](https://github.com/SpotlightKid/ykchorus) | - [YK Chorus](https://github.com/SpotlightKid/ykchorus) | ||||
| - [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | - [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | ||||
| ## Work in progress | ## Work in progress | ||||
| - [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2) | - [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2) | ||||
| - [fverb](https://github.com/jpcima/fverb) | |||||
| - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) | - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) | ||||
| - [gunshot](https://github.com/soerenbnoergaard/gunshot) | - [gunshot](https://github.com/soerenbnoergaard/gunshot) | ||||
| - [midiomatic](https://github.com/SpotlightKid/midiomatic) | - [midiomatic](https://github.com/SpotlightKid/midiomatic) | ||||
| @@ -53,6 +53,7 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
| DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
| DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
| DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
| DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
| # undef DGL_EXT | # undef DGL_EXT | ||||
| #endif | #endif | ||||
| @@ -66,20 +67,32 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||||
| #if defined(NANOVG_GL2) | #if defined(NANOVG_GL2) | ||||
| # define nvgCreateGL nvgCreateGL2 | # define nvgCreateGL nvgCreateGL2 | ||||
| # define nvgDeleteGL nvgDeleteGL2 | # define nvgDeleteGL nvgDeleteGL2 | ||||
| # define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2 | |||||
| # define nvglImageHandle nvglImageHandleGL2 | |||||
| #elif defined(NANOVG_GL3) | #elif defined(NANOVG_GL3) | ||||
| # define nvgCreateGL nvgCreateGL3 | # define nvgCreateGL nvgCreateGL3 | ||||
| # define nvgDeleteGL nvgDeleteGL3 | # define nvgDeleteGL nvgDeleteGL3 | ||||
| # define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3 | |||||
| # define nvglImageHandle nvglImageHandleGL3 | |||||
| #elif defined(NANOVG_GLES2) | #elif defined(NANOVG_GLES2) | ||||
| # define nvgCreateGL nvgCreateGLES2 | # define nvgCreateGL nvgCreateGLES2 | ||||
| # define nvgDeleteGL nvgDeleteGLES2 | # define nvgDeleteGL nvgDeleteGLES2 | ||||
| # define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2 | |||||
| # define nvglImageHandle nvglImageHandleGLES2 | |||||
| #elif defined(NANOVG_GLES3) | #elif defined(NANOVG_GLES3) | ||||
| # define nvgCreateGL nvgCreateGLES3 | # define nvgCreateGL nvgCreateGLES3 | ||||
| # define nvgDeleteGL nvgDeleteGLES3 | # define nvgDeleteGL nvgDeleteGLES3 | ||||
| # define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3 | |||||
| # define nvglImageHandle nvglImageHandleGLES3 | |||||
| #endif | #endif | ||||
| static NVGcontext* nvgCreateGL_helper(int flags) | static NVGcontext* nvgCreateGL_helper(int flags) | ||||
| { | { | ||||
| #if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
| # if defined(__GNUC__) && (__GNUC__ >= 10) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wcast-function-type" | |||||
| # endif | |||||
| static bool needsInit = true; | static bool needsInit = true; | ||||
| # define DGL_EXT(PROC, func) \ | # define DGL_EXT(PROC, func) \ | ||||
| if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | ||||
| @@ -111,8 +124,12 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
| DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
| DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
| DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
| DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
| # undef DGL_EXT | # undef DGL_EXT | ||||
| needsInit = false; | needsInit = false; | ||||
| # if defined(__GNUC__) && (__GNUC__ >= 10) | |||||
| # pragma GCC diagnostic pop | |||||
| # endif | |||||
| #endif | #endif | ||||
| return nvgCreateGL(flags); | return nvgCreateGL(flags); | ||||
| } | } | ||||
| @@ -922,7 +939,7 @@ void NanoVG::loadSharedResources() | |||||
| using namespace dpf_resources; | using namespace dpf_resources; | ||||
| nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0); | |||||
| nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0); | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -38,10 +38,15 @@ enum FONSalign { | |||||
| FONS_ALIGN_BASELINE = 1<<6, // Default | FONS_ALIGN_BASELINE = 1<<6, // Default | ||||
| }; | }; | ||||
| enum FONSglyphBitmap { | |||||
| FONS_GLYPH_BITMAP_OPTIONAL = 1, | |||||
| FONS_GLYPH_BITMAP_REQUIRED = 2, | |||||
| }; | |||||
| enum FONSerrorCode { | enum FONSerrorCode { | ||||
| // Font atlas is full. | // Font atlas is full. | ||||
| FONS_ATLAS_FULL = 1, | FONS_ATLAS_FULL = 1, | ||||
| // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||||
| // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||||
| FONS_SCRATCH_FULL = 2, | FONS_SCRATCH_FULL = 2, | ||||
| // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. | // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. | ||||
| FONS_STATES_OVERFLOW = 3, | FONS_STATES_OVERFLOW = 3, | ||||
| @@ -78,6 +83,7 @@ struct FONStextIter { | |||||
| const char* next; | const char* next; | ||||
| const char* end; | const char* end; | ||||
| unsigned int utf8state; | unsigned int utf8state; | ||||
| int bitmapOption; | |||||
| }; | }; | ||||
| typedef struct FONStextIter FONStextIter; | typedef struct FONStextIter FONStextIter; | ||||
| @@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s); | |||||
| void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | ||||
| // Returns current atlas size. | // Returns current atlas size. | ||||
| void fonsGetAtlasSize(FONScontext* s, int* width, int* height); | void fonsGetAtlasSize(FONScontext* s, int* width, int* height); | ||||
| // Expands the atlas size. | |||||
| // Expands the atlas size. | |||||
| int fonsExpandAtlas(FONScontext* s, int width, int height); | int fonsExpandAtlas(FONScontext* s, int width, int height); | ||||
| // Resets the whole stash. | // Resets the whole stash. | ||||
| int fonsResetAtlas(FONScontext* stash, int width, int height); | int fonsResetAtlas(FONScontext* stash, int width, int height); | ||||
| // Add fonts | // Add fonts | ||||
| int fonsAddFont(FONScontext* s, const char* name, const char* path); | |||||
| int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData); | |||||
| int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); | |||||
| int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); | |||||
| int fonsGetFontByName(FONScontext* s, const char* name); | int fonsGetFontByName(FONScontext* s, const char* name); | ||||
| // State handling | // State handling | ||||
| @@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); | |||||
| void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); | void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); | ||||
| // Text iterator | // Text iterator | ||||
| int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); | |||||
| int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption); | |||||
| int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); | ||||
| // Pull texture changes | // Pull texture changes | ||||
| @@ -154,20 +160,28 @@ typedef struct FONSttFontImpl FONSttFontImpl; | |||||
| static FT_Library ftLibrary; | static FT_Library ftLibrary; | ||||
| int fons__tt_init(FONScontext *context) | int fons__tt_init(FONScontext *context) | ||||
| { | |||||
| { | |||||
| FT_Error ftError; | FT_Error ftError; | ||||
| FONS_NOTUSED(context); | |||||
| FONS_NOTUSED(context); | |||||
| ftError = FT_Init_FreeType(&ftLibrary); | ftError = FT_Init_FreeType(&ftLibrary); | ||||
| return ftError == 0; | return ftError == 0; | ||||
| } | } | ||||
| int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||||
| int fons__tt_done(FONScontext *context) | |||||
| { | |||||
| FT_Error ftError; | |||||
| FONS_NOTUSED(context); | |||||
| ftError = FT_Done_FreeType(ftLibrary); | |||||
| return ftError == 0; | |||||
| } | |||||
| int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||||
| { | { | ||||
| FT_Error ftError; | FT_Error ftError; | ||||
| FONS_NOTUSED(context); | FONS_NOTUSED(context); | ||||
| //font->font.userdata = stash; | //font->font.userdata = stash; | ||||
| ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); | |||||
| ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); | |||||
| return ftError == 0; | return ftError == 0; | ||||
| } | } | ||||
| @@ -180,7 +194,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i | |||||
| float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | ||||
| { | { | ||||
| #if 1 | |||||
| // Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
| return size / (font->font->ascender - font->font->descender); | return size / (font->font->ascender - font->font->descender); | ||||
| #else | |||||
| return size / font->font->units_per_EM; | |||||
| #endif | |||||
| } | } | ||||
| int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | ||||
| @@ -193,16 +212,28 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float | |||||
| { | { | ||||
| FT_Error ftError; | FT_Error ftError; | ||||
| FT_GlyphSlot ftGlyph; | FT_GlyphSlot ftGlyph; | ||||
| FT_Fixed advFixed; | |||||
| FONS_NOTUSED(scale); | FONS_NOTUSED(scale); | ||||
| #if 1 | |||||
| // Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
| ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | ||||
| #else | |||||
| ftError = FT_Set_Pixel_Sizes(font->font, 0, size); | |||||
| #endif | |||||
| if (ftError) return 0; | if (ftError) return 0; | ||||
| #if 1 | |||||
| // Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
| ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | ||||
| #else | |||||
| ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); | |||||
| #endif | |||||
| if (ftError) return 0; | if (ftError) return 0; | ||||
| ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance); | |||||
| ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); | |||||
| if (ftError) return 0; | if (ftError) return 0; | ||||
| ftGlyph = font->font->glyph; | ftGlyph = font->font->glyph; | ||||
| *lsb = ftGlyph->metrics.horiBearingX; | |||||
| *advance = (int)advFixed; | |||||
| *lsb = (int)ftGlyph->metrics.horiBearingX; | |||||
| *x0 = ftGlyph->bitmap_left; | *x0 = ftGlyph->bitmap_left; | ||||
| *x1 = *x0 + ftGlyph->bitmap.width; | *x1 = *x0 + ftGlyph->bitmap.width; | ||||
| *y0 = -ftGlyph->bitmap_top; | *y0 = -ftGlyph->bitmap_top; | ||||
| @@ -215,7 +246,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int | |||||
| { | { | ||||
| FT_GlyphSlot ftGlyph = font->font->glyph; | FT_GlyphSlot ftGlyph = font->font->glyph; | ||||
| int ftGlyphOffset = 0; | int ftGlyphOffset = 0; | ||||
| int x, y; | |||||
| unsigned int x, y; | |||||
| FONS_NOTUSED(outWidth); | FONS_NOTUSED(outWidth); | ||||
| FONS_NOTUSED(outHeight); | FONS_NOTUSED(outHeight); | ||||
| FONS_NOTUSED(scaleX); | FONS_NOTUSED(scaleX); | ||||
| @@ -233,7 +264,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
| { | { | ||||
| FT_Vector ftKerning; | FT_Vector ftKerning; | ||||
| FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | ||||
| return ftKerning.x; | |||||
| return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer | |||||
| } | } | ||||
| #else | #else | ||||
| @@ -256,13 +287,24 @@ int fons__tt_init(FONScontext *context) | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||||
| int fons__tt_done(FONScontext *context) | |||||
| { | |||||
| FONS_NOTUSED(context); | |||||
| return 1; | |||||
| } | |||||
| int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||||
| { | { | ||||
| int stbError; | |||||
| int offset, stbError; | |||||
| FONS_NOTUSED(dataSize); | FONS_NOTUSED(dataSize); | ||||
| font->font.userdata = context; | font->font.userdata = context; | ||||
| stbError = stbtt_InitFont(&font->font, data, 0); | |||||
| offset = stbtt_GetFontOffsetForIndex(data, fontIndex); | |||||
| if (offset == -1) { | |||||
| stbError = 0; | |||||
| } else { | |||||
| stbError = stbtt_InitFont(&font->font, data, offset); | |||||
| } | |||||
| return stbError; | return stbError; | ||||
| } | } | ||||
| @@ -273,7 +315,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i | |||||
| float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | ||||
| { | { | ||||
| #if 1 | |||||
| // Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
| return stbtt_ScaleForPixelHeight(&font->font, size); | return stbtt_ScaleForPixelHeight(&font->font, size); | ||||
| #else | |||||
| return stbtt_ScaleForMappingEmToPixels(&font->font, size); | |||||
| #endif | |||||
| } | } | ||||
| int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | ||||
| @@ -304,7 +351,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
| #endif | #endif | ||||
| #ifndef FONS_SCRATCH_BUF_SIZE | #ifndef FONS_SCRATCH_BUF_SIZE | ||||
| # define FONS_SCRATCH_BUF_SIZE 16000 | |||||
| # define FONS_SCRATCH_BUF_SIZE 96000 | |||||
| #endif | #endif | ||||
| #ifndef FONS_HASH_LUT_SIZE | #ifndef FONS_HASH_LUT_SIZE | ||||
| # define FONS_HASH_LUT_SIZE 256 | # define FONS_HASH_LUT_SIZE 256 | ||||
| @@ -324,6 +371,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
| #ifndef FONS_MAX_STATES | #ifndef FONS_MAX_STATES | ||||
| # define FONS_MAX_STATES 20 | # define FONS_MAX_STATES 20 | ||||
| #endif | #endif | ||||
| #ifndef FONS_MAX_FALLBACKS | |||||
| # define FONS_MAX_FALLBACKS 20 | |||||
| #endif | |||||
| static unsigned int fons__hashint(unsigned int a) | static unsigned int fons__hashint(unsigned int a) | ||||
| { | { | ||||
| @@ -361,7 +411,7 @@ struct FONSfont | |||||
| { | { | ||||
| FONSttFontImpl font; | FONSttFontImpl font; | ||||
| char name[64]; | char name[64]; | ||||
| const unsigned char* data; | |||||
| unsigned char* data; | |||||
| int dataSize; | int dataSize; | ||||
| unsigned char freeData; | unsigned char freeData; | ||||
| float ascender; | float ascender; | ||||
| @@ -371,6 +421,8 @@ struct FONSfont | |||||
| int cglyphs; | int cglyphs; | ||||
| int nglyphs; | int nglyphs; | ||||
| int lut[FONS_HASH_LUT_SIZE]; | int lut[FONS_HASH_LUT_SIZE]; | ||||
| int fallbacks[FONS_MAX_FALLBACKS]; | |||||
| int nfallbacks; | |||||
| }; | }; | ||||
| typedef struct FONSfont FONSfont; | typedef struct FONSfont FONSfont; | ||||
| @@ -421,6 +473,8 @@ struct FONScontext | |||||
| void* errorUptr; | void* errorUptr; | ||||
| }; | }; | ||||
| #ifdef STB_TRUETYPE_IMPLEMENTATION | |||||
| static void* fons__tmpalloc(size_t size, void* up) | static void* fons__tmpalloc(size_t size, void* up) | ||||
| { | { | ||||
| unsigned char* ptr; | unsigned char* ptr; | ||||
| @@ -446,6 +500,8 @@ static void fons__tmpfree(void* ptr, void* up) | |||||
| // empty | // empty | ||||
| } | } | ||||
| #endif // STB_TRUETYPE_IMPLEMENTATION | |||||
| // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> | // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> | ||||
| // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. | ||||
| @@ -751,6 +807,27 @@ static FONSstate* fons__getState(FONScontext* stash) | |||||
| return &stash->states[stash->nstates-1]; | return &stash->states[stash->nstates-1]; | ||||
| } | } | ||||
| int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) | |||||
| { | |||||
| FONSfont* baseFont = stash->fonts[base]; | |||||
| if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { | |||||
| baseFont->fallbacks[baseFont->nfallbacks++] = fallback; | |||||
| return 1; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| void fonsResetFallbackFont(FONScontext* stash, int base) | |||||
| { | |||||
| int i; | |||||
| FONSfont* baseFont = stash->fonts[base]; | |||||
| baseFont->nfallbacks = 0; | |||||
| baseFont->nglyphs = 0; | |||||
| for (i = 0; i < FONS_HASH_LUT_SIZE; i++) | |||||
| baseFont->lut[i] = -1; | |||||
| } | |||||
| void fonsSetSize(FONScontext* stash, float size) | void fonsSetSize(FONScontext* stash, float size) | ||||
| { | { | ||||
| fons__getState(stash)->size = size; | fons__getState(stash)->size = size; | ||||
| @@ -818,7 +895,7 @@ static void fons__freeFont(FONSfont* font) | |||||
| { | { | ||||
| if (font == NULL) return; | if (font == NULL) return; | ||||
| if (font->glyphs) free(font->glyphs); | if (font->glyphs) free(font->glyphs); | ||||
| if (font->freeData && font->data) free((void*)font->data); | |||||
| if (font->freeData && font->data) free(font->data); | |||||
| free(font); | free(font); | ||||
| } | } | ||||
| @@ -849,12 +926,12 @@ error: | |||||
| return FONS_INVALID; | return FONS_INVALID; | ||||
| } | } | ||||
| int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||||
| int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) | |||||
| { | { | ||||
| FILE* fp = 0; | FILE* fp = 0; | ||||
| int dataSize = 0; | int dataSize = 0; | ||||
| size_t readed; | |||||
| unsigned char* data = NULL; | unsigned char* data = NULL; | ||||
| size_t ignore; | |||||
| // Read in the font data. | // Read in the font data. | ||||
| fp = fopen(path, "rb"); | fp = fopen(path, "rb"); | ||||
| @@ -864,21 +941,20 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||||
| fseek(fp,0,SEEK_SET); | fseek(fp,0,SEEK_SET); | ||||
| data = (unsigned char*)malloc(dataSize); | data = (unsigned char*)malloc(dataSize); | ||||
| if (data == NULL) goto error; | if (data == NULL) goto error; | ||||
| ignore = fread(data, 1, dataSize, fp); | |||||
| readed = fread(data, 1, dataSize, fp); | |||||
| fclose(fp); | fclose(fp); | ||||
| fp = 0; | fp = 0; | ||||
| if (readed != (size_t)dataSize) goto error; | |||||
| return fonsAddFontMem(stash, name, data, dataSize, 1); | |||||
| return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); | |||||
| error: | error: | ||||
| if (data) free(data); | if (data) free(data); | ||||
| if (fp) fclose(fp); | if (fp) fclose(fp); | ||||
| return FONS_INVALID; | return FONS_INVALID; | ||||
| FONS_NOTUSED(ignore); | |||||
| } | } | ||||
| int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData) | |||||
| int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) | |||||
| { | { | ||||
| int i, ascent, descent, fh, lineGap; | int i, ascent, descent, fh, lineGap; | ||||
| FONSfont* font; | FONSfont* font; | ||||
| @@ -903,15 +979,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da | |||||
| // Init font | // Init font | ||||
| stash->nscratch = 0; | stash->nscratch = 0; | ||||
| if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; | |||||
| if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; | |||||
| // Store normalized line height. The real line height is got | // Store normalized line height. The real line height is got | ||||
| // by multiplying the lineh by font size. | // by multiplying the lineh by font size. | ||||
| fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | ||||
| ascent += lineGap; | |||||
| fh = ascent - descent; | fh = ascent - descent; | ||||
| font->ascender = (float)ascent / (float)fh; | font->ascender = (float)ascent / (float)fh; | ||||
| font->descender = (float)descent / (float)fh; | font->descender = (float)descent / (float)fh; | ||||
| font->lineh = (float)(fh + lineGap) / (float)fh; | |||||
| font->lineh = font->ascender - font->descender; | |||||
| return idx; | return idx; | ||||
| @@ -1010,7 +1087,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int | |||||
| } | } | ||||
| static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, | static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, | ||||
| short isize, short iblur) | |||||
| short isize, short iblur, int bitmapOption) | |||||
| { | { | ||||
| int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; | int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; | ||||
| float scale; | float scale; | ||||
| @@ -1020,6 +1097,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
| int pad, added; | int pad, added; | ||||
| unsigned char* bdst; | unsigned char* bdst; | ||||
| unsigned char* dst; | unsigned char* dst; | ||||
| FONSfont* renderFont = font; | |||||
| if (isize < 2) return NULL; | if (isize < 2) return NULL; | ||||
| if (iblur > 20) iblur = 20; | if (iblur > 20) iblur = 20; | ||||
| @@ -1032,32 +1110,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
| h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); | h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); | ||||
| i = font->lut[h]; | i = font->lut[h]; | ||||
| while (i != -1) { | while (i != -1) { | ||||
| if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) | |||||
| return &font->glyphs[i]; | |||||
| if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) { | |||||
| glyph = &font->glyphs[i]; | |||||
| if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) { | |||||
| return glyph; | |||||
| } | |||||
| // At this point, glyph exists but the bitmap data is not yet created. | |||||
| break; | |||||
| } | |||||
| i = font->glyphs[i].next; | i = font->glyphs[i].next; | ||||
| } | } | ||||
| // Could not find glyph, create it. | |||||
| scale = fons__tt_getPixelHeightScale(&font->font, size); | |||||
| // Create a new glyph or rasterize bitmap data for a cached glyph. | |||||
| g = fons__tt_getGlyphIndex(&font->font, codepoint); | g = fons__tt_getGlyphIndex(&font->font, codepoint); | ||||
| fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||||
| // Try to find the glyph in fallback fonts. | |||||
| if (g == 0) { | |||||
| for (i = 0; i < font->nfallbacks; ++i) { | |||||
| FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; | |||||
| int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); | |||||
| if (fallbackIndex != 0) { | |||||
| g = fallbackIndex; | |||||
| renderFont = fallbackFont; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // It is possible that we did not find a fallback glyph. | |||||
| // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. | |||||
| } | |||||
| scale = fons__tt_getPixelHeightScale(&renderFont->font, size); | |||||
| fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||||
| gw = x1-x0 + pad*2; | gw = x1-x0 + pad*2; | ||||
| gh = y1-y0 + pad*2; | gh = y1-y0 + pad*2; | ||||
| // Find free spot for the rect in the atlas | |||||
| added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||||
| if (added == 0 && stash->handleError != NULL) { | |||||
| // Atlas is full, let the user to resize the atlas (or not), and try again. | |||||
| stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||||
| // Determines the spot to draw glyph in the atlas. | |||||
| if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) { | |||||
| // Find free spot for the rect in the atlas | |||||
| added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | ||||
| if (added == 0 && stash->handleError != NULL) { | |||||
| // Atlas is full, let the user to resize the atlas (or not), and try again. | |||||
| stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||||
| added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||||
| } | |||||
| if (added == 0) return NULL; | |||||
| } else { | |||||
| // Negative coordinate indicates there is no bitmap data created. | |||||
| gx = -1; | |||||
| gy = -1; | |||||
| } | } | ||||
| if (added == 0) return NULL; | |||||
| // Init glyph. | // Init glyph. | ||||
| glyph = fons__allocGlyph(font); | |||||
| glyph->codepoint = codepoint; | |||||
| glyph->size = isize; | |||||
| glyph->blur = iblur; | |||||
| if (glyph == NULL) { | |||||
| glyph = fons__allocGlyph(font); | |||||
| glyph->codepoint = codepoint; | |||||
| glyph->size = isize; | |||||
| glyph->blur = iblur; | |||||
| glyph->next = 0; | |||||
| // Insert char to hash lookup. | |||||
| glyph->next = font->lut[h]; | |||||
| font->lut[h] = font->nglyphs-1; | |||||
| } | |||||
| glyph->index = g; | glyph->index = g; | ||||
| glyph->x0 = (short)gx; | glyph->x0 = (short)gx; | ||||
| glyph->y0 = (short)gy; | glyph->y0 = (short)gy; | ||||
| @@ -1066,15 +1178,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
| glyph->xadv = (short)(scale * advance * 10.0f); | glyph->xadv = (short)(scale * advance * 10.0f); | ||||
| glyph->xoff = (short)(x0 - pad); | glyph->xoff = (short)(x0 - pad); | ||||
| glyph->yoff = (short)(y0 - pad); | glyph->yoff = (short)(y0 - pad); | ||||
| glyph->next = 0; | |||||
| // Insert char to hash lookup. | |||||
| glyph->next = font->lut[h]; | |||||
| font->lut[h] = font->nglyphs-1; | |||||
| if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) { | |||||
| return glyph; | |||||
| } | |||||
| // Rasterize | // Rasterize | ||||
| dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; | dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; | ||||
| fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); | |||||
| fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g); | |||||
| // Make sure there is one pixel empty border. | // Make sure there is one pixel empty border. | ||||
| dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | ||||
| @@ -1101,7 +1212,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
| if (iblur > 0) { | if (iblur > 0) { | ||||
| stash->nscratch = 0; | stash->nscratch = 0; | ||||
| bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | ||||
| fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); | |||||
| fons__blur(stash, bdst, gw, gh, stash->params.width, iblur); | |||||
| } | } | ||||
| stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); | ||||
| @@ -1134,8 +1245,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||||
| y1 = (float)(glyph->y1-1); | y1 = (float)(glyph->y1-1); | ||||
| if (stash->params.flags & FONS_ZERO_TOPLEFT) { | if (stash->params.flags & FONS_ZERO_TOPLEFT) { | ||||
| rx = (float)(int)(*x + xoff); | |||||
| ry = (float)(int)(*y + yoff); | |||||
| rx = floorf(*x + xoff); | |||||
| ry = floorf(*y + yoff); | |||||
| q->x0 = rx; | q->x0 = rx; | ||||
| q->y0 = ry; | q->y0 = ry; | ||||
| @@ -1147,8 +1258,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||||
| q->s1 = x1 * stash->itw; | q->s1 = x1 * stash->itw; | ||||
| q->t1 = y1 * stash->ith; | q->t1 = y1 * stash->ith; | ||||
| } else { | } else { | ||||
| rx = (float)(int)(*x + xoff); | |||||
| ry = (float)(int)(*y - yoff); | |||||
| rx = floorf(*x + xoff); | |||||
| ry = floorf(*y - yoff); | |||||
| q->x0 = rx; | q->x0 = rx; | ||||
| q->y0 = ry; | q->y0 = ry; | ||||
| @@ -1226,7 +1337,7 @@ float fonsDrawText(FONScontext* stash, | |||||
| const char* str, const char* end) | const char* str, const char* end) | ||||
| { | { | ||||
| FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
| unsigned int codepoint = 0; | |||||
| unsigned int codepoint; | |||||
| unsigned int utf8state = 0; | unsigned int utf8state = 0; | ||||
| FONSglyph* glyph = NULL; | FONSglyph* glyph = NULL; | ||||
| FONSquad q; | FONSquad q; | ||||
| @@ -1263,7 +1374,7 @@ float fonsDrawText(FONScontext* stash, | |||||
| for (; str != end; ++str) { | for (; str != end; ++str) { | ||||
| if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | ||||
| continue; | continue; | ||||
| glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||||
| glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); | |||||
| if (glyph != NULL) { | if (glyph != NULL) { | ||||
| fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | ||||
| @@ -1286,7 +1397,7 @@ float fonsDrawText(FONScontext* stash, | |||||
| } | } | ||||
| int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | ||||
| float x, float y, const char* str, const char* end) | |||||
| float x, float y, const char* str, const char* end, int bitmapOption) | |||||
| { | { | ||||
| FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
| float width; | float width; | ||||
| @@ -1326,6 +1437,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | |||||
| iter->end = end; | iter->end = end; | ||||
| iter->codepoint = 0; | iter->codepoint = 0; | ||||
| iter->prevGlyphIndex = -1; | iter->prevGlyphIndex = -1; | ||||
| iter->bitmapOption = bitmapOption; | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| @@ -1346,7 +1458,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) | |||||
| // Get glyph and quad | // Get glyph and quad | ||||
| iter->x = iter->nextx; | iter->x = iter->nextx; | ||||
| iter->y = iter->nexty; | iter->y = iter->nexty; | ||||
| glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); | |||||
| glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption); | |||||
| // If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid. | |||||
| if (glyph != NULL) | if (glyph != NULL) | ||||
| fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | ||||
| iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; | iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; | ||||
| @@ -1406,12 +1519,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y) | |||||
| } | } | ||||
| float fonsTextBounds(FONScontext* stash, | float fonsTextBounds(FONScontext* stash, | ||||
| float x, float y, | |||||
| float x, float y, | |||||
| const char* str, const char* end, | const char* str, const char* end, | ||||
| float* bounds) | float* bounds) | ||||
| { | { | ||||
| FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
| unsigned int codepoint = 0; | |||||
| unsigned int codepoint; | |||||
| unsigned int utf8state = 0; | unsigned int utf8state = 0; | ||||
| FONSquad q; | FONSquad q; | ||||
| FONSglyph* glyph = NULL; | FONSglyph* glyph = NULL; | ||||
| @@ -1443,7 +1556,7 @@ float fonsTextBounds(FONScontext* stash, | |||||
| for (; str != end; ++str) { | for (; str != end; ++str) { | ||||
| if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | ||||
| continue; | continue; | ||||
| glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||||
| glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
| if (glyph != NULL) { | if (glyph != NULL) { | ||||
| fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | ||||
| if (q.x0 < minx) minx = q.x0; | if (q.x0 < minx) minx = q.x0; | ||||
| @@ -1568,6 +1681,7 @@ void fonsDeleteInternal(FONScontext* stash) | |||||
| if (stash->texData) free(stash->texData); | if (stash->texData) free(stash->texData); | ||||
| if (stash->scratch) free(stash->scratch); | if (stash->scratch) free(stash->scratch); | ||||
| free(stash); | free(stash); | ||||
| fons__tt_done(stash); | |||||
| } | } | ||||
| void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) | void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) | ||||
| @@ -1594,7 +1708,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height) | |||||
| height = fons__maxi(height, stash->params.height); | height = fons__maxi(height, stash->params.height); | ||||
| if (width == stash->params.width && height == stash->params.height) | if (width == stash->params.width && height == stash->params.height) | ||||
| return 1; | |||||
| return 1; | |||||
| // Flush pending glyphs. | // Flush pending glyphs. | ||||
| fons__flush(stash); | fons__flush(stash); | ||||
| @@ -16,8 +16,11 @@ | |||||
| // 3. This notice may not be removed or altered from any source distribution. | // 3. This notice may not be removed or altered from any source distribution. | ||||
| // | // | ||||
| #include <stdlib.h> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <memory.h> | |||||
| #include "nanovg.h" | #include "nanovg.h" | ||||
| #define FONTSTASH_IMPLEMENTATION | #define FONTSTASH_IMPLEMENTATION | ||||
| #include "fontstash.h" | #include "fontstash.h" | ||||
| @@ -63,6 +66,8 @@ enum NVGpointFlags | |||||
| }; | }; | ||||
| struct NVGstate { | struct NVGstate { | ||||
| NVGcompositeOperationState compositeOperation; | |||||
| int shapeAntiAlias; | |||||
| NVGpaint fill; | NVGpaint fill; | ||||
| NVGpaint stroke; | NVGpaint stroke; | ||||
| float strokeWidth; | float strokeWidth; | ||||
| @@ -200,6 +205,84 @@ static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio) | |||||
| ctx->devicePxRatio = ratio; | ctx->devicePxRatio = ratio; | ||||
| } | } | ||||
| static NVGcompositeOperationState nvg__compositeOperationState(int op) | |||||
| { | |||||
| int sfactor, dfactor; | |||||
| if (op == NVG_SOURCE_OVER) | |||||
| { | |||||
| sfactor = NVG_ONE; | |||||
| dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
| } | |||||
| else if (op == NVG_SOURCE_IN) | |||||
| { | |||||
| sfactor = NVG_DST_ALPHA; | |||||
| dfactor = NVG_ZERO; | |||||
| } | |||||
| else if (op == NVG_SOURCE_OUT) | |||||
| { | |||||
| sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
| dfactor = NVG_ZERO; | |||||
| } | |||||
| else if (op == NVG_ATOP) | |||||
| { | |||||
| sfactor = NVG_DST_ALPHA; | |||||
| dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
| } | |||||
| else if (op == NVG_DESTINATION_OVER) | |||||
| { | |||||
| sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
| dfactor = NVG_ONE; | |||||
| } | |||||
| else if (op == NVG_DESTINATION_IN) | |||||
| { | |||||
| sfactor = NVG_ZERO; | |||||
| dfactor = NVG_SRC_ALPHA; | |||||
| } | |||||
| else if (op == NVG_DESTINATION_OUT) | |||||
| { | |||||
| sfactor = NVG_ZERO; | |||||
| dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
| } | |||||
| else if (op == NVG_DESTINATION_ATOP) | |||||
| { | |||||
| sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
| dfactor = NVG_SRC_ALPHA; | |||||
| } | |||||
| else if (op == NVG_LIGHTER) | |||||
| { | |||||
| sfactor = NVG_ONE; | |||||
| dfactor = NVG_ONE; | |||||
| } | |||||
| else if (op == NVG_COPY) | |||||
| { | |||||
| sfactor = NVG_ONE; | |||||
| dfactor = NVG_ZERO; | |||||
| } | |||||
| else if (op == NVG_XOR) | |||||
| { | |||||
| sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
| dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
| } | |||||
| else | |||||
| { | |||||
| sfactor = NVG_ONE; | |||||
| dfactor = NVG_ZERO; | |||||
| } | |||||
| NVGcompositeOperationState state; | |||||
| state.srcRGB = sfactor; | |||||
| state.dstRGB = dfactor; | |||||
| state.srcAlpha = sfactor; | |||||
| state.dstAlpha = dfactor; | |||||
| return state; | |||||
| } | |||||
| static NVGstate* nvg__getState(NVGcontext* ctx) | |||||
| { | |||||
| return &ctx->states[ctx->nstates-1]; | |||||
| } | |||||
| NVGcontext* nvgCreateInternal(NVGparams* params) | NVGcontext* nvgCreateInternal(NVGparams* params) | ||||
| { | { | ||||
| FONSparams fontParams; | FONSparams fontParams; | ||||
| @@ -280,7 +363,7 @@ void nvgDeleteInternal(NVGcontext* ctx) | |||||
| free(ctx); | free(ctx); | ||||
| } | } | ||||
| void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio) | |||||
| void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio) | |||||
| { | { | ||||
| /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", | /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", | ||||
| ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, | ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, | ||||
| @@ -291,8 +374,8 @@ void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float dev | |||||
| nvgReset(ctx); | nvgReset(ctx); | ||||
| nvg__setDevicePixelRatio(ctx, devicePixelRatio); | nvg__setDevicePixelRatio(ctx, devicePixelRatio); | ||||
| ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight); | |||||
| ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio); | |||||
| ctx->drawCallCount = 0; | ctx->drawCallCount = 0; | ||||
| ctx->fillTriCount = 0; | ctx->fillTriCount = 0; | ||||
| @@ -383,7 +466,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) | |||||
| { | { | ||||
| int i; | int i; | ||||
| float oneminu; | float oneminu; | ||||
| NVGcolor cint; | |||||
| NVGcolor cint = {{{0}}}; | |||||
| u = nvg__clampf(u, 0.0f, 1.0f); | u = nvg__clampf(u, 0.0f, 1.0f); | ||||
| oneminu = 1.0f - u; | oneminu = 1.0f - u; | ||||
| @@ -391,7 +474,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) | |||||
| { | { | ||||
| cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; | cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; | ||||
| } | } | ||||
| return cint; | return cint; | ||||
| } | } | ||||
| @@ -430,12 +513,6 @@ NVGcolor nvgHSLA(float h, float s, float l, unsigned char a) | |||||
| return col; | return col; | ||||
| } | } | ||||
| static NVGstate* nvg__getState(NVGcontext* ctx) | |||||
| { | |||||
| return &ctx->states[ctx->nstates-1]; | |||||
| } | |||||
| void nvgTransformIdentity(float* t) | void nvgTransformIdentity(float* t) | ||||
| { | { | ||||
| t[0] = 1.0f; t[1] = 0.0f; | t[0] = 1.0f; t[1] = 0.0f; | ||||
| @@ -568,6 +645,8 @@ void nvgReset(NVGcontext* ctx) | |||||
| nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); | nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); | ||||
| nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); | nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); | ||||
| state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER); | |||||
| state->shapeAntiAlias = 1; | |||||
| state->strokeWidth = 1.0f; | state->strokeWidth = 1.0f; | ||||
| state->miterLimit = 10.0f; | state->miterLimit = 10.0f; | ||||
| state->lineCap = NVG_BUTT; | state->lineCap = NVG_BUTT; | ||||
| @@ -587,6 +666,12 @@ void nvgReset(NVGcontext* ctx) | |||||
| } | } | ||||
| // State setting | // State setting | ||||
| void nvgShapeAntiAlias(NVGcontext* ctx, int enabled) | |||||
| { | |||||
| NVGstate* state = nvg__getState(ctx); | |||||
| state->shapeAntiAlias = enabled; | |||||
| } | |||||
| void nvgStrokeWidth(NVGcontext* ctx, float width) | void nvgStrokeWidth(NVGcontext* ctx, float width) | ||||
| { | { | ||||
| NVGstate* state = nvg__getState(ctx); | NVGstate* state = nvg__getState(ctx); | ||||
| @@ -719,7 +804,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) | |||||
| return image; | return image; | ||||
| } | } | ||||
| int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata) | |||||
| int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) | |||||
| { | { | ||||
| int w, h, n, image; | int w, h, n, image; | ||||
| unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); | unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); | ||||
| @@ -913,7 +998,7 @@ void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h) | |||||
| } | } | ||||
| // Transform the current scissor rect into current transform space. | // Transform the current scissor rect into current transform space. | ||||
| // If there is difference in rotation, this will be approximation. | |||||
| // If there is difference in rotation, this will be approximation. | |||||
| memcpy(pxform, state->scissor.xform, sizeof(float)*6); | memcpy(pxform, state->scissor.xform, sizeof(float)*6); | ||||
| ex = state->scissor.extent[0]; | ex = state->scissor.extent[0]; | ||||
| ey = state->scissor.extent[1]; | ey = state->scissor.extent[1]; | ||||
| @@ -936,6 +1021,30 @@ void nvgResetScissor(NVGcontext* ctx) | |||||
| state->scissor.extent[1] = -1.0f; | state->scissor.extent[1] = -1.0f; | ||||
| } | } | ||||
| // Global composite operation. | |||||
| void nvgGlobalCompositeOperation(NVGcontext* ctx, int op) | |||||
| { | |||||
| NVGstate* state = nvg__getState(ctx); | |||||
| state->compositeOperation = nvg__compositeOperationState(op); | |||||
| } | |||||
| void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor) | |||||
| { | |||||
| nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor); | |||||
| } | |||||
| void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) | |||||
| { | |||||
| NVGcompositeOperationState op; | |||||
| op.srcRGB = srcRGB; | |||||
| op.dstRGB = dstRGB; | |||||
| op.srcAlpha = srcAlpha; | |||||
| op.dstAlpha = dstAlpha; | |||||
| NVGstate* state = nvg__getState(ctx); | |||||
| state->compositeOperation = op; | |||||
| } | |||||
| static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) | static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) | ||||
| { | { | ||||
| float dx = x2 - x1; | float dx = x2 - x1; | ||||
| @@ -1173,7 +1282,7 @@ static void nvg__tesselateBezier(NVGcontext* ctx, | |||||
| { | { | ||||
| float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; | float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; | ||||
| float dx,dy,d2,d3; | float dx,dy,d2,d3; | ||||
| if (level > 10) return; | if (level > 10) return; | ||||
| x12 = (x1+x2)*0.5f; | x12 = (x1+x2)*0.5f; | ||||
| @@ -1205,8 +1314,8 @@ static void nvg__tesselateBezier(NVGcontext* ctx, | |||||
| x1234 = (x123+x234)*0.5f; | x1234 = (x123+x234)*0.5f; | ||||
| y1234 = (y123+y234)*0.5f; | y1234 = (y123+y234)*0.5f; | ||||
| nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); | |||||
| nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); | |||||
| nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); | |||||
| nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); | |||||
| } | } | ||||
| static void nvg__flattenPaths(NVGcontext* ctx) | static void nvg__flattenPaths(NVGcontext* ctx) | ||||
| @@ -1331,7 +1440,8 @@ static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w, | |||||
| } | } | ||||
| static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | ||||
| float lw, float rw, float lu, float ru, int ncap, float fringe) | |||||
| float lw, float rw, float lu, float ru, int ncap, | |||||
| float fringe) | |||||
| { | { | ||||
| int i, n; | int i, n; | ||||
| float dlx0 = p0->dy; | float dlx0 = p0->dy; | ||||
| @@ -1464,36 +1574,39 @@ static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | |||||
| } | } | ||||
| static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, | ||||
| float dx, float dy, float w, float d, float aa) | |||||
| float dx, float dy, float w, float d, | |||||
| float aa, float u0, float u1) | |||||
| { | { | ||||
| float px = p->x - dx*d; | float px = p->x - dx*d; | ||||
| float py = p->y - dy*d; | float py = p->y - dy*d; | ||||
| float dlx = dy; | float dlx = dy; | ||||
| float dly = -dx; | float dly = -dx; | ||||
| nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, 0,0); dst++; | |||||
| nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, 1,0); dst++; | |||||
| nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
| nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++; | |||||
| nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++; | |||||
| nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
| return dst; | return dst; | ||||
| } | } | ||||
| static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, | ||||
| float dx, float dy, float w, float d, float aa) | |||||
| float dx, float dy, float w, float d, | |||||
| float aa, float u0, float u1) | |||||
| { | { | ||||
| float px = p->x + dx*d; | float px = p->x + dx*d; | ||||
| float py = p->y + dy*d; | float py = p->y + dy*d; | ||||
| float dlx = dy; | float dlx = dy; | ||||
| float dly = -dx; | float dly = -dx; | ||||
| nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
| nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, 0,0); dst++; | |||||
| nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, 1,0); dst++; | |||||
| nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
| nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++; | |||||
| nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++; | |||||
| return dst; | return dst; | ||||
| } | } | ||||
| static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | ||||
| float dx, float dy, float w, int ncap, float aa) | |||||
| float dx, float dy, float w, int ncap, | |||||
| float aa, float u0, float u1) | |||||
| { | { | ||||
| int i; | int i; | ||||
| float px = p->x; | float px = p->x; | ||||
| @@ -1504,16 +1617,17 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | |||||
| for (i = 0; i < ncap; i++) { | for (i = 0; i < ncap; i++) { | ||||
| float a = i/(float)(ncap-1)*NVG_PI; | float a = i/(float)(ncap-1)*NVG_PI; | ||||
| float ax = cosf(a) * w, ay = sinf(a) * w; | float ax = cosf(a) * w, ay = sinf(a) * w; | ||||
| nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++; | |||||
| nvg__vset(dst, px, py, 0.5f,1); dst++; | nvg__vset(dst, px, py, 0.5f,1); dst++; | ||||
| } | } | ||||
| nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
| nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
| return dst; | return dst; | ||||
| } | } | ||||
| static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | ||||
| float dx, float dy, float w, int ncap, float aa) | |||||
| float dx, float dy, float w, int ncap, | |||||
| float aa, float u0, float u1) | |||||
| { | { | ||||
| int i; | int i; | ||||
| float px = p->x; | float px = p->x; | ||||
| @@ -1521,13 +1635,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | |||||
| float dlx = dy; | float dlx = dy; | ||||
| float dly = -dx; | float dly = -dx; | ||||
| NVG_NOTUSED(aa); | NVG_NOTUSED(aa); | ||||
| nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
| nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
| for (i = 0; i < ncap; i++) { | for (i = 0; i < ncap; i++) { | ||||
| float a = i/(float)(ncap-1)*NVG_PI; | float a = i/(float)(ncap-1)*NVG_PI; | ||||
| float ax = cosf(a) * w, ay = sinf(a) * w; | float ax = cosf(a) * w, ay = sinf(a) * w; | ||||
| nvg__vset(dst, px, py, 0.5f,1); dst++; | nvg__vset(dst, px, py, 0.5f,1); dst++; | ||||
| nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, 0,1); dst++; | |||||
| nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++; | |||||
| } | } | ||||
| return dst; | return dst; | ||||
| } | } | ||||
| @@ -1603,15 +1717,24 @@ static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float mi | |||||
| } | } | ||||
| static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin, float miterLimit) | |||||
| { | |||||
| static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit) | |||||
| { | |||||
| NVGpathCache* cache = ctx->cache; | NVGpathCache* cache = ctx->cache; | ||||
| NVGvertex* verts; | NVGvertex* verts; | ||||
| NVGvertex* dst; | NVGvertex* dst; | ||||
| int cverts, i, j; | int cverts, i, j; | ||||
| float aa = ctx->fringeWidth; | |||||
| float aa = fringe;//ctx->fringeWidth; | |||||
| float u0 = 0.0f, u1 = 1.0f; | |||||
| int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. | int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. | ||||
| w += aa * 0.5f; | |||||
| // Disable the gradient used for antialiasing when antialiasing is not used. | |||||
| if (aa == 0.0f) { | |||||
| u0 = 0.5f; | |||||
| u1 = 0.5f; | |||||
| } | |||||
| nvg__calculateJoins(ctx, w, lineJoin, miterLimit); | nvg__calculateJoins(ctx, w, lineJoin, miterLimit); | ||||
| // Calculate max vertex usage. | // Calculate max vertex usage. | ||||
| @@ -1672,42 +1795,42 @@ static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin | |||||
| dy = p1->y - p0->y; | dy = p1->y - p0->y; | ||||
| nvg__normalize(&dx, &dy); | nvg__normalize(&dx, &dy); | ||||
| if (lineCap == NVG_BUTT) | if (lineCap == NVG_BUTT) | ||||
| dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa); | |||||
| dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1); | |||||
| else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | ||||
| dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa); | |||||
| dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1); | |||||
| else if (lineCap == NVG_ROUND) | else if (lineCap == NVG_ROUND) | ||||
| dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa); | |||||
| dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1); | |||||
| } | } | ||||
| for (j = s; j < e; ++j) { | for (j = s; j < e; ++j) { | ||||
| if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { | if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { | ||||
| if (lineJoin == NVG_ROUND) { | if (lineJoin == NVG_ROUND) { | ||||
| dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa); | |||||
| dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa); | |||||
| } else { | } else { | ||||
| dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa); | |||||
| dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa); | |||||
| } | } | ||||
| } else { | } else { | ||||
| nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), 0,1); dst++; | |||||
| nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), 1,1); dst++; | |||||
| nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++; | |||||
| nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++; | |||||
| } | } | ||||
| p0 = p1++; | p0 = p1++; | ||||
| } | } | ||||
| if (loop) { | if (loop) { | ||||
| // Loop it | // Loop it | ||||
| nvg__vset(dst, verts[0].x, verts[0].y, 0,1); dst++; | |||||
| nvg__vset(dst, verts[1].x, verts[1].y, 1,1); dst++; | |||||
| nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++; | |||||
| nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++; | |||||
| } else { | } else { | ||||
| // Add cap | // Add cap | ||||
| dx = p1->x - p0->x; | dx = p1->x - p0->x; | ||||
| dy = p1->y - p0->y; | dy = p1->y - p0->y; | ||||
| nvg__normalize(&dx, &dy); | nvg__normalize(&dx, &dy); | ||||
| if (lineCap == NVG_BUTT) | if (lineCap == NVG_BUTT) | ||||
| dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa); | |||||
| dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1); | |||||
| else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | ||||
| dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa); | |||||
| dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1); | |||||
| else if (lineCap == NVG_ROUND) | else if (lineCap == NVG_ROUND) | ||||
| dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa); | |||||
| dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1); | |||||
| } | } | ||||
| path->nstroke = (int)(dst - verts); | path->nstroke = (int)(dst - verts); | ||||
| @@ -1868,7 +1991,7 @@ void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y) | |||||
| { | { | ||||
| float x0 = ctx->commandx; | float x0 = ctx->commandx; | ||||
| float y0 = ctx->commandy; | float y0 = ctx->commandy; | ||||
| float vals[] = { NVG_BEZIERTO, | |||||
| float vals[] = { NVG_BEZIERTO, | |||||
| x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), | x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), | ||||
| x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), | x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), | ||||
| x, y }; | x, y }; | ||||
| @@ -1950,7 +2073,7 @@ void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, in | |||||
| float px = 0, py = 0, ptanx = 0, ptany = 0; | float px = 0, py = 0, ptanx = 0, ptany = 0; | ||||
| float vals[3 + 5*7 + 100]; | float vals[3 + 5*7 + 100]; | ||||
| int i, ndivs, nvals; | int i, ndivs, nvals; | ||||
| int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; | |||||
| int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; | |||||
| // Clamp angles | // Clamp angles | ||||
| da = a1 - a0; | da = a1 - a0; | ||||
| @@ -2022,22 +2145,31 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h) | |||||
| void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) | ||||
| { | { | ||||
| if (r < 0.1f) { | |||||
| nvgRect(ctx, x,y,w,h); | |||||
| nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r); | |||||
| } | |||||
| void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) | |||||
| { | |||||
| if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) { | |||||
| nvgRect(ctx, x, y, w, h); | |||||
| return; | return; | ||||
| } | |||||
| else { | |||||
| float rx = nvg__minf(r, nvg__absf(w)*0.5f) * nvg__signf(w), ry = nvg__minf(r, nvg__absf(h)*0.5f) * nvg__signf(h); | |||||
| } else { | |||||
| float halfw = nvg__absf(w)*0.5f; | |||||
| float halfh = nvg__absf(h)*0.5f; | |||||
| float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h); | |||||
| float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h); | |||||
| float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h); | |||||
| float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h); | |||||
| float vals[] = { | float vals[] = { | ||||
| NVG_MOVETO, x, y+ry, | |||||
| NVG_LINETO, x, y+h-ry, | |||||
| NVG_BEZIERTO, x, y+h-ry*(1-NVG_KAPPA90), x+rx*(1-NVG_KAPPA90), y+h, x+rx, y+h, | |||||
| NVG_LINETO, x+w-rx, y+h, | |||||
| NVG_BEZIERTO, x+w-rx*(1-NVG_KAPPA90), y+h, x+w, y+h-ry*(1-NVG_KAPPA90), x+w, y+h-ry, | |||||
| NVG_LINETO, x+w, y+ry, | |||||
| NVG_BEZIERTO, x+w, y+ry*(1-NVG_KAPPA90), x+w-rx*(1-NVG_KAPPA90), y, x+w-rx, y, | |||||
| NVG_LINETO, x+rx, y, | |||||
| NVG_BEZIERTO, x+rx*(1-NVG_KAPPA90), y, x, y+ry*(1-NVG_KAPPA90), x, y+ry, | |||||
| NVG_MOVETO, x, y + ryTL, | |||||
| NVG_LINETO, x, y + h - ryBL, | |||||
| NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h, | |||||
| NVG_LINETO, x + w - rxBR, y + h, | |||||
| NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR, | |||||
| NVG_LINETO, x + w, y + ryTR, | |||||
| NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y, | |||||
| NVG_LINETO, x + rxTL, y, | |||||
| NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL, | |||||
| NVG_CLOSE | NVG_CLOSE | ||||
| }; | }; | ||||
| nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); | nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); | ||||
| @@ -2092,7 +2224,7 @@ void nvgFill(NVGcontext* ctx) | |||||
| int i; | int i; | ||||
| nvg__flattenPaths(ctx); | nvg__flattenPaths(ctx); | ||||
| if (ctx->params.edgeAntiAlias) | |||||
| if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) | |||||
| nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); | nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); | ||||
| else | else | ||||
| nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); | nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); | ||||
| @@ -2101,7 +2233,7 @@ void nvgFill(NVGcontext* ctx) | |||||
| fillPaint.innerColor.a *= state->alpha; | fillPaint.innerColor.a *= state->alpha; | ||||
| fillPaint.outerColor.a *= state->alpha; | fillPaint.outerColor.a *= state->alpha; | ||||
| ctx->params.renderFill(ctx->params.userPtr, &fillPaint, &state->scissor, ctx->fringeWidth, | |||||
| ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, | |||||
| ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); | ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); | ||||
| // Count triangles | // Count triangles | ||||
| @@ -2122,6 +2254,7 @@ void nvgStroke(NVGcontext* ctx) | |||||
| const NVGpath* path; | const NVGpath* path; | ||||
| int i; | int i; | ||||
| if (strokeWidth < ctx->fringeWidth) { | if (strokeWidth < ctx->fringeWidth) { | ||||
| // If the stroke width is less than pixel size, use alpha to emulate coverage. | // If the stroke width is less than pixel size, use alpha to emulate coverage. | ||||
| // Since coverage is area, scale by alpha*alpha. | // Since coverage is area, scale by alpha*alpha. | ||||
| @@ -2137,12 +2270,12 @@ void nvgStroke(NVGcontext* ctx) | |||||
| nvg__flattenPaths(ctx); | nvg__flattenPaths(ctx); | ||||
| if (ctx->params.edgeAntiAlias) | |||||
| nvg__expandStroke(ctx, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
| if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) | |||||
| nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit); | |||||
| else | else | ||||
| nvg__expandStroke(ctx, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
| nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
| ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, &state->scissor, ctx->fringeWidth, | |||||
| ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, | |||||
| strokeWidth, ctx->cache->paths, ctx->cache->npaths); | strokeWidth, ctx->cache->paths, ctx->cache->npaths); | ||||
| // Count triangles | // Count triangles | ||||
| @@ -2154,14 +2287,24 @@ void nvgStroke(NVGcontext* ctx) | |||||
| } | } | ||||
| // Add fonts | // Add fonts | ||||
| int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path) | |||||
| int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) | |||||
| { | { | ||||
| return fonsAddFont(ctx->fs, name, path); | |||||
| return fonsAddFont(ctx->fs, name, filename, 0); | |||||
| } | } | ||||
| int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData) | |||||
| int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) | |||||
| { | { | ||||
| return fonsAddFontMem(ctx->fs, name, data, ndata, freeData); | |||||
| return fonsAddFont(ctx->fs, name, filename, fontIndex); | |||||
| } | |||||
| int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) | |||||
| { | |||||
| return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); | |||||
| } | |||||
| int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) | |||||
| { | |||||
| return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); | |||||
| } | } | ||||
| int nvgFindFont(NVGcontext* ctx, const char* name) | int nvgFindFont(NVGcontext* ctx, const char* name) | ||||
| @@ -2170,6 +2313,28 @@ int nvgFindFont(NVGcontext* ctx, const char* name) | |||||
| return fonsGetFontByName(ctx->fs, name); | return fonsGetFontByName(ctx->fs, name); | ||||
| } | } | ||||
| int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont) | |||||
| { | |||||
| if(baseFont == -1 || fallbackFont == -1) return 0; | |||||
| return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont); | |||||
| } | |||||
| int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont) | |||||
| { | |||||
| return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); | |||||
| } | |||||
| void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) | |||||
| { | |||||
| fonsResetFallbackFont(ctx->fs, baseFont); | |||||
| } | |||||
| void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) | |||||
| { | |||||
| nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); | |||||
| } | |||||
| // State setting | // State setting | ||||
| void nvgFontSize(NVGcontext* ctx, float size) | void nvgFontSize(NVGcontext* ctx, float size) | ||||
| { | { | ||||
| @@ -2278,7 +2443,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) | |||||
| paint.innerColor.a *= state->alpha; | paint.innerColor.a *= state->alpha; | ||||
| paint.outerColor.a *= state->alpha; | paint.outerColor.a *= state->alpha; | ||||
| ctx->params.renderTriangles(ctx->params.userPtr, &paint, &state->scissor, verts, nverts); | |||||
| ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); | |||||
| ctx->drawCallCount++; | ctx->drawCallCount++; | ||||
| ctx->textTriCount += nverts/3; | ctx->textTriCount += nverts/3; | ||||
| @@ -2310,17 +2475,17 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||||
| verts = nvg__allocTempVerts(ctx, cverts); | verts = nvg__allocTempVerts(ctx, cverts); | ||||
| if (verts == NULL) return x; | if (verts == NULL) return x; | ||||
| fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end); | |||||
| fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); | |||||
| prevIter = iter; | prevIter = iter; | ||||
| while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
| float c[4*2]; | float c[4*2]; | ||||
| if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? | if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? | ||||
| if (!nvg__allocTextAtlas(ctx)) | |||||
| break; // no memory :( | |||||
| if (nverts != 0) { | if (nverts != 0) { | ||||
| nvg__renderText(ctx, verts, nverts); | nvg__renderText(ctx, verts, nverts); | ||||
| nverts = 0; | nverts = 0; | ||||
| } | } | ||||
| if (!nvg__allocTextAtlas(ctx)) | |||||
| break; // no memory :( | |||||
| iter = prevIter; | iter = prevIter; | ||||
| fonsTextIterNext(ctx->fs, &iter, &q); // try again | fonsTextIterNext(ctx->fs, &iter, &q); // try again | ||||
| if (iter.prevGlyphIndex == -1) // still can not find glyph? | if (iter.prevGlyphIndex == -1) // still can not find glyph? | ||||
| @@ -2343,12 +2508,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||||
| } | } | ||||
| } | } | ||||
| // TODO: add back-end bit to do this just once per frame. | |||||
| // TODO: add back-end bit to do this just once per frame. | |||||
| nvg__flushTextTexture(ctx); | nvg__flushTextTexture(ctx); | ||||
| nvg__renderText(ctx, verts, nverts); | nvg__renderText(ctx, verts, nverts); | ||||
| return iter.x; | |||||
| return iter.nextx / scale; | |||||
| } | } | ||||
| void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) | ||||
| @@ -2407,7 +2572,7 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, | |||||
| fonsSetAlign(ctx->fs, state->textAlign); | fonsSetAlign(ctx->fs, state->textAlign); | ||||
| fonsSetFont(ctx->fs, state->fontId); | fonsSetFont(ctx->fs, state->fontId); | ||||
| fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end); | |||||
| fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
| prevIter = iter; | prevIter = iter; | ||||
| while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
| if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | ||||
| @@ -2431,6 +2596,7 @@ enum NVGcodepointType { | |||||
| NVG_SPACE, | NVG_SPACE, | ||||
| NVG_NEWLINE, | NVG_NEWLINE, | ||||
| NVG_CHAR, | NVG_CHAR, | ||||
| NVG_CJK_CHAR, | |||||
| }; | }; | ||||
| int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) | ||||
| @@ -2472,7 +2638,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
| breakRowWidth *= scale; | breakRowWidth *= scale; | ||||
| fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end); | |||||
| fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
| prevIter = iter; | prevIter = iter; | ||||
| while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
| if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | ||||
| @@ -2498,7 +2664,15 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
| type = NVG_NEWLINE; | type = NVG_NEWLINE; | ||||
| break; | break; | ||||
| default: | default: | ||||
| type = NVG_CHAR; | |||||
| if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || | |||||
| (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || | |||||
| (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || | |||||
| (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || | |||||
| (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || | |||||
| (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF)) | |||||
| type = NVG_CJK_CHAR; | |||||
| else | |||||
| type = NVG_CHAR; | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -2525,12 +2699,12 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
| } else { | } else { | ||||
| if (rowStart == NULL) { | if (rowStart == NULL) { | ||||
| // Skip white space until the beginning of the line | // Skip white space until the beginning of the line | ||||
| if (type == NVG_CHAR) { | |||||
| if (type == NVG_CHAR || type == NVG_CJK_CHAR) { | |||||
| // The current char is the row so far | // The current char is the row so far | ||||
| rowStartX = iter.x; | rowStartX = iter.x; | ||||
| rowStart = iter.str; | rowStart = iter.str; | ||||
| rowEnd = iter.next; | rowEnd = iter.next; | ||||
| rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||||
| rowWidth = iter.nextx - rowStartX; | |||||
| rowMinX = q.x0 - rowStartX; | rowMinX = q.x0 - rowStartX; | ||||
| rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
| wordStart = iter.str; | wordStart = iter.str; | ||||
| @@ -2545,26 +2719,26 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
| float nextWidth = iter.nextx - rowStartX; | float nextWidth = iter.nextx - rowStartX; | ||||
| // track last non-white space character | // track last non-white space character | ||||
| if (type == NVG_CHAR) { | |||||
| if (type == NVG_CHAR || type == NVG_CJK_CHAR) { | |||||
| rowEnd = iter.next; | rowEnd = iter.next; | ||||
| rowWidth = iter.nextx - rowStartX; | rowWidth = iter.nextx - rowStartX; | ||||
| rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
| } | } | ||||
| // track last end of a word | // track last end of a word | ||||
| if (ptype == NVG_CHAR && type == NVG_SPACE) { | |||||
| if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) { | |||||
| breakEnd = iter.str; | breakEnd = iter.str; | ||||
| breakWidth = rowWidth; | breakWidth = rowWidth; | ||||
| breakMaxX = rowMaxX; | breakMaxX = rowMaxX; | ||||
| } | } | ||||
| // track last beginning of a word | // track last beginning of a word | ||||
| if (ptype == NVG_SPACE && type == NVG_CHAR) { | |||||
| if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { | |||||
| wordStart = iter.str; | wordStart = iter.str; | ||||
| wordStartX = iter.x; | wordStartX = iter.x; | ||||
| wordMinX = q.x0 - rowStartX; | |||||
| wordMinX = q.x0; | |||||
| } | } | ||||
| // Break to new line when a character is beyond break width. | // Break to new line when a character is beyond break width. | ||||
| if (type == NVG_CHAR && nextWidth > breakRowWidth) { | |||||
| if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) { | |||||
| // The run length is too long, need to break to new line. | // The run length is too long, need to break to new line. | ||||
| if (breakEnd == rowStart) { | if (breakEnd == rowStart) { | ||||
| // The current word is longer than the row length, just break it from here. | // The current word is longer than the row length, just break it from here. | ||||
| @@ -2597,13 +2771,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
| nrows++; | nrows++; | ||||
| if (nrows >= maxRows) | if (nrows >= maxRows) | ||||
| return nrows; | return nrows; | ||||
| // Update row | |||||
| rowStartX = wordStartX; | rowStartX = wordStartX; | ||||
| rowStart = wordStart; | rowStart = wordStart; | ||||
| rowEnd = iter.next; | rowEnd = iter.next; | ||||
| rowWidth = iter.nextx - rowStartX; | rowWidth = iter.nextx - rowStartX; | ||||
| rowMinX = wordMinX; | |||||
| rowMinX = wordMinX - rowStartX; | |||||
| rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
| // No change to the word start | |||||
| } | } | ||||
| // Set null break point | // Set null break point | ||||
| breakEnd = rowStart; | breakEnd = rowStart; | ||||
| @@ -79,10 +79,46 @@ enum NVGalign { | |||||
| // Vertical align | // Vertical align | ||||
| NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | ||||
| NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | ||||
| NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||||
| NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||||
| NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||||
| 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, | |||||
| }; | |||||
| enum NVGcompositeOperation { | |||||
| NVG_SOURCE_OVER, | |||||
| NVG_SOURCE_IN, | |||||
| NVG_SOURCE_OUT, | |||||
| NVG_ATOP, | |||||
| NVG_DESTINATION_OVER, | |||||
| NVG_DESTINATION_IN, | |||||
| NVG_DESTINATION_OUT, | |||||
| NVG_DESTINATION_ATOP, | |||||
| NVG_LIGHTER, | |||||
| NVG_COPY, | |||||
| NVG_XOR, | |||||
| }; | |||||
| struct NVGcompositeOperationState { | |||||
| int srcRGB; | |||||
| int dstRGB; | |||||
| int srcAlpha; | |||||
| int dstAlpha; | |||||
| }; | |||||
| typedef struct NVGcompositeOperationState NVGcompositeOperationState; | |||||
| 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. | ||||
| @@ -100,11 +136,12 @@ struct NVGtextRow { | |||||
| typedef struct NVGtextRow NVGtextRow; | typedef struct NVGtextRow NVGtextRow; | ||||
| enum NVGimageFlags { | enum NVGimageFlags { | ||||
| NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||||
| NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||||
| NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | ||||
| NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | ||||
| NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | ||||
| NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||||
| NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||||
| NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear | |||||
| }; | }; | ||||
| // Begin drawing a new frame | // Begin drawing a new frame | ||||
| @@ -115,7 +152,7 @@ enum NVGimageFlags { | |||||
| // For example, GLFW returns two dimension for an opened window: window size and | // For example, GLFW returns two dimension for an opened window: window size and | ||||
| // frame buffer size. In that case you would set windowWidth/Height to the window size | // frame buffer size. In that case you would set windowWidth/Height to the window size | ||||
| // devicePixelRatio to: frameBufferWidth / windowWidth. | // devicePixelRatio to: frameBufferWidth / windowWidth. | ||||
| void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); | |||||
| void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); | |||||
| // Cancels drawing the current frame. | // Cancels drawing the current frame. | ||||
| void nvgCancelFrame(NVGcontext* ctx); | void nvgCancelFrame(NVGcontext* ctx); | ||||
| @@ -123,6 +160,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 | |||||
| // | |||||
| // The composite operations in NanoVG are modeled after HTML Canvas API, and | |||||
| // the blend func is based on OpenGL (see corresponding manuals for more info). | |||||
| // The colors in the blending state have premultiplied alpha. | |||||
| // Sets the composite operation. The op parameter should be one of NVGcompositeOperation. | |||||
| void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); | |||||
| // Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. | |||||
| void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); | |||||
| // Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. | |||||
| void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); | |||||
| // | // | ||||
| // Color utils | // Color utils | ||||
| // | // | ||||
| @@ -183,7 +236,10 @@ void nvgReset(NVGcontext* ctx); | |||||
| // Solid color is simply defined as a color value, different kinds of paints can be created | // Solid color is simply defined as a color value, different kinds of paints can be created | ||||
| // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | ||||
| // | // | ||||
| // Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. | |||||
| void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); | |||||
| // Sets current stroke style to a solid color. | // Sets current stroke style to a solid color. | ||||
| void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | ||||
| @@ -231,7 +287,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||||
| // Apart from nvgResetTransform(), each transformation function first creates | // Apart from nvgResetTransform(), each transformation function first creates | ||||
| // specific transformation matrix and pre-multiplies the current transformation by it. | // specific transformation matrix and pre-multiplies the current transformation by it. | ||||
| // | // | ||||
| // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Resets current transform to a identity matrix. | // Resets current transform to a identity matrix. | ||||
| void nvgResetTransform(NVGcontext* ctx); | void nvgResetTransform(NVGcontext* ctx); | ||||
| @@ -317,7 +373,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); | |||||
| // Creates image by loading it from the specified chunk of memory. | // Creates image by loading it from the specified chunk of memory. | ||||
| // Returns handle to the image. | // Returns handle to the image. | ||||
| int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); | |||||
| int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); | |||||
| // Creates image from specified image data. | // Creates image from specified image data. | ||||
| // Returns handle to the image. | // Returns handle to the image. | ||||
| @@ -368,7 +424,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey | |||||
| // Scissoring | // Scissoring | ||||
| // | // | ||||
| // Scissoring allows you to clip the rendering into a rectangle. This is useful for various | // Scissoring allows you to clip the rendering into a rectangle. This is useful for various | ||||
| // user interface cases like rendering a text edit or a timeline. | |||||
| // user interface cases like rendering a text edit or a timeline. | |||||
| // Sets the current scissor rectangle. | // Sets the current scissor rectangle. | ||||
| // The scissor rectangle is transformed by the current transform. | // The scissor rectangle is transformed by the current transform. | ||||
| @@ -423,7 +479,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad | |||||
| // Closes current sub-path with a line segment. | // Closes current sub-path with a line segment. | ||||
| void nvgClosePath(NVGcontext* ctx); | void nvgClosePath(NVGcontext* ctx); | ||||
| // Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||||
| // Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||||
| void nvgPathWinding(NVGcontext* ctx, int dir); | void nvgPathWinding(NVGcontext* ctx, int dir); | ||||
| // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | ||||
| @@ -437,10 +493,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); | |||||
| // Creates new rounded rectangle shaped sub-path. | // Creates new rounded rectangle shaped sub-path. | ||||
| void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | ||||
| // Creates new rounded rectangle shaped sub-path with varying radii for each corner. | |||||
| void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); | |||||
| // Creates new ellipse shaped sub-path. | // Creates new ellipse shaped sub-path. | ||||
| void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | ||||
| // Creates new circle shaped sub-path. | |||||
| // Creates new circle shaped sub-path. | |||||
| void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | ||||
| // Fills the current path with current fill style. | // Fills the current path with current fill style. | ||||
| @@ -487,13 +546,31 @@ void nvgStroke(NVGcontext* ctx); | |||||
| // Returns handle to the font. | // Returns handle to the font. | ||||
| int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | ||||
| // Creates image by loading it from the specified memory chunk. | |||||
| // fontIndex specifies which font face to load from a .ttf/.ttc file. | |||||
| int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); | |||||
| // Creates font by loading it from the specified memory chunk. | |||||
| // Returns handle to the font. | // Returns handle to the font. | ||||
| int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData); | |||||
| int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); | |||||
| // fontIndex specifies which font face to load from a .ttf/.ttc file. | |||||
| int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); | |||||
| // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | ||||
| int nvgFindFont(NVGcontext* ctx, const char* name); | int nvgFindFont(NVGcontext* ctx, const char* name); | ||||
| // Adds a fallback font by handle. | |||||
| int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); | |||||
| // Adds a fallback font by name. | |||||
| int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); | |||||
| // Resets fallback fonts by handle. | |||||
| void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); | |||||
| // Resets fallback fonts by name. | |||||
| void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); | |||||
| // Sets the font size of current text style. | // Sets the font size of current text style. | ||||
| void nvgFontSize(NVGcontext* ctx, float size); | void nvgFontSize(NVGcontext* ctx, float size); | ||||
| @@ -503,7 +580,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur); | |||||
| // Sets the letter spacing of current text style. | // Sets the letter spacing of current text style. | ||||
| void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | ||||
| // Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
| // Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
| void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | ||||
| // Sets the text align of current text style, see NVGalign for options. | // Sets the text align of current text style, see NVGalign for options. | ||||
| @@ -588,12 +665,12 @@ struct NVGparams { | |||||
| int (*renderDeleteTexture)(void* uptr, int image); | int (*renderDeleteTexture)(void* uptr, int image); | ||||
| int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | ||||
| 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); | |||||
| void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); | |||||
| void (*renderCancel)(void* uptr); | void (*renderCancel)(void* uptr); | ||||
| 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 (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||||
| void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); | |||||
| void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||||
| void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||||
| void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); | |||||
| void (*renderDelete)(void* uptr); | void (*renderDelete)(void* uptr); | ||||
| }; | }; | ||||
| typedef struct NVGparams NVGparams; | typedef struct NVGparams NVGparams; | ||||
| @@ -59,6 +59,9 @@ enum NVGcreateFlags { | |||||
| NVGcontext* nvgCreateGL2(int flags); | NVGcontext* nvgCreateGL2(int flags); | ||||
| void nvgDeleteGL2(NVGcontext* ctx); | void nvgDeleteGL2(NVGcontext* ctx); | ||||
| int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
| GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); | |||||
| #endif | #endif | ||||
| #if defined NANOVG_GL3 | #if defined NANOVG_GL3 | ||||
| @@ -66,6 +69,9 @@ void nvgDeleteGL2(NVGcontext* ctx); | |||||
| NVGcontext* nvgCreateGL3(int flags); | NVGcontext* nvgCreateGL3(int flags); | ||||
| void nvgDeleteGL3(NVGcontext* ctx); | void nvgDeleteGL3(NVGcontext* ctx); | ||||
| int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
| GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); | |||||
| #endif | #endif | ||||
| #if defined NANOVG_GLES2 | #if defined NANOVG_GLES2 | ||||
| @@ -73,6 +79,9 @@ void nvgDeleteGL3(NVGcontext* ctx); | |||||
| NVGcontext* nvgCreateGLES2(int flags); | NVGcontext* nvgCreateGLES2(int flags); | ||||
| void nvgDeleteGLES2(NVGcontext* ctx); | void nvgDeleteGLES2(NVGcontext* ctx); | ||||
| int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
| GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); | |||||
| #endif | #endif | ||||
| #if defined NANOVG_GLES3 | #if defined NANOVG_GLES3 | ||||
| @@ -80,6 +89,9 @@ void nvgDeleteGLES2(NVGcontext* ctx); | |||||
| NVGcontext* nvgCreateGLES3(int flags); | NVGcontext* nvgCreateGLES3(int flags); | ||||
| void nvgDeleteGLES3(NVGcontext* ctx); | void nvgDeleteGLES3(NVGcontext* ctx); | ||||
| int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
| GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); | |||||
| #endif | #endif | ||||
| // These are additional flags on top of NVGimageFlags. | // These are additional flags on top of NVGimageFlags. | ||||
| @@ -87,10 +99,6 @@ enum NVGimageFlagsGL { | |||||
| NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. | NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. | ||||
| }; | }; | ||||
| int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
| GLuint nvglImageHandle(NVGcontext* ctx, int image); | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -142,6 +150,15 @@ struct GLNVGtexture { | |||||
| }; | }; | ||||
| typedef struct GLNVGtexture GLNVGtexture; | typedef struct GLNVGtexture GLNVGtexture; | ||||
| struct GLNVGblend | |||||
| { | |||||
| GLenum srcRGB; | |||||
| GLenum dstRGB; | |||||
| GLenum srcAlpha; | |||||
| GLenum dstAlpha; | |||||
| }; | |||||
| typedef struct GLNVGblend GLNVGblend; | |||||
| enum GLNVGcallType { | enum GLNVGcallType { | ||||
| GLNVG_NONE = 0, | GLNVG_NONE = 0, | ||||
| GLNVG_FILL, | GLNVG_FILL, | ||||
| @@ -158,6 +175,7 @@ struct GLNVGcall { | |||||
| int triangleOffset; | int triangleOffset; | ||||
| int triangleCount; | int triangleCount; | ||||
| int uniformOffset; | int uniformOffset; | ||||
| GLNVGblend blendFunc; | |||||
| }; | }; | ||||
| typedef struct GLNVGcall GLNVGcall; | typedef struct GLNVGcall GLNVGcall; | ||||
| @@ -248,7 +266,10 @@ struct GLNVGcontext { | |||||
| GLenum stencilFunc; | GLenum stencilFunc; | ||||
| GLint stencilFuncRef; | GLint stencilFuncRef; | ||||
| GLuint stencilFuncMask; | GLuint stencilFuncMask; | ||||
| GLNVGblend blendFunc; | |||||
| #endif | #endif | ||||
| int dummyTex; | |||||
| }; | }; | ||||
| typedef struct GLNVGcontext GLNVGcontext; | typedef struct GLNVGcontext GLNVGcontext; | ||||
| @@ -298,7 +319,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||||
| if ((gl->stencilFunc != func) || | if ((gl->stencilFunc != func) || | ||||
| (gl->stencilFuncRef != ref) || | (gl->stencilFuncRef != ref) || | ||||
| (gl->stencilFuncMask != mask)) { | (gl->stencilFuncMask != mask)) { | ||||
| gl->stencilFunc = func; | gl->stencilFunc = func; | ||||
| gl->stencilFuncRef = ref; | gl->stencilFuncRef = ref; | ||||
| gl->stencilFuncMask = mask; | gl->stencilFuncMask = mask; | ||||
| @@ -308,6 +329,21 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||||
| glStencilFunc(func, ref, mask); | glStencilFunc(func, ref, mask); | ||||
| #endif | #endif | ||||
| } | } | ||||
| static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) | |||||
| { | |||||
| #if NANOVG_GL_USE_STATE_FILTER | |||||
| if ((gl->blendFunc.srcRGB != blend->srcRGB) || | |||||
| (gl->blendFunc.dstRGB != blend->dstRGB) || | |||||
| (gl->blendFunc.srcAlpha != blend->srcAlpha) || | |||||
| (gl->blendFunc.dstAlpha != blend->dstAlpha)) { | |||||
| gl->blendFunc = *blend; | |||||
| glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||||
| } | |||||
| #else | |||||
| glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||||
| #endif | |||||
| } | |||||
| static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | ||||
| { | { | ||||
| @@ -331,10 +367,10 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | |||||
| } | } | ||||
| tex = &gl->textures[gl->ntextures++]; | tex = &gl->textures[gl->ntextures++]; | ||||
| } | } | ||||
| memset(tex, 0, sizeof(*tex)); | memset(tex, 0, sizeof(*tex)); | ||||
| tex->id = ++gl->textureId; | tex->id = ++gl->textureId; | ||||
| return tex; | return tex; | ||||
| } | } | ||||
| @@ -363,8 +399,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||||
| static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) | ||||
| { | { | ||||
| char str[512+1]; | |||||
| int len = 0; | |||||
| GLchar str[512+1]; | |||||
| GLsizei len = 0; | |||||
| glGetShaderInfoLog(shader, 512, &len, str); | glGetShaderInfoLog(shader, 512, &len, str); | ||||
| if (len > 512) len = 512; | if (len > 512) len = 512; | ||||
| str[len] = '\0'; | str[len] = '\0'; | ||||
| @@ -373,8 +409,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* | |||||
| static void glnvg__dumpProgramError(GLuint prog, const char* name) | static void glnvg__dumpProgramError(GLuint prog, const char* name) | ||||
| { | { | ||||
| char str[512+1]; | |||||
| int len = 0; | |||||
| GLchar str[512+1]; | |||||
| GLsizei len = 0; | |||||
| glGetProgramInfoLog(prog, 512, &len, str); | glGetProgramInfoLog(prog, 512, &len, str); | ||||
| if (len > 512) len = 512; | if (len > 512) len = 512; | ||||
| str[len] = '\0'; | str[len] = '\0'; | ||||
| @@ -466,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader) | |||||
| #endif | #endif | ||||
| } | } | ||||
| static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||||
| static int glnvg__renderCreate(void* uptr) | static int glnvg__renderCreate(void* uptr) | ||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| @@ -514,7 +552,7 @@ static int glnvg__renderCreate(void* uptr) | |||||
| " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" | ||||
| "}\n"; | "}\n"; | ||||
| static const char* fillFragShader = | |||||
| static const char* fillFragShader = | |||||
| "#ifdef GL_ES\n" | "#ifdef GL_ES\n" | ||||
| "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" | "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" | ||||
| " precision highp float;\n" | " precision highp float;\n" | ||||
| @@ -592,6 +630,7 @@ static int glnvg__renderCreate(void* uptr) | |||||
| " float scissor = scissorMask(fpos);\n" | " float scissor = scissorMask(fpos);\n" | ||||
| "#ifdef EDGE_AA\n" | "#ifdef EDGE_AA\n" | ||||
| " float strokeAlpha = strokeMask();\n" | " float strokeAlpha = strokeMask();\n" | ||||
| " if (strokeAlpha < strokeThr) discard;\n" | |||||
| "#else\n" | "#else\n" | ||||
| " float strokeAlpha = 1.0;\n" | " float strokeAlpha = 1.0;\n" | ||||
| "#endif\n" | "#endif\n" | ||||
| @@ -631,9 +670,6 @@ static int glnvg__renderCreate(void* uptr) | |||||
| " color *= scissor;\n" | " color *= scissor;\n" | ||||
| " result = color * innerCol;\n" | " result = color * innerCol;\n" | ||||
| " }\n" | " }\n" | ||||
| "#ifdef EDGE_AA\n" | |||||
| " if (strokeAlpha < strokeThr) discard;\n" | |||||
| "#endif\n" | |||||
| "#ifdef NANOVG_GL3\n" | "#ifdef NANOVG_GL3\n" | ||||
| " outColor = result;\n" | " outColor = result;\n" | ||||
| "#else\n" | "#else\n" | ||||
| @@ -663,11 +699,15 @@ static int glnvg__renderCreate(void* uptr) | |||||
| #if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
| // Create UBOs | // Create UBOs | ||||
| glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); | ||||
| glGenBuffers(1, &gl->fragBuf); | |||||
| glGenBuffers(1, &gl->fragBuf); | |||||
| glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); | ||||
| #endif | #endif | ||||
| gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | ||||
| // Some platforms does not allow to have samples to unset textures. | |||||
| // Create empty one which is bound when there's no texture specified. | |||||
| gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); | |||||
| glnvg__checkError(gl, "create done"); | glnvg__checkError(gl, "create done"); | ||||
| glFinish(); | glFinish(); | ||||
| @@ -690,7 +730,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
| printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); | printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); | ||||
| imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | ||||
| } | } | ||||
| // No mips. | |||||
| // No mips. | |||||
| if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | ||||
| printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | ||||
| imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | ||||
| @@ -722,7 +762,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
| if (type == NVG_TEXTURE_RGBA) | if (type == NVG_TEXTURE_RGBA) | ||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
| else | else | ||||
| #if defined(NANOVG_GLES2) | |||||
| #if defined(NANOVG_GLES2) || defined (NANOVG_GL2) | |||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | ||||
| #elif defined(NANOVG_GLES3) | #elif defined(NANOVG_GLES3) | ||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
| @@ -731,11 +771,24 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
| #endif | #endif | ||||
| if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||||
| if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); | |||||
| } else { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||||
| } | |||||
| } else { | |||||
| if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||||
| } else { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
| } | |||||
| } | |||||
| if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||||
| } else { | } else { | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
| } | } | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
| if (imageFlags & NVG_IMAGE_REPEATX) | if (imageFlags & NVG_IMAGE_REPEATX) | ||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | ||||
| @@ -801,7 +854,7 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||||
| if (tex->type == NVG_TEXTURE_RGBA) | if (tex->type == NVG_TEXTURE_RGBA) | ||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
| else | else | ||||
| #ifdef NANOVG_GLES2 | |||||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GL2) | |||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | ||||
| #else | #else | ||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
| @@ -887,19 +940,30 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai | |||||
| tex = glnvg__findTexture(gl, paint->image); | tex = glnvg__findTexture(gl, paint->image); | ||||
| if (tex == NULL) return 0; | if (tex == NULL) return 0; | ||||
| if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { | if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { | ||||
| float flipped[6]; | |||||
| nvgTransformScale(flipped, 1.0f, -1.0f); | |||||
| nvgTransformMultiply(flipped, paint->xform); | |||||
| nvgTransformInverse(invxform, flipped); | |||||
| 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 { | } else { | ||||
| nvgTransformInverse(invxform, paint->xform); | nvgTransformInverse(invxform, paint->xform); | ||||
| } | } | ||||
| frag->type = NSVG_SHADER_FILLIMG; | frag->type = NSVG_SHADER_FILLIMG; | ||||
| #if NANOVG_GL_USE_UNIFORMBUFFER | |||||
| if (tex->type == NVG_TEXTURE_RGBA) | if (tex->type == NVG_TEXTURE_RGBA) | ||||
| frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | ||||
| else | else | ||||
| frag->texType = 2; | frag->texType = 2; | ||||
| #else | |||||
| if (tex->type == NVG_TEXTURE_RGBA) | |||||
| frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; | |||||
| else | |||||
| frag->texType = 2.0f; | |||||
| #endif | |||||
| // printf("frag->texType = %d\n", frag->texType); | // printf("frag->texType = %d\n", frag->texType); | ||||
| } else { | } else { | ||||
| frag->type = NSVG_SHADER_FILLGRAD; | frag->type = NSVG_SHADER_FILLGRAD; | ||||
| @@ -917,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); | |||||
| static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | ||||
| { | { | ||||
| GLNVGtexture* tex = NULL; | |||||
| #if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
| glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | ||||
| #else | #else | ||||
| @@ -925,19 +990,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||||
| #endif | #endif | ||||
| if (image != 0) { | if (image != 0) { | ||||
| GLNVGtexture* tex = glnvg__findTexture(gl, image); | |||||
| glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||||
| glnvg__checkError(gl, "tex paint tex"); | |||||
| } else { | |||||
| glnvg__bindTexture(gl, 0); | |||||
| tex = glnvg__findTexture(gl, image); | |||||
| } | } | ||||
| // If no image is set, use empty texture | |||||
| if (tex == NULL) { | |||||
| tex = glnvg__findTexture(gl, gl->dummyTex); | |||||
| } | |||||
| glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||||
| glnvg__checkError(gl, "tex paint tex"); | |||||
| } | } | ||||
| static void glnvg__renderViewport(void* uptr, int width, int height) | |||||
| static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) | |||||
| { | { | ||||
| NVG_NOTUSED(devicePixelRatio); | |||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| gl->view[0] = (float)width; | |||||
| gl->view[1] = (float)height; | |||||
| gl->view[0] = width; | |||||
| gl->view[1] = height; | |||||
| } | } | ||||
| static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | ||||
| @@ -979,7 +1047,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | |||||
| // Draw fill | // Draw fill | ||||
| glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); | glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); | ||||
| glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | ||||
| glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); | |||||
| glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); | |||||
| glDisable(GL_STENCIL_TEST); | glDisable(GL_STENCIL_TEST); | ||||
| } | } | ||||
| @@ -992,12 +1060,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) | |||||
| glnvg__setUniforms(gl, call->uniformOffset, call->image); | glnvg__setUniforms(gl, call->uniformOffset, call->image); | ||||
| glnvg__checkError(gl, "convex fill"); | glnvg__checkError(gl, "convex fill"); | ||||
| for (i = 0; i < npaths; i++) | |||||
| for (i = 0; i < npaths; i++) { | |||||
| glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); | ||||
| if (gl->flags & NVG_ANTIALIAS) { | |||||
| // Draw fringes | // Draw fringes | ||||
| for (i = 0; i < npaths; i++) | |||||
| if (paths[i].strokeCount > 0) { | |||||
| glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1026,7 +1094,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) | |||||
| for (i = 0; i < npaths; i++) | for (i = 0; i < npaths; i++) | ||||
| glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | ||||
| // Clear stencil buffer. | |||||
| // Clear stencil buffer. | |||||
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | ||||
| glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); | glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); | ||||
| glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | ||||
| @@ -1064,6 +1132,50 @@ 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; | |||||
| return GL_INVALID_ENUM; | |||||
| } | |||||
| static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) | |||||
| { | |||||
| GLNVGblend blend; | |||||
| blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); | |||||
| blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); | |||||
| blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); | |||||
| blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); | |||||
| if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) | |||||
| { | |||||
| blend.srcRGB = GL_ONE; | |||||
| blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; | |||||
| blend.srcAlpha = GL_ONE; | |||||
| blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; | |||||
| } | |||||
| return blend; | |||||
| } | |||||
| static void glnvg__renderFlush(void* uptr) | static void glnvg__renderFlush(void* uptr) | ||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| @@ -1074,7 +1186,6 @@ 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); | |||||
| glEnable(GL_CULL_FACE); | glEnable(GL_CULL_FACE); | ||||
| glCullFace(GL_BACK); | glCullFace(GL_BACK); | ||||
| glFrontFace(GL_CCW); | glFrontFace(GL_CCW); | ||||
| @@ -1093,6 +1204,10 @@ static void glnvg__renderFlush(void* uptr) | |||||
| gl->stencilFunc = GL_ALWAYS; | gl->stencilFunc = GL_ALWAYS; | ||||
| gl->stencilFuncRef = 0; | gl->stencilFuncRef = 0; | ||||
| gl->stencilFuncMask = 0xffffffff; | gl->stencilFuncMask = 0xffffffff; | ||||
| gl->blendFunc.srcRGB = GL_INVALID_ENUM; | |||||
| gl->blendFunc.srcAlpha = GL_INVALID_ENUM; | |||||
| gl->blendFunc.dstRGB = GL_INVALID_ENUM; | |||||
| gl->blendFunc.dstAlpha = GL_INVALID_ENUM; | |||||
| #endif | #endif | ||||
| #if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
| @@ -1122,6 +1237,7 @@ static void glnvg__renderFlush(void* uptr) | |||||
| for (i = 0; i < gl->ncalls; i++) { | for (i = 0; i < gl->ncalls; i++) { | ||||
| GLNVGcall* call = &gl->calls[i]; | GLNVGcall* call = &gl->calls[i]; | ||||
| glnvg__blendFuncSeparate(gl,&call->blendFunc); | |||||
| if (call->type == GLNVG_FILL) | if (call->type == GLNVG_FILL) | ||||
| glnvg__fill(gl, call); | glnvg__fill(gl, call); | ||||
| else if (call->type == GLNVG_CONVEXFILL) | else if (call->type == GLNVG_CONVEXFILL) | ||||
| @@ -1136,9 +1252,9 @@ static void glnvg__renderFlush(void* uptr) | |||||
| glDisableVertexAttribArray(1); | glDisableVertexAttribArray(1); | ||||
| #if defined NANOVG_GL3 | #if defined NANOVG_GL3 | ||||
| glBindVertexArray(0); | glBindVertexArray(0); | ||||
| #endif | |||||
| #endif | |||||
| glDisable(GL_CULL_FACE); | glDisable(GL_CULL_FACE); | ||||
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |||||
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |||||
| glUseProgram(0); | glUseProgram(0); | ||||
| glnvg__bindTexture(gl, 0); | glnvg__bindTexture(gl, 0); | ||||
| } | } | ||||
| @@ -1237,7 +1353,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) | |||||
| vtx->v = v; | vtx->v = v; | ||||
| } | } | ||||
| static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||||
| static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||||
| const float* bounds, const NVGpath* paths, int npaths) | const float* bounds, const NVGpath* paths, int npaths) | ||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| @@ -1249,16 +1365,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
| if (call == NULL) return; | if (call == NULL) return; | ||||
| call->type = GLNVG_FILL; | call->type = GLNVG_FILL; | ||||
| call->triangleCount = 4; | |||||
| call->pathOffset = glnvg__allocPaths(gl, npaths); | call->pathOffset = glnvg__allocPaths(gl, npaths); | ||||
| if (call->pathOffset == -1) goto error; | if (call->pathOffset == -1) goto error; | ||||
| call->pathCount = npaths; | call->pathCount = npaths; | ||||
| call->image = paint->image; | call->image = paint->image; | ||||
| call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
| if (npaths == 1 && paths[0].convex) | if (npaths == 1 && paths[0].convex) | ||||
| { | |||||
| call->type = GLNVG_CONVEXFILL; | call->type = GLNVG_CONVEXFILL; | ||||
| call->triangleCount = 0; // Bounding box fill quad not needed for convex fill | |||||
| } | |||||
| // Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
| maxverts = glnvg__maxVertCount(paths, npaths) + 6; | |||||
| maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; | |||||
| offset = glnvg__allocVerts(gl, maxverts); | offset = glnvg__allocVerts(gl, maxverts); | ||||
| if (offset == -1) goto error; | if (offset == -1) goto error; | ||||
| @@ -1280,20 +1401,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
| } | } | ||||
| } | } | ||||
| // Quad | |||||
| call->triangleOffset = offset; | |||||
| call->triangleCount = 6; | |||||
| quad = &gl->verts[call->triangleOffset]; | |||||
| glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f); | |||||
| // Setup uniforms for draw calls | // Setup uniforms for draw calls | ||||
| if (call->type == GLNVG_FILL) { | if (call->type == GLNVG_FILL) { | ||||
| // Quad | |||||
| call->triangleOffset = offset; | |||||
| quad = &gl->verts[call->triangleOffset]; | |||||
| glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
| glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); | |||||
| call->uniformOffset = glnvg__allocFragUniforms(gl, 2); | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); | ||||
| if (call->uniformOffset == -1) goto error; | if (call->uniformOffset == -1) goto error; | ||||
| // Simple shader for stencil | // Simple shader for stencil | ||||
| @@ -1318,7 +1435,7 @@ error: | |||||
| if (gl->ncalls > 0) gl->ncalls--; | if (gl->ncalls > 0) gl->ncalls--; | ||||
| } | } | ||||
| static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||||
| static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||||
| float strokeWidth, const NVGpath* paths, int npaths) | float strokeWidth, const NVGpath* paths, int npaths) | ||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| @@ -1332,6 +1449,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor | |||||
| if (call->pathOffset == -1) goto error; | if (call->pathOffset == -1) goto error; | ||||
| call->pathCount = npaths; | call->pathCount = npaths; | ||||
| call->image = paint->image; | call->image = paint->image; | ||||
| call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
| // Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
| maxverts = glnvg__maxVertCount(paths, npaths); | maxverts = glnvg__maxVertCount(paths, npaths); | ||||
| @@ -1373,8 +1491,8 @@ error: | |||||
| if (gl->ncalls > 0) gl->ncalls--; | if (gl->ncalls > 0) gl->ncalls--; | ||||
| } | } | ||||
| static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
| const NVGvertex* verts, int nverts) | |||||
| static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, | |||||
| const NVGvertex* verts, int nverts, float fringe) | |||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
| GLNVGcall* call = glnvg__allocCall(gl); | GLNVGcall* call = glnvg__allocCall(gl); | ||||
| @@ -1384,6 +1502,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||||
| call->type = GLNVG_TRIANGLES; | call->type = GLNVG_TRIANGLES; | ||||
| call->image = paint->image; | call->image = paint->image; | ||||
| call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
| // Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
| call->triangleOffset = glnvg__allocVerts(gl, nverts); | call->triangleOffset = glnvg__allocVerts(gl, nverts); | ||||
| @@ -1396,7 +1515,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||||
| call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | ||||
| if (call->uniformOffset == -1) goto error; | if (call->uniformOffset == -1) goto error; | ||||
| frag = nvg__fragUniformPtr(gl, call->uniformOffset); | frag = nvg__fragUniformPtr(gl, call->uniformOffset); | ||||
| glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); | |||||
| glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); | |||||
| frag->type = NSVG_SHADER_IMG; | frag->type = NSVG_SHADER_IMG; | ||||
| return; | return; | ||||
| @@ -1499,7 +1618,15 @@ void nvgDeleteGLES3(NVGcontext* ctx) | |||||
| nvgDeleteInternal(ctx); | nvgDeleteInternal(ctx); | ||||
| } | } | ||||
| int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
| #if defined NANOVG_GL2 | |||||
| int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
| #elif defined NANOVG_GL3 | |||||
| int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
| #elif defined NANOVG_GLES2 | |||||
| int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
| #elif defined NANOVG_GLES3 | |||||
| int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
| #endif | |||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | ||||
| GLNVGtexture* tex = glnvg__allocTexture(gl); | GLNVGtexture* tex = glnvg__allocTexture(gl); | ||||
| @@ -1515,7 +1642,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i | |||||
| return tex->id; | return tex->id; | ||||
| } | } | ||||
| GLuint nvglImageHandle(NVGcontext* ctx, int image) | |||||
| #if defined NANOVG_GL2 | |||||
| GLuint nvglImageHandleGL2(NVGcontext* ctx, int image) | |||||
| #elif defined NANOVG_GL3 | |||||
| GLuint nvglImageHandleGL3(NVGcontext* ctx, int image) | |||||
| #elif defined NANOVG_GLES2 | |||||
| GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image) | |||||
| #elif defined NANOVG_GLES3 | |||||
| GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image) | |||||
| #endif | |||||
| { | { | ||||
| GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | ||||
| GLNVGtexture* tex = glnvg__findTexture(gl, image); | GLNVGtexture* tex = glnvg__findTexture(gl, image); | ||||
| @@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer; | |||||
| // Helper function to create GL frame buffer to render to. | // Helper function to create GL frame buffer to render to. | ||||
| void nvgluBindFramebuffer(NVGLUframebuffer* fb); | void nvgluBindFramebuffer(NVGLUframebuffer* fb); | ||||
| NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | ||||
| void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb); | |||||
| void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); | |||||
| #endif // NANOVG_GL_UTILS_H | #endif // NANOVG_GL_UTILS_H | ||||
| @@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
| memset(fb, 0, sizeof(NVGLUframebuffer)); | memset(fb, 0, sizeof(NVGLUframebuffer)); | ||||
| fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | ||||
| fb->texture = nvglImageHandle(ctx, fb->image); | |||||
| #if defined NANOVG_GL2 | |||||
| fb->texture = nvglImageHandleGL2(ctx, fb->image); | |||||
| #elif defined NANOVG_GL3 | |||||
| fb->texture = nvglImageHandleGL3(ctx, fb->image); | |||||
| #elif defined NANOVG_GLES2 | |||||
| fb->texture = nvglImageHandleGLES2(ctx, fb->image); | |||||
| #elif defined NANOVG_GLES3 | |||||
| fb->texture = nvglImageHandleGLES3(ctx, fb->image); | |||||
| #endif | |||||
| fb->ctx = ctx; | |||||
| // frame buffer object | // frame buffer object | ||||
| glGenFramebuffers(1, &fb->fbo); | glGenFramebuffers(1, &fb->fbo); | ||||
| @@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | ||||
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | ||||
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; | |||||
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | |||||
| #ifdef GL_DEPTH24_STENCIL8 | |||||
| // If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback. | |||||
| // Some graphics cards require a depth buffer along with a stencil. | |||||
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); | |||||
| 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) | |||||
| #endif // GL_DEPTH24_STENCIL8 | |||||
| goto error; | |||||
| } | |||||
| glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | ||||
| glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | ||||
| @@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
| error: | error: | ||||
| glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | ||||
| glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | ||||
| nvgluDeleteFramebuffer(ctx, fb); | |||||
| nvgluDeleteFramebuffer(fb); | |||||
| return NULL; | return NULL; | ||||
| #else | #else | ||||
| NVG_NOTUSED(ctx); | NVG_NOTUSED(ctx); | ||||
| @@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb) | |||||
| #endif | #endif | ||||
| } | } | ||||
| void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||||
| void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) | |||||
| { | { | ||||
| #ifdef NANOVG_FBO_VALID | #ifdef NANOVG_FBO_VALID | ||||
| if (fb == NULL) return; | if (fb == NULL) return; | ||||
| @@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||||
| if (fb->rbo != 0) | if (fb->rbo != 0) | ||||
| glDeleteRenderbuffers(1, &fb->rbo); | glDeleteRenderbuffers(1, &fb->rbo); | ||||
| if (fb->image >= 0) | if (fb->image >= 0) | ||||
| nvgDeleteImage(ctx, fb->image); | |||||
| nvgDeleteImage(fb->ctx, fb->image); | |||||
| fb->ctx = NULL; | |||||
| fb->fbo = 0; | fb->fbo = 0; | ||||
| fb->rbo = 0; | fb->rbo = 0; | ||||
| fb->texture = 0; | fb->texture = 0; | ||||
| fb->image = -1; | fb->image = -1; | ||||
| free(fb); | free(fb); | ||||
| #else | #else | ||||
| NVG_NOTUSED(ctx); | |||||
| NVG_NOTUSED(fb); | NVG_NOTUSED(fb); | ||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -83,6 +83,7 @@ static int attrListSgl[] = { | |||||
| GLX_GREEN_SIZE, 4, | GLX_GREEN_SIZE, 4, | ||||
| GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
| GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
| GLX_STENCIL_SIZE, 8, | |||||
| GLX_ARB_multisample, 1, | GLX_ARB_multisample, 1, | ||||
| None | None | ||||
| }; | }; | ||||
| @@ -98,6 +99,7 @@ static int attrListDbl[] = { | |||||
| GLX_GREEN_SIZE, 4, | GLX_GREEN_SIZE, 4, | ||||
| GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
| GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
| GLX_STENCIL_SIZE, 8, | |||||
| GLX_ARB_multisample, 1, | GLX_ARB_multisample, 1, | ||||
| None | None | ||||
| }; | }; | ||||
| @@ -114,6 +116,7 @@ static int attrListDblMS[] = { | |||||
| GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
| GLX_ALPHA_SIZE, 4, | GLX_ALPHA_SIZE, 4, | ||||
| GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
| GLX_STENCIL_SIZE, 8, | |||||
| GLX_SAMPLE_BUFFERS, 1, | GLX_SAMPLE_BUFFERS, 1, | ||||
| GLX_SAMPLES, 4, | GLX_SAMPLES, 4, | ||||
| None | None | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -35,7 +35,7 @@ START_NAMESPACE_DISTRHO | |||||
| */ | */ | ||||
| /** | /** | ||||
| Audio port can be used as control voltage (LV2 only). | |||||
| Audio port can be used as control voltage (LV2 and JACK standalone only). | |||||
| */ | */ | ||||
| static const uint32_t kAudioPortIsCV = 0x1; | static const uint32_t kAudioPortIsCV = 0x1; | ||||
| @@ -110,6 +110,9 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; | |||||
| /** | /** | ||||
| Audio Port. | Audio Port. | ||||
| Can be used as CV port by specifying kAudioPortIsCV in hints,@n | |||||
| but this is only supported in LV2 and JACK standalone formats. | |||||
| */ | */ | ||||
| struct AudioPort { | struct AudioPort { | ||||
| /** | /** | ||||
| @@ -189,7 +192,7 @@ struct ParameterRanges { | |||||
| float max; | float max; | ||||
| /** | /** | ||||
| Default constructor, using 0.0 as minimum, 1.0 as maximum and 0.0 as default. | |||||
| Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum. | |||||
| */ | */ | ||||
| ParameterRanges() noexcept | ParameterRanges() noexcept | ||||
| : def(0.0f), | : def(0.0f), | ||||
| @@ -827,6 +830,13 @@ protected: | |||||
| virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; | virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; | ||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| /** | |||||
| TODO API under construction | |||||
| */ | |||||
| virtual bool isStateFile(uint32_t index) = 0; | |||||
| #endif | |||||
| /* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
| * Internal data */ | * Internal data */ | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -81,6 +81,18 @@ public: | |||||
| /* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
| * Host state */ | * Host state */ | ||||
| /** | |||||
| Get the color used for UI background (i.e. window color) in RGBA format. | |||||
| Returns 0 by default, in case of error or lack of host support. | |||||
| */ | |||||
| uint getBackgroundColor() const noexcept; | |||||
| /** | |||||
| Get the color used for UI foreground (i.e. text color) in RGBA format. | |||||
| Returns 0xffffffff by default, in case of error or lack of host support. | |||||
| */ | |||||
| uint getForegroundColor() const noexcept; | |||||
| /** | /** | ||||
| Get the current sample rate used in plugin processing. | Get the current sample rate used in plugin processing. | ||||
| @see sampleRateChanged(double) | @see sampleRateChanged(double) | ||||
| @@ -92,13 +104,13 @@ public: | |||||
| Touch/pressed-down event. | Touch/pressed-down event. | ||||
| Lets the host know the user is tweaking a parameter. | Lets the host know the user is tweaking a parameter. | ||||
| Required in some hosts to record automation. | |||||
| Required in some hosts to record automation. | |||||
| */ | */ | ||||
| void editParameter(uint32_t index, bool started); | void editParameter(uint32_t index, bool started); | ||||
| /** | /** | ||||
| setParameterValue. | setParameterValue. | ||||
| Change a parameter value in the Plugin. | Change a parameter value in the Plugin. | ||||
| */ | */ | ||||
| void setParameterValue(uint32_t index, float value); | void setParameterValue(uint32_t index, float value); | ||||
| @@ -111,6 +123,19 @@ public: | |||||
| void setState(const char* key, const char* value); | void setState(const char* key, const char* value); | ||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| /** | |||||
| Request a new file from the host, matching the properties of a state key.@n | |||||
| This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n | |||||
| Response will be sent asynchronously to stateChanged, with the matching key and the new file as the value.@n | |||||
| It is not possible to know if the action was cancelled by the user. | |||||
| @return Success if a file-browser was opened, otherwise false. | |||||
| @note You cannot request more than one file at a time. | |||||
| */ | |||||
| bool requestStateFile(const char* key); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
| /** | /** | ||||
| sendNote. | sendNote. | ||||
| @@ -174,7 +174,7 @@ public: | |||||
| fBufferLen(0) | fBufferLen(0) | ||||
| { | { | ||||
| char strBuf[0xff+1]; | char strBuf[0xff+1]; | ||||
| std::snprintf(strBuf, 0xff, "%f", value); | |||||
| std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value)); | |||||
| strBuf[0xff] = '\0'; | strBuf[0xff] = '\0'; | ||||
| _dup(strBuf); | _dup(strBuf); | ||||
| @@ -188,7 +188,7 @@ public: | |||||
| fBufferLen(0) | fBufferLen(0) | ||||
| { | { | ||||
| char strBuf[0xff+1]; | char strBuf[0xff+1]; | ||||
| std::snprintf(strBuf, 0xff, "%g", value); | |||||
| std::snprintf(strBuf, 0xff, "%.24g", value); | |||||
| strBuf[0xff] = '\0'; | strBuf[0xff] = '\0'; | ||||
| _dup(strBuf); | _dup(strBuf); | ||||
| @@ -77,6 +77,10 @@ | |||||
| # define DISTRHO_PLUGIN_WANT_STATE 0 | # define DISTRHO_PLUGIN_WANT_STATE 0 | ||||
| #endif | #endif | ||||
| #ifndef DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| # define DISTRHO_PLUGIN_WANT_STATEFILES 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_FULL_STATE | #ifndef DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
| # define DISTRHO_PLUGIN_WANT_FULL_STATE 0 | # define DISTRHO_PLUGIN_WANT_FULL_STATE 0 | ||||
| #endif | #endif | ||||
| @@ -108,7 +112,7 @@ | |||||
| // Define DISTRHO_UI_URI if needed | // Define DISTRHO_UI_URI if needed | ||||
| #ifndef DISTRHO_UI_URI | #ifndef DISTRHO_UI_URI | ||||
| # define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||||
| # define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI" | |||||
| #endif | #endif | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -127,6 +131,14 @@ | |||||
| # error Synths need MIDI input to work! | # error Synths need MIDI input to work! | ||||
| #endif | #endif | ||||
| // ----------------------------------------------------------------------- | |||||
| // Enable state if plugin wants state files | |||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES && ! DISTRHO_PLUGIN_WANT_STATE | |||||
| # undef DISTRHO_PLUGIN_WANT_STATE | |||||
| # define DISTRHO_PLUGIN_WANT_STATE 1 | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // Enable full state if plugin exports presets | // Enable full state if plugin exports presets | ||||
| @@ -486,6 +486,15 @@ public: | |||||
| return fData->stateDefValues[index]; | return fData->stateDefValues[index]; | ||||
| } | } | ||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| bool isStateFile(const uint32_t index) const | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, false); | |||||
| return fPlugin->isStateFile(index); | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_FULL_STATE | # if DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
| String getState(const char* key) const | String getState(const char* key) const | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU Lesser General Public | * modify it under the terms of the GNU Lesser General Public | ||||
| @@ -101,7 +101,16 @@ public: | |||||
| PluginJack(jack_client_t* const client) | PluginJack(jack_client_t* const client) | ||||
| : fPlugin(this, writeMidiCallback), | : fPlugin(this, writeMidiCallback), | ||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), | |||||
| fUI(this, 0, | |||||
| nullptr, // edit param | |||||
| setParameterValueCallback, | |||||
| setStateCallback, | |||||
| nullptr, // send note | |||||
| setSizeCallback, | |||||
| nullptr, // file request | |||||
| nullptr, // bundle | |||||
| fPlugin.getInstancePointer(), | |||||
| getDesktopScaleFactor()), | |||||
| #endif | #endif | ||||
| fClient(client) | fClient(client) | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -19,15 +19,19 @@ | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | ||||
| # error Cannot use MIDI Output with LADSPA or DSSI | # error Cannot use MIDI Output with LADSPA or DSSI | ||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_WANT_FULL_STATE | |||||
| # error Cannot use full state with LADSPA or DSSI | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS && !defined(DISTRHO_NO_WARNINGS) | |||||
| # warning LADSPA/DSSI does not support TimePos | |||||
| #endif | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | #ifdef DISTRHO_PLUGIN_TARGET_DSSI | ||||
| # include "dssi/dssi.h" | # include "dssi/dssi.h" | ||||
| # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| # error DSSI does not support MIDI output | |||||
| # endif | |||||
| #else | #else | ||||
| # include "ladspa/ladspa.h" | # include "ladspa/ladspa.h" | ||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| # error Cannot use MIDI with LADSPA | # error Cannot use MIDI with LADSPA | ||||
| # endif | # endif | ||||
| # if DISTRHO_PLUGIN_WANT_STATE && !defined(DISTRHO_NO_WARNINGS) | # if DISTRHO_PLUGIN_WANT_STATE && !defined(DISTRHO_NO_WARNINGS) | ||||
| @@ -35,10 +39,6 @@ | |||||
| # endif | # endif | ||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS && !defined(DISTRHO_NO_WARNINGS) | |||||
| # warning LADSPA/DSSI does not support TimePos | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -543,15 +543,14 @@ static DSSI_Descriptor sDssiDescriptor = { | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| class DescriptorInitializer | |||||
| static const struct DescriptorInitializer | |||||
| { | { | ||||
| public: | |||||
| DescriptorInitializer() | DescriptorInitializer() | ||||
| { | { | ||||
| // Create dummy plugin to get data from | // Create dummy plugin to get data from | ||||
| d_lastBufferSize = 512; | d_lastBufferSize = 512; | ||||
| d_lastSampleRate = 44100.0; | d_lastSampleRate = 44100.0; | ||||
| PluginExporter plugin(nullptr, nullptr); | |||||
| const PluginExporter plugin(nullptr, nullptr); | |||||
| d_lastBufferSize = 0; | d_lastBufferSize = 0; | ||||
| d_lastSampleRate = 0.0; | d_lastSampleRate = 0.0; | ||||
| @@ -616,23 +615,23 @@ public: | |||||
| { | { | ||||
| const ParameterRanges& ranges(plugin.getParameterRanges(i)); | const ParameterRanges& ranges(plugin.getParameterRanges(i)); | ||||
| const float defValue(ranges.def); | |||||
| const float defValue = ranges.def; | |||||
| portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; | portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; | ||||
| portRangeHints[port].LowerBound = ranges.min; | portRangeHints[port].LowerBound = ranges.min; | ||||
| portRangeHints[port].UpperBound = ranges.max; | portRangeHints[port].UpperBound = ranges.max; | ||||
| if (defValue == 0.0f) | |||||
| /**/ if (d_isZero(defValue)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; | ||||
| else if (defValue == 1.0f) | |||||
| else if (d_isEqual(defValue, 1.0f)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; | ||||
| else if (defValue == 100.0f) | |||||
| else if (d_isEqual(defValue, 100.0f)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; | ||||
| else if (defValue == 440.0f) | |||||
| else if (d_isEqual(defValue, 440.0f)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; | ||||
| else if (ranges.min == defValue) | |||||
| else if (d_isEqual(ranges.min, defValue)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; | ||||
| else if (ranges.max == defValue) | |||||
| else if (d_isEqual(ranges.max, defValue)) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; | ||||
| else | else | ||||
| { | { | ||||
| @@ -640,7 +639,7 @@ public: | |||||
| const float middleLow = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | const float middleLow = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | ||||
| const float middleHigh = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | const float middleHigh = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | ||||
| if (defValue < middleLow) | |||||
| /**/ if (defValue < middleLow) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; | ||||
| else if (defValue > middleHigh) | else if (defValue > middleHigh) | ||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; | ||||
| @@ -650,7 +649,7 @@ public: | |||||
| } | } | ||||
| { | { | ||||
| const uint32_t hints(plugin.getParameterHints(i)); | |||||
| const uint32_t hints = plugin.getParameterHints(i); | |||||
| if (hints & kParameterIsBoolean) | if (hints & kParameterIsBoolean) | ||||
| { | { | ||||
| @@ -728,9 +727,7 @@ public: | |||||
| sLadspaDescriptor.PortNames = nullptr; | sLadspaDescriptor.PortNames = nullptr; | ||||
| } | } | ||||
| } | } | ||||
| }; | |||||
| static DescriptorInitializer sDescInit; | |||||
| } sDescInit; | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -24,6 +24,7 @@ | |||||
| #include "lv2/midi.h" | #include "lv2/midi.h" | ||||
| #include "lv2/options.h" | #include "lv2/options.h" | ||||
| #include "lv2/parameters.h" | #include "lv2/parameters.h" | ||||
| #include "lv2/patch.h" | |||||
| #include "lv2/state.h" | #include "lv2/state.h" | ||||
| #include "lv2/time.h" | #include "lv2/time.h" | ||||
| #include "lv2/urid.h" | #include "lv2/urid.h" | ||||
| @@ -54,7 +55,8 @@ | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| typedef std::map<const String, String> StringMap; | |||||
| typedef std::map<const String, String> StringToStringMap; | |||||
| typedef std::map<const LV2_URID, String> UridToStringMap; | |||||
| #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | ||||
| static const writeMidiFunc writeMidiCallback = nullptr; | static const writeMidiFunc writeMidiCallback = nullptr; | ||||
| @@ -65,7 +67,10 @@ static const writeMidiFunc writeMidiCallback = nullptr; | |||||
| class PluginLv2 | class PluginLv2 | ||||
| { | { | ||||
| public: | public: | ||||
| PluginLv2(const double sampleRate, const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker, const bool usingNominal) | |||||
| PluginLv2(const double sampleRate, | |||||
| const LV2_URID_Map* const uridMap, | |||||
| const LV2_Worker_Schedule* const worker, | |||||
| const bool usingNominal) | |||||
| : fPlugin(this, writeMidiCallback), | : fPlugin(this, writeMidiCallback), | ||||
| fUsingNominal(usingNominal), | fUsingNominal(usingNominal), | ||||
| #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | ||||
| @@ -127,6 +132,15 @@ public: | |||||
| const String& dkey(fPlugin.getStateKey(i)); | const String& dkey(fPlugin.getStateKey(i)); | ||||
| fStateMap[dkey] = fPlugin.getStateDefaultValue(i); | fStateMap[dkey] = fPlugin.getStateDefaultValue(i); | ||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| if (fPlugin.isStateFile(i)) | |||||
| { | |||||
| const String dpf_lv2_key(DISTRHO_PLUGIN_URI "#" + dkey); | |||||
| const LV2_URID urid = uridMap->map(uridMap->handle, dpf_lv2_key.buffer()); | |||||
| fUridStateFileMap[urid] = dkey; | |||||
| } | |||||
| # endif | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| @@ -521,16 +535,16 @@ public: | |||||
| } | } | ||||
| #endif | #endif | ||||
| // check for messages from UI | |||||
| #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI | |||||
| // check for messages from UI or files | |||||
| #if DISTRHO_PLUGIN_WANT_STATE && (DISTRHO_PLUGIN_HAS_UI || DISTRHO_PLUGIN_WANT_STATEFILES) | |||||
| LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | ||||
| { | { | ||||
| if (event == nullptr) | if (event == nullptr) | ||||
| break; | break; | ||||
| if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) | |||||
| if (event->body.type == fURIDs.dpfKeyValue) | |||||
| { | { | ||||
| const void* const data((const void*)(event + 1)); | |||||
| const void* const data = (const void*)(event + 1); | |||||
| // check if this is our special message | // check if this is our special message | ||||
| if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) | if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) | ||||
| @@ -538,12 +552,28 @@ public: | |||||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | ||||
| fNeededUiSends[i] = true; | fNeededUiSends[i] = true; | ||||
| } | } | ||||
| else | |||||
| // no, send to DSP as usual | // no, send to DSP as usual | ||||
| else if (fWorker != nullptr) | |||||
| { | { | ||||
| fWorker->schedule_work(fWorker->handle, event->body.size, data); | |||||
| fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |||||
| } | } | ||||
| } | } | ||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| else if (event->body.type == fURIDs.atomObject && fWorker != nullptr) | |||||
| { | |||||
| const LV2_Atom_Object* const object = (const LV2_Atom_Object*)&event->body; | |||||
| const LV2_Atom* property = nullptr; | |||||
| const LV2_Atom* value = nullptr; | |||||
| lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |||||
| if (property != nullptr && property->type == fURIDs.atomURID && | |||||
| value != nullptr && value->type == fURIDs.atomPath) | |||||
| { | |||||
| fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |||||
| } | |||||
| } | |||||
| # endif | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -653,19 +683,19 @@ public: | |||||
| if (! fNeededUiSends[i]) | if (! fNeededUiSends[i]) | ||||
| continue; | continue; | ||||
| const String& key = fPlugin.getStateKey(i); | |||||
| const String& curKey(fPlugin.getStateKey(i)); | |||||
| for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| { | { | ||||
| const String& curKey = cit->first; | |||||
| const String& key(cit->first); | |||||
| if (curKey != key) | if (curKey != key) | ||||
| continue; | continue; | ||||
| const String& value = cit->second; | |||||
| const String& value(cit->second); | |||||
| // set msg size (key + value + separator + 2x null terminator) | // set msg size (key + value + separator + 2x null terminator) | ||||
| const size_t msgSize(key.length()+value.length()+3); | |||||
| const size_t msgSize = key.length()+value.length()+3; | |||||
| if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | ||||
| { | { | ||||
| @@ -673,21 +703,18 @@ public: | |||||
| break; | break; | ||||
| } | } | ||||
| // reserve msg space | |||||
| // FIXME create a large enough buffer beforehand | |||||
| char msgBuf[msgSize]; | |||||
| std::memset(msgBuf, 0, msgSize); | |||||
| // write key and value in atom bufer | |||||
| std::memcpy(msgBuf, key.buffer(), key.length()+1); | |||||
| std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |||||
| // put data | // put data | ||||
| aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | ||||
| aev->time.frames = 0; | aev->time.frames = 0; | ||||
| aev->body.type = fURIDs.distrhoState; | |||||
| aev->body.type = fURIDs.dpfKeyValue; | |||||
| aev->body.size = msgSize; | aev->body.size = msgSize; | ||||
| std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize); | |||||
| uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); | |||||
| std::memset(msgBuf, 0, msgSize); | |||||
| // write key and value in atom buffer | |||||
| std::memcpy(msgBuf, key.buffer(), key.length()+1); | |||||
| std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |||||
| fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | ||||
| @@ -795,7 +822,7 @@ public: | |||||
| # if DISTRHO_PLUGIN_WANT_FULL_STATE | # if DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
| // Update state | // Update state | ||||
| for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| { | { | ||||
| const String& key = cit->first; | const String& key = cit->first; | ||||
| fStateMap[key] = fPlugin.getState(key); | fStateMap[key] = fPlugin.getState(key); | ||||
| @@ -811,22 +838,52 @@ public: | |||||
| { | { | ||||
| # if DISTRHO_PLUGIN_WANT_FULL_STATE | # if DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
| // Update current state | // Update current state | ||||
| for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| { | { | ||||
| const String& key = cit->first; | const String& key = cit->first; | ||||
| fStateMap[key] = fPlugin.getState(key); | fStateMap[key] = fPlugin.getState(key); | ||||
| } | } | ||||
| # endif | # endif | ||||
| for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| String dpf_lv2_key; | |||||
| LV2_URID urid; | |||||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||||
| { | { | ||||
| const String& key = cit->first; | |||||
| const String& value = cit->second; | |||||
| const String& curKey(fPlugin.getStateKey(i)); | |||||
| for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||||
| { | |||||
| const String& key(cit->first); | |||||
| if (curKey != key) | |||||
| continue; | |||||
| const String& value(cit->second); | |||||
| const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key); | |||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| if (fPlugin.isStateFile(i)) | |||||
| { | |||||
| dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; | |||||
| urid = fURIDs.atomPath; | |||||
| } | |||||
| else | |||||
| # endif | |||||
| { | |||||
| dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |||||
| urid = fURIDs.atomString; | |||||
| } | |||||
| // some hosts need +1 for the null terminator, even though the type is string | |||||
| store(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), value.buffer(), value.length()+1, fURIDs.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||||
| dpf_lv2_key += key; | |||||
| // some hosts need +1 for the null terminator, even though the type is string | |||||
| store(handle, | |||||
| fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||||
| value.buffer(), | |||||
| value.length()+1, | |||||
| urid, | |||||
| LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||||
| } | |||||
| } | } | ||||
| return LV2_STATE_SUCCESS; | return LV2_STATE_SUCCESS; | ||||
| @@ -837,23 +894,42 @@ public: | |||||
| size_t size; | size_t size; | ||||
| uint32_t type, flags; | uint32_t type, flags; | ||||
| String dpf_lv2_key; | |||||
| LV2_URID urid; | |||||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | ||||
| { | { | ||||
| const String& key(fPlugin.getStateKey(i)); | const String& key(fPlugin.getStateKey(i)); | ||||
| const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key); | |||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| if (fPlugin.isStateFile(i)) | |||||
| { | |||||
| dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; | |||||
| urid = fURIDs.atomPath; | |||||
| } | |||||
| else | |||||
| # endif | |||||
| { | |||||
| dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |||||
| urid = fURIDs.atomString; | |||||
| } | |||||
| dpf_lv2_key += key; | |||||
| size = 0; | size = 0; | ||||
| type = 0; | type = 0; | ||||
| flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; | flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; | ||||
| const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), &size, &type, &flags); | |||||
| const void* data = retrieve(handle, | |||||
| fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||||
| &size, &type, &flags); | |||||
| if (data == nullptr || size == 0) | if (data == nullptr || size == 0) | ||||
| continue; | continue; | ||||
| DISTRHO_SAFE_ASSERT_CONTINUE(type == fURIDs.atomString); | |||||
| DISTRHO_SAFE_ASSERT_CONTINUE(type == urid); | |||||
| const char* const value((const char*)data); | |||||
| const std::size_t length(std::strlen(value)); | |||||
| const char* const value = (const char*)data; | |||||
| const std::size_t length = std::strlen(value); | |||||
| DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size); | DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size); | ||||
| setState(key, value); | setState(key, value); | ||||
| @@ -871,12 +947,55 @@ public: | |||||
| LV2_Worker_Status lv2_work(const void* const data) | LV2_Worker_Status lv2_work(const void* const data) | ||||
| { | { | ||||
| const char* const key((const char*)data); | |||||
| const char* const value(key+std::strlen(key)+1); | |||||
| const LV2_Atom* const eventBody = (const LV2_Atom*)data; | |||||
| setState(key, value); | |||||
| if (eventBody->type == fURIDs.dpfKeyValue) | |||||
| { | |||||
| const char* const key = (const char*)(eventBody + 1); | |||||
| const char* const value = key + (std::strlen(key) + 1U); | |||||
| return LV2_WORKER_SUCCESS; | |||||
| setState(key, value); | |||||
| return LV2_WORKER_SUCCESS; | |||||
| } | |||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| if (eventBody->type == fURIDs.atomObject) | |||||
| { | |||||
| const LV2_Atom_Object* const object = (const LV2_Atom_Object*)eventBody; | |||||
| const LV2_Atom* property = nullptr; | |||||
| const LV2_Atom* value = nullptr; | |||||
| lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(property != nullptr, LV2_WORKER_ERR_UNKNOWN); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID, LV2_WORKER_ERR_UNKNOWN); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, LV2_WORKER_ERR_UNKNOWN); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath, LV2_WORKER_ERR_UNKNOWN); | |||||
| const LV2_URID urid = ((const LV2_Atom_URID*)property)->body; | |||||
| const char* const filename = (const char*)(value + 1); | |||||
| String key; | |||||
| try { | |||||
| key = fUridStateFileMap[urid]; | |||||
| } DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateFileMap[urid]", LV2_WORKER_ERR_UNKNOWN); | |||||
| setState(key, filename); | |||||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||||
| { | |||||
| if (fPlugin.getStateKey(i) == key) | |||||
| { | |||||
| fNeededUiSends[i] = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return LV2_WORKER_SUCCESS; | |||||
| } | |||||
| # endif | |||||
| return LV2_WORKER_ERR_UNKNOWN; | |||||
| } | } | ||||
| LV2_Worker_Status lv2_work_response(uint32_t, const void*) | LV2_Worker_Status lv2_work_response(uint32_t, const void*) | ||||
| @@ -995,16 +1114,21 @@ private: | |||||
| // LV2 URIDs | // LV2 URIDs | ||||
| struct URIDs { | struct URIDs { | ||||
| const LV2_URID_Map* _uridMap; | |||||
| LV2_URID atomBlank; | LV2_URID atomBlank; | ||||
| LV2_URID atomObject; | LV2_URID atomObject; | ||||
| LV2_URID atomDouble; | LV2_URID atomDouble; | ||||
| LV2_URID atomFloat; | LV2_URID atomFloat; | ||||
| LV2_URID atomInt; | LV2_URID atomInt; | ||||
| LV2_URID atomLong; | LV2_URID atomLong; | ||||
| LV2_URID atomPath; | |||||
| LV2_URID atomSequence; | LV2_URID atomSequence; | ||||
| LV2_URID atomString; | LV2_URID atomString; | ||||
| LV2_URID distrhoState; | |||||
| LV2_URID atomURID; | |||||
| LV2_URID dpfKeyValue; | |||||
| LV2_URID midiEvent; | LV2_URID midiEvent; | ||||
| LV2_URID patchProperty; | |||||
| LV2_URID patchValue; | |||||
| LV2_URID timePosition; | LV2_URID timePosition; | ||||
| LV2_URID timeBar; | LV2_URID timeBar; | ||||
| LV2_URID timeBarBeat; | LV2_URID timeBarBeat; | ||||
| @@ -1016,25 +1140,35 @@ private: | |||||
| LV2_URID timeSpeed; | LV2_URID timeSpeed; | ||||
| URIDs(const LV2_URID_Map* const uridMap) | URIDs(const LV2_URID_Map* const uridMap) | ||||
| : atomBlank(uridMap->map(uridMap->handle, LV2_ATOM__Blank)), | |||||
| atomObject(uridMap->map(uridMap->handle, LV2_ATOM__Object)), | |||||
| atomDouble(uridMap->map(uridMap->handle, LV2_ATOM__Double)), | |||||
| atomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float)), | |||||
| atomInt(uridMap->map(uridMap->handle, LV2_ATOM__Int)), | |||||
| atomLong(uridMap->map(uridMap->handle, LV2_ATOM__Long)), | |||||
| atomSequence(uridMap->map(uridMap->handle, LV2_ATOM__Sequence)), | |||||
| atomString(uridMap->map(uridMap->handle, LV2_ATOM__String)), | |||||
| distrhoState(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||||
| midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||||
| timePosition(uridMap->map(uridMap->handle, LV2_TIME__Position)), | |||||
| timeBar(uridMap->map(uridMap->handle, LV2_TIME__bar)), | |||||
| timeBarBeat(uridMap->map(uridMap->handle, LV2_TIME__barBeat)), | |||||
| timeBeatUnit(uridMap->map(uridMap->handle, LV2_TIME__beatUnit)), | |||||
| timeBeatsPerBar(uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar)), | |||||
| timeBeatsPerMinute(uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute)), | |||||
| timeTicksPerBeat(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)), | |||||
| timeFrame(uridMap->map(uridMap->handle, LV2_TIME__frame)), | |||||
| timeSpeed(uridMap->map(uridMap->handle, LV2_TIME__speed)) {} | |||||
| : _uridMap(uridMap), | |||||
| atomBlank(map(LV2_ATOM__Blank)), | |||||
| atomObject(map(LV2_ATOM__Object)), | |||||
| atomDouble(map(LV2_ATOM__Double)), | |||||
| atomFloat(map(LV2_ATOM__Float)), | |||||
| atomInt(map(LV2_ATOM__Int)), | |||||
| atomLong(map(LV2_ATOM__Long)), | |||||
| atomPath(map(LV2_ATOM__Path)), | |||||
| atomSequence(map(LV2_ATOM__Sequence)), | |||||
| atomString(map(LV2_ATOM__String)), | |||||
| atomURID(map(LV2_ATOM__URID)), | |||||
| dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||||
| midiEvent(map(LV2_MIDI__MidiEvent)), | |||||
| patchProperty(map(LV2_PATCH__property)), | |||||
| patchValue(map(LV2_PATCH__value)), | |||||
| timePosition(map(LV2_TIME__Position)), | |||||
| timeBar(map(LV2_TIME__bar)), | |||||
| timeBarBeat(map(LV2_TIME__barBeat)), | |||||
| timeBeatUnit(map(LV2_TIME__beatUnit)), | |||||
| timeBeatsPerBar(map(LV2_TIME__beatsPerBar)), | |||||
| timeBeatsPerMinute(map(LV2_TIME__beatsPerMinute)), | |||||
| timeTicksPerBeat(map(LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)), | |||||
| timeFrame(map(LV2_TIME__frame)), | |||||
| timeSpeed(map(LV2_TIME__speed)) {} | |||||
| inline LV2_URID map(const char* const uri) const | |||||
| { | |||||
| return _uridMap->map(_uridMap->handle, uri); | |||||
| } | |||||
| } fURIDs; | } fURIDs; | ||||
| // LV2 features | // LV2 features | ||||
| @@ -1042,7 +1176,7 @@ private: | |||||
| const LV2_Worker_Schedule* const fWorker; | const LV2_Worker_Schedule* const fWorker; | ||||
| #if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
| StringMap fStateMap; | |||||
| StringToStringMap fStateMap; | |||||
| bool* fNeededUiSends; | bool* fNeededUiSends; | ||||
| void setState(const char* const key, const char* const newValue) | void setState(const char* const key, const char* const newValue) | ||||
| @@ -1054,7 +1188,7 @@ private: | |||||
| return; | return; | ||||
| // check if key already exists | // check if key already exists | ||||
| for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |||||
| for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |||||
| { | { | ||||
| const String& dkey(it->first); | const String& dkey(it->first); | ||||
| @@ -1067,6 +1201,10 @@ private: | |||||
| d_stderr("Failed to find plugin state with key \"%s\"", key); | d_stderr("Failed to find plugin state with key \"%s\"", key); | ||||
| } | } | ||||
| # if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| UridToStringMap fUridStateFileMap; | |||||
| # endif | |||||
| #endif | #endif | ||||
| void updateParameterOutputsAndTriggers() | void updateParameterOutputsAndTriggers() | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include "lv2/instance-access.h" | #include "lv2/instance-access.h" | ||||
| #include "lv2/midi.h" | #include "lv2/midi.h" | ||||
| #include "lv2/options.h" | #include "lv2/options.h" | ||||
| #include "lv2/patch.h" | |||||
| #include "lv2/port-props.h" | #include "lv2/port-props.h" | ||||
| #include "lv2/presets.h" | #include "lv2/presets.h" | ||||
| #include "lv2/resize-port.h" | #include "lv2/resize-port.h" | ||||
| @@ -45,6 +46,10 @@ | |||||
| # error DISTRHO_PLUGIN_URI undefined! | # error DISTRHO_PLUGIN_URI undefined! | ||||
| #endif | #endif | ||||
| #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX | |||||
| # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | ||||
| # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | ||||
| #endif | #endif | ||||
| @@ -70,6 +75,8 @@ | |||||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | ||||
| #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | ||||
| #define DISTRHO_BYPASS_PARAMETER_NAME "lv2_enabled" | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| static const char* const lv2ManifestPluginExtensionData[] = | static const char* const lv2ManifestPluginExtensionData[] = | ||||
| { | { | ||||
| @@ -139,6 +146,9 @@ static const char* const lv2ManifestUiOptionalFeatures[] = | |||||
| "ui:parent", | "ui:parent", | ||||
| "ui:resize", | "ui:resize", | ||||
| "ui:touch", | "ui:touch", | ||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| "ui:requestValue", | |||||
| #endif | #endif | ||||
| nullptr | nullptr | ||||
| }; | }; | ||||
| @@ -220,8 +230,8 @@ void lv2_generate_ttl(const char* const basename) | |||||
| d_lastBufferSize = 0; | d_lastBufferSize = 0; | ||||
| d_lastSampleRate = 0.0; | d_lastSampleRate = 0.0; | ||||
| String pluginDLL(basename); | |||||
| String pluginTTL(pluginDLL + ".ttl"); | |||||
| const String pluginDLL(basename); | |||||
| const String pluginTTL(pluginDLL + ".ttl"); | |||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| String pluginUI(pluginDLL); | String pluginUI(pluginDLL); | ||||
| @@ -317,26 +327,45 @@ void lv2_generate_ttl(const char* const basename) | |||||
| // header | // header | ||||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | ||||
| pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||||
| pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||||
| #endif | #endif | ||||
| pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||||
| pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||||
| pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||||
| pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||||
| pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||||
| pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||||
| #ifdef DISTRHO_PLUGIN_BRAND | #ifdef DISTRHO_PLUGIN_BRAND | ||||
| pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |||||
| pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |||||
| #endif | #endif | ||||
| pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||||
| pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||||
| pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||||
| pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||||
| pluginString += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n"; | |||||
| pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||||
| pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | ||||
| pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||||
| pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||||
| pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||||
| #endif | #endif | ||||
| pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||||
| pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||||
| pluginString += "\n"; | pluginString += "\n"; | ||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| // define writable states as lv2 parameters | |||||
| bool hasStateFiles = false; | |||||
| for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |||||
| { | |||||
| if (! plugin.isStateFile(i)) | |||||
| continue; | |||||
| const String& key(plugin.getStateKey(i)); | |||||
| pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n"; | |||||
| pluginString += " a lv2:Parameter ;\n"; | |||||
| pluginString += " rdfs:label \"" + key + "\" ;\n"; | |||||
| pluginString += " rdfs:range atom:Path .\n\n"; | |||||
| hasStateFiles = true; | |||||
| } | |||||
| #endif | |||||
| // plugin | // plugin | ||||
| pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | ||||
| #ifdef DISTRHO_PLUGIN_LV2_CATEGORY | #ifdef DISTRHO_PLUGIN_LV2_CATEGORY | ||||
| @@ -353,6 +382,21 @@ void lv2_generate_ttl(const char* const basename) | |||||
| addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); | addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); | ||||
| addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); | addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); | ||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| if (hasStateFiles) | |||||
| { | |||||
| for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |||||
| { | |||||
| if (! plugin.isStateFile(i)) | |||||
| continue; | |||||
| const String& key(plugin.getStateKey(i)); | |||||
| pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; | |||||
| } | |||||
| pluginString += "\n"; | |||||
| } | |||||
| #endif | |||||
| // UI | // UI | ||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | ||||
| @@ -499,7 +543,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
| case kParameterDesignationBypass: | case kParameterDesignationBypass: | ||||
| designated = true; | designated = true; | ||||
| pluginString += " lv2:name \"Enabled\" ;\n"; | pluginString += " lv2:name \"Enabled\" ;\n"; | ||||
| pluginString += " lv2:symbol \"lv2_enabled\" ;\n"; | |||||
| pluginString += " lv2:symbol \"" DISTRHO_BYPASS_PARAMETER_NAME "\" ;\n"; | |||||
| pluginString += " lv2:default 1 ;\n"; | pluginString += " lv2:default 1 ;\n"; | ||||
| pluginString += " lv2:minimum 0 ;\n"; | pluginString += " lv2:minimum 0 ;\n"; | ||||
| pluginString += " lv2:maximum 1 ;\n"; | pluginString += " lv2:maximum 1 ;\n"; | ||||
| @@ -533,14 +577,14 @@ void lv2_generate_ttl(const char* const basename) | |||||
| if (plugin.getParameterHints(i) & kParameterIsInteger) | if (plugin.getParameterHints(i) & kParameterIsInteger) | ||||
| { | { | ||||
| if (plugin.isParameterInput(i)) | if (plugin.isParameterInput(i)) | ||||
| pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; | |||||
| pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n"; | |||||
| pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | ||||
| pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (plugin.isParameterInput(i)) | if (plugin.isParameterInput(i)) | ||||
| pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; | |||||
| pluginString += " lv2:default " + String(ranges.def) + " ;\n"; | |||||
| pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | ||||
| pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | ||||
| } | } | ||||
| @@ -582,33 +626,36 @@ void lv2_generate_ttl(const char* const basename) | |||||
| // unit | // unit | ||||
| const String& unit(plugin.getParameterUnit(i)); | const String& unit(plugin.getParameterUnit(i)); | ||||
| if (! unit.isEmpty()) | |||||
| if (unit.isNotEmpty() && ! unit.contains(" ")) | |||||
| { | { | ||||
| if (unit == "db" || unit == "dB") | |||||
| String lunit(unit); | |||||
| lunit.toLower(); | |||||
| /**/ if (lunit == "db") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:db ;\n"; | pluginString += " unit:unit unit:db ;\n"; | ||||
| } | } | ||||
| else if (unit == "hz" || unit == "Hz") | |||||
| else if (lunit == "hz") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:hz ;\n"; | pluginString += " unit:unit unit:hz ;\n"; | ||||
| } | } | ||||
| else if (unit == "khz" || unit == "kHz") | |||||
| else if (lunit == "khz") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:khz ;\n"; | pluginString += " unit:unit unit:khz ;\n"; | ||||
| } | } | ||||
| else if (unit == "mhz" || unit == "mHz") | |||||
| else if (lunit == "mhz") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:mhz ;\n"; | pluginString += " unit:unit unit:mhz ;\n"; | ||||
| } | } | ||||
| else if (unit == "ms") | |||||
| else if (lunit == "ms") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:ms ;\n"; | pluginString += " unit:unit unit:ms ;\n"; | ||||
| } | } | ||||
| else if (unit == "s") | |||||
| else if (lunit == "s") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:s ;\n"; | pluginString += " unit:unit unit:s ;\n"; | ||||
| } | } | ||||
| else if (unit == "%") | |||||
| else if (lunit == "%") | |||||
| { | { | ||||
| pluginString += " unit:unit unit:pc ;\n"; | pluginString += " unit:unit unit:pc ;\n"; | ||||
| } | } | ||||
| @@ -796,7 +843,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
| const String key = plugin.getStateKey(j); | const String key = plugin.getStateKey(j); | ||||
| const String value = plugin.getState(key); | const String value = plugin.getState(key); | ||||
| presetString += " <urn:distrho:" + key + ">"; | |||||
| presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">"; | |||||
| if (value.length() < 10) | if (value.length() < 10) | ||||
| presetString += " \"" + value + "\" ;\n"; | presetString += " \"" + value + "\" ;\n"; | ||||
| @@ -827,12 +874,21 @@ void lv2_generate_ttl(const char* const basename) | |||||
| presetString += " [\n"; | presetString += " [\n"; | ||||
| } | } | ||||
| presetString += " lv2:symbol \"" + plugin.getParameterSymbol(j) + "\" ;\n"; | |||||
| String parameterSymbol = plugin.getParameterSymbol(j); | |||||
| float parameterValue = plugin.getParameterValue(j); | |||||
| if (plugin.getParameterDesignation(j) == kParameterDesignationBypass) | |||||
| { | |||||
| parameterSymbol = DISTRHO_BYPASS_PARAMETER_NAME; | |||||
| parameterValue = 1.0f - parameterValue; | |||||
| } | |||||
| presetString += " lv2:symbol \"" + parameterSymbol + "\" ;\n"; | |||||
| if (plugin.getParameterHints(j) & kParameterIsInteger) | if (plugin.getParameterHints(j) & kParameterIsInteger) | ||||
| presetString += " pset:value " + String(int(plugin.getParameterValue(j))) + " ;\n"; | |||||
| presetString += " pset:value " + String(int(parameterValue)) + " ;\n"; | |||||
| else | else | ||||
| presetString += " pset:value " + String(plugin.getParameterValue(j)) + " ;\n"; | |||||
| presetString += " pset:value " + String(parameterValue) + " ;\n"; | |||||
| if (j+1 == numParameters || plugin.isParameterOutput(j+1)) | if (j+1 == numParameters || plugin.isParameterOutput(j+1)) | ||||
| presetString += " ] .\n\n"; | presetString += " ] .\n\n"; | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -47,6 +47,7 @@ | |||||
| #define effCanBeAutomated 26 | #define effCanBeAutomated 26 | ||||
| #define effGetProgramNameIndexed 29 | #define effGetProgramNameIndexed 29 | ||||
| #define effGetPlugCategory 35 | #define effGetPlugCategory 35 | ||||
| #define effVendorSpecific 50 | |||||
| #define effEditKeyDown 59 | #define effEditKeyDown 59 | ||||
| #define effEditKeyUp 60 | #define effEditKeyUp 60 | ||||
| #define kVstVersion 2400 | #define kVstVersion 2400 | ||||
| @@ -161,12 +162,25 @@ public: | |||||
| class UIVst | class UIVst | ||||
| { | { | ||||
| public: | public: | ||||
| UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor) | |||||
| UIVst(const audioMasterCallback audioMaster, | |||||
| AEffect* const effect, | |||||
| ParameterCheckHelper* const uiHelper, | |||||
| PluginExporter* const plugin, | |||||
| const intptr_t winId, const float scaleFactor) | |||||
| : fAudioMaster(audioMaster), | : fAudioMaster(audioMaster), | ||||
| fEffect(effect), | fEffect(effect), | ||||
| fUiHelper(uiHelper), | fUiHelper(uiHelper), | ||||
| fPlugin(plugin), | fPlugin(plugin), | ||||
| fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, plugin->getInstancePointer()), | |||||
| fUI(this, winId, | |||||
| editParameterCallback, | |||||
| setParameterCallback, | |||||
| setStateCallback, | |||||
| sendNoteCallback, | |||||
| setSizeCallback, | |||||
| nullptr, // TODO file request | |||||
| nullptr, | |||||
| plugin->getInstancePointer(), | |||||
| scaleFactor), | |||||
| fShouldCaptureVstKeys(false) | fShouldCaptureVstKeys(false) | ||||
| { | { | ||||
| // FIXME only needed for windows? | // FIXME only needed for windows? | ||||
| @@ -397,11 +411,12 @@ public: | |||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| fVstUI = nullptr; | |||||
| fVstRect.top = 0; | |||||
| fVstRect.left = 0; | |||||
| fVstRect.bottom = 0; | |||||
| fVstRect.right = 0; | |||||
| fVstUI = nullptr; | |||||
| fVstRect.top = 0; | |||||
| fVstRect.left = 0; | |||||
| fVstRect.bottom = 0; | |||||
| fVstRect.right = 0; | |||||
| fLastScaleFactor = 1.0f; | |||||
| if (const uint32_t paramCount = fPlugin.getParameterCount()) | if (const uint32_t paramCount = fPlugin.getParameterCount()) | ||||
| { | { | ||||
| @@ -578,10 +593,9 @@ public: | |||||
| { | { | ||||
| d_lastUiSampleRate = fPlugin.getSampleRate(); | d_lastUiSampleRate = fPlugin.getSampleRate(); | ||||
| // TODO | |||||
| const float scaleFactor = 1.0f; | |||||
| UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, scaleFactor, fPlugin.getInstancePointer()); | |||||
| UIExporter tmpUI(nullptr, 0, | |||||
| nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |||||
| fPlugin.getInstancePointer(), fLastScaleFactor); | |||||
| fVstRect.right = tmpUI.getWidth(); | fVstRect.right = tmpUI.getWidth(); | ||||
| fVstRect.bottom = tmpUI.getHeight(); | fVstRect.bottom = tmpUI.getHeight(); | ||||
| tmpUI.quit(); | tmpUI.quit(); | ||||
| @@ -602,10 +616,7 @@ public: | |||||
| # endif | # endif | ||||
| d_lastUiSampleRate = fPlugin.getSampleRate(); | d_lastUiSampleRate = fPlugin.getSampleRate(); | ||||
| // TODO | |||||
| const float scaleFactor = 1.0f; | |||||
| fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, scaleFactor); | |||||
| fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor); | |||||
| # if DISTRHO_PLUGIN_WANT_FULL_STATE | # if DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
| // Update current state from plugin side | // Update current state from plugin side | ||||
| @@ -908,6 +919,13 @@ public: | |||||
| } | } | ||||
| break; | break; | ||||
| case effVendorSpecific: | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| if (index == CCONST('P', 'r', 'e', 'S') && value == CCONST('A', 'e', 'C', 's')) | |||||
| fLastScaleFactor = opt; | |||||
| #endif | |||||
| break; | |||||
| //case effStartProcess: | //case effStartProcess: | ||||
| //case effStopProcess: | //case effStopProcess: | ||||
| // unused | // unused | ||||
| @@ -952,18 +970,18 @@ public: | |||||
| void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) | void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) | ||||
| { | { | ||||
| if (sampleFrames <= 0) | |||||
| { | |||||
| updateParameterOutputsAndTriggers(); | |||||
| return; | |||||
| } | |||||
| if (! fPlugin.isActive()) | if (! fPlugin.isActive()) | ||||
| { | { | ||||
| // host has not activated the plugin yet, nasty! | // host has not activated the plugin yet, nasty! | ||||
| vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f); | vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f); | ||||
| } | } | ||||
| if (sampleFrames <= 0) | |||||
| { | |||||
| updateParameterOutputsAndTriggers(); | |||||
| return; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | #if DISTRHO_PLUGIN_WANT_TIMEPOS | ||||
| static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid); | static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid); | ||||
| @@ -1054,6 +1072,7 @@ private: | |||||
| #if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
| UIVst* fVstUI; | UIVst* fVstUI; | ||||
| ERect fVstRect; | ERect fVstRect; | ||||
| float fLastScaleFactor; | |||||
| # if DISTRHO_OS_MAC | # if DISTRHO_OS_MAC | ||||
| bool fUsingNsView; | bool fUsingNsView; | ||||
| # endif | # endif | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -81,6 +81,16 @@ void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRa | |||||
| /* ------------------------------------------------------------------------------------------------------------ | /* ------------------------------------------------------------------------------------------------------------ | ||||
| * Host state */ | * Host state */ | ||||
| uint UI::getBackgroundColor() const noexcept | |||||
| { | |||||
| return pData->bgColor; | |||||
| } | |||||
| uint UI::getForegroundColor() const noexcept | |||||
| { | |||||
| return pData->fgColor; | |||||
| } | |||||
| double UI::getSampleRate() const noexcept | double UI::getSampleRate() const noexcept | ||||
| { | { | ||||
| return pData->sampleRate; | return pData->sampleRate; | ||||
| @@ -103,6 +113,13 @@ void UI::setState(const char* key, const char* value) | |||||
| } | } | ||||
| #endif | #endif | ||||
| #if DISTRHO_PLUGIN_WANT_STATEFILES | |||||
| bool UI::requestStateFile(const char* key) | |||||
| { | |||||
| return pData->fileRequestCallback(key); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
| void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | ||||
| { | { | ||||
| @@ -97,7 +97,7 @@ class UIDssi | |||||
| { | { | ||||
| public: | public: | ||||
| UIDssi(const OscData& oscData, const char* const uiTitle) | UIDssi(const OscData& oscData, const char* const uiTitle) | ||||
| : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), | |||||
| : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, nullptr), | |||||
| fHostClosed(false), | fHostClosed(false), | ||||
| fOscData(oscData) | fOscData(oscData) | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -48,11 +48,12 @@ extern Window* d_lastUiWindow; | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // UI callbacks | // UI callbacks | ||||
| typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||||
| typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); | |||||
| typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||||
| typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); | |||||
| typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||||
| typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||||
| typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); | |||||
| typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||||
| typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); | |||||
| typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||||
| typedef bool (*fileRequestFunc) (void* ptr, const char* key); | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // UI private data | // UI private data | ||||
| @@ -70,14 +71,17 @@ struct UI::PrivateData { | |||||
| bool resizeInProgress; | bool resizeInProgress; | ||||
| uint minWidth; | uint minWidth; | ||||
| uint minHeight; | uint minHeight; | ||||
| uint bgColor; | |||||
| uint fgColor; | |||||
| // Callbacks | // Callbacks | ||||
| void* callbacksPtr; | |||||
| editParamFunc editParamCallbackFunc; | |||||
| setParamFunc setParamCallbackFunc; | |||||
| setStateFunc setStateCallbackFunc; | |||||
| sendNoteFunc sendNoteCallbackFunc; | |||||
| setSizeFunc setSizeCallbackFunc; | |||||
| void* callbacksPtr; | |||||
| editParamFunc editParamCallbackFunc; | |||||
| setParamFunc setParamCallbackFunc; | |||||
| setStateFunc setStateCallbackFunc; | |||||
| sendNoteFunc sendNoteCallbackFunc; | |||||
| setSizeFunc setSizeCallbackFunc; | |||||
| fileRequestFunc fileRequestCallbackFunc; | |||||
| PrivateData() noexcept | PrivateData() noexcept | ||||
| : sampleRate(d_lastUiSampleRate), | : sampleRate(d_lastUiSampleRate), | ||||
| @@ -89,12 +93,15 @@ struct UI::PrivateData { | |||||
| resizeInProgress(false), | resizeInProgress(false), | ||||
| minWidth(0), | minWidth(0), | ||||
| minHeight(0), | minHeight(0), | ||||
| bgColor(0), | |||||
| fgColor(0), | |||||
| callbacksPtr(nullptr), | callbacksPtr(nullptr), | ||||
| editParamCallbackFunc(nullptr), | editParamCallbackFunc(nullptr), | ||||
| setParamCallbackFunc(nullptr), | setParamCallbackFunc(nullptr), | ||||
| setStateCallbackFunc(nullptr), | setStateCallbackFunc(nullptr), | ||||
| sendNoteCallbackFunc(nullptr), | sendNoteCallbackFunc(nullptr), | ||||
| setSizeCallbackFunc(nullptr) | |||||
| setSizeCallbackFunc(nullptr), | |||||
| fileRequestCallbackFunc(nullptr) | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | ||||
| @@ -144,6 +151,16 @@ struct UI::PrivateData { | |||||
| if (setSizeCallbackFunc != nullptr) | if (setSizeCallbackFunc != nullptr) | ||||
| setSizeCallbackFunc(callbacksPtr, width, height); | setSizeCallbackFunc(callbacksPtr, width, height); | ||||
| } | } | ||||
| bool fileRequestCallback(const char* key) | |||||
| { | |||||
| if (fileRequestCallbackFunc != nullptr) | |||||
| return fileRequestCallbackFunc(callbacksPtr, key); | |||||
| // TODO use old style DPF dialog here | |||||
| return false; | |||||
| } | |||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -258,9 +275,12 @@ public: | |||||
| const setStateFunc setStateCall, | const setStateFunc setStateCall, | ||||
| const sendNoteFunc sendNoteCall, | const sendNoteFunc sendNoteCall, | ||||
| const setSizeFunc setSizeCall, | const setSizeFunc setSizeCall, | ||||
| const float scaleFactor = 1.0f, | |||||
| const fileRequestFunc fileRequestCall, | |||||
| const char* const bundlePath = nullptr, | |||||
| void* const dspPtr = nullptr, | void* const dspPtr = nullptr, | ||||
| const char* const bundlePath = nullptr) | |||||
| const float scaleFactor = 1.0f, | |||||
| const uint32_t bgColor = 0, | |||||
| const uint32_t fgColor = 0xffffffff) | |||||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | ||||
| : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)), | : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)), | ||||
| #else | #else | ||||
| @@ -274,12 +294,16 @@ public: | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | ||||
| fData->callbacksPtr = callbacksPtr; | |||||
| fData->editParamCallbackFunc = editParamCall; | |||||
| fData->setParamCallbackFunc = setParamCall; | |||||
| fData->setStateCallbackFunc = setStateCall; | |||||
| fData->sendNoteCallbackFunc = sendNoteCall; | |||||
| fData->setSizeCallbackFunc = setSizeCall; | |||||
| fData->bgColor = bgColor; | |||||
| fData->fgColor = fgColor; | |||||
| fData->callbacksPtr = callbacksPtr; | |||||
| fData->editParamCallbackFunc = editParamCall; | |||||
| fData->setParamCallbackFunc = setParamCall; | |||||
| fData->setStateCallbackFunc = setStateCall; | |||||
| fData->sendNoteCallbackFunc = sendNoteCall; | |||||
| fData->setSizeCallbackFunc = setSizeCall; | |||||
| fData->fileRequestCallbackFunc = fileRequestCall; | |||||
| #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | ||||
| // unused | // unused | ||||
| @@ -341,6 +365,20 @@ public: | |||||
| } | } | ||||
| #endif | #endif | ||||
| uint getBackgroundColor() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->bgColor; | |||||
| } | |||||
| uint getForegroundColor() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0xffffffff); | |||||
| return fData->fgColor; | |||||
| } | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| uint32_t getParameterOffset() const noexcept | uint32_t getParameterOffset() const noexcept | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -25,6 +25,7 @@ | |||||
| #include "lv2/midi.h" | #include "lv2/midi.h" | ||||
| #include "lv2/options.h" | #include "lv2/options.h" | ||||
| #include "lv2/parameters.h" | #include "lv2/parameters.h" | ||||
| #include "lv2/patch.h" | |||||
| #include "lv2/ui.h" | #include "lv2/ui.h" | ||||
| #include "lv2/urid.h" | #include "lv2/urid.h" | ||||
| #include "lv2/lv2_kxstudio_properties.h" | #include "lv2/lv2_kxstudio_properties.h" | ||||
| @@ -47,22 +48,52 @@ static const sendNoteFunc sendNoteCallback = nullptr; | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| template <class LV2F> | |||||
| static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri) | |||||
| { | |||||
| for (int i=0; features[i] != nullptr; ++i) | |||||
| { | |||||
| if (std::strcmp(features[i]->URI, uri) == 0) | |||||
| return (const LV2F*)features[i]->data; | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| class UiLv2 | class UiLv2 | ||||
| { | { | ||||
| public: | public: | ||||
| UiLv2(const char* const bundlePath, const intptr_t winId, | |||||
| const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, | |||||
| const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, | |||||
| const float scaleFactor, LV2UI_Widget* const widget, void* const dspPtr) | |||||
| : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, dspPtr, bundlePath), | |||||
| UiLv2(const char* const bundlePath, | |||||
| const intptr_t winId, | |||||
| const LV2_Options_Option* options, | |||||
| const LV2_URID_Map* const uridMap, | |||||
| const LV2_Feature* const* const features, | |||||
| const LV2UI_Controller controller, | |||||
| const LV2UI_Write_Function writeFunc, | |||||
| LV2UI_Widget* const widget, | |||||
| void* const dspPtr, | |||||
| const float scaleFactor, | |||||
| const uint32_t bgColor, | |||||
| const uint32_t fgColor) | |||||
| : fUI(this, winId, | |||||
| editParameterCallback, | |||||
| setParameterCallback, | |||||
| setStateCallback, | |||||
| sendNoteCallback, | |||||
| setSizeCallback, | |||||
| fileRequestCallback, | |||||
| bundlePath, | |||||
| dspPtr, | |||||
| scaleFactor, | |||||
| bgColor, | |||||
| fgColor), | |||||
| fUridMap(uridMap), | fUridMap(uridMap), | ||||
| fUiResize(uiResz), | |||||
| fUiTouch(uiTouch), | |||||
| fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | |||||
| fUiResize(getLv2Feature<LV2UI_Resize>(features, LV2_UI__resize)), | |||||
| fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | |||||
| fController(controller), | fController(controller), | ||||
| fWriteFunction(writeFunc), | fWriteFunction(writeFunc), | ||||
| fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||||
| fMidiEventURID(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||||
| fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||||
| fURIDs(uridMap), | |||||
| fWinIdWasNull(winId == 0) | fWinIdWasNull(winId == 0) | ||||
| { | { | ||||
| if (fUiResize != nullptr && winId != 0) | if (fUiResize != nullptr && winId != 0) | ||||
| @@ -82,8 +113,8 @@ public: | |||||
| // if winId == 0 then options must not be null | // if winId == 0 then options must not be null | ||||
| DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | ||||
| const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle)); | |||||
| const LV2_URID uridTransientWinId(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId)); | |||||
| const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle); | |||||
| const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId); | |||||
| bool hasTitle = false; | bool hasTitle = false; | ||||
| @@ -91,7 +122,7 @@ public: | |||||
| { | { | ||||
| if (options[i].key == uridTransientWinId) | if (options[i].key == uridTransientWinId) | ||||
| { | { | ||||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Long)) | |||||
| if (options[i].type == fURIDs.atomLong) | |||||
| { | { | ||||
| if (const int64_t transientWinId = *(const int64_t*)options[i].value) | if (const int64_t transientWinId = *(const int64_t*)options[i].value) | ||||
| fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); | fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); | ||||
| @@ -101,7 +132,7 @@ public: | |||||
| } | } | ||||
| else if (options[i].key == uridWindowTitle) | else if (options[i].key == uridWindowTitle) | ||||
| { | { | ||||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__String)) | |||||
| if (options[i].type == fURIDs.atomString) | |||||
| { | { | ||||
| if (const char* const windowTitle = (const char*)options[i].value) | if (const char* const windowTitle = (const char*)options[i].value) | ||||
| { | { | ||||
| @@ -124,27 +155,32 @@ public: | |||||
| { | { | ||||
| if (format == 0) | if (format == 0) | ||||
| { | { | ||||
| const uint32_t parameterOffset(fUI.getParameterOffset()); | |||||
| const uint32_t parameterOffset = fUI.getParameterOffset(); | |||||
| if (rindex < parameterOffset) | if (rindex < parameterOffset) | ||||
| return; | return; | ||||
| DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | ||||
| const float value(*(const float*)buffer); | |||||
| const float value = *(const float*)buffer; | |||||
| fUI.parameterChanged(rindex-parameterOffset, value); | fUI.parameterChanged(rindex-parameterOffset, value); | ||||
| } | } | ||||
| #if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
| else if (format == fEventTransferURID) | |||||
| else if (format == fURIDs.atomEventTransfer) | |||||
| { | { | ||||
| const LV2_Atom* const atom((const LV2_Atom*)buffer); | |||||
| const LV2_Atom* const atom = (const LV2_Atom*)buffer; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(atom->type == fKeyValueURID,); | |||||
| const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||||
| const char* const value = key+(std::strlen(key)+1); | |||||
| if (atom->type == fURIDs.dpfKeyValue) | |||||
| { | |||||
| const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||||
| const char* const value = key+(std::strlen(key)+1); | |||||
| fUI.stateChanged(key, value); | |||||
| fUI.stateChanged(key, value); | |||||
| } | |||||
| else | |||||
| { | |||||
| d_stdout("received atom not dpfKeyValue"); | |||||
| } | |||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -187,11 +223,11 @@ public: | |||||
| { | { | ||||
| for (int i=0; options[i].key != 0; ++i) | for (int i=0; options[i].key != 0; ++i) | ||||
| { | { | ||||
| if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate)) | |||||
| if (options[i].key == fURIDs.paramSampleRate) | |||||
| { | { | ||||
| if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Float)) | |||||
| if (options[i].type == fURIDs.atomFloat) | |||||
| { | { | ||||
| const float sampleRate(*(const float*)options[i].value); | |||||
| const float sampleRate = *(const float*)options[i].value; | |||||
| fUI.setSampleRate(sampleRate); | fUI.setSampleRate(sampleRate); | ||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -211,7 +247,7 @@ public: | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | #if DISTRHO_PLUGIN_WANT_PROGRAMS | ||||
| void lv2ui_select_program(const uint32_t bank, const uint32_t program) | void lv2ui_select_program(const uint32_t bank, const uint32_t program) | ||||
| { | { | ||||
| const uint32_t realProgram(bank * 128 + program); | |||||
| const uint32_t realProgram = bank * 128 + program; | |||||
| fUI.programLoaded(realProgram); | fUI.programLoaded(realProgram); | ||||
| } | } | ||||
| @@ -237,7 +273,7 @@ protected: | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | ||||
| const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||||
| const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| // join key and value | // join key and value | ||||
| String tmpStr; | String tmpStr; | ||||
| @@ -248,23 +284,23 @@ protected: | |||||
| tmpStr[std::strlen(key)] = '\0'; | tmpStr[std::strlen(key)] = '\0'; | ||||
| // set msg size (key + separator + value + null terminator) | // set msg size (key + separator + value + null terminator) | ||||
| const size_t msgSize(tmpStr.length()+1); | |||||
| const size_t msgSize = tmpStr.length() + 1U; | |||||
| // reserve atom space | // reserve atom space | ||||
| const size_t atomSize(sizeof(LV2_Atom) + msgSize); | |||||
| const size_t atomSize = sizeof(LV2_Atom) + msgSize; | |||||
| char atomBuf[atomSize]; | char atomBuf[atomSize]; | ||||
| std::memset(atomBuf, 0, atomSize); | std::memset(atomBuf, 0, atomSize); | ||||
| // set atom info | // set atom info | ||||
| LV2_Atom* const atom((LV2_Atom*)atomBuf); | |||||
| LV2_Atom* const atom = (LV2_Atom*)atomBuf; | |||||
| atom->size = msgSize; | atom->size = msgSize; | ||||
| atom->type = fKeyValueURID; | |||||
| atom->type = fURIDs.dpfKeyValue; | |||||
| // set atom data | // set atom data | ||||
| std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); | std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); | ||||
| // send to DSP side | // send to DSP side | ||||
| fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||||
| fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom); | |||||
| } | } | ||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
| @@ -275,18 +311,19 @@ protected: | |||||
| if (channel > 0xF) | if (channel > 0xF) | ||||
| return; | return; | ||||
| const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||||
| const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| LV2_Atom_MidiEvent atomMidiEvent; | LV2_Atom_MidiEvent atomMidiEvent; | ||||
| atomMidiEvent.atom.size = 3; | atomMidiEvent.atom.size = 3; | ||||
| atomMidiEvent.atom.type = fMidiEventURID; | |||||
| atomMidiEvent.atom.type = fURIDs.midiEvent; | |||||
| atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); | atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); | ||||
| atomMidiEvent.data[1] = note; | atomMidiEvent.data[1] = note; | ||||
| atomMidiEvent.data[2] = velocity; | atomMidiEvent.data[2] = velocity; | ||||
| // send to DSP side | // send to DSP side | ||||
| fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fEventTransferURID, &atomMidiEvent); | |||||
| fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), | |||||
| fURIDs.atomEventTransfer, &atomMidiEvent); | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -298,22 +335,68 @@ protected: | |||||
| fUiResize->ui_resize(fUiResize->handle, width, height); | fUiResize->ui_resize(fUiResize->handle, width, height); | ||||
| } | } | ||||
| bool fileRequest(const char* const key) | |||||
| { | |||||
| d_stdout("UI file request %s %p", key, fUiRequestValue); | |||||
| if (fUiRequestValue == nullptr) | |||||
| return false; | |||||
| String dpf_lv2_key(DISTRHO_PLUGIN_URI "#"); | |||||
| dpf_lv2_key += key; | |||||
| const int r = fUiRequestValue->request(fUiRequestValue->handle, | |||||
| fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||||
| fURIDs.atomPath, | |||||
| nullptr); | |||||
| d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r); | |||||
| return r == LV2UI_REQUEST_VALUE_SUCCESS; | |||||
| } | |||||
| private: | private: | ||||
| UIExporter fUI; | UIExporter fUI; | ||||
| // LV2 features | // LV2 features | ||||
| const LV2_URID_Map* const fUridMap; | |||||
| const LV2UI_Resize* const fUiResize; | |||||
| const LV2UI_Touch* const fUiTouch; | |||||
| const LV2_URID_Map* const fUridMap; | |||||
| const LV2UI_Request_Value* const fUiRequestValue; | |||||
| const LV2UI_Resize* const fUiResize; | |||||
| const LV2UI_Touch* const fUiTouch; | |||||
| // LV2 UI stuff | // LV2 UI stuff | ||||
| const LV2UI_Controller fController; | const LV2UI_Controller fController; | ||||
| const LV2UI_Write_Function fWriteFunction; | const LV2UI_Write_Function fWriteFunction; | ||||
| // Need to save this | |||||
| const LV2_URID fEventTransferURID; | |||||
| const LV2_URID fMidiEventURID; | |||||
| const LV2_URID fKeyValueURID; | |||||
| // LV2 URIDs | |||||
| const struct URIDs { | |||||
| const LV2_URID_Map* _uridMap; | |||||
| LV2_URID dpfKeyValue; | |||||
| LV2_URID atomEventTransfer; | |||||
| LV2_URID atomFloat; | |||||
| LV2_URID atomLong; | |||||
| LV2_URID atomPath; | |||||
| LV2_URID atomString; | |||||
| LV2_URID midiEvent; | |||||
| LV2_URID paramSampleRate; | |||||
| LV2_URID patchSet; | |||||
| URIDs(const LV2_URID_Map* const uridMap) | |||||
| : _uridMap(uridMap), | |||||
| dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||||
| atomEventTransfer(map(LV2_ATOM__eventTransfer)), | |||||
| atomFloat(map(LV2_ATOM__Float)), | |||||
| atomLong(map(LV2_ATOM__Long)), | |||||
| atomPath(map(LV2_ATOM__Path)), | |||||
| atomString(map(LV2_ATOM__String)), | |||||
| midiEvent(map(LV2_MIDI__MidiEvent)), | |||||
| paramSampleRate(map(LV2_PARAMETERS__sampleRate)), | |||||
| patchSet(map(LV2_PATCH__Set)) {} | |||||
| inline LV2_URID map(const char* const uri) const | |||||
| { | |||||
| return _uridMap->map(_uridMap->handle, uri); | |||||
| } | |||||
| } fURIDs; | |||||
| // using ui:showInterface if true | // using ui:showInterface if true | ||||
| bool fWinIdWasNull; | bool fWinIdWasNull; | ||||
| @@ -350,13 +433,23 @@ private: | |||||
| uiPtr->setSize(width, height); | uiPtr->setSize(width, height); | ||||
| } | } | ||||
| static bool fileRequestCallback(void* ptr, const char* key) | |||||
| { | |||||
| return uiPtr->fileRequest(key); | |||||
| } | |||||
| #undef uiPtr | #undef uiPtr | ||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char* bundlePath, | |||||
| LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) | |||||
| static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||||
| const char* const uri, | |||||
| const char* const bundlePath, | |||||
| const LV2UI_Write_Function writeFunction, | |||||
| const LV2UI_Controller controller, | |||||
| LV2UI_Widget* const widget, | |||||
| const LV2_Feature* const* const features) | |||||
| { | { | ||||
| if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | ||||
| { | { | ||||
| @@ -364,12 +457,10 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| const LV2_Options_Option* options = nullptr; | |||||
| const LV2_URID_Map* uridMap = nullptr; | |||||
| const LV2UI_Resize* uiResize = nullptr; | |||||
| const LV2UI_Touch* uiTouch = nullptr; | |||||
| void* parentId = nullptr; | |||||
| void* instance = nullptr; | |||||
| const LV2_Options_Option* options = nullptr; | |||||
| const LV2_URID_Map* uridMap = nullptr; | |||||
| void* parentId = nullptr; | |||||
| void* instance = nullptr; | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
| struct LV2_DirectAccess_Interface { | struct LV2_DirectAccess_Interface { | ||||
| @@ -380,16 +471,12 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||||
| for (int i=0; features[i] != nullptr; ++i) | for (int i=0; features[i] != nullptr; ++i) | ||||
| { | { | ||||
| if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||||
| /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||||
| options = (const LV2_Options_Option*)features[i]->data; | options = (const LV2_Options_Option*)features[i]->data; | ||||
| else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | ||||
| uridMap = (const LV2_URID_Map*)features[i]->data; | uridMap = (const LV2_URID_Map*)features[i]->data; | ||||
| else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) | |||||
| uiResize = (const LV2UI_Resize*)features[i]->data; | |||||
| else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) | else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) | ||||
| parentId = features[i]->data; | parentId = features[i]->data; | ||||
| else if (std::strcmp(features[i]->URI, LV2_UI__touch) == 0) | |||||
| uiTouch = (const LV2UI_Touch*)features[i]->data; | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
| else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) | else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) | ||||
| extData = (const LV2_Extension_Data_Feature*)features[i]->data; | extData = (const LV2_Extension_Data_Feature*)features[i]->data; | ||||
| @@ -434,14 +521,19 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||||
| } | } | ||||
| #endif | #endif | ||||
| const intptr_t winId = (intptr_t)parentId; | |||||
| float scaleFactor = 1.0f; | float scaleFactor = 1.0f; | ||||
| const intptr_t winId((intptr_t)parentId); | |||||
| uint32_t bgColor = 0; | |||||
| uint32_t fgColor = 0xffffffff; | |||||
| if (options != nullptr) | if (options != nullptr) | ||||
| { | { | ||||
| const LV2_URID uridAtomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float)); | |||||
| const LV2_URID uridSampleRate(uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate)); | |||||
| const LV2_URID uridScaleFactor(uridMap->map(uridMap->handle, LV2_UI__scaleFactor)); | |||||
| const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); | |||||
| const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); | |||||
| const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate); | |||||
| const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor); | |||||
| const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor); | |||||
| const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor); | |||||
| for (int i=0; options[i].key != 0; ++i) | for (int i=0; options[i].key != 0; ++i) | ||||
| { | { | ||||
| @@ -459,6 +551,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||||
| else | else | ||||
| d_stderr("Host provides UI scale factor but has wrong value type"); | d_stderr("Host provides UI scale factor but has wrong value type"); | ||||
| } | } | ||||
| else if (options[i].key == uridBgColor) | |||||
| { | |||||
| if (options[i].type == uridAtomInt) | |||||
| bgColor = (uint32_t)*(const int32_t*)options[i].value; | |||||
| else | |||||
| d_stderr("Host provides UI background color but has wrong value type"); | |||||
| } | |||||
| else if (options[i].key == uridFgColor) | |||||
| { | |||||
| if (options[i].type == uridAtomInt) | |||||
| fgColor = (uint32_t)*(const int32_t*)options[i].value; | |||||
| else | |||||
| d_stderr("Host provides UI foreground color but has wrong value type"); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -468,7 +574,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||||
| d_lastUiSampleRate = 44100.0; | d_lastUiSampleRate = 44100.0; | ||||
| } | } | ||||
| return new UiLv2(bundlePath, winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, scaleFactor, widget, instance); | |||||
| return new UiLv2(bundlePath, winId, options, uridMap, features, | |||||
| controller, writeFunction, widget, instance, | |||||
| scaleFactor, bgColor, fgColor); | |||||
| } | } | ||||
| #define uiPtr ((UiLv2*)ui) | #define uiPtr ((UiLv2*)ui) | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| LV2 UI Extension | LV2 UI Extension | ||||
| Copyright 2009-2014 David Robillard <d@drobilla.net> | |||||
| Copyright 2009-2016 David Robillard <d@drobilla.net> | |||||
| Copyright 2006-2011 Lars Luthman <lars.luthman@gmail.com> | Copyright 2006-2011 Lars Luthman <lars.luthman@gmail.com> | ||||
| Permission to use, copy, modify, and/or distribute this software for any | Permission to use, copy, modify, and/or distribute this software for any | ||||
| @@ -17,9 +17,12 @@ | |||||
| */ | */ | ||||
| /** | /** | ||||
| @file ui.h User Interface API. | |||||
| @defgroup ui User Interfaces | |||||
| For high-level documentation, see <http://lv2plug.in/ns/extensions/ui>. | |||||
| User interfaces of any type for plugins, | |||||
| <http://lv2plug.in/ns/extensions/ui> for details. | |||||
| @{ | |||||
| */ | */ | ||||
| #ifndef LV2_UI_H | #ifndef LV2_UI_H | ||||
| @@ -28,39 +31,48 @@ | |||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "lv2.h" | #include "lv2.h" | ||||
| #define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" | |||||
| #define LV2_UI_PREFIX LV2_UI_URI "#" | |||||
| #define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" | |||||
| #define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" | |||||
| #define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" | |||||
| #define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" | |||||
| #define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" | |||||
| #define LV2_UI__UI LV2_UI_PREFIX "UI" | |||||
| #define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" | |||||
| #define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" | |||||
| #define LV2_UI__binary LV2_UI_PREFIX "binary" | |||||
| #define LV2_UI__fixedSize LV2_UI_PREFIX "fixedSize" | |||||
| #define LV2_UI__idleInterface LV2_UI_PREFIX "idleInterface" | |||||
| #define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" | |||||
| #define LV2_UI__notifyType LV2_UI_PREFIX "notifyType" | |||||
| #define LV2_UI__parent LV2_UI_PREFIX "parent" | |||||
| #define LV2_UI__plugin LV2_UI_PREFIX "plugin" | |||||
| #define LV2_UI__portIndex LV2_UI_PREFIX "portIndex" | |||||
| #define LV2_UI__portMap LV2_UI_PREFIX "portMap" | |||||
| #define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" | |||||
| #define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" | |||||
| #define LV2_UI__resize LV2_UI_PREFIX "resize" | |||||
| #define LV2_UI__scaleFactor LV2_UI_PREFIX "scaleFactor" | |||||
| #define LV2_UI__showInterface LV2_UI_PREFIX "showInterface" | |||||
| #define LV2_UI__touch LV2_UI_PREFIX "touch" | |||||
| #define LV2_UI__ui LV2_UI_PREFIX "ui" | |||||
| #define LV2_UI__updateRate LV2_UI_PREFIX "updateRate" | |||||
| #define LV2_UI__windowTitle LV2_UI_PREFIX "windowTitle" | |||||
| #include "urid.h" | |||||
| #define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" ///< http://lv2plug.in/ns/extensions/ui | |||||
| #define LV2_UI_PREFIX LV2_UI_URI "#" ///< http://lv2plug.in/ns/extensions/ui# | |||||
| #define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" ///< http://lv2plug.in/ns/extensions/ui#CocoaUI | |||||
| #define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" ///< http://lv2plug.in/ns/extensions/ui#Gtk3UI | |||||
| #define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" ///< http://lv2plug.in/ns/extensions/ui#GtkUI | |||||
| #define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" ///< http://lv2plug.in/ns/extensions/ui#PortNotification | |||||
| #define LV2_UI__PortProtocol LV2_UI_PREFIX "PortProtocol" ///< http://lv2plug.in/ns/extensions/ui#PortProtocol | |||||
| #define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" ///< http://lv2plug.in/ns/extensions/ui#Qt4UI | |||||
| #define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" ///< http://lv2plug.in/ns/extensions/ui#Qt5UI | |||||
| #define LV2_UI__UI LV2_UI_PREFIX "UI" ///< http://lv2plug.in/ns/extensions/ui#UI | |||||
| #define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" ///< http://lv2plug.in/ns/extensions/ui#WindowsUI | |||||
| #define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" ///< http://lv2plug.in/ns/extensions/ui#X11UI | |||||
| #define LV2_UI__backgroundColor LV2_UI_PREFIX "backgroundColor" ///< http://lv2plug.in/ns/extensions/ui#backgroundColor | |||||
| #define LV2_UI__binary LV2_UI_PREFIX "binary" ///< http://lv2plug.in/ns/extensions/ui#binary | |||||
| #define LV2_UI__fixedSize LV2_UI_PREFIX "fixedSize" ///< http://lv2plug.in/ns/extensions/ui#fixedSize | |||||
| #define LV2_UI__foregroundColor LV2_UI_PREFIX "foregroundColor" ///< http://lv2plug.in/ns/extensions/ui#foregroundColor | |||||
| #define LV2_UI__idleInterface LV2_UI_PREFIX "idleInterface" ///< http://lv2plug.in/ns/extensions/ui#idleInterface | |||||
| #define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" ///< http://lv2plug.in/ns/extensions/ui#noUserResize | |||||
| #define LV2_UI__notifyType LV2_UI_PREFIX "notifyType" ///< http://lv2plug.in/ns/extensions/ui#notifyType | |||||
| #define LV2_UI__parent LV2_UI_PREFIX "parent" ///< http://lv2plug.in/ns/extensions/ui#parent | |||||
| #define LV2_UI__plugin LV2_UI_PREFIX "plugin" ///< http://lv2plug.in/ns/extensions/ui#plugin | |||||
| #define LV2_UI__portIndex LV2_UI_PREFIX "portIndex" ///< http://lv2plug.in/ns/extensions/ui#portIndex | |||||
| #define LV2_UI__portMap LV2_UI_PREFIX "portMap" ///< http://lv2plug.in/ns/extensions/ui#portMap | |||||
| #define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" ///< http://lv2plug.in/ns/extensions/ui#portNotification | |||||
| #define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" ///< http://lv2plug.in/ns/extensions/ui#portSubscribe | |||||
| #define LV2_UI__protocol LV2_UI_PREFIX "protocol" ///< http://lv2plug.in/ns/extensions/ui#protocol | |||||
| #define LV2_UI__floatProtocol LV2_UI_PREFIX "floatProtocol" ///< http://lv2plug.in/ns/extensions/ui#floatProtocol | |||||
| #define LV2_UI__peakProtocol LV2_UI_PREFIX "peakProtocol" ///< http://lv2plug.in/ns/extensions/ui#peakProtocol | |||||
| #define LV2_UI__requestValue LV2_UI_PREFIX "requestValue" ///< http://lv2plug.in/ns/extensions/ui#requestValue | |||||
| #define LV2_UI__resize LV2_UI_PREFIX "resize" ///< http://lv2plug.in/ns/extensions/ui#resize | |||||
| #define LV2_UI__scaleFactor LV2_UI_PREFIX "scaleFactor" ///< http://lv2plug.in/ns/extensions/ui#scaleFactor | |||||
| #define LV2_UI__showInterface LV2_UI_PREFIX "showInterface" ///< http://lv2plug.in/ns/extensions/ui#showInterface | |||||
| #define LV2_UI__touch LV2_UI_PREFIX "touch" ///< http://lv2plug.in/ns/extensions/ui#touch | |||||
| #define LV2_UI__ui LV2_UI_PREFIX "ui" ///< http://lv2plug.in/ns/extensions/ui#ui | |||||
| #define LV2_UI__updateRate LV2_UI_PREFIX "updateRate" ///< http://lv2plug.in/ns/extensions/ui#updateRate | |||||
| #define LV2_UI__windowTitle LV2_UI_PREFIX "windowTitle" ///< http://lv2plug.in/ns/extensions/ui#windowTitle | |||||
| /** | /** | ||||
| The index returned by LV2_UI_Port_Port::port_index() for unknown ports. | |||||
| The index returned by LV2UI_Port_Map::port_index() for unknown ports. | |||||
| */ | */ | ||||
| #define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) | #define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) | ||||
| @@ -99,18 +111,21 @@ typedef void* LV2UI_Feature_Handle; | |||||
| /** | /** | ||||
| A host-provided function that sends data to a plugin's input ports. | A host-provided function that sends data to a plugin's input ports. | ||||
| The @p buffer parameter must point to a block of data, @p buffer_size bytes | |||||
| large. The format of this data and how the host should use it is defined by | |||||
| the @p port_protocol. This buffer is owned by the UI and is only valid for | |||||
| the duration of this call. | |||||
| @param controller The opaque controller pointer passed to | |||||
| LV2UI_Descriptor::instantiate(). | |||||
| @param port_index Index of the port to update. | |||||
| The @p port_protocol parameter should either be 0 or the URID for a | |||||
| ui:PortProtocol. If it is 0, the protocol is implicitly ui:floatProtocol, | |||||
| the port MUST be an lv2:ControlPort input, @p buffer MUST point to a single | |||||
| float value, and @p buffer_size MUST be sizeof(float). | |||||
| @param buffer Buffer containing `buffer_size` bytes of data. | |||||
| The UI SHOULD NOT use a protocol not supported by the host, but the host | |||||
| MUST gracefully ignore any protocol it does not understand. | |||||
| @param buffer_size Size of `buffer` in bytes. | |||||
| @param port_protocol Either 0 or the URID for a ui:PortProtocol. If 0, the | |||||
| protocol is implicitly ui:floatProtocol, the port MUST be an lv2:ControlPort | |||||
| input, `buffer` MUST point to a single float value, and `buffer_size` MUST | |||||
| be sizeof(float). The UI SHOULD NOT use a protocol not supported by the | |||||
| host, but the host MUST gracefully ignore any protocol it does not | |||||
| understand. | |||||
| */ | */ | ||||
| typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, | typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, | ||||
| uint32_t port_index, | uint32_t port_index, | ||||
| @@ -144,7 +159,7 @@ typedef struct _LV2UI_Descriptor { | |||||
| @param write_function A function that the UI can use to send data to the | @param write_function A function that the UI can use to send data to the | ||||
| plugin's input ports. | plugin's input ports. | ||||
| @param controller A handle for the plugin instance to be passed as the | |||||
| @param controller A handle for the UI instance to be passed as the | |||||
| first parameter of UI methods. | first parameter of UI methods. | ||||
| @param widget (output) widget pointer. The UI points this at its main | @param widget (output) widget pointer. The UI points this at its main | ||||
| @@ -174,18 +189,18 @@ typedef struct _LV2UI_Descriptor { | |||||
| /** | /** | ||||
| Tell the UI that something interesting has happened at a plugin port. | Tell the UI that something interesting has happened at a plugin port. | ||||
| What is "interesting" and how it is written to @p buffer is defined by | |||||
| @p format, which has the same meaning as in LV2UI_Write_Function(). | |||||
| What is "interesting" and how it is written to `buffer` is defined by | |||||
| `format`, which has the same meaning as in LV2UI_Write_Function(). | |||||
| Format 0 is a special case for lv2:ControlPort, where this function | Format 0 is a special case for lv2:ControlPort, where this function | ||||
| should be called when the port value changes (but not necessarily for | should be called when the port value changes (but not necessarily for | ||||
| every change), @p buffer_size must be sizeof(float), and @p buffer | |||||
| every change), `buffer_size` must be sizeof(float), and `buffer` | |||||
| points to a single IEEE-754 float. | points to a single IEEE-754 float. | ||||
| By default, the host should only call this function for lv2:ControlPort | By default, the host should only call this function for lv2:ControlPort | ||||
| inputs. However, the UI can request updates for other ports statically | inputs. However, the UI can request updates for other ports statically | ||||
| with ui:portNotification or dynamicaly with ui:portSubscribe. | with ui:portNotification or dynamicaly with ui:portSubscribe. | ||||
| The UI MUST NOT retain any reference to @p buffer after this function | |||||
| The UI MUST NOT retain any reference to `buffer` after this function | |||||
| returns, it is only valid for the duration of the call. | returns, it is only valid for the duration of the call. | ||||
| This member may be NULL if the UI is not interested in any port events. | This member may be NULL if the UI is not interested in any port events. | ||||
| @@ -202,7 +217,7 @@ typedef struct _LV2UI_Descriptor { | |||||
| This member may be set to NULL if the UI is not interested in supporting | This member may be set to NULL if the UI is not interested in supporting | ||||
| any extensions. This is similar to LV2_Descriptor::extension_data(). | any extensions. This is similar to LV2_Descriptor::extension_data(). | ||||
| */ | */ | ||||
| const void* (*extension_data)(const char* uri); | const void* (*extension_data)(const char* uri); | ||||
| } LV2UI_Descriptor; | } LV2UI_Descriptor; | ||||
| @@ -227,7 +242,8 @@ typedef struct _LV2UI_Resize { | |||||
| host about the size of the UI. | host about the size of the UI. | ||||
| When provided by the UI, the host may call this function to notify the | When provided by the UI, the host may call this function to notify the | ||||
| UI that it should change its size accordingly. | |||||
| UI that it should change its size accordingly. In this case, the host | |||||
| must pass the LV2UI_Handle to provide access to the UI instance. | |||||
| @return 0 on success. | @return 0 on success. | ||||
| */ | */ | ||||
| @@ -248,7 +264,7 @@ typedef struct _LV2UI_Port_Map { | |||||
| LV2UI_Feature_Handle handle; | LV2UI_Feature_Handle handle; | ||||
| /** | /** | ||||
| Get the index for the port with the given @p symbol. | |||||
| Get the index for the port with the given `symbol`. | |||||
| @return The index of the port, or LV2UI_INVALID_PORT_INDEX if no such | @return The index of the port, or LV2UI_INVALID_PORT_INDEX if no such | ||||
| port is found. | port is found. | ||||
| @@ -272,7 +288,7 @@ typedef struct _LV2UI_Port_Subscribe { | |||||
| This means that the host will call the UI's port_event() function when | This means that the host will call the UI's port_event() function when | ||||
| the port value changes (as defined by protocol). | the port value changes (as defined by protocol). | ||||
| Calling this function with the same @p port_index and @p port_protocol | |||||
| Calling this function with the same `port_index` and `port_protocol` | |||||
| as an already active subscription has no effect. | as an already active subscription has no effect. | ||||
| @param handle The handle field of this struct. | @param handle The handle field of this struct. | ||||
| @@ -292,7 +308,7 @@ typedef struct _LV2UI_Port_Subscribe { | |||||
| This means that the host will cease calling calling port_event() when | This means that the host will cease calling calling port_event() when | ||||
| the port value changes. | the port value changes. | ||||
| Calling this function with a @p port_index and @p port_protocol that | |||||
| Calling this function with a `port_index` and `port_protocol` that | |||||
| does not refer to an active port subscription has no effect. | does not refer to an active port subscription has no effect. | ||||
| @param handle The handle field of this struct. | @param handle The handle field of this struct. | ||||
| @@ -332,6 +348,94 @@ typedef struct _LV2UI_Touch { | |||||
| bool grabbed); | bool grabbed); | ||||
| } LV2UI_Touch; | } LV2UI_Touch; | ||||
| /** | |||||
| A status code for LV2UI_Request_Value::request. | |||||
| */ | |||||
| typedef enum { | |||||
| /** | |||||
| Completed successfully. | |||||
| The host will set the parameter later if the user choses a new value. | |||||
| */ | |||||
| LV2UI_REQUEST_VALUE_SUCCESS, | |||||
| /** | |||||
| Parameter already being requested. | |||||
| The host is already requesting a parameter from the user (for example, a | |||||
| dialog is visible), or the UI is otherwise busy and can not make this | |||||
| request. | |||||
| */ | |||||
| LV2UI_REQUEST_VALUE_BUSY, | |||||
| /** | |||||
| Unknown parameter. | |||||
| The host is not aware of this parameter, and is not able to set a new | |||||
| value for it. | |||||
| */ | |||||
| LV2UI_REQUEST_VALUE_ERR_UNKNOWN, | |||||
| /** | |||||
| Unsupported parameter. | |||||
| The host knows about this parameter, but does not support requesting a | |||||
| new value for it from the user. This is likely because the host does | |||||
| not have UI support for choosing a value with the appropriate type. | |||||
| */ | |||||
| LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED | |||||
| } LV2UI_Request_Value_Status; | |||||
| /** | |||||
| A feature to request a new parameter value from the host. | |||||
| */ | |||||
| typedef struct { | |||||
| /** | |||||
| Pointer to opaque data which must be passed to request(). | |||||
| */ | |||||
| LV2UI_Feature_Handle handle; | |||||
| /** | |||||
| Request a value for a parameter from the host. | |||||
| This is mainly used by UIs to request values for complex parameters that | |||||
| don't change often, such as file paths, but it may be used to request | |||||
| any parameter value. | |||||
| This function returns immediately, and the return value indicates | |||||
| whether the host can fulfill the request. The host may notify the | |||||
| plugin about the new parameter value, for example when a file is | |||||
| selected by the user, via the usual mechanism. Typically, the host will | |||||
| send a message to the plugin that sets the new parameter value, and the | |||||
| plugin will notify the UI via a message as usual for any other parameter | |||||
| change. | |||||
| To provide an appropriate UI, the host can determine details about the | |||||
| parameter from the plugin data as usual. The additional parameters of | |||||
| this function provide support for more advanced use cases, but in the | |||||
| simple common case, the plugin will simply pass the key of the desired | |||||
| parameter and zero for everything else. | |||||
| @param handle The handle field of this struct. | |||||
| @param key The URID of the parameter. | |||||
| @param type The optional type of the value to request. This can be used | |||||
| to request a specific value type for parameters that support several. | |||||
| If non-zero, it must be the URID of an instance of rdfs:Class or | |||||
| rdfs:Datatype. | |||||
| @param features Additional features for this request, or NULL. | |||||
| @return A status code which is 0 on success. | |||||
| */ | |||||
| LV2UI_Request_Value_Status (*request)(LV2UI_Feature_Handle handle, | |||||
| LV2_URID key, | |||||
| LV2_URID type, | |||||
| const LV2_Feature* const* features); | |||||
| } LV2UI_Request_Value; | |||||
| /** | /** | ||||
| UI Idle Interface (LV2_UI__idleInterface) | UI Idle Interface (LV2_UI__idleInterface) | ||||
| @@ -429,3 +533,7 @@ typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); | |||||
| #endif | #endif | ||||
| #endif /* LV2_UI_H */ | #endif /* LV2_UI_H */ | ||||
| /** | |||||
| @} | |||||
| */ | |||||
| @@ -0,0 +1,47 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2020 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. | |||||
| */ | |||||
| #ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| #define DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| #define DISTRHO_PLUGIN_BRAND "DISTRHO" | |||||
| #define DISTRHO_PLUGIN_NAME "FileHandling" | |||||
| #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/FileHandling" | |||||
| #define DISTRHO_PLUGIN_HAS_UI 1 | |||||
| #define DISTRHO_PLUGIN_IS_RT_SAFE 1 | |||||
| #define DISTRHO_PLUGIN_NUM_INPUTS 1 | |||||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 | |||||
| #define DISTRHO_PLUGIN_WANT_STATE 1 | |||||
| #define DISTRHO_PLUGIN_WANT_STATEFILES 1 | |||||
| #define DISTRHO_UI_USER_RESIZABLE 1 | |||||
| #define DISTRHO_UI_USE_NANOVG 1 | |||||
| enum Parameters { | |||||
| kParameterFileSize1 = 0, | |||||
| kParameterFileSize2, | |||||
| kParameterFileSize3, | |||||
| kParameterCount | |||||
| }; | |||||
| enum States { | |||||
| kStateFile1 = 0, | |||||
| kStateFile2, | |||||
| kStateFile3, | |||||
| kStateCount | |||||
| }; | |||||
| #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| @@ -0,0 +1,257 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2020 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. | |||||
| */ | |||||
| #include "DistrhoPlugin.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| /** | |||||
| Plugin to demonstrate File handling within DPF. | |||||
| */ | |||||
| class FileHandlingExamplePlugin : public Plugin | |||||
| { | |||||
| public: | |||||
| FileHandlingExamplePlugin() | |||||
| : Plugin(kParameterCount, 0, kStateCount) | |||||
| { | |||||
| std::memset(fParameters, 0, sizeof(fParameters)); | |||||
| } | |||||
| protected: | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Information */ | |||||
| /** | |||||
| Get the plugin label. | |||||
| This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. | |||||
| */ | |||||
| const char* getLabel() const override | |||||
| { | |||||
| return "FileHandling"; | |||||
| } | |||||
| /** | |||||
| Get an extensive comment/description about the plugin. | |||||
| */ | |||||
| const char* getDescription() const override | |||||
| { | |||||
| return "Plugin to demonstrate File handling."; | |||||
| } | |||||
| /** | |||||
| Get the plugin author/maker. | |||||
| */ | |||||
| const char* getMaker() const override | |||||
| { | |||||
| return "DISTRHO"; | |||||
| } | |||||
| /** | |||||
| Get the plugin homepage. | |||||
| */ | |||||
| const char* getHomePage() const override | |||||
| { | |||||
| return "https://github.com/DISTRHO/DPF"; | |||||
| } | |||||
| /** | |||||
| Get the plugin license name (a single line of text). | |||||
| For commercial plugins this should return some short copyright information. | |||||
| */ | |||||
| const char* getLicense() const override | |||||
| { | |||||
| return "ISC"; | |||||
| } | |||||
| /** | |||||
| Get the plugin version, in hexadecimal. | |||||
| */ | |||||
| uint32_t getVersion() const override | |||||
| { | |||||
| return d_version(0, 0, 0); | |||||
| } | |||||
| /** | |||||
| Get the plugin unique Id. | |||||
| This value is used by LADSPA, DSSI and VST plugin formats. | |||||
| */ | |||||
| int64_t getUniqueId() const override | |||||
| { | |||||
| return d_cconst('d', 'F', 'i', 'H'); | |||||
| } | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Init */ | |||||
| /** | |||||
| Initialize the parameter @a index. | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| void initParameter(uint32_t index, Parameter& param) override | |||||
| { | |||||
| param.hints = kParameterIsOutput|kParameterIsInteger; | |||||
| switch (index) | |||||
| { | |||||
| case kParameterFileSize1: | |||||
| param.name = "Size #1"; | |||||
| param.symbol = "size1"; | |||||
| break; | |||||
| case kParameterFileSize2: | |||||
| param.name = "Size #2"; | |||||
| param.symbol = "size2"; | |||||
| break; | |||||
| case kParameterFileSize3: | |||||
| param.name = "Size #3"; | |||||
| param.symbol = "size3"; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /** | |||||
| Set the state key and default value of @a index.@n | |||||
| This function will be called once, shortly after the plugin is created.@n | |||||
| Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. | |||||
| */ | |||||
| void initState(uint32_t index, String& stateKey, String& defaultStateValue) override | |||||
| { | |||||
| switch (index) | |||||
| { | |||||
| case kStateFile1: | |||||
| stateKey = "file1"; | |||||
| break; | |||||
| case kStateFile2: | |||||
| stateKey = "file2"; | |||||
| break; | |||||
| case kStateFile3: | |||||
| stateKey = "file3"; | |||||
| break; | |||||
| } | |||||
| defaultStateValue = ""; | |||||
| } | |||||
| /** | |||||
| TODO API under construction | |||||
| */ | |||||
| bool isStateFile(uint32_t index) override | |||||
| { | |||||
| switch (index) | |||||
| { | |||||
| case kStateFile1: | |||||
| case kStateFile2: | |||||
| case kStateFile3: | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Internal data */ | |||||
| /** | |||||
| Get the current value of a parameter. | |||||
| The host may call this function from any context, including realtime processing. | |||||
| We have no parameters in this plugin example, so we do nothing with the function. | |||||
| */ | |||||
| float getParameterValue(uint32_t index) const override | |||||
| { | |||||
| return fParameters[index]; | |||||
| } | |||||
| /** | |||||
| Change a parameter value.@n | |||||
| The host may call this function from any context, including realtime processing. | |||||
| This function will only be called for parameter inputs. | |||||
| Since we have no parameters inputs in this example, so we do nothing with the function. | |||||
| */ | |||||
| void setParameterValue(uint32_t, float) override {} | |||||
| /** | |||||
| Change an internal state @a key to @a value. | |||||
| */ | |||||
| void setState(const char* key, const char* value) override | |||||
| { | |||||
| d_stdout("DSP setState %s %s", key, value); | |||||
| int fileId = -1; | |||||
| /**/ if (std::strcmp(key, "file1") == 0) | |||||
| fileId = 0; | |||||
| else if (std::strcmp(key, "file2") == 0) | |||||
| fileId = 1; | |||||
| else if (std::strcmp(key, "file3") == 0) | |||||
| fileId = 2; | |||||
| if (fileId == -1) | |||||
| return; | |||||
| if (FILE* const fh = fopen(value, "r")) | |||||
| { | |||||
| fseek(fh, 0, SEEK_END); | |||||
| // filesize | |||||
| const long int size = ftell(fh); | |||||
| d_stdout("size of %s is %li", value, size); | |||||
| fParameters[kParameterFileSize1 + fileId] = static_cast<float>(size) / 1000.0f; | |||||
| fclose(fh); | |||||
| } | |||||
| } | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Audio/MIDI Processing */ | |||||
| /** | |||||
| Run/process function for plugins without MIDI input. | |||||
| @note Some parameters might be null if there are no audio inputs or outputs. | |||||
| */ | |||||
| void run(const float** inputs, float** outputs, uint32_t frames) override | |||||
| { | |||||
| /** | |||||
| This plugin doesn't do audio, it just demonstrates file handling usage. | |||||
| So here we directly copy inputs over outputs, leaving the audio untouched. | |||||
| We need to be careful in case the host re-uses the same buffer for both ins and outs. | |||||
| */ | |||||
| if (outputs[0] != inputs[0]) | |||||
| std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); | |||||
| } | |||||
| // ------------------------------------------------------------------------------------------------------- | |||||
| private: | |||||
| float fParameters[kParameterCount]; | |||||
| /** | |||||
| Set our plugin class as non-copyable and add a leak detector just in case. | |||||
| */ | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FileHandlingExamplePlugin) | |||||
| }; | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Plugin entry point, called by DPF to create a new plugin instance. */ | |||||
| Plugin* createPlugin() | |||||
| { | |||||
| return new FileHandlingExamplePlugin(); | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,269 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2020 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. | |||||
| */ | |||||
| #include "DistrhoUI.hpp" | |||||
| #include "extra/String.hpp" | |||||
| #include "DistrhoPluginInfo.h" | |||||
| #include "NanoButton.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| const char* kStateKeys[kStateCount] = { | |||||
| "file1", | |||||
| "file2", | |||||
| "file3", | |||||
| }; | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| static void setupButton(Button& btn, int y) | |||||
| { | |||||
| btn.setAbsolutePos(5, y); | |||||
| btn.setLabel("Open..."); | |||||
| btn.setSize(100, 30); | |||||
| } | |||||
| class FileHandlingExampleUI : public UI, | |||||
| public Button::Callback | |||||
| { | |||||
| public: | |||||
| static const uint kInitialWidth = 600; | |||||
| static const uint kInitialHeight = 350; | |||||
| FileHandlingExampleUI() | |||||
| : UI(kInitialWidth, kInitialHeight), | |||||
| fButton1(this, this), | |||||
| fButton2(this, this), | |||||
| fButton3(this, this), | |||||
| fScale(1.0f) | |||||
| { | |||||
| std::memset(fParameters, 0, sizeof(fParameters)); | |||||
| std::memset(fStrBuf, 0, sizeof(fStrBuf)); | |||||
| setupButton(fButton1, 5); | |||||
| setupButton(fButton2, 105); | |||||
| setupButton(fButton3, 205); | |||||
| #ifdef DGL_NO_SHARED_RESOURCES | |||||
| createFontFromFile("sans", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); | |||||
| #else | |||||
| loadSharedResources(); | |||||
| #endif | |||||
| setGeometryConstraints(kInitialWidth, kInitialHeight, true); | |||||
| } | |||||
| protected: | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * DSP/Plugin Callbacks */ | |||||
| /** | |||||
| A parameter has changed on the plugin side.@n | |||||
| This is called by the host to inform the UI about parameter changes. | |||||
| */ | |||||
| void parameterChanged(uint32_t index, float value) override | |||||
| { | |||||
| fParameters[index] = value; | |||||
| repaint(); | |||||
| } | |||||
| /** | |||||
| A state has changed on the plugin side.@n | |||||
| This is called by the host to inform the UI about state changes. | |||||
| */ | |||||
| void stateChanged(const char* key, const char* value) override | |||||
| { | |||||
| States stateId = kStateCount; | |||||
| /**/ if (std::strcmp(key, "file1") == 0) | |||||
| stateId = kStateFile1; | |||||
| else if (std::strcmp(key, "file2") == 0) | |||||
| stateId = kStateFile2; | |||||
| else if (std::strcmp(key, "file3") == 0) | |||||
| stateId = kStateFile3; | |||||
| if (stateId == kStateCount) | |||||
| return; | |||||
| fState[stateId] = value; | |||||
| repaint(); | |||||
| } | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Widget Callbacks */ | |||||
| /** | |||||
| The NanoVG drawing function. | |||||
| */ | |||||
| void onNanoDisplay() override | |||||
| { | |||||
| const float lineHeight = 20 * fScale; | |||||
| float y; | |||||
| fontSize(15.0f * fScale); | |||||
| textLineHeight(lineHeight); | |||||
| // --------------------------------------------------------------------------------------- | |||||
| // File 1 | |||||
| y = 45.0f * fScale; | |||||
| if (fState[kStateFile1].isNotEmpty()) | |||||
| { | |||||
| drawLeft(0.0f, y, "Name:"); | |||||
| drawRight(0.0f, y, fState[kStateFile1]); | |||||
| y += lineHeight; | |||||
| drawLeft(0.0f, y, "Size:"); | |||||
| drawRight(0.0f, y, getTextBufFileSize(fParameters[kParameterFileSize1])); | |||||
| y += lineHeight; | |||||
| } | |||||
| else | |||||
| { | |||||
| drawLeft(0.0f, y, "No file loaded"); | |||||
| } | |||||
| // --------------------------------------------------------------------------------------- | |||||
| // File 2 | |||||
| y = 145.0f * fScale; | |||||
| if (fState[kStateFile2].isNotEmpty()) | |||||
| { | |||||
| drawLeft(0.0f, y, "Name:"); | |||||
| drawRight(0.0f, y, fState[kStateFile2]); | |||||
| y += lineHeight; | |||||
| drawLeft(0.0f, y, "Size:"); | |||||
| drawRight(0.0f, y, getTextBufFileSize(fParameters[kParameterFileSize2])); | |||||
| y += lineHeight; | |||||
| } | |||||
| else | |||||
| { | |||||
| drawLeft(0.0f, y, "No file loaded"); | |||||
| } | |||||
| // --------------------------------------------------------------------------------------- | |||||
| // File 3 | |||||
| y = 245.0f * fScale; | |||||
| if (fState[kStateFile3].isNotEmpty()) | |||||
| { | |||||
| drawLeft(0.0f, y, "Name:"); | |||||
| drawRight(0.0f, y, fState[kStateFile3]); | |||||
| y += lineHeight; | |||||
| drawLeft(0.0f, y, "Size:"); | |||||
| drawRight(0.0f, y, getTextBufFileSize(fParameters[kParameterFileSize3])); | |||||
| y += lineHeight; | |||||
| } | |||||
| else | |||||
| { | |||||
| drawLeft(0.0f, y, "No file loaded"); | |||||
| } | |||||
| } | |||||
| void onResize(const ResizeEvent& ev) override | |||||
| { | |||||
| fScale = static_cast<float>(ev.size.getHeight())/kInitialHeight; | |||||
| UI::onResize(ev); | |||||
| } | |||||
| void buttonClicked(Button* const button, bool) override | |||||
| { | |||||
| States stateId = kStateCount; | |||||
| /**/ if (button == &fButton1) | |||||
| stateId = kStateFile1; | |||||
| else if (button == &fButton2) | |||||
| stateId = kStateFile2; | |||||
| else if (button == &fButton3) | |||||
| stateId = kStateFile3; | |||||
| if (stateId == kStateCount) | |||||
| return; | |||||
| requestStateFile(kStateKeys[stateId]); | |||||
| } | |||||
| // ------------------------------------------------------------------------------------------------------- | |||||
| private: | |||||
| // Parameters | |||||
| float fParameters[kParameterCount]; | |||||
| // State (files) | |||||
| String fState[kStateCount]; | |||||
| Button fButton1, fButton2, fButton3; | |||||
| // UI stuff | |||||
| float fScale; | |||||
| // temp buf for text | |||||
| char fStrBuf[0xff]; | |||||
| // helpers for putting text into fStrBuf and returning it | |||||
| const char* getTextBufFileSize(const float value) | |||||
| { | |||||
| /**/ if (value > 1024*1024) | |||||
| std::snprintf(fStrBuf, 0xfe, "%.2f GiB", value/(1024*1024)); | |||||
| else if (value > 1024) | |||||
| std::snprintf(fStrBuf, 0xfe, "%.2f MiB", value/1024); | |||||
| else | |||||
| std::snprintf(fStrBuf, 0xfe, "%.2f KiB", value); | |||||
| return fStrBuf; | |||||
| } | |||||
| // helpers for drawing text | |||||
| void drawLeft(const float x, const float y, const char* const text) | |||||
| { | |||||
| beginPath(); | |||||
| fillColor(200, 200, 200); | |||||
| textAlign(ALIGN_RIGHT|ALIGN_TOP); | |||||
| textBox(x, y, 100 * fScale, text); | |||||
| closePath(); | |||||
| } | |||||
| void drawRight(const float x, const float y, const char* const text) | |||||
| { | |||||
| beginPath(); | |||||
| fillColor(255, 255, 255); | |||||
| textAlign(ALIGN_LEFT|ALIGN_TOP); | |||||
| textBox(x + (105 * fScale), y, (kInitialWidth - x) * fScale, text); | |||||
| closePath(); | |||||
| } | |||||
| /** | |||||
| Set our UI class as non-copyable and add a leak detector just in case. | |||||
| */ | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FileHandlingExampleUI) | |||||
| }; | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * UI entry point, called by DPF to create a new UI instance. */ | |||||
| UI* createUI() | |||||
| { | |||||
| return new FileHandlingExampleUI(); | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,46 @@ | |||||
| #!/usr/bin/make -f | |||||
| # Makefile for DISTRHO Plugins # | |||||
| # ---------------------------- # | |||||
| # Created by falkTX | |||||
| # | |||||
| # -------------------------------------------------------------- | |||||
| # Project name, used for binaries | |||||
| NAME = d_files | |||||
| # -------------------------------------------------------------- | |||||
| # Files to build | |||||
| FILES_DSP = \ | |||||
| FileHandlingPlugin.cpp | |||||
| FILES_UI = \ | |||||
| FileHandlingUI.cpp \ | |||||
| NanoButton.cpp | |||||
| # -------------------------------------------------------------- | |||||
| # Do some magic | |||||
| include ../../Makefile.plugins.mk | |||||
| # -------------------------------------------------------------- | |||||
| # Enable all possible plugin types | |||||
| ifeq ($(HAVE_JACK),true) | |||||
| ifeq ($(HAVE_OPENGL),true) | |||||
| TARGETS += jack | |||||
| endif | |||||
| endif | |||||
| ifeq ($(HAVE_OPENGL),true) | |||||
| TARGETS += lv2_sep | |||||
| else | |||||
| TARGETS += lv2_dsp | |||||
| endif | |||||
| TARGETS += vst | |||||
| all: $(TARGETS) | |||||
| # -------------------------------------------------------------- | |||||
| @@ -0,0 +1,104 @@ | |||||
| /* | |||||
| * Copyright (C) 2018-2019 Rob van den Berg <rghvdberg at gmail dot org> | |||||
| * | |||||
| * This file is part of CharacterCompressor | |||||
| * | |||||
| * Nnjas2 is free software: you can redistribute it and/or modify | |||||
| * it under the terms of the GNU General Public License as published by | |||||
| * the Free Software Foundation, either version 3 of the License, or | |||||
| * (at your option) any later version. | |||||
| * | |||||
| * CharacterCompressor is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with CharacterCompressor. If not, see <https://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #include "NanoButton.hpp" | |||||
| #include "Window.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| Button::Button(Widget *parent, Callback *cb) | |||||
| : NanoWidget(parent), | |||||
| fCallback(cb), | |||||
| buttonActive(false) | |||||
| { | |||||
| loadSharedResources(); | |||||
| fNanoFont = findFont(NANOVG_DEJAVU_SANS_TTF); | |||||
| labelColor = Color(255, 255, 255); | |||||
| backgroundColor = Color(32,32,32); | |||||
| Label = "button"; | |||||
| } | |||||
| void Button::onNanoDisplay() | |||||
| { | |||||
| auto w = getWidth(); | |||||
| auto h = getHeight(); | |||||
| auto margin = 1.0f; | |||||
| // Background | |||||
| beginPath(); | |||||
| fillColor(backgroundColor); | |||||
| strokeColor(borderColor); | |||||
| rect(margin, margin, w - 2 * margin, h-2*margin); | |||||
| fill(); | |||||
| stroke(); | |||||
| closePath(); | |||||
| //Label | |||||
| beginPath(); | |||||
| fontFaceId(fNanoFont); | |||||
| fontSize(14); | |||||
| fillColor(labelColor); | |||||
| Rectangle<float> bounds; | |||||
| textBounds(0, 0, Label.c_str(), NULL, bounds); | |||||
| // float tw = bounds.getWidth(); | |||||
| // float th = bounds.getHeight(); | |||||
| float tx = w / 2.0f ; | |||||
| float ty = h / 2.0f; | |||||
| textAlign(ALIGN_CENTER | ALIGN_MIDDLE); | |||||
| fillColor(255, 255, 255, 255); | |||||
| text(tx, ty, Label.c_str(), NULL); | |||||
| closePath(); | |||||
| } | |||||
| void Button::setLabel(std::string label) | |||||
| { | |||||
| Label = label; | |||||
| } | |||||
| void Button::setLabelColor(const Color color) | |||||
| { | |||||
| labelColor = color; | |||||
| borderColor = color; | |||||
| } | |||||
| void Button::setBackgroundColor(const Color color) | |||||
| { | |||||
| backgroundColor = color; | |||||
| } | |||||
| bool Button::onMouse(const MouseEvent &ev) | |||||
| { | |||||
| if (ev.press & contains(ev.pos)) | |||||
| { | |||||
| buttonActive = true; | |||||
| setLabelColor(labelColor); | |||||
| fCallback->buttonClicked(this, true); | |||||
| return true; | |||||
| } | |||||
| else if (buttonActive) | |||||
| { | |||||
| buttonActive = false; | |||||
| //setLabelColor(Color(128,128,128)); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,60 @@ | |||||
| /* | |||||
| * Copyright (C) 2018-2019 Rob van den Berg <rghvdberg at gmail dot org> | |||||
| * | |||||
| * This file is part of CharacterCompressor | |||||
| * | |||||
| * Nnjas2 is free software: you can redistribute it and/or modify | |||||
| * it under the terms of the GNU General Public License as published by | |||||
| * the Free Software Foundation, either version 3 of the License, or | |||||
| * (at your option) any later version. | |||||
| * | |||||
| * CharacterCompressor is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with CharacterCompressor. If not, see <https://www.gnu.org/licenses/>. | |||||
| */ | |||||
| #ifndef BUTTON_HPP_INCLUDED | |||||
| #define BUTTON_HPP_INCLUDED | |||||
| #include "Widget.hpp" | |||||
| #include "NanoVG.hpp" | |||||
| #include <string> | |||||
| START_NAMESPACE_DISTRHO | |||||
| class Button : public NanoWidget | |||||
| { | |||||
| public: | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void buttonClicked(Button *button, bool value) = 0; | |||||
| }; | |||||
| explicit Button(Widget *parent, Callback *cb); | |||||
| void setLabel(std::string label); | |||||
| void setLabelColor(Color color); | |||||
| void setBackgroundColor(Color color); | |||||
| protected: | |||||
| void onNanoDisplay() override; | |||||
| bool onMouse(const MouseEvent &ev) override; | |||||
| private: | |||||
| std::string Label; | |||||
| Color labelColor,backgroundColor,borderColor; | |||||
| Callback *const fCallback; | |||||
| bool buttonActive; | |||||
| FontId fNanoFont; | |||||
| DISTRHO_LEAK_DETECTOR(Button) | |||||
| }; | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif | |||||
| @@ -253,7 +253,7 @@ protected: | |||||
| * Callbacks (optional) */ | * Callbacks (optional) */ | ||||
| /** | /** | ||||
| Optional callback to inform the plugin about a buffer size change.@ | |||||
| Optional callback to inform the plugin about a buffer size change. | |||||
| This function will only be called when the plugin is deactivated. | This function will only be called when the plugin is deactivated. | ||||
| @note This value is only a hint! | @note This value is only a hint! | ||||
| Hosts might call run() with a higher or lower number of frames. | Hosts might call run() with a higher or lower number of frames. | ||||
| @@ -35,7 +35,12 @@ public: | |||||
| std::memset(fStrBuf, 0, sizeof(char)*(0xff+1)); | std::memset(fStrBuf, 0, sizeof(char)*(0xff+1)); | ||||
| fSampleRate = getSampleRate(); | fSampleRate = getSampleRate(); | ||||
| fFont = createFontFromFile("sans", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); | |||||
| #ifdef DGL_NO_SHARED_RESOURCES | |||||
| createFontFromFile("sans", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); | |||||
| #else | |||||
| loadSharedResources(); | |||||
| #endif | |||||
| setGeometryConstraints(405, 256, true); | setGeometryConstraints(405, 256, true); | ||||
| } | } | ||||
| @@ -169,7 +174,6 @@ private: | |||||
| double fSampleRate; | double fSampleRate; | ||||
| // UI stuff | // UI stuff | ||||
| FontId fFont; | |||||
| float fScale; | float fScale; | ||||
| // temp buf for text | // temp buf for text | ||||
| @@ -39,7 +39,7 @@ public: | |||||
| std::memset(fParamGrid, 0, sizeof(bool)*9); | std::memset(fParamGrid, 0, sizeof(bool)*9); | ||||
| // TODO explain why this is here | // TODO explain why this is here | ||||
| setGeometryConstraints(128, 128, true); | |||||
| setGeometryConstraints(128, 128, true, false); | |||||
| } | } | ||||
| protected: | protected: | ||||
| @@ -104,16 +104,40 @@ protected: | |||||
| { | { | ||||
| const uint width = getWidth(); | const uint width = getWidth(); | ||||
| const uint height = getHeight(); | const uint height = getHeight(); | ||||
| const uint minwh = std::min(width, height); | |||||
| const uint bgColor = getBackgroundColor(); | |||||
| Rectangle<int> r; | Rectangle<int> r; | ||||
| r.setWidth(width/3 - 6); | |||||
| r.setHeight(height/3 - 6); | |||||
| // if host doesn't respect aspect-ratio but supports ui background, draw out-of-bounds color from it | |||||
| if (width != height && bgColor != 0) | |||||
| { | |||||
| const GLubyte red = (bgColor >> 24) & 0xff; | |||||
| const GLubyte green = (bgColor >> 16) & 0xff; | |||||
| const GLubyte blue = (bgColor >> 8) & 0xff; | |||||
| glColor3ub(red, green, blue); | |||||
| if (width > height) | |||||
| { | |||||
| r.setPos(height, 0); | |||||
| r.setSize(width-height, height); | |||||
| } | |||||
| else | |||||
| { | |||||
| r.setPos(0, width); | |||||
| r.setSize(width, height-width); | |||||
| } | |||||
| r.draw(); | |||||
| } | |||||
| r.setWidth(minwh/3 - 6); | |||||
| r.setHeight(minwh/3 - 6); | |||||
| // draw left, center and right columns | // draw left, center and right columns | ||||
| for (int i=0; i<3; ++i) | for (int i=0; i<3; ++i) | ||||
| { | { | ||||
| r.setX(3 + i*width/3); | |||||
| r.setX(3 + i*minwh/3); | |||||
| // top | // top | ||||
| r.setY(3); | r.setY(3); | ||||
| @@ -126,7 +150,7 @@ protected: | |||||
| r.draw(); | r.draw(); | ||||
| // middle | // middle | ||||
| r.setY(3 + height/3); | |||||
| r.setY(3 + minwh/3); | |||||
| if (fParamGrid[3+i]) | if (fParamGrid[3+i]) | ||||
| glColor3f(0.8f, 0.5f, 0.3f); | glColor3f(0.8f, 0.5f, 0.3f); | ||||
| @@ -136,7 +160,7 @@ protected: | |||||
| r.draw(); | r.draw(); | ||||
| // bottom | // bottom | ||||
| r.setY(3 + height*2/3); | |||||
| r.setY(3 + minwh*2/3); | |||||
| if (fParamGrid[6+i]) | if (fParamGrid[6+i]) | ||||
| glColor3f(0.8f, 0.5f, 0.3f); | glColor3f(0.8f, 0.5f, 0.3f); | ||||
| @@ -159,16 +183,17 @@ protected: | |||||
| const uint width = getWidth(); | const uint width = getWidth(); | ||||
| const uint height = getHeight(); | const uint height = getHeight(); | ||||
| const uint minwh = std::min(width, height); | |||||
| Rectangle<int> r; | Rectangle<int> r; | ||||
| r.setWidth(width/3 - 6); | |||||
| r.setHeight(height/3 - 6); | |||||
| r.setWidth(minwh/3 - 6); | |||||
| r.setHeight(minwh/3 - 6); | |||||
| // handle left, center and right columns | // handle left, center and right columns | ||||
| for (int i=0; i<3; ++i) | for (int i=0; i<3; ++i) | ||||
| { | { | ||||
| r.setX(3 + i*width/3); | |||||
| r.setX(3 + i*minwh/3); | |||||
| // top | // top | ||||
| r.setY(3); | r.setY(3); | ||||
| @@ -190,7 +215,7 @@ protected: | |||||
| } | } | ||||
| // middle | // middle | ||||
| r.setY(3 + height/3); | |||||
| r.setY(3 + minwh/3); | |||||
| if (r.contains(ev.pos)) | if (r.contains(ev.pos)) | ||||
| { | { | ||||
| @@ -203,7 +228,7 @@ protected: | |||||
| } | } | ||||
| // bottom | // bottom | ||||
| r.setY(3 + height*2/3); | |||||
| r.setY(3 + minwh*2/3); | |||||
| if (r.contains(ev.pos)) | if (r.contains(ev.pos)) | ||||
| { | { | ||||
| @@ -32,14 +32,6 @@ TARGETS += jack | |||||
| endif | endif | ||||
| endif | endif | ||||
| ifneq ($(MACOS_OR_WINDOWS),true) | |||||
| ifeq ($(HAVE_LIBLO),true) | |||||
| ifeq ($(HAVE_OPENGL),true) | |||||
| TARGETS += dssi | |||||
| endif | |||||
| endif | |||||
| endif | |||||
| ifeq ($(HAVE_OPENGL),true) | ifeq ($(HAVE_OPENGL),true) | ||||
| TARGETS += lv2_sep | TARGETS += lv2_sep | ||||
| else | else | ||||