Browse Source

Merge pull request #268 from DISTRHO/develop

Merge develop branch
master
Filipe Coelho GitHub 4 years ago
parent
commit
8401d865cd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 11680 additions and 3822 deletions
  1. +3
    -6
      .travis.yml
  2. +5
    -3
      .travis/before_install.sh
  3. +7
    -5
      .travis/install.sh
  4. +17
    -5
      Makefile.base.mk
  5. +28
    -15
      Makefile.plugins.mk
  6. +2
    -0
      README.md
  7. +18
    -1
      dgl/src/NanoVG.cpp
  8. +176
    -62
      dgl/src/nanovg/fontstash.h
  9. +268
    -94
      dgl/src/nanovg/nanovg.c
  10. +95
    -18
      dgl/src/nanovg/nanovg.h
  11. +197
    -62
      dgl/src/nanovg/nanovg_gl.h
  12. +29
    -7
      dgl/src/nanovg/nanovg_gl_utils.h
  13. +4160
    -1152
      dgl/src/nanovg/stb_image.h
  14. +5011
    -2081
      dgl/src/nanovg/stb_truetype.h
  15. +3
    -0
      dgl/src/pugl/pugl_x11.c
  16. +13
    -3
      distrho/DistrhoPlugin.hpp
  17. +28
    -3
      distrho/DistrhoUI.hpp
  18. +2
    -2
      distrho/extra/String.hpp
  19. +13
    -1
      distrho/src/DistrhoPluginChecks.h
  20. +9
    -0
      distrho/src/DistrhoPluginInternal.hpp
  21. +11
    -2
      distrho/src/DistrhoPluginJack.cpp
  22. +21
    -24
      distrho/src/DistrhoPluginLADSPA+DSSI.cpp
  23. +202
    -64
      distrho/src/DistrhoPluginLV2.cpp
  24. +85
    -29
      distrho/src/DistrhoPluginLV2export.cpp
  25. +41
    -22
      distrho/src/DistrhoPluginVST.cpp
  26. +18
    -1
      distrho/src/DistrhoUI.cpp
  27. +1
    -1
      distrho/src/DistrhoUIDSSI.cpp
  28. +59
    -21
      distrho/src/DistrhoUIInternal.hpp
  29. +170
    -62
      distrho/src/DistrhoUILV2.cpp
  30. +162
    -54
      distrho/src/lv2/ui.h
  31. +47
    -0
      examples/FileHandling/DistrhoPluginInfo.h
  32. +257
    -0
      examples/FileHandling/FileHandlingPlugin.cpp
  33. +269
    -0
      examples/FileHandling/FileHandlingUI.cpp
  34. +46
    -0
      examples/FileHandling/Makefile
  35. +104
    -0
      examples/FileHandling/NanoButton.cpp
  36. +60
    -0
      examples/FileHandling/NanoButton.hpp
  37. +1
    -1
      examples/Info/InfoExamplePlugin.cpp
  38. +6
    -2
      examples/Info/InfoExampleUI.cpp
  39. +36
    -11
      examples/Parameters/ExampleUIParameters.cpp
  40. +0
    -8
      examples/States/Makefile

+ 3
- 6
.travis.yml View File

@@ -1,8 +1,5 @@
os:
- linux

sudo: required
dist: trusty
os: linux
dist: bionic

languages: c++
compiler: gcc
@@ -13,6 +10,6 @@ install:
- sh ${TRAVIS_BUILD_DIR}/.travis/install.sh
script:
- 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-win64.sh

+ 5
- 3
.travis/before_install.sh View File

@@ -2,9 +2,11 @@

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 install kxstudio-repos
sudo apt-get update -qq

+ 7
- 5
.travis/install.sh View File

@@ -10,8 +10,10 @@ sudo apt-get install -y \
liblo-dev \
libgl1-mesa-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

+ 17
- 5
Makefile.base.mk View File

@@ -157,7 +157,7 @@ endif
endif

ifeq ($(NOOPT),true)
# No CPU-specific optimization flags
# Non-CPU-specific optimization flags
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections
endif

@@ -167,9 +167,6 @@ ifeq ($(WINDOWS),true)
BASE_OPTS += -fno-rerun-cse-after-loop
# See https://github.com/falkTX/Carla/issues/855
BASE_OPTS += -mstackrealign
ifeq ($(BUILDING_FOR_WINDOWS),true)
BASE_FLAGS += -DBUILDING_CARLA_FOR_WINDOWS
endif
else
# Not needed for Windows
BASE_FLAGS += -fPIC -DPIC
@@ -229,12 +226,14 @@ endif

HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true)

ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
ifeq ($(MACOS_OR_WINDOWS),true)
HAVE_OPENGL = true
else
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true)
ifneq ($(HAIKU),true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
endif
endif

# ---------------------------------------------------------------------------------------------------------------------
# Check for optional libraries
@@ -308,6 +307,19 @@ HAVE_CAIRO_OR_OPENGL = true

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



+ 28
- 15
Makefile.plugins.mk View File

@@ -7,31 +7,40 @@
# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file!


ifeq ($(DPF_CUSTOM_PATH),)
ifeq (,$(wildcard ../../Makefile.base.mk))
DPF_PATH=../../dpf
else
DPF_PATH=../..
endif
else
DPF_PATH = $(DPF_CUSTOM_PATH)
endif

include $(DPF_PATH)/Makefile.base.mk

# ---------------------------------------------------------------------------------------------------------------------
# Basic setup

ifeq ($(DPF_CUSTOM_PATH),)
TARGET_DIR = ../../bin
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_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)
BASE_FLAGS += -DHAVE_JACK
endif
@@ -70,8 +79,8 @@ UI_TYPE = opengl
endif

ifeq ($(UI_TYPE),cairo)
DGL_FLAGS += -DDGL_CAIRO
ifeq ($(HAVE_CAIRO),true)
DGL_FLAGS += -DDGL_CAIRO
DGL_FLAGS += $(CAIRO_FLAGS)
DGL_LIBS += $(CAIRO_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a
@@ -82,8 +91,8 @@ endif
endif

ifeq ($(UI_TYPE),opengl)
DGL_FLAGS += -DDGL_OPENGL
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a
@@ -107,6 +116,10 @@ DGL_LIBS =
OBJS_UI =
endif

ifneq ($(HAVE_LIBLO),true)
dssi_ui =
endif

# TODO split dsp and ui object build 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
-@mkdir -p $(BUILD_DIR)
@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
-@mkdir -p $(BUILD_DIR)
@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
@@ -171,7 +184,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@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
@@ -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)
-@mkdir -p $(shell dirname $@)
@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
@@ -239,7 +252,7 @@ endif
# ---------------------------------------------------------------------------------------------------------------------

-include $(OBJS_DSP:%.o=%.d)
ifeq ($(HAVE_DGL),true)
ifneq ($(UI_TYPE),)
-include $(OBJS_UI:%.o=%.d)
endif



+ 2
- 0
README.md View File

@@ -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)
- [String-machine](https://github.com/jpcima/string-machine)
- [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 Spectrum](https://github.com/pdesaulniers/wolf-spectrum)
- [YK Chorus](https://github.com/SpotlightKid/ykchorus)
- [ZamAudio Suite](https://github.com/zamaudio/zam-plugins)
## Work in progress
- [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)
- [gunshot](https://github.com/soerenbnoergaard/gunshot)
- [midiomatic](https://github.com/SpotlightKid/midiomatic)


+ 18
- 1
dgl/src/NanoVG.cpp View File

@@ -53,6 +53,7 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# undef DGL_EXT
#endif

@@ -66,20 +67,32 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
#if defined(NANOVG_GL2)
# define nvgCreateGL nvgCreateGL2
# define nvgDeleteGL nvgDeleteGL2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2
# define nvglImageHandle nvglImageHandleGL2
#elif defined(NANOVG_GL3)
# define nvgCreateGL nvgCreateGL3
# define nvgDeleteGL nvgDeleteGL3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3
# define nvglImageHandle nvglImageHandleGL3
#elif defined(NANOVG_GLES2)
# define nvgCreateGL nvgCreateGLES2
# define nvgDeleteGL nvgDeleteGLES2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2
# define nvglImageHandle nvglImageHandleGLES2
#elif defined(NANOVG_GLES3)
# define nvgCreateGL nvgCreateGLES3
# define nvgDeleteGL nvgDeleteGLES3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3
# define nvglImageHandle nvglImageHandleGLES3
#endif

static NVGcontext* nvgCreateGL_helper(int flags)
{
#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;
# define DGL_EXT(PROC, func) \
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \
@@ -111,8 +124,12 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# undef DGL_EXT
needsInit = false;
# if defined(__GNUC__) && (__GNUC__ >= 10)
# pragma GCC diagnostic pop
# endif
#endif
return nvgCreateGL(flags);
}
@@ -922,7 +939,7 @@ void NanoVG::loadSharedResources()

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



+ 176
- 62
dgl/src/nanovg/fontstash.h View File

@@ -38,10 +38,15 @@ enum FONSalign {
FONS_ALIGN_BASELINE = 1<<6, // Default
};

enum FONSglyphBitmap {
FONS_GLYPH_BITMAP_OPTIONAL = 1,
FONS_GLYPH_BITMAP_REQUIRED = 2,
};

enum FONSerrorCode {
// Font atlas is full.
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,
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
FONS_STATES_OVERFLOW = 3,
@@ -78,6 +83,7 @@ struct FONStextIter {
const char* next;
const char* end;
unsigned int utf8state;
int bitmapOption;
};
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);
// Returns current atlas size.
void fonsGetAtlasSize(FONScontext* s, int* width, int* height);
// Expands the atlas size.
// Expands the atlas size.
int fonsExpandAtlas(FONScontext* s, int width, int height);
// Resets the whole stash.
int fonsResetAtlas(FONScontext* stash, int width, int height);

// 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);

// 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);

// 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);

// Pull texture changes
@@ -154,20 +160,28 @@ typedef struct FONSttFontImpl FONSttFontImpl;
static FT_Library ftLibrary;

int fons__tt_init(FONScontext *context)
{
{
FT_Error ftError;
FONS_NOTUSED(context);
FONS_NOTUSED(context);
ftError = FT_Init_FreeType(&ftLibrary);
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;
FONS_NOTUSED(context);

//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;
}

@@ -180,7 +194,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i

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);
#else
return size / font->font->units_per_EM;
#endif
}

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_GlyphSlot ftGlyph;
FT_Fixed advFixed;
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)));
#else
ftError = FT_Set_Pixel_Sizes(font->font, 0, size);
#endif
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);
#else
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
#endif
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;
ftGlyph = font->font->glyph;
*lsb = ftGlyph->metrics.horiBearingX;
*advance = (int)advFixed;
*lsb = (int)ftGlyph->metrics.horiBearingX;
*x0 = ftGlyph->bitmap_left;
*x1 = *x0 + ftGlyph->bitmap.width;
*y0 = -ftGlyph->bitmap_top;
@@ -215,7 +246,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int
{
FT_GlyphSlot ftGlyph = font->font->glyph;
int ftGlyphOffset = 0;
int x, y;
unsigned int x, y;
FONS_NOTUSED(outWidth);
FONS_NOTUSED(outHeight);
FONS_NOTUSED(scaleX);
@@ -233,7 +264,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{
FT_Vector 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
@@ -256,13 +287,24 @@ int fons__tt_init(FONScontext *context)
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);

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;
}

@@ -273,7 +315,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i

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);
#else
return stbtt_ScaleForMappingEmToPixels(&font->font, size);
#endif
}

int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
@@ -304,7 +351,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
#endif

#ifndef FONS_SCRATCH_BUF_SIZE
# define FONS_SCRATCH_BUF_SIZE 16000
# define FONS_SCRATCH_BUF_SIZE 96000
#endif
#ifndef FONS_HASH_LUT_SIZE
# define FONS_HASH_LUT_SIZE 256
@@ -324,6 +371,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
#ifndef FONS_MAX_STATES
# define FONS_MAX_STATES 20
#endif
#ifndef FONS_MAX_FALLBACKS
# define FONS_MAX_FALLBACKS 20
#endif

static unsigned int fons__hashint(unsigned int a)
{
@@ -361,7 +411,7 @@ struct FONSfont
{
FONSttFontImpl font;
char name[64];
const unsigned char* data;
unsigned char* data;
int dataSize;
unsigned char freeData;
float ascender;
@@ -371,6 +421,8 @@ struct FONSfont
int cglyphs;
int nglyphs;
int lut[FONS_HASH_LUT_SIZE];
int fallbacks[FONS_MAX_FALLBACKS];
int nfallbacks;
};
typedef struct FONSfont FONSfont;

@@ -421,6 +473,8 @@ struct FONScontext
void* errorUptr;
};

#ifdef STB_TRUETYPE_IMPLEMENTATION

static void* fons__tmpalloc(size_t size, void* up)
{
unsigned char* ptr;
@@ -446,6 +500,8 @@ static void fons__tmpfree(void* ptr, void* up)
// empty
}

#endif // STB_TRUETYPE_IMPLEMENTATION

// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// 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];
}

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)
{
fons__getState(stash)->size = size;
@@ -818,7 +895,7 @@ static void fons__freeFont(FONSfont* font)
{
if (font == NULL) return;
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);
}

@@ -849,12 +926,12 @@ error:
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;
int dataSize = 0;
size_t readed;
unsigned char* data = NULL;
size_t ignore;

// Read in the font data.
fp = fopen(path, "rb");
@@ -864,21 +941,20 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path)
fseek(fp,0,SEEK_SET);
data = (unsigned char*)malloc(dataSize);
if (data == NULL) goto error;
ignore = fread(data, 1, dataSize, fp);
readed = fread(data, 1, dataSize, fp);
fclose(fp);
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:
if (data) free(data);
if (fp) fclose(fp);
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;
FONSfont* font;
@@ -903,15 +979,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da

// Init font
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
// by multiplying the lineh by font size.
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
ascent += lineGap;
fh = ascent - descent;
font->ascender = (float)ascent / (float)fh;
font->descender = (float)descent / (float)fh;
font->lineh = (float)(fh + lineGap) / (float)fh;
font->lineh = font->ascender - font->descender;

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,
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;
float scale;
@@ -1020,6 +1097,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
int pad, added;
unsigned char* bdst;
unsigned char* dst;
FONSfont* renderFont = font;

if (isize < 2) return NULL;
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);
i = font->lut[h];
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;
}

// 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);
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;
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);
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.
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->x0 = (short)gx;
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->xoff = (short)(x0 - 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
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.
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) {
stash->nscratch = 0;
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);
@@ -1134,8 +1245,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
y1 = (float)(glyph->y1-1);

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->y0 = ry;
@@ -1147,8 +1258,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
q->s1 = x1 * stash->itw;
q->t1 = y1 * stash->ith;
} else {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y - yoff);
rx = floorf(*x + xoff);
ry = floorf(*y - yoff);

q->x0 = rx;
q->y0 = ry;
@@ -1226,7 +1337,7 @@ float fonsDrawText(FONScontext* stash,
const char* str, const char* end)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0;
FONSglyph* glyph = NULL;
FONSquad q;
@@ -1263,7 +1374,7 @@ float fonsDrawText(FONScontext* stash,
for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED);
if (glyph != NULL) {
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,
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);
float width;
@@ -1326,6 +1437,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
iter->end = end;
iter->codepoint = 0;
iter->prevGlyphIndex = -1;
iter->bitmapOption = bitmapOption;

return 1;
}
@@ -1346,7 +1458,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
// Get glyph and quad
iter->x = iter->nextx;
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)
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
@@ -1406,12 +1519,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y)
}

float fonsTextBounds(FONScontext* stash,
float x, float y,
float x, float y,
const char* str, const char* end,
float* bounds)
{
FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0;
FONSquad q;
FONSglyph* glyph = NULL;
@@ -1443,7 +1556,7 @@ float fonsTextBounds(FONScontext* stash,
for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
if (q.x0 < minx) minx = q.x0;
@@ -1568,6 +1681,7 @@ void fonsDeleteInternal(FONScontext* stash)
if (stash->texData) free(stash->texData);
if (stash->scratch) free(stash->scratch);
free(stash);
fons__tt_done(stash);
}

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);

if (width == stash->params.width && height == stash->params.height)
return 1;
return 1;

// Flush pending glyphs.
fons__flush(stash);


+ 268
- 94
dgl/src/nanovg/nanovg.c View File

@@ -16,8 +16,11 @@
// 3. This notice may not be removed or altered from any source distribution.
//

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <memory.h>

#include "nanovg.h"
#define FONTSTASH_IMPLEMENTATION
#include "fontstash.h"
@@ -63,6 +66,8 @@ enum NVGpointFlags
};

struct NVGstate {
NVGcompositeOperationState compositeOperation;
int shapeAntiAlias;
NVGpaint fill;
NVGpaint stroke;
float strokeWidth;
@@ -200,6 +205,84 @@ static void nvg__setDevicePixelRatio(NVGcontext* ctx, float 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)
{
FONSparams fontParams;
@@ -280,7 +363,7 @@ void nvgDeleteInternal(NVGcontext* 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",
ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
@@ -291,8 +374,8 @@ void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float dev
nvgReset(ctx);

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->fillTriCount = 0;
@@ -383,7 +466,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
{
int i;
float oneminu;
NVGcolor cint;
NVGcolor cint = {{{0}}};

u = nvg__clampf(u, 0.0f, 1.0f);
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;
}
return cint;
}

@@ -430,12 +513,6 @@ NVGcolor nvgHSLA(float h, float s, float l, unsigned char a)
return col;
}


static NVGstate* nvg__getState(NVGcontext* ctx)
{
return &ctx->states[ctx->nstates-1];
}

void nvgTransformIdentity(float* t)
{
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->stroke, nvgRGBA(0,0,0,255));
state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER);
state->shapeAntiAlias = 1;
state->strokeWidth = 1.0f;
state->miterLimit = 10.0f;
state->lineCap = NVG_BUTT;
@@ -587,6 +666,12 @@ void nvgReset(NVGcontext* ctx)
}

// State setting
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled)
{
NVGstate* state = nvg__getState(ctx);
state->shapeAntiAlias = enabled;
}

void nvgStrokeWidth(NVGcontext* ctx, float width)
{
NVGstate* state = nvg__getState(ctx);
@@ -719,7 +804,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
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;
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.
// 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);
ex = state->scissor.extent[0];
ey = state->scissor.extent[1];
@@ -936,6 +1021,30 @@ void nvgResetScissor(NVGcontext* ctx)
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)
{
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 dx,dy,d2,d3;
if (level > 10) return;

x12 = (x1+x2)*0.5f;
@@ -1205,8 +1314,8 @@ static void nvg__tesselateBezier(NVGcontext* ctx,
x1234 = (x123+x234)*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)
@@ -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,
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;
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,
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 py = p->y - dy*d;
float dlx = dy;
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;
}

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 py = p->y + dy*d;
float dlx = dy;
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;
}


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;
float px = p->x;
@@ -1504,16 +1617,17 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NVG_PI;
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 + 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;
}

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;
float px = p->x;
@@ -1521,13 +1635,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
float dlx = dy;
float dly = -dx;
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++) {
float a = i/(float)(ncap-1)*NVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w;
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;
}
@@ -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;
NVGvertex* verts;
NVGvertex* dst;
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.

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);

// 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;
nvg__normalize(&dx, &dy);
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)
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)
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) {
if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
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 {
dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa);
dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa);
}
} 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++;
}

if (loop) {
// 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 {
// Add cap
dx = p1->x - p0->x;
dy = p1->y - p0->y;
nvg__normalize(&dx, &dy);
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)
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)
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);
@@ -1868,7 +1991,7 @@ void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y)
{
float x0 = ctx->commandx;
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),
x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - 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 vals[3 + 5*7 + 100];
int i, ndivs, nvals;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;

// Clamp angles
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)
{
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;
}
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[] = {
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__appendCommands(ctx, vals, NVG_COUNTOF(vals));
@@ -2092,7 +2224,7 @@ void nvgFill(NVGcontext* ctx)
int i;

nvg__flattenPaths(ctx);
if (ctx->params.edgeAntiAlias)
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f);
else
nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f);
@@ -2101,7 +2233,7 @@ void nvgFill(NVGcontext* ctx)
fillPaint.innerColor.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);

// Count triangles
@@ -2122,6 +2254,7 @@ void nvgStroke(NVGcontext* ctx)
const NVGpath* path;
int i;


if (strokeWidth < ctx->fringeWidth) {
// If the stroke width is less than pixel size, use alpha to emulate coverage.
// Since coverage is area, scale by alpha*alpha.
@@ -2137,12 +2270,12 @@ void nvgStroke(NVGcontext* 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
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);

// Count triangles
@@ -2154,14 +2287,24 @@ void nvgStroke(NVGcontext* ctx)
}

// 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)
@@ -2170,6 +2313,28 @@ int nvgFindFont(NVGcontext* ctx, const char* 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
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.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->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);
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;
while (fonsTextIterNext(ctx->fs, &iter, &q)) {
float c[4*2];
if (iter.prevGlyphIndex == -1) { // can not retrieve glyph?
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
if (nverts != 0) {
nvg__renderText(ctx, verts, nverts);
nverts = 0;
}
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
iter = prevIter;
fonsTextIterNext(ctx->fs, &iter, &q); // try again
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__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)
@@ -2407,7 +2572,7 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string,
fonsSetAlign(ctx->fs, state->textAlign);
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;
while (fonsTextIterNext(ctx->fs, &iter, &q)) {
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
@@ -2431,6 +2596,7 @@ enum NVGcodepointType {
NVG_SPACE,
NVG_NEWLINE,
NVG_CHAR,
NVG_CJK_CHAR,
};

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;

fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end);
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) {
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;
break;
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;
}

@@ -2525,12 +2699,12 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
} else {
if (rowStart == NULL) {
// 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
rowStartX = iter.x;
rowStart = iter.str;
rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX;
rowWidth = iter.nextx - rowStartX;
rowMinX = q.x0 - rowStartX;
rowMaxX = q.x1 - rowStartX;
wordStart = iter.str;
@@ -2545,26 +2719,26 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
float nextWidth = iter.nextx - rowStartX;

// track last non-white space character
if (type == NVG_CHAR) {
if (type == NVG_CHAR || type == NVG_CJK_CHAR) {
rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX;
rowMaxX = q.x1 - rowStartX;
}
// 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;
breakWidth = rowWidth;
breakMaxX = rowMaxX;
}
// 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;
wordStartX = iter.x;
wordMinX = q.x0 - rowStartX;
wordMinX = q.x0;
}

// 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.
if (breakEnd == rowStart) {
// 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++;
if (nrows >= maxRows)
return nrows;
// Update row
rowStartX = wordStartX;
rowStart = wordStart;
rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX;
rowMinX = wordMinX;
rowMinX = wordMinX - rowStartX;
rowMaxX = q.x1 - rowStartX;
// No change to the word start
}
// Set null break point
breakEnd = rowStart;


+ 95
- 18
dgl/src/nanovg/nanovg.h View File

@@ -79,10 +79,46 @@ enum NVGalign {
// Vertical align
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
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 {
const char* str; // Position of the glyph in the input string.
float x; // The x-coordinate of the logical glyph position.
@@ -100,11 +136,12 @@ struct NVGtextRow {
typedef struct NVGtextRow NVGtextRow;

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_REPEATY = 1<<2, // Repeat image in Y direction.
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
@@ -115,7 +152,7 @@ enum NVGimageFlags {
// 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
// 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.
void nvgCancelFrame(NVGcontext* ctx);
@@ -123,6 +160,22 @@ void nvgCancelFrame(NVGcontext* ctx);
// Ends drawing flushing remaining render state.
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
//
@@ -183,7 +236,10 @@ void nvgReset(NVGcontext* ctx);
// Solid color is simply defined as a color value, different kinds of paints can be created
// 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.
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
@@ -231,7 +287,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
// Apart from nvgResetTransform(), each transformation function first creates
// 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.
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.
// 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.
// Returns handle to the image.
@@ -368,7 +424,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey
// Scissoring
//
// 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.
// 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.
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);

// 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.
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.
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);

// Fills the current path with current fill style.
@@ -487,13 +546,31 @@ void nvgStroke(NVGcontext* ctx);
// Returns handle to the font.
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.
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.
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.
void nvgFontSize(NVGcontext* ctx, float size);

@@ -503,7 +580,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur);
// Sets the letter spacing of current text style.
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);

// 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 (*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);
void (*renderViewport)(void* uptr, int width, int height);
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
void (*renderCancel)(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);
};
typedef struct NVGparams NVGparams;


+ 197
- 62
dgl/src/nanovg/nanovg_gl.h View File

@@ -59,6 +59,9 @@ enum NVGcreateFlags {
NVGcontext* nvgCreateGL2(int flags);
void nvgDeleteGL2(NVGcontext* ctx);

int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GL3
@@ -66,6 +69,9 @@ void nvgDeleteGL2(NVGcontext* ctx);
NVGcontext* nvgCreateGL3(int flags);
void nvgDeleteGL3(NVGcontext* ctx);

int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GLES2
@@ -73,6 +79,9 @@ void nvgDeleteGL3(NVGcontext* ctx);
NVGcontext* nvgCreateGLES2(int flags);
void nvgDeleteGLES2(NVGcontext* ctx);

int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image);

#endif

#if defined NANOVG_GLES3
@@ -80,6 +89,9 @@ void nvgDeleteGLES2(NVGcontext* ctx);
NVGcontext* nvgCreateGLES3(int flags);
void nvgDeleteGLES3(NVGcontext* ctx);

int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image);

#endif

// 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.
};

int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandle(NVGcontext* ctx, int image);


#ifdef __cplusplus
}
#endif
@@ -142,6 +150,15 @@ struct GLNVGtexture {
};
typedef struct GLNVGtexture GLNVGtexture;

struct GLNVGblend
{
GLenum srcRGB;
GLenum dstRGB;
GLenum srcAlpha;
GLenum dstAlpha;
};
typedef struct GLNVGblend GLNVGblend;

enum GLNVGcallType {
GLNVG_NONE = 0,
GLNVG_FILL,
@@ -158,6 +175,7 @@ struct GLNVGcall {
int triangleOffset;
int triangleCount;
int uniformOffset;
GLNVGblend blendFunc;
};
typedef struct GLNVGcall GLNVGcall;

@@ -248,7 +266,10 @@ struct GLNVGcontext {
GLenum stencilFunc;
GLint stencilFuncRef;
GLuint stencilFuncMask;
GLNVGblend blendFunc;
#endif

int dummyTex;
};
typedef struct GLNVGcontext GLNVGcontext;

@@ -298,7 +319,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
if ((gl->stencilFunc != func) ||
(gl->stencilFuncRef != ref) ||
(gl->stencilFuncMask != mask)) {
gl->stencilFunc = func;
gl->stencilFuncRef = ref;
gl->stencilFuncMask = mask;
@@ -308,6 +329,21 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
glStencilFunc(func, ref, mask);
#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)
{
@@ -331,10 +367,10 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
}
tex = &gl->textures[gl->ntextures++];
}
memset(tex, 0, sizeof(*tex));
tex->id = ++gl->textureId;
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)
{
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512;
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)
{
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512;
str[len] = '\0';
@@ -466,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader)
#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)
{
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"
"}\n";

static const char* fillFragShader =
static const char* fillFragShader =
"#ifdef GL_ES\n"
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"
" precision highp float;\n"
@@ -592,6 +630,7 @@ static int glnvg__renderCreate(void* uptr)
" float scissor = scissorMask(fpos);\n"
"#ifdef EDGE_AA\n"
" float strokeAlpha = strokeMask();\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#else\n"
" float strokeAlpha = 1.0;\n"
"#endif\n"
@@ -631,9 +670,6 @@ static int glnvg__renderCreate(void* uptr)
" color *= scissor;\n"
" result = color * innerCol;\n"
" }\n"
"#ifdef EDGE_AA\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#endif\n"
"#ifdef NANOVG_GL3\n"
" outColor = result;\n"
"#else\n"
@@ -663,11 +699,15 @@ static int glnvg__renderCreate(void* uptr)
#if NANOVG_GL_USE_UNIFORMBUFFER
// Create UBOs
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);
#endif
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");

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);
imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
}
// No mips.
// No mips.
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h);
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)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
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);
#elif defined(NANOVG_GLES3)
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

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 {
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)
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)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
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);
#else
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);
if (tex == NULL) return 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 {
nvgTransformInverse(invxform, paint->xform);
}
frag->type = NSVG_SHADER_FILLIMG;

#if NANOVG_GL_USE_UNIFORMBUFFER
if (tex->type == NVG_TEXTURE_RGBA)
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
else
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);
} else {
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)
{
GLNVGtexture* tex = NULL;
#if NANOVG_GL_USE_UNIFORMBUFFER
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms));
#else
@@ -925,19 +990,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
#endif

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;
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)
@@ -979,7 +1047,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
// Draw fill
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);
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);
}
@@ -992,12 +1060,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call)
glnvg__setUniforms(gl, call->uniformOffset, call->image);
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);
if (gl->flags & NVG_ANTIALIAS) {
// Draw fringes
for (i = 0; i < npaths; i++)
if (paths[i].strokeCount > 0) {
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++)
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);
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
@@ -1064,6 +1132,50 @@ static void glnvg__renderCancel(void* uptr) {
gl->nuniforms = 0;
}

static GLenum glnvg_convertBlendFuncFactor(int factor)
{
if (factor == NVG_ZERO)
return GL_ZERO;
if (factor == NVG_ONE)
return GL_ONE;
if (factor == NVG_SRC_COLOR)
return GL_SRC_COLOR;
if (factor == NVG_ONE_MINUS_SRC_COLOR)
return GL_ONE_MINUS_SRC_COLOR;
if (factor == NVG_DST_COLOR)
return GL_DST_COLOR;
if (factor == NVG_ONE_MINUS_DST_COLOR)
return GL_ONE_MINUS_DST_COLOR;
if (factor == NVG_SRC_ALPHA)
return GL_SRC_ALPHA;
if (factor == NVG_ONE_MINUS_SRC_ALPHA)
return GL_ONE_MINUS_SRC_ALPHA;
if (factor == NVG_DST_ALPHA)
return GL_DST_ALPHA;
if (factor == NVG_ONE_MINUS_DST_ALPHA)
return GL_ONE_MINUS_DST_ALPHA;
if (factor == NVG_SRC_ALPHA_SATURATE)
return GL_SRC_ALPHA_SATURATE;
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)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1074,7 +1186,6 @@ static void glnvg__renderFlush(void* uptr)
// Setup require GL state.
glUseProgram(gl->shader.prog);

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
@@ -1093,6 +1204,10 @@ static void glnvg__renderFlush(void* uptr)
gl->stencilFunc = GL_ALWAYS;
gl->stencilFuncRef = 0;
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

#if NANOVG_GL_USE_UNIFORMBUFFER
@@ -1122,6 +1237,7 @@ static void glnvg__renderFlush(void* uptr)

for (i = 0; i < gl->ncalls; i++) {
GLNVGcall* call = &gl->calls[i];
glnvg__blendFuncSeparate(gl,&call->blendFunc);
if (call->type == GLNVG_FILL)
glnvg__fill(gl, call);
else if (call->type == GLNVG_CONVEXFILL)
@@ -1136,9 +1252,9 @@ static void glnvg__renderFlush(void* uptr)
glDisableVertexAttribArray(1);
#if defined NANOVG_GL3
glBindVertexArray(0);
#endif
#endif
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(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;
}

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)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1249,16 +1365,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
if (call == NULL) return;

call->type = GLNVG_FILL;
call->triangleCount = 4;
call->pathOffset = glnvg__allocPaths(gl, npaths);
if (call->pathOffset == -1) goto error;
call->pathCount = npaths;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

if (npaths == 1 && paths[0].convex)
{
call->type = GLNVG_CONVEXFILL;
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill
}

// Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount;
offset = glnvg__allocVerts(gl, maxverts);
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
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);
if (call->uniformOffset == -1) goto error;
// Simple shader for stencil
@@ -1318,7 +1435,7 @@ error:
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)
{
GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1332,6 +1449,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor
if (call->pathOffset == -1) goto error;
call->pathCount = npaths;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

// Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths);
@@ -1373,8 +1491,8 @@ error:
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;
GLNVGcall* call = glnvg__allocCall(gl);
@@ -1384,6 +1502,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis

call->type = GLNVG_TRIANGLES;
call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);

// Allocate vertices for all the paths.
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);
if (call->uniformOffset == -1) goto error;
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;

return;
@@ -1499,7 +1618,15 @@ void nvgDeleteGLES3(NVGcontext* 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;
GLNVGtexture* tex = glnvg__allocTexture(gl);
@@ -1515,7 +1642,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i
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;
GLNVGtexture* tex = glnvg__findTexture(gl, image);


+ 29
- 7
dgl/src/nanovg/nanovg_gl_utils.h View File

@@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer;
// Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb);
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

@@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
memset(fb, 0, sizeof(NVGLUframebuffer));

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
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);
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);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
@@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
error:
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
nvgluDeleteFramebuffer(ctx, fb);
nvgluDeleteFramebuffer(fb);
return NULL;
#else
NVG_NOTUSED(ctx);
@@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb)
#endif
}

void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
if (fb == NULL) return;
@@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
if (fb->rbo != 0)
glDeleteRenderbuffers(1, &fb->rbo);
if (fb->image >= 0)
nvgDeleteImage(ctx, fb->image);
nvgDeleteImage(fb->ctx, fb->image);
fb->ctx = NULL;
fb->fbo = 0;
fb->rbo = 0;
fb->texture = 0;
fb->image = -1;
free(fb);
#else
NVG_NOTUSED(ctx);
NVG_NOTUSED(fb);
#endif
}


+ 4160
- 1152
dgl/src/nanovg/stb_image.h
File diff suppressed because it is too large
View File


+ 5011
- 2081
dgl/src/nanovg/stb_truetype.h
File diff suppressed because it is too large
View File


+ 3
- 0
dgl/src/pugl/pugl_x11.c View File

@@ -83,6 +83,7 @@ static int attrListSgl[] = {
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_STENCIL_SIZE, 8,
GLX_ARB_multisample, 1,
None
};
@@ -98,6 +99,7 @@ static int attrListDbl[] = {
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_STENCIL_SIZE, 8,
GLX_ARB_multisample, 1,
None
};
@@ -114,6 +116,7 @@ static int attrListDblMS[] = {
GLX_BLUE_SIZE, 4,
GLX_ALPHA_SIZE, 4,
GLX_DEPTH_SIZE, 16,
GLX_STENCIL_SIZE, 8,
GLX_SAMPLE_BUFFERS, 1,
GLX_SAMPLES, 4,
None


+ 13
- 3
distrho/DistrhoPlugin.hpp View File

@@ -1,6 +1,6 @@
/*
* 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
* 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;

@@ -110,6 +110,9 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean;

/**
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 {
/**
@@ -189,7 +192,7 @@ struct ParameterRanges {
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
: def(0.0f),
@@ -827,6 +830,13 @@ protected:
virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0;
#endif

#if DISTRHO_PLUGIN_WANT_STATEFILES
/**
TODO API under construction
*/
virtual bool isStateFile(uint32_t index) = 0;
#endif

/* --------------------------------------------------------------------------------------------------------
* Internal data */



+ 28
- 3
distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -81,6 +81,18 @@ public:
/* --------------------------------------------------------------------------------------------------------
* 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.
@see sampleRateChanged(double)
@@ -92,13 +104,13 @@ public:

Touch/pressed-down event.
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);

/**
setParameterValue.
Change a parameter value in the Plugin.
*/
void setParameterValue(uint32_t index, float value);
@@ -111,6 +123,19 @@ public:
void setState(const char* key, const char* value);
#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
/**
sendNote.


+ 2
- 2
distrho/extra/String.hpp View File

@@ -174,7 +174,7 @@ public:
fBufferLen(0)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%f", value);
std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
strBuf[0xff] = '\0';

_dup(strBuf);
@@ -188,7 +188,7 @@ public:
fBufferLen(0)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%g", value);
std::snprintf(strBuf, 0xff, "%.24g", value);
strBuf[0xff] = '\0';

_dup(strBuf);


+ 13
- 1
distrho/src/DistrhoPluginChecks.h View File

@@ -77,6 +77,10 @@
# define DISTRHO_PLUGIN_WANT_STATE 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_STATEFILES
# define DISTRHO_PLUGIN_WANT_STATEFILES 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#endif
@@ -108,7 +112,7 @@
// Define DISTRHO_UI_URI if needed

#ifndef DISTRHO_UI_URI
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI"
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI"
#endif

// -----------------------------------------------------------------------
@@ -127,6 +131,14 @@
# error Synths need MIDI input to work!
#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



+ 9
- 0
distrho/src/DistrhoPluginInternal.hpp View File

@@ -486,6 +486,15 @@ public:
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
String getState(const char* key) const
{


+ 11
- 2
distrho/src/DistrhoPluginJack.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* modify it under the terms of the GNU Lesser General Public
@@ -101,7 +101,16 @@ public:
PluginJack(jack_client_t* const client)
: fPlugin(this, writeMidiCallback),
#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
fClient(client)
{


+ 21
- 24
distrho/src/DistrhoPluginLADSPA+DSSI.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,15 +19,19 @@
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error Cannot use MIDI Output with LADSPA or DSSI
#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
# include "dssi/dssi.h"
# if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error DSSI does not support MIDI output
# endif
#else
# 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
# endif
# if DISTRHO_PLUGIN_WANT_STATE && !defined(DISTRHO_NO_WARNINGS)
@@ -35,10 +39,6 @@
# endif
#endif

#if DISTRHO_PLUGIN_WANT_TIMEPOS && !defined(DISTRHO_NO_WARNINGS)
# warning LADSPA/DSSI does not support TimePos
#endif

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
@@ -543,15 +543,14 @@ static DSSI_Descriptor sDssiDescriptor = {

// -----------------------------------------------------------------------

class DescriptorInitializer
static const struct DescriptorInitializer
{
public:
DescriptorInitializer()
{
// Create dummy plugin to get data from
d_lastBufferSize = 512;
d_lastSampleRate = 44100.0;
PluginExporter plugin(nullptr, nullptr);
const PluginExporter plugin(nullptr, nullptr);
d_lastBufferSize = 0;
d_lastSampleRate = 0.0;

@@ -616,23 +615,23 @@ public:

{
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].LowerBound = ranges.min;
portRangeHints[port].UpperBound = ranges.max;

if (defValue == 0.0f)
/**/ if (d_isZero(defValue))
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;
else if (defValue == 100.0f)
else if (d_isEqual(defValue, 100.0f))
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;
else if (ranges.min == defValue)
else if (d_isEqual(ranges.min, defValue))
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;
else
{
@@ -640,7 +639,7 @@ public:
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;

if (defValue < middleLow)
/**/ if (defValue < middleLow)
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
else if (defValue > middleHigh)
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)
{
@@ -728,9 +727,7 @@ public:
sLadspaDescriptor.PortNames = nullptr;
}
}
};

static DescriptorInitializer sDescInit;
} sDescInit;

// -----------------------------------------------------------------------



+ 202
- 64
distrho/src/DistrhoPluginLV2.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -24,6 +24,7 @@
#include "lv2/midi.h"
#include "lv2/options.h"
#include "lv2/parameters.h"
#include "lv2/patch.h"
#include "lv2/state.h"
#include "lv2/time.h"
#include "lv2/urid.h"
@@ -54,7 +55,8 @@

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
static const writeMidiFunc writeMidiCallback = nullptr;
@@ -65,7 +67,10 @@ static const writeMidiFunc writeMidiCallback = nullptr;
class PluginLv2
{
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),
fUsingNominal(usingNominal),
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
@@ -127,6 +132,15 @@ public:

const String& dkey(fPlugin.getStateKey(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
@@ -521,16 +535,16 @@ public:
}
#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)
{
if (event == nullptr)
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
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)
fNeededUiSends[i] = true;
}
else
// 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

@@ -653,19 +683,19 @@ public:
if (! fNeededUiSends[i])
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)
continue;

const String& value = cit->second;
const String& value(cit->second);

// 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)
{
@@ -673,21 +703,18 @@ public:
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
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset);
aev->time.frames = 0;
aev->body.type = fURIDs.distrhoState;
aev->body.type = fURIDs.dpfKeyValue;
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));

@@ -795,7 +822,7 @@ public:

# if DISTRHO_PLUGIN_WANT_FULL_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;
fStateMap[key] = fPlugin.getState(key);
@@ -811,22 +838,52 @@ public:
{
# if DISTRHO_PLUGIN_WANT_FULL_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;
fStateMap[key] = fPlugin.getState(key);
}
# 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;
@@ -837,23 +894,42 @@ public:
size_t size;
uint32_t type, flags;

String dpf_lv2_key;
LV2_URID urid;

for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++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;
type = 0;
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)
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);

setState(key, value);
@@ -871,12 +947,55 @@ public:

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*)
@@ -995,16 +1114,21 @@ private:

// LV2 URIDs
struct URIDs {
const LV2_URID_Map* _uridMap;
LV2_URID atomBlank;
LV2_URID atomObject;
LV2_URID atomDouble;
LV2_URID atomFloat;
LV2_URID atomInt;
LV2_URID atomLong;
LV2_URID atomPath;
LV2_URID atomSequence;
LV2_URID atomString;
LV2_URID distrhoState;
LV2_URID atomURID;
LV2_URID dpfKeyValue;
LV2_URID midiEvent;
LV2_URID patchProperty;
LV2_URID patchValue;
LV2_URID timePosition;
LV2_URID timeBar;
LV2_URID timeBarBeat;
@@ -1016,25 +1140,35 @@ private:
LV2_URID timeSpeed;

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;

// LV2 features
@@ -1042,7 +1176,7 @@ private:
const LV2_Worker_Schedule* const fWorker;

#if DISTRHO_PLUGIN_WANT_STATE
StringMap fStateMap;
StringToStringMap fStateMap;
bool* fNeededUiSends;

void setState(const char* const key, const char* const newValue)
@@ -1054,7 +1188,7 @@ private:
return;

// 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);

@@ -1067,6 +1201,10 @@ private:

d_stderr("Failed to find plugin state with key \"%s\"", key);
}

# if DISTRHO_PLUGIN_WANT_STATEFILES
UridToStringMap fUridStateFileMap;
# endif
#endif

void updateParameterOutputsAndTriggers()


+ 85
- 29
distrho/src/DistrhoPluginLV2export.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* 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/midi.h"
#include "lv2/options.h"
#include "lv2/patch.h"
#include "lv2/port-props.h"
#include "lv2/presets.h"
#include "lv2/resize-port.h"
@@ -45,6 +46,10 @@
# error DISTRHO_PLUGIN_URI undefined!
#endif

#ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
# define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
#endif

#ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048
#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_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[] =
{
@@ -139,6 +146,9 @@ static const char* const lv2ManifestUiOptionalFeatures[] =
"ui:parent",
"ui:resize",
"ui:touch",
#endif
#if DISTRHO_PLUGIN_WANT_STATEFILES
"ui:requestValue",
#endif
nullptr
};
@@ -220,8 +230,8 @@ void lv2_generate_ttl(const char* const basename)
d_lastBufferSize = 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
String pluginUI(pluginDLL);
@@ -317,26 +327,45 @@ void lv2_generate_ttl(const char* const basename)

// header
#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
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
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
#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
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n";
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n";
#endif
#if DISTRHO_PLUGIN_HAS_UI
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
#endif
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\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
pluginString += "<" DISTRHO_PLUGIN_URI ">\n";
#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, "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
#if DISTRHO_PLUGIN_HAS_UI
pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n";
@@ -499,7 +543,7 @@ void lv2_generate_ttl(const char* const basename)
case kParameterDesignationBypass:
designated = true;
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:minimum 0 ;\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.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:maximum " + String(int(ranges.max)) + " ;\n";
}
else
{
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:maximum " + String(ranges.max) + " ;\n";
}
@@ -582,33 +626,36 @@ void lv2_generate_ttl(const char* const basename)
// unit
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";
}
else if (unit == "hz" || unit == "Hz")
else if (lunit == "hz")
{
pluginString += " unit:unit unit:hz ;\n";
}
else if (unit == "khz" || unit == "kHz")
else if (lunit == "khz")
{
pluginString += " unit:unit unit:khz ;\n";
}
else if (unit == "mhz" || unit == "mHz")
else if (lunit == "mhz")
{
pluginString += " unit:unit unit:mhz ;\n";
}
else if (unit == "ms")
else if (lunit == "ms")
{
pluginString += " unit:unit unit:ms ;\n";
}
else if (unit == "s")
else if (lunit == "s")
{
pluginString += " unit:unit unit:s ;\n";
}
else if (unit == "%")
else if (lunit == "%")
{
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 value = plugin.getState(key);

presetString += " <urn:distrho:" + key + ">";
presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">";

if (value.length() < 10)
presetString += " \"" + value + "\" ;\n";
@@ -827,12 +874,21 @@ void lv2_generate_ttl(const char* const basename)
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)
presetString += " pset:value " + String(int(plugin.getParameterValue(j))) + " ;\n";
presetString += " pset:value " + String(int(parameterValue)) + " ;\n";
else
presetString += " pset:value " + String(plugin.getParameterValue(j)) + " ;\n";
presetString += " pset:value " + String(parameterValue) + " ;\n";

if (j+1 == numParameters || plugin.isParameterOutput(j+1))
presetString += " ] .\n\n";


+ 41
- 22
distrho/src/DistrhoPluginVST.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -47,6 +47,7 @@
#define effCanBeAutomated 26
#define effGetProgramNameIndexed 29
#define effGetPlugCategory 35
#define effVendorSpecific 50
#define effEditKeyDown 59
#define effEditKeyUp 60
#define kVstVersion 2400
@@ -161,12 +162,25 @@ public:
class UIVst
{
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),
fEffect(effect),
fUiHelper(uiHelper),
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)
{
// FIXME only needed for windows?
@@ -397,11 +411,12 @@ public:
#endif

#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())
{
@@ -578,10 +593,9 @@ public:
{
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.bottom = tmpUI.getHeight();
tmpUI.quit();
@@ -602,10 +616,7 @@ public:
# endif
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
// Update current state from plugin side
@@ -908,6 +919,13 @@ public:
}
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 effStopProcess:
// unused
@@ -952,18 +970,18 @@ public:

void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames)
{
if (sampleFrames <= 0)
{
updateParameterOutputsAndTriggers();
return;
}

if (! fPlugin.isActive())
{
// host has not activated the plugin yet, nasty!
vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f);
}

if (sampleFrames <= 0)
{
updateParameterOutputsAndTriggers();
return;
}

#if DISTRHO_PLUGIN_WANT_TIMEPOS
static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid);

@@ -1054,6 +1072,7 @@ private:
#if DISTRHO_PLUGIN_HAS_UI
UIVst* fVstUI;
ERect fVstRect;
float fLastScaleFactor;
# if DISTRHO_OS_MAC
bool fUsingNsView;
# endif


+ 18
- 1
distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* 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 */

uint UI::getBackgroundColor() const noexcept
{
return pData->bgColor;
}

uint UI::getForegroundColor() const noexcept
{
return pData->fgColor;
}

double UI::getSampleRate() const noexcept
{
return pData->sampleRate;
@@ -103,6 +113,13 @@ void UI::setState(const char* key, const char* value)
}
#endif

#if DISTRHO_PLUGIN_WANT_STATEFILES
bool UI::requestStateFile(const char* key)
{
return pData->fileRequestCallback(key);
}
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
{


+ 1
- 1
distrho/src/DistrhoUIDSSI.cpp View File

@@ -97,7 +97,7 @@ class UIDssi
{
public:
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),
fOscData(oscData)
{


+ 59
- 21
distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -48,11 +48,12 @@ extern Window* d_lastUiWindow;
// -----------------------------------------------------------------------
// 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
@@ -70,14 +71,17 @@ struct UI::PrivateData {
bool resizeInProgress;
uint minWidth;
uint minHeight;
uint bgColor;
uint fgColor;

// 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
: sampleRate(d_lastUiSampleRate),
@@ -89,12 +93,15 @@ struct UI::PrivateData {
resizeInProgress(false),
minWidth(0),
minHeight(0),
bgColor(0),
fgColor(0),
callbacksPtr(nullptr),
editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr)
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));

@@ -144,6 +151,16 @@ struct UI::PrivateData {
if (setSizeCallbackFunc != nullptr)
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 sendNoteFunc sendNoteCall,
const setSizeFunc setSizeCall,
const float scaleFactor = 1.0f,
const fileRequestFunc fileRequestCall,
const char* const bundlePath = 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
: fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)),
#else
@@ -274,12 +294,16 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fUI != 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
// unused
@@ -341,6 +365,20 @@ public:
}
#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


+ 170
- 62
distrho/src/DistrhoUILV2.cpp View File

@@ -1,6 +1,6 @@
/*
* 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
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -25,6 +25,7 @@
#include "lv2/midi.h"
#include "lv2/options.h"
#include "lv2/parameters.h"
#include "lv2/patch.h"
#include "lv2/ui.h"
#include "lv2/urid.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
{
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),
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),
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)
{
if (fUiResize != nullptr && winId != 0)
@@ -82,8 +113,8 @@ public:
// if winId == 0 then options must not be null
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;

@@ -91,7 +122,7 @@ public:
{
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)
fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
@@ -101,7 +132,7 @@ public:
}
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)
{
@@ -124,27 +155,32 @@ public:
{
if (format == 0)
{
const uint32_t parameterOffset(fUI.getParameterOffset());
const uint32_t parameterOffset = fUI.getParameterOffset();

if (rindex < parameterOffset)
return;

DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)

const float value(*(const float*)buffer);
const float value = *(const float*)buffer;
fUI.parameterChanged(rindex-parameterOffset, value);
}
#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
}
@@ -187,11 +223,11 @@ public:
{
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);
continue;
}
@@ -211,7 +247,7 @@ public:
#if DISTRHO_PLUGIN_WANT_PROGRAMS
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);
}
@@ -237,7 +273,7 @@ protected:
{
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
String tmpStr;
@@ -248,23 +284,23 @@ protected:
tmpStr[std::strlen(key)] = '\0';

// 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
const size_t atomSize(sizeof(LV2_Atom) + msgSize);
const size_t atomSize = sizeof(LV2_Atom) + msgSize;
char atomBuf[atomSize];
std::memset(atomBuf, 0, atomSize);

// set atom info
LV2_Atom* const atom((LV2_Atom*)atomBuf);
LV2_Atom* const atom = (LV2_Atom*)atomBuf;
atom->size = msgSize;
atom->type = fKeyValueURID;
atom->type = fURIDs.dpfKeyValue;

// set atom data
std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);

// send to DSP side
fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom);
fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -275,18 +311,19 @@ protected:
if (channel > 0xF)
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;
atomMidiEvent.atom.size = 3;
atomMidiEvent.atom.type = fMidiEventURID;
atomMidiEvent.atom.type = fURIDs.midiEvent;

atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80);
atomMidiEvent.data[1] = note;
atomMidiEvent.data[2] = velocity;

// 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

@@ -298,22 +335,68 @@ protected:
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:
UIExporter fUI;

// 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
const LV2UI_Controller fController;
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
bool fWinIdWasNull;
@@ -350,13 +433,23 @@ private:
uiPtr->setSize(width, height);
}

static bool fileRequestCallback(void* ptr, const char* key)
{
return uiPtr->fileRequest(key);
}

#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)
{
@@ -364,12 +457,10 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
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
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)
{
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;
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
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)
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
else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
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

const intptr_t winId = (intptr_t)parentId;
float scaleFactor = 1.0f;
const intptr_t winId((intptr_t)parentId);
uint32_t bgColor = 0;
uint32_t fgColor = 0xffffffff;

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)
{
@@ -459,6 +551,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
else
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;
}

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)


+ 162
- 54
distrho/src/lv2/ui.h View File

@@ -1,6 +1,6 @@
/*
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>

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
@@ -28,39 +31,48 @@
#include <stdint.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)

@@ -99,18 +111,21 @@ typedef void* LV2UI_Feature_Handle;
/**
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,
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
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.

@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.

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
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.

By default, the host should only call this function for lv2:ControlPort
inputs. However, the UI can request updates for other ports statically
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.

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
any extensions. This is similar to LV2_Descriptor::extension_data().
*/
const void* (*extension_data)(const char* uri);
} LV2UI_Descriptor;
@@ -227,7 +242,8 @@ typedef struct _LV2UI_Resize {
host about the size of the UI.

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.
*/
@@ -248,7 +264,7 @@ typedef struct _LV2UI_Port_Map {
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
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
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.

@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
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.

@param handle The handle field of this struct.
@@ -332,6 +348,94 @@ typedef struct _LV2UI_Touch {
bool grabbed);
} 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)

@@ -429,3 +533,7 @@ typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index);
#endif

#endif /* LV2_UI_H */

/**
@}
*/

+ 47
- 0
examples/FileHandling/DistrhoPluginInfo.h View File

@@ -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

+ 257
- 0
examples/FileHandling/FileHandlingPlugin.cpp View File

@@ -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

+ 269
- 0
examples/FileHandling/FileHandlingUI.cpp View File

@@ -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

+ 46
- 0
examples/FileHandling/Makefile View File

@@ -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)

# --------------------------------------------------------------

+ 104
- 0
examples/FileHandling/NanoButton.cpp View File

@@ -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

+ 60
- 0
examples/FileHandling/NanoButton.hpp View File

@@ -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

+ 1
- 1
examples/Info/InfoExamplePlugin.cpp View File

@@ -253,7 +253,7 @@ protected:
* 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.
@note This value is only a hint!
Hosts might call run() with a higher or lower number of frames.


+ 6
- 2
examples/Info/InfoExampleUI.cpp View File

@@ -35,7 +35,12 @@ public:
std::memset(fStrBuf, 0, sizeof(char)*(0xff+1));

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);
}
@@ -169,7 +174,6 @@ private:
double fSampleRate;

// UI stuff
FontId fFont;
float fScale;

// temp buf for text


+ 36
- 11
examples/Parameters/ExampleUIParameters.cpp View File

@@ -39,7 +39,7 @@ public:
std::memset(fParamGrid, 0, sizeof(bool)*9);

// TODO explain why this is here
setGeometryConstraints(128, 128, true);
setGeometryConstraints(128, 128, true, false);
}

protected:
@@ -104,16 +104,40 @@ protected:
{
const uint width = getWidth();
const uint height = getHeight();
const uint minwh = std::min(width, height);
const uint bgColor = getBackgroundColor();

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
for (int i=0; i<3; ++i)
{
r.setX(3 + i*width/3);
r.setX(3 + i*minwh/3);

// top
r.setY(3);
@@ -126,7 +150,7 @@ protected:
r.draw();

// middle
r.setY(3 + height/3);
r.setY(3 + minwh/3);

if (fParamGrid[3+i])
glColor3f(0.8f, 0.5f, 0.3f);
@@ -136,7 +160,7 @@ protected:
r.draw();

// bottom
r.setY(3 + height*2/3);
r.setY(3 + minwh*2/3);

if (fParamGrid[6+i])
glColor3f(0.8f, 0.5f, 0.3f);
@@ -159,16 +183,17 @@ protected:

const uint width = getWidth();
const uint height = getHeight();
const uint minwh = std::min(width, height);

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
for (int i=0; i<3; ++i)
{
r.setX(3 + i*width/3);
r.setX(3 + i*minwh/3);

// top
r.setY(3);
@@ -190,7 +215,7 @@ protected:
}

// middle
r.setY(3 + height/3);
r.setY(3 + minwh/3);

if (r.contains(ev.pos))
{
@@ -203,7 +228,7 @@ protected:
}

// bottom
r.setY(3 + height*2/3);
r.setY(3 + minwh*2/3);

if (r.contains(ev.pos))
{


+ 0
- 8
examples/States/Makefile View File

@@ -32,14 +32,6 @@ TARGETS += jack
endif
endif

ifneq ($(MACOS_OR_WINDOWS),true)
ifeq ($(HAVE_LIBLO),true)
ifeq ($(HAVE_OPENGL),true)
TARGETS += dssi
endif
endif
endif

ifeq ($(HAVE_OPENGL),true)
TARGETS += lv2_sep
else


Loading…
Cancel
Save