Browse Source

Update dpf

Signed-off-by: falkTX <falktx@falktx.com>
tags/v1.5
falkTX 3 years ago
parent
commit
0d682df5a4
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
100 changed files with 21293 additions and 7844 deletions
  1. +50
    -0
      dpf/CMakeLists.txt
  2. +1
    -1
      dpf/LICENSE
  3. +82
    -15
      dpf/Makefile.base.mk
  4. +146
    -29
      dpf/Makefile.plugins.mk
  5. +2
    -0
      dpf/README.md
  6. +660
    -0
      dpf/cmake/DPF-plugin.cmake
  7. +33
    -14
      dpf/dgl/Application.hpp
  8. +83
    -25
      dpf/dgl/Base.hpp
  9. +160
    -7
      dpf/dgl/Cairo.hpp
  10. +13
    -8
      dpf/dgl/Color.hpp
  11. +117
    -36
      dpf/dgl/Geometry.hpp
  12. +10
    -99
      dpf/dgl/Image.hpp
  13. +47
    -16
      dpf/dgl/ImageBase.hpp
  14. +223
    -0
      dpf/dgl/ImageBaseWidgets.hpp
  15. +13
    -255
      dpf/dgl/ImageWidgets.hpp
  16. +88
    -25
      dpf/dgl/Makefile
  17. +39
    -19
      dpf/dgl/NanoVG.hpp
  18. +175
    -3
      dpf/dgl/OpenGL.hpp
  19. +35
    -20
      dpf/dgl/StandaloneWindow.hpp
  20. +163
    -0
      dpf/dgl/SubWidget.hpp
  21. +125
    -0
      dpf/dgl/TopLevelWidget.hpp
  22. +103
    -0
      dpf/dgl/Vulkan.hpp
  23. +157
    -122
      dpf/dgl/Widget.hpp
  24. +336
    -64
      dpf/dgl/Window.hpp
  25. +33
    -30
      dpf/dgl/src/Application.cpp
  26. +159
    -0
      dpf/dgl/src/ApplicationPrivateData.cpp
  27. +68
    -38
      dpf/dgl/src/ApplicationPrivateData.hpp
  28. +733
    -36
      dpf/dgl/src/Cairo.cpp
  29. +34
    -37
      dpf/dgl/src/Color.cpp
  30. +72
    -8
      dpf/dgl/src/Common.hpp
  31. +231
    -182
      dpf/dgl/src/Geometry.cpp
  32. +0
    -150
      dpf/dgl/src/Image.cpp
  33. +57
    -31
      dpf/dgl/src/ImageBase.cpp
  34. +1061
    -0
      dpf/dgl/src/ImageBaseWidgets.cpp
  35. +0
    -1108
      dpf/dgl/src/ImageWidgets.cpp
  36. +56
    -68
      dpf/dgl/src/NanoVG.cpp
  37. +575
    -58
      dpf/dgl/src/OpenGL.cpp
  38. +163
    -0
      dpf/dgl/src/SubWidget.cpp
  39. +44
    -0
      dpf/dgl/src/SubWidgetPrivateData.cpp
  40. +49
    -0
      dpf/dgl/src/SubWidgetPrivateData.hpp
  41. +98
    -0
      dpf/dgl/src/TopLevelWidget.cpp
  42. +168
    -0
      dpf/dgl/src/TopLevelWidgetPrivateData.cpp
  43. +51
    -0
      dpf/dgl/src/TopLevelWidgetPrivateData.hpp
  44. +243
    -0
      dpf/dgl/src/Vulkan.cpp
  45. +46
    -94
      dpf/dgl/src/Widget.cpp
  46. +212
    -63
      dpf/dgl/src/WidgetPrivateData.cpp
  47. +22
    -47
      dpf/dgl/src/WidgetPrivateData.hpp
  48. +191
    -1660
      dpf/dgl/src/Window.cpp
  49. +1158
    -0
      dpf/dgl/src/WindowPrivateData.cpp
  50. +309
    -0
      dpf/dgl/src/WindowPrivateData.hpp
  51. +176
    -62
      dpf/dgl/src/nanovg/fontstash.h
  52. +268
    -94
      dpf/dgl/src/nanovg/nanovg.c
  53. +95
    -18
      dpf/dgl/src/nanovg/nanovg.h
  54. +197
    -62
      dpf/dgl/src/nanovg/nanovg_gl.h
  55. +29
    -7
      dpf/dgl/src/nanovg/nanovg_gl_utils.h
  56. +4160
    -1152
      dpf/dgl/src/nanovg/stb_image.h
  57. +5011
    -2081
      dpf/dgl/src/nanovg/stb_truetype.h
  58. +0
    -0
      dpf/dgl/src/pugl-custom/pugl.h
  59. +0
    -0
      dpf/dgl/src/pugl-custom/pugl_haiku.cpp
  60. +0
    -0
      dpf/dgl/src/pugl-custom/pugl_internal.h
  61. +0
    -0
      dpf/dgl/src/pugl-custom/pugl_osx.m
  62. +0
    -0
      dpf/dgl/src/pugl-custom/pugl_win.cpp
  63. +3
    -0
      dpf/dgl/src/pugl-custom/pugl_x11.c
  64. +29
    -0
      dpf/dgl/src/pugl-extra/extras.c
  65. +50
    -0
      dpf/dgl/src/pugl-extra/extras.h
  66. +81
    -0
      dpf/dgl/src/pugl-extra/haiku.cpp
  67. +35
    -0
      dpf/dgl/src/pugl-extra/haiku.h
  68. +48
    -0
      dpf/dgl/src/pugl-extra/mac.m
  69. +118
    -0
      dpf/dgl/src/pugl-extra/win.c
  70. +111
    -0
      dpf/dgl/src/pugl-extra/x11.c
  71. +28
    -0
      dpf/dgl/src/pugl-upstream/.clang-format
  72. +4
    -0
      dpf/dgl/src/pugl-upstream/.clang-tidy
  73. +13
    -0
      dpf/dgl/src/pugl-upstream/.clant.json
  74. +19
    -0
      dpf/dgl/src/pugl-upstream/.editorconfig
  75. +3
    -0
      dpf/dgl/src/pugl-upstream/.gitignore
  76. +153
    -0
      dpf/dgl/src/pugl-upstream/.gitlab-ci.yml
  77. +15
    -0
      dpf/dgl/src/pugl-upstream/.includes.imp
  78. +13
    -0
      dpf/dgl/src/pugl-upstream/AUTHORS
  79. +13
    -0
      dpf/dgl/src/pugl-upstream/COPYING
  80. +98
    -0
      dpf/dgl/src/pugl-upstream/README.md
  81. +14
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/.clang-tidy
  82. +12
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/meson.build
  83. +45
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/cairo.hpp
  84. +70
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/gl.hpp
  85. +734
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/pugl.hpp
  86. +45
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/stub.hpp
  87. +168
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/vulkan.hpp
  88. +7
    -0
      dpf/dgl/src/pugl-upstream/bindings/cpp/meson.build
  89. +2
    -0
      dpf/dgl/src/pugl-upstream/doc/_static/meson.build
  90. +32
    -0
      dpf/dgl/src/pugl-upstream/doc/c/Doxyfile.in
  91. +5
    -0
      dpf/dgl/src/pugl-upstream/doc/c/api/meson.build
  92. +101
    -0
      dpf/dgl/src/pugl-upstream/doc/c/event-loop.rst
  93. +84
    -0
      dpf/dgl/src/pugl-upstream/doc/c/events.rst
  94. +11
    -0
      dpf/dgl/src/pugl-upstream/doc/c/index.rst
  95. +48
    -0
      dpf/dgl/src/pugl-upstream/doc/c/meson.build
  96. +26
    -0
      dpf/dgl/src/pugl-upstream/doc/c/overview.rst
  97. +20
    -0
      dpf/dgl/src/pugl-upstream/doc/c/shutting-down.rst
  98. +321
    -0
      dpf/dgl/src/pugl-upstream/doc/c/view.rst
  99. +65
    -0
      dpf/dgl/src/pugl-upstream/doc/c/world.rst
  100. +19
    -0
      dpf/dgl/src/pugl-upstream/doc/c/xml/meson.build

+ 50
- 0
dpf/CMakeLists.txt View File

@@ -0,0 +1,50 @@
# DISTRHO Plugin Framework (DPF)
# Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
#
# SPDX-License-Identifier: ISC

cmake_minimum_required(VERSION 3.7)

project(DPF)

# ensure c++11 at minimum, the parent project can override
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()

# check if we are building from this project, or are imported by another
if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(DPF_BUILD_FROM_HERE TRUE)
else()
set(DPF_BUILD_FROM_HERE FALSE)
endif()

option(DPF_LIBRARIES "Build the libraries" "${DPF_BUILD_FROM_HERE}")
option(DPF_EXAMPLES "Build the examples" "${DPF_BUILD_FROM_HERE}")

set(DPF_ROOT_DIR "${PROJECT_SOURCE_DIR}" CACHE INTERNAL
"Root directory of the DISTRHO Plugin Framework.")

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(DPF-plugin)

if(DPF_LIBRARIES)
if(NOT (MSVC OR APPLE)) # TODO skip this one for now
dpf__add_dgl_cairo()
endif()
dpf__add_dgl_opengl()
endif()

if(DPF_EXAMPLES)
if(NOT (MSVC OR APPLE)) # TODO skip this one for now
add_subdirectory("examples/CairoUI")
endif()
#add_subdirectory("examples/ExternalUI")
add_subdirectory("examples/FileHandling")
add_subdirectory("examples/Info")
add_subdirectory("examples/Latency")
add_subdirectory("examples/Meters")
add_subdirectory("examples/MidiThrough")
add_subdirectory("examples/Parameters")
add_subdirectory("examples/States")
endif()

+ 1
- 1
dpf/LICENSE View File

@@ -1,5 +1,5 @@
DISTRHO Plugin Framework (DPF) DISTRHO Plugin Framework (DPF)
Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>


Permission to use, copy, modify, and/or distribute this software for any purpose with Permission to use, copy, modify, and/or distribute this software for any purpose with
or without fee is hereby granted, provided that the above copyright notice and this or without fee is hereby granted, provided that the above copyright notice and this


+ 82
- 15
dpf/Makefile.base.mk View File

@@ -38,6 +38,9 @@ endif
ifneq (,$(findstring mingw,$(TARGET_MACHINE))) ifneq (,$(findstring mingw,$(TARGET_MACHINE)))
WINDOWS=true WINDOWS=true
endif endif
ifneq (,$(findstring windows,$(TARGET_MACHINE)))
WINDOWS=true
endif


endif endif
endif endif
@@ -148,6 +151,9 @@ endif
ifeq ($(MACOS),true) ifeq ($(MACOS),true)
# MacOS linker flags # MacOS linker flags
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs
ifneq ($(SKIP_STRIPPING),true)
LINK_OPTS += -Wl,-x
endif
else else
# Common linker flags # Common linker flags
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed
@@ -157,19 +163,13 @@ endif
endif endif


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


ifeq ($(WINDOWS),true) ifeq ($(WINDOWS),true)
# mingw has issues with this specific optimization
# See https://github.com/falkTX/Carla/issues/696
BASE_OPTS += -fno-rerun-cse-after-loop
# See https://github.com/falkTX/Carla/issues/855
# Needed for windows, see https://github.com/falkTX/Carla/issues/855
BASE_OPTS += -mstackrealign BASE_OPTS += -mstackrealign
ifeq ($(BUILDING_FOR_WINDOWS),true)
BASE_FLAGS += -DBUILDING_CARLA_FOR_WINDOWS
endif
else else
# Not needed for Windows # Not needed for Windows
BASE_FLAGS += -fPIC -DPIC BASE_FLAGS += -fPIC -DPIC
@@ -206,7 +206,7 @@ endif


ifeq ($(TESTBUILD),true) ifeq ($(TESTBUILD),true)
BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings
BASE_FLAGS += -Wpointer-arith -Wabi -Winit-self -Wuninitialized -Wstrict-overflow=5
BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5
# BASE_FLAGS += -Wfloat-equal # BASE_FLAGS += -Wfloat-equal
ifeq ($(CC),clang) ifeq ($(CC),clang)
BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command
@@ -228,20 +228,28 @@ endif
# Check for required libraries # Check for required libraries


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


ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
ifeq ($(MACOS_OR_WINDOWS),true)
HAVE_OPENGL = true HAVE_OPENGL = true
else else
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
ifneq ($(HAIKU),true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true)
HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true)
HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true)
endif
endif endif


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Check for optional libraries # Check for optional libraries


HAVE_JACK = $(shell $(PKG_CONFIG) --exists jack && echo true)
HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true)


# backwards compat
HAVE_JACK = true

# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Set Generic DGL stuff # Set Generic DGL stuff


@@ -250,7 +258,7 @@ DGL_SYSTEM_LIBS += -lbe
endif endif


ifeq ($(MACOS),true) ifeq ($(MACOS),true)
DGL_SYSTEM_LIBS += -framework Cocoa
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo
endif endif


ifeq ($(WINDOWS),true) ifeq ($(WINDOWS),true)
@@ -259,8 +267,21 @@ endif


ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
ifeq ($(HAVE_X11),true) ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11)
ifeq ($(HAVE_XCURSOR),true)
# TODO -DHAVE_XCURSOR
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor)
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xcursor)
endif
ifeq ($(HAVE_XEXT),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xext) -DHAVE_XEXT -DHAVE_XSYNC
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xext)
endif
ifeq ($(HAVE_XRANDR),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr)
endif
endif endif
endif endif


@@ -291,7 +312,7 @@ OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl)
endif endif


ifeq ($(MACOS),true) ifeq ($(MACOS),true)
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations
OPENGL_LIBS = -framework OpenGL OPENGL_LIBS = -framework OpenGL
endif endif


@@ -308,6 +329,52 @@ HAVE_CAIRO_OR_OPENGL = true


endif endif


# ---------------------------------------------------------------------------------------------------------------------
# Set Stub specific stuff

ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
HAVE_STUB = true
else
HAVE_STUB = $(HAVE_X11)
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set Vulkan specific stuff

ifeq ($(HAVE_VULKAN),true)

DGL_FLAGS += -DHAVE_VULKAN

VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan)
VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan)

ifneq ($(WINDOWS),true)
VULKAN_LIBS += -ldl
endif

endif

# ---------------------------------------------------------------------------------------------------------------------
# Set optional libraries specific stuff

ifeq ($(HAVE_LIBLO),true)
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo)
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo)
endif

# ---------------------------------------------------------------------------------------------------------------------
# Backwards-compatible HAVE_DGL

ifeq ($(MACOS_OR_WINDOWS),true)
HAVE_DGL = true
else ifeq ($(HAVE_OPENGL),true)
ifeq ($(HAIKU),true)
HAVE_DGL = true
else
HAVE_DGL = $(HAVE_X11)
endif
endif

# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Set app extension # Set app extension




+ 146
- 29
dpf/Makefile.plugins.mk View File

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




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


include $(DPF_PATH)/Makefile.base.mk include $(DPF_PATH)/Makefile.base.mk


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


ifeq ($(DPF_CUSTOM_PATH),)
TARGET_DIR = ../../bin TARGET_DIR = ../../bin
BUILD_DIR = ../../build/$(NAME) BUILD_DIR = ../../build/$(NAME)
else
ifeq ($(DPF_CUSTOM_TARGET_DIR),)
$(error DPF_CUSTOM_TARGET_DIR is not set)
else
TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR)
endif
ifeq ($(DPF_CUSTOM_BUILD_DIR),)
$(error DPF_CUSTOM_BUILD_DIR is not set)
else
BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR)
endif
endif


BUILD_C_FLAGS += -I. BUILD_C_FLAGS += -I.
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl


ifeq ($(HAVE_CAIRO),true)
DGL_FLAGS += -DHAVE_CAIRO
ifeq ($(HAVE_LIBLO),true)
BASE_FLAGS += -DHAVE_LIBLO
endif endif


ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DHAVE_OPENGL
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
JACK_LIBS = -ldl
endif endif


ifeq ($(HAVE_JACK),true)
# backwards compat
BASE_FLAGS += -DHAVE_JACK BASE_FLAGS += -DHAVE_JACK
endif


ifeq ($(HAVE_LIBLO),true)
BASE_FLAGS += -DHAVE_LIBLO
# ---------------------------------------------------------------------------------------------------------------------
# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html

ifeq ($(LINUX),true)
VST3_FILENAME = $(TARGET_PROCESSOR)-linux/$(NAME).so
endif
ifeq ($(MACOS),true)
VST3_FILENAME = MacOS/$(NAME)
endif
ifeq ($(WINDOWS),true)
VST3_FILENAME = $(TARGET_PROCESSOR)-win/$(NAME).vst3
endif endif


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
@@ -46,6 +71,10 @@ endif
OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o)
OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o)


ifeq ($(MACOS),true)
OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o
endif

# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Set plugin binary file targets # Set plugin binary file targets


@@ -56,12 +85,28 @@ dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT)
lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT)
lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT)
lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT)
vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT)
vst2 = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT)
ifneq ($(VST3_FILENAME),)
vst3 = $(TARGET_DIR)/$(NAME).vst3/Contents/$(VST3_FILENAME)
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set plugin symbols to export

ifeq ($(MACOS),true)
SYMBOLS_LADSPA = -Wl,-exported_symbol,_ladspa_descriptor
SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_dssi_descriptor
SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl
SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor
SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain
SYMBOLS_VST3 = -Wl,-exported_symbol,_GetPluginFactory -Wl,-exported_symbol,_bundleEntry -Wl,-exported_symbol,_bundleExit
endif


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Handle UI stuff, disable UI support automatically # Handle UI stuff, disable UI support automatically


ifeq ($(FILES_UI),) ifeq ($(FILES_UI),)
HAVE_DGL = false
UI_TYPE = none UI_TYPE = none
endif endif


@@ -69,9 +114,17 @@ ifeq ($(UI_TYPE),)
UI_TYPE = opengl UI_TYPE = opengl
endif endif


ifeq ($(UI_TYPE),generic)
ifeq ($(HAVE_OPENGL),true)
UI_TYPE = opengl
else ifeq ($(HAVE_CAIRO),true)
UI_TYPE = cairo
endif
endif

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


ifeq ($(UI_TYPE),opengl) ifeq ($(UI_TYPE),opengl)
DGL_FLAGS += -DDGL_OPENGL
ifeq ($(HAVE_OPENGL),true) ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL
DGL_FLAGS += $(OPENGL_FLAGS) DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS) DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a
@@ -93,11 +146,32 @@ HAVE_DGL = false
endif endif
endif endif


ifeq ($(UI_TYPE),vulkan)
ifeq ($(HAVE_VULKAN),true)
DGL_FLAGS += -DDGL_VULKAN
DGL_FLAGS += $(VULKAN_FLAGS)
DGL_LIBS += $(VULKAN_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),external) ifeq ($(UI_TYPE),external)
DGL_FLAGS += -DDGL_EXTERNAL DGL_FLAGS += -DDGL_EXTERNAL
HAVE_DGL = true HAVE_DGL = true
endif endif


ifeq ($(UI_TYPE),stub)
ifeq ($(HAVE_STUB),true)
DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm


ifneq ($(HAVE_DGL),true) ifneq ($(HAVE_DGL),true)
@@ -107,6 +181,10 @@ DGL_LIBS =
OBJS_UI = OBJS_UI =
endif endif


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

# TODO split dsp and ui object build flags # TODO split dsp and ui object build flags
BASE_FLAGS += $(DGL_FLAGS) BASE_FLAGS += $(DGL_FLAGS)


@@ -118,6 +196,11 @@ all:
# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Common # Common


$(BUILD_DIR)/%.S.o: %.S
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@

$(BUILD_DIR)/%.c.o: %.c $(BUILD_DIR)/%.c.o: %.c
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<" @echo "Compiling $<"
@@ -138,6 +221,23 @@ clean:
rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# DGL

$(DPF_PATH)/build/libdgl-cairo.a:
$(MAKE) -C $(DPF_PATH)/dgl cairo

$(DPF_PATH)/build/libdgl-opengl.a:
$(MAKE) -C $(DPF_PATH)/dgl opengl

$(DPF_PATH)/build/libdgl-stub.a:
$(MAKE) -C $(DPF_PATH)/dgl stub

$(DPF_PATH)/build/libdgl-vulkan.a:
$(MAKE) -C $(DPF_PATH)/dgl vulkan

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

AS_PUGL_NAMESPACE = $(subst -,_,$(1))


$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp
-@mkdir -p $(BUILD_DIR) -@mkdir -p $(BUILD_DIR)
@@ -149,15 +249,20 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp
@echo "Compiling DistrhoUIMain.cpp ($*)" @echo "Compiling DistrhoUIMain.cpp ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@


$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUI_macOS.mm ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$(call AS_PUGL_NAMESPACE,$*) -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@

$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp
-@mkdir -p $(BUILD_DIR) -@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoPluginMain.cpp (JACK)" @echo "Compiling DistrhoPluginMain.cpp (JACK)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags jack) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@


$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp
-@mkdir -p $(BUILD_DIR) -@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUIMain.cpp (DSSI)" @echo "Compiling DistrhoUIMain.cpp (DSSI)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags liblo) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# JACK # JACK
@@ -171,7 +276,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o
endif endif
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating JACK standalone for $(NAME)" @echo "Creating JACK standalone for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs jack) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(JACK_LIBS) -o $@


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# LADSPA # LADSPA
@@ -181,7 +286,7 @@ ladspa: $(ladspa_dsp)
$(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o $(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating LADSPA plugin for $(NAME)" @echo "Creating LADSPA plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# DSSI # DSSI
@@ -193,12 +298,12 @@ dssi_ui: $(dssi_ui)
$(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating DSSI plugin library for $(NAME)" @echo "Creating DSSI plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@


$(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating DSSI UI for $(NAME)" @echo "Creating DSSI UI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs liblo) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# LV2 # LV2
@@ -210,36 +315,46 @@ lv2_sep: $(lv2_dsp) $(lv2_ui)
$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin for $(NAME)" @echo "Creating LV2 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) $(SYMBOLS_LV2UI) -o $@


$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin library for $(NAME)" @echo "Creating LV2 plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2) -o $@


$(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin UI for $(NAME)" @echo "Creating LV2 plugin UI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# VST
# VST2


vst: $(vst)
vst2 vst: $(vst2)


ifeq ($(HAVE_DGL),true) ifeq ($(HAVE_DGL),true)
$(vst): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST.cpp.o $(DGL_LIB)
$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB)
else else
$(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o
$(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o
endif endif
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
@echo "Creating VST plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
@echo "Creating VST2 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# VST3

vst3: $(vst3)

$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating VST3 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@


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


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


@@ -247,11 +362,13 @@ endif
-include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d


-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d


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

+ 2
- 0
dpf/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) - [Stone Phaser](https://github.com/jpcima/stone-phaser)
- [String-machine](https://github.com/jpcima/string-machine) - [String-machine](https://github.com/jpcima/string-machine)
- [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins) - [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins)
- [VL1-emulator](https://github.com/linuxmao-org/VL1-emulator)
- [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper) - [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper)
- [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum) - [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum)
- [YK Chorus](https://github.com/SpotlightKid/ykchorus) - [YK Chorus](https://github.com/SpotlightKid/ykchorus)
- [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) - [ZamAudio Suite](https://github.com/zamaudio/zam-plugins)
## Work in progress ## Work in progress
- [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2) - [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2)
- [fverb](https://github.com/jpcima/fverb)
- [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins)
- [gunshot](https://github.com/soerenbnoergaard/gunshot) - [gunshot](https://github.com/soerenbnoergaard/gunshot)
- [midiomatic](https://github.com/SpotlightKid/midiomatic) - [midiomatic](https://github.com/SpotlightKid/midiomatic)


+ 660
- 0
dpf/cmake/DPF-plugin.cmake View File

@@ -0,0 +1,660 @@
# DISTRHO Plugin Framework (DPF)
# Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
#
# SPDX-License-Identifier: ISC

# ------------------------------------------------------------------------------
# CMake support module for the DISTRHO Plugin Framework
#
# The purpose of this module is to help building music plugins easily, when the
# project uses CMake as its build system.
#
# In order to use the helpers provided by this module, a plugin author should
# add DPF as a subproject, making the function `dpf_add_plugin` available.
# The usage of this function is documented below in greater detail.
#
# Example project `CMakeLists.txt`:
#
# ```
# cmake_minimum_required(VERSION 3.7)
# project(MyPlugin)
#
# add_subdirectory(DPF)
#
# dpf_add_plugin(MyPlugin
# TARGETS lv2 vst2
# UI_TYPE opengl
# FILES_DSP
# src/MyPlugin.cpp
# FILES_UI
# src/MyUI.cpp)
#
# target_include_directories(MyPlugin
# PUBLIC src)
# ```
#
# Important: note that properties, such as include directories, definitions,
# and linked libraries *must* be marked with `PUBLIC` so they take effect and
# propagate into all the plugin targets.

include(CMakeParseArguments)

# ------------------------------------------------------------------------------
# DPF public functions
# ------------------------------------------------------------------------------

# dpf_add_plugin(name <args...>)
# ------------------------------------------------------------------------------
#
# Add a plugin built using the DISTRHO Plugin Framework.
#
# ------------------------------------------------------------------------------
# Created targets:
#
# `<name>`
# static library: the common part of the plugin
# The public properties set on this target apply to both DSP and UI.
#
# `<name>-dsp`
# static library: the DSP part of the plugin
# The public properties set on this target apply to the DSP only.
#
# `<name>-ui`
# static library: the UI part of the plugin
# The public properties set on this target apply to the UI only.
#
# `<name>-<target>` for each target specified with the `TARGETS` argument.
# This is target-dependent and not intended for public use.
#
# ------------------------------------------------------------------------------
# Arguments:
#
# `TARGETS` <tgt1>...<tgtN>
# a list of one of more of the following target types:
# `jack`, `ladspa`, `dssi`, `lv2`, `vst2`
#
# `UI_TYPE` <type>
# the user interface type: `opengl` (default), `cairo`
#
# `MONOLITHIC`
# build LV2 as a single binary for UI and DSP
#
# `FILES_DSP` <file1>...<fileN>
# list of sources which are part of the DSP
#
# `FILES_UI` <file1>...<fileN>
# list of sources which are part of the UI
# empty indicates the plugin does not have UI
#
# `FILES_COMMON` <file1>...<fileN>
# list of sources which are part of both DSP and UI
#
function(dpf_add_plugin NAME)
set(options MONOLITHIC)
set(oneValueArgs UI_TYPE)
set(multiValueArgs TARGETS FILES_DSP FILES_UI FILES_COMMON)
cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if("${_dpf_plugin_UI_TYPE}" STREQUAL "")
set(_dpf_plugin_UI_TYPE "opengl")
endif()

set(_dgl_library)
if(_dpf_plugin_FILES_UI)
if(_dpf_plugin_UI_TYPE STREQUAL "cairo")
dpf__add_dgl_cairo()
set(_dgl_library dgl-cairo)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl")
dpf__add_dgl_opengl()
set(_dgl_library dgl-opengl)
else()
message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}")
endif()
endif()

###
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_COMMON)
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP)
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_UI)

###
dpf__add_static_library("${NAME}" ${_dpf_plugin_FILES_COMMON})
target_include_directories("${NAME}" PUBLIC
"${DPF_ROOT_DIR}/distrho")

if(_dgl_library)
# make sure that all code will see DGL_* definitions
target_link_libraries("${NAME}" PUBLIC
"${_dgl_library}-definitions"
dgl-system-libs-definitions)
endif()

dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP})
target_link_libraries("${NAME}-dsp" PUBLIC "${NAME}")

if(_dgl_library)
dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI})
target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library})
# add the files containing Objective-C classes, recompiled under namespace
dpf__add_plugin_specific_ui_sources("${NAME}-ui")
else()
add_library("${NAME}-ui" INTERFACE)
endif()

###
foreach(_target ${_dpf_plugin_TARGETS})
if(_target STREQUAL "jack")
dpf__build_jack("${NAME}" "${_dgl_library}")
elseif(_target STREQUAL "ladspa")
dpf__build_ladspa("${NAME}")
elseif(_target STREQUAL "dssi")
dpf__build_dssi("${NAME}" "${_dgl_library}")
elseif(_target STREQUAL "lv2")
dpf__build_lv2("${NAME}" "${_dgl_library}" "${_dpf_plugin_MONOLITHIC}")
elseif(_target STREQUAL "vst2")
dpf__build_vst2("${NAME}" "${_dgl_library}")
else()
message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}")
endif()
endforeach()
endfunction()

# ------------------------------------------------------------------------------
# DPF private functions (prefixed with `dpf__`)
# ------------------------------------------------------------------------------

# Note: The $<0:> trick is to prevent MSVC from appending the build type
# to the output directory.
#

# dpf__build_jack
# ------------------------------------------------------------------------------
#
# Add build rules for a JACK program.
#
function(dpf__build_jack NAME DGL_LIBRARY)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_executable("${NAME}-jack" ${_no_srcs})
dpf__add_plugin_main("${NAME}-jack" "jack")
dpf__add_ui_main("${NAME}-jack" "jack" "${DGL_LIBRARY}")
target_link_libraries("${NAME}-jack" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-jack" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}")

# Note: libjack will be linked at runtime
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
target_link_libraries("${NAME}-jack" PRIVATE "dl")
endif()
endfunction()

# dpf__build_ladspa
# ------------------------------------------------------------------------------
#
# Add build rules for a DSSI plugin.
#
function(dpf__build_ladspa NAME)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-ladspa" ${_no_srcs})
dpf__add_plugin_main("${NAME}-ladspa" "ladspa")
target_link_libraries("${NAME}-ladspa" PRIVATE "${NAME}-dsp")
set_target_properties("${NAME}-ladspa" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}-ladspa"
PREFIX "")
endfunction()

# dpf__build_dssi
# ------------------------------------------------------------------------------
#
# Add build rules for a DSSI plugin.
#
function(dpf__build_dssi NAME DGL_LIBRARY)
find_package(PkgConfig)
pkg_check_modules(LIBLO "liblo")
if(NOT LIBLO_FOUND)
dpf__warn_once_only(missing_liblo
"liblo is not found, skipping the `dssi` plugin targets")
return()
endif()

link_directories(${LIBLO_LIBRARY_DIRS})

dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-dssi" ${_no_srcs})
dpf__add_plugin_main("${NAME}-dssi" "dssi")
target_link_libraries("${NAME}-dssi" PRIVATE "${NAME}-dsp")
set_target_properties("${NAME}-dssi" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}-dssi"
PREFIX "")

if(DGL_LIBRARY)
dpf__add_executable("${NAME}-dssi-ui" ${_no_srcs})
dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${DGL_LIBRARY}")
target_link_libraries("${NAME}-dssi-ui" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-dssi-ui" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}-dssi/$<0:>"
OUTPUT_NAME "${NAME}_ui")

target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS})
target_link_libraries("${NAME}-dssi-ui" PRIVATE ${LIBLO_LIBRARIES})
endif()
endfunction()

# dpf__build_lv2
# ------------------------------------------------------------------------------
#
# Add build rules for a LV2 plugin.
#
function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-lv2" ${_no_srcs})
dpf__add_plugin_main("${NAME}-lv2" "lv2")
target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-dsp")
set_target_properties("${NAME}-lv2" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>"
OUTPUT_NAME "${NAME}_dsp"
PREFIX "")

if(DGL_LIBRARY)
if(MONOLITHIC)
dpf__add_ui_main("${NAME}-lv2" "lv2" "${DGL_LIBRARY}")
target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-lv2" PROPERTIES
OUTPUT_NAME "${NAME}")
else()
dpf__add_module("${NAME}-lv2-ui" ${_no_srcs})
dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${DGL_LIBRARY}")
target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-lv2-ui" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>"
OUTPUT_NAME "${NAME}_ui"
PREFIX "")
endif()
endif()

dpf__add_lv2_ttl_generator()
add_dependencies("${NAME}-lv2" lv2_ttl_generator)

add_custom_command(TARGET "${NAME}-lv2" POST_BUILD
COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
"$<TARGET_FILE:lv2_ttl_generator>"
"$<TARGET_FILE:${NAME}-lv2>"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2"
DEPENDS lv2_ttl_generator)
endfunction()

# dpf__build_vst2
# ------------------------------------------------------------------------------
#
# Add build rules for a VST2 plugin.
#
function(dpf__build_vst2 NAME DGL_LIBRARY)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-vst2" ${_no_srcs})
dpf__add_plugin_main("${NAME}-vst2" "vst2")
dpf__add_ui_main("${NAME}-vst2" "vst2" "${DGL_LIBRARY}")
target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-vst2" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}-vst2"
PREFIX "")
endfunction()

# dpf__add_dgl_cairo
# ------------------------------------------------------------------------------
#
# Add the Cairo variant of DGL, if not already available.
#
function(dpf__add_dgl_cairo)
if(TARGET dgl-cairo)
return()
endif()

find_package(PkgConfig)
pkg_check_modules(CAIRO "cairo" REQUIRED)

link_directories(${CAIRO_LIBRARY_DIRS})

dpf__add_static_library(dgl-cairo STATIC
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"
"${DPF_ROOT_DIR}/dgl/src/Resources.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp"
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Cairo.cpp")
if(NOT APPLE)
target_sources(dgl-cairo PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp")
else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm
#target_sources(dgl-opengl PRIVATE
# "${DPF_ROOT_DIR}/dgl/src/pugl.mm")
endif()
target_include_directories(dgl-cairo PUBLIC
"${DPF_ROOT_DIR}/dgl")
target_include_directories(dgl-cairo PUBLIC
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include")

dpf__add_dgl_system_libs()
target_link_libraries(dgl-cairo PRIVATE dgl-system-libs)

add_library(dgl-cairo-definitions INTERFACE)
target_compile_definitions(dgl-cairo-definitions INTERFACE "DGL_CAIRO" "HAVE_CAIRO")

target_include_directories(dgl-cairo PUBLIC ${CAIRO_INCLUDE_DIRS})
if(MINGW)
target_link_libraries(dgl-cairo PRIVATE ${CAIRO_STATIC_LIBRARIES})
else()
target_link_libraries(dgl-cairo PRIVATE ${CAIRO_LIBRARIES})
endif()
target_link_libraries(dgl-cairo PRIVATE dgl-cairo-definitions)
endfunction()

# dpf__add_dgl_opengl
# ------------------------------------------------------------------------------
#
# Add the OpenGL variant of DGL, if not already available.
#
function(dpf__add_dgl_opengl)
if(TARGET dgl-opengl)
return()
endif()

if(NOT OpenGL_GL_PREFERENCE)
set(OpenGL_GL_PREFERENCE "LEGACY")
endif()

find_package(OpenGL REQUIRED)

dpf__add_static_library(dgl-opengl STATIC
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"
"${DPF_ROOT_DIR}/dgl/src/Resources.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp"
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp"
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp")
if(NOT APPLE)
target_sources(dgl-opengl PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp")
else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm
#target_sources(dgl-opengl PRIVATE
# "${DPF_ROOT_DIR}/dgl/src/pugl.mm")
endif()
target_include_directories(dgl-opengl PUBLIC
"${DPF_ROOT_DIR}/dgl")
target_include_directories(dgl-opengl PUBLIC
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include")

if(APPLE)
target_compile_definitions(dgl-opengl PUBLIC "GL_SILENCE_DEPRECATION")
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-opengl PRIVATE dgl-system-libs)

add_library(dgl-opengl-definitions INTERFACE)
target_compile_definitions(dgl-opengl-definitions INTERFACE "DGL_OPENGL" "HAVE_OPENGL")

target_include_directories(dgl-opengl PUBLIC "${OPENGL_INCLUDE_DIR}")
target_link_libraries(dgl-opengl PRIVATE dgl-opengl-definitions "${OPENGL_gl_LIBRARY}")
endfunction()

# dpf__add_plugin_specific_ui_sources
# ------------------------------------------------------------------------------
#
# Compile plugin-specific UI sources into the target designated by the given
# name. There are some special considerations here:
# - On most platforms, sources can be compiled only once, as part of DGL;
# - On macOS, for any sources which define Objective-C interfaces, these must
# be recompiled for each plugin under a unique namespace. In this case, the
# name must be a plugin-specific identifier, and it will be used for computing
# the unique ID along with the project version.
function(dpf__add_plugin_specific_ui_sources NAME)
if(APPLE)
target_sources("${NAME}" PRIVATE
"${DPF_ROOT_DIR}/distrho/DistrhoUI_macOS.mm")
string(SHA256 _hash "${NAME}:${PROJECT_VERSION}")
target_compile_definitions("${NAME}" PUBLIC "PUGL_NAMESPACE=${_hash}")
endif()
endfunction()

# dpf__add_dgl_system_libs
# ------------------------------------------------------------------------------
#
# Find system libraries required by DGL and add them as an interface target.
#
function(dpf__add_dgl_system_libs)
if(TARGET dgl-system-libs)
return()
endif()
add_library(dgl-system-libs INTERFACE)
add_library(dgl-system-libs-definitions INTERFACE)
if(HAIKU)
target_link_libraries(dgl-system-libs INTERFACE "be")
elseif(WIN32)
target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32")
elseif(APPLE)
find_library(APPLE_COCOA_FRAMEWORK "Cocoa")
find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo")
target_link_libraries(dgl-system-libs INTERFACE "${APPLE_COCOA_FRAMEWORK}" "${APPLE_COREVIDEO_FRAMEWORK}")
else()
find_package(X11 REQUIRED)
target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}")
target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11")
if(X11_Xext_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT")
endif()
if(X11_XSync_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC")
endif()
if(X11_Xrandr_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR")
endif()
#if(X11_Xcursor_FOUND)
# target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}")
# target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR")
#endif()
endif()

if(MSVC)
file(MAKE_DIRECTORY "${DPF_ROOT_DIR}/khronos/GL")
foreach(_gl_header "glext.h")
if(NOT EXISTS "${DPF_ROOT_DIR}/khronos/GL/${_gl_header}")
file(DOWNLOAD "https://www.khronos.org/registry/OpenGL/api/GL/${_gl_header}" "${DPF_ROOT_DIR}/khronos/GL/${_gl_header}" SHOW_PROGRESS)
endif()
endforeach()
foreach(_khr_header "khrplatform.h")
if(NOT EXISTS "${DPF_ROOT_DIR}/khronos/KHR/${_khr_header}")
file(DOWNLOAD "https://www.khronos.org/registry/EGL/api/KHR/${_khr_header}" "${DPF_ROOT_DIR}/khronos/KHR/${_khr_header}" SHOW_PROGRESS)
endif()
endforeach()
target_include_directories(dgl-system-libs-definitions INTERFACE "${DPF_ROOT_DIR}/khronos")
endif()

target_link_libraries(dgl-system-libs INTERFACE dgl-system-libs-definitions)
endfunction()

# dpf__add_executable
# ------------------------------------------------------------------------------
#
# Adds an executable target, and set some default properties on the target.
#
function(dpf__add_executable NAME)
add_executable("${NAME}" ${ARGN})
dpf__set_target_defaults("${NAME}")
if(MINGW)
target_link_libraries("${NAME}" PRIVATE "-static")
endif()
endfunction()

# dpf__add_module
# ------------------------------------------------------------------------------
#
# Adds a module target, and set some default properties on the target.
#
function(dpf__add_module NAME)
add_library("${NAME}" MODULE ${ARGN})
dpf__set_target_defaults("${NAME}")
if(MINGW)
target_link_libraries("${NAME}" PRIVATE "-static")
endif()
endfunction()

# dpf__add_static_library
# ------------------------------------------------------------------------------
#
# Adds a static library target, and set some default properties on the target.
#
function(dpf__add_static_library NAME)
add_library("${NAME}" STATIC ${ARGN})
dpf__set_target_defaults("${NAME}")
endfunction()

# dpf__set_target_defaults
# ------------------------------------------------------------------------------
#
# Set default properties which must apply to all DPF-defined targets.
#
function(dpf__set_target_defaults NAME)
set_target_properties("${NAME}" PROPERTIES
POSITION_INDEPENDENT_CODE TRUE
C_VISIBILITY_PRESET "hidden"
CXX_VISIBILITY_PRESET "hidden"
VISIBILITY_INLINES_HIDDEN TRUE)
if(WIN32)
target_compile_definitions("${NAME}" PUBLIC "NOMINMAX")
endif()
if (MINGW)
target_compile_options("${NAME}" PUBLIC "-mstackrealign")
endif()
if (MSVC)
target_compile_options("${NAME}" PUBLIC "/UTF-8")
target_compile_definitions("${NAME}" PUBLIC "_CRT_SECURE_NO_WARNINGS")
endif()
endfunction()

# dpf__add_plugin_main
# ------------------------------------------------------------------------------
#
# Adds plugin code to the given target.
#
function(dpf__add_plugin_main NAME TARGET)
target_sources("${NAME}" PRIVATE
"${DPF_ROOT_DIR}/distrho/DistrhoPluginMain.cpp")
dpf__add_plugin_target_definition("${NAME}" "${TARGET}")
endfunction()

# dpf__add_ui_main
# ------------------------------------------------------------------------------
#
# Adds UI code to the given target (only if the target has UI).
#
function(dpf__add_ui_main NAME TARGET HAS_UI)
if(HAS_UI)
target_sources("${NAME}" PRIVATE
"${DPF_ROOT_DIR}/distrho/DistrhoUIMain.cpp")
dpf__add_plugin_target_definition("${NAME}" "${TARGET}")
endif()
endfunction()

# dpf__add_plugin_target_definition
# ------------------------------------------------------------------------------
#
# Adds the plugins target macro definition.
# This selects which entry file is compiled according to the target type.
#
function(dpf__add_plugin_target_definition NAME TARGET)
string(TOUPPER "${TARGET}" _upperTarget)
target_compile_definitions("${NAME}" PRIVATE "DISTRHO_PLUGIN_TARGET_${_upperTarget}")
endfunction()

# dpf__add_lv2_ttl_generator
# ------------------------------------------------------------------------------
#
# Build the LV2 TTL generator.
#
function(dpf__add_lv2_ttl_generator)
if(TARGET lv2_ttl_generator)
return()
endif()
add_executable(lv2_ttl_generator "${DPF_ROOT_DIR}/utils/lv2-ttl-generator/lv2_ttl_generator.c")
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
target_link_libraries(lv2_ttl_generator PRIVATE "dl")
endif()
endfunction()

# dpf__ensure_sources_non_empty
# ------------------------------------------------------------------------------
#
# Ensure the given source list contains at least one file.
# The function appends an empty source file to the list if necessary.
# This is useful when CMake does not permit to add targets without sources.
#
function(dpf__ensure_sources_non_empty VAR)
if(NOT "" STREQUAL "${${VAR}}")
return()
endif()
set(_file "${CMAKE_CURRENT_BINARY_DIR}/_dpf_empty.c")
if(NOT EXISTS "${_file}")
file(WRITE "${_file}" "")
endif()
set("${VAR}" "${_file}" PARENT_SCOPE)
endfunction()

# dpf__create_dummy_source_list
# ------------------------------------------------------------------------------
#
# Create a dummy source list which is equivalent to compiling nothing.
# This is only for compatibility with older CMake versions, which refuse to add
# targets without any sources.
#
macro(dpf__create_dummy_source_list VAR)
set("${VAR}")
if(CMAKE_VERSION VERSION_LESS "3.11")
dpf__ensure_sources_non_empty("${VAR}")
endif()
endmacro()

# dpf__warn_once
# ------------------------------------------------------------------------------
#
# Prints a warning message once only.
#
function(dpf__warn_once_only TOKEN MESSAGE)
get_property(_warned GLOBAL PROPERTY "dpf__have_warned_${TOKEN}")
if(NOT _warned)
set_property(GLOBAL PROPERTY "dpf__have_warned_${TOKEN}" TRUE)
message(WARNING "${MESSAGE}")
endif()
endfunction()

+ 33
- 14
dpf/dgl/Application.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,22 +21,15 @@


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// Forward class names

class Window;

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


/** /**
Base DGL Application class. Base DGL Application class.


One application instance is required for creating a window. One application instance is required for creating a window.
There's no single/global application instance in DGL, and multiple
windows can share the same app instance.
There's no single/global application instance in DGL, and multiple windows can share the same app instance.


In standalone mode an application will automatically quit its
event-loop when all its windows are closed.
In standalone mode an application will automatically quit its event-loop when all its windows are closed.
*/ */
class Application class Application
{ {
@@ -44,7 +37,8 @@ public:
/** /**
Constructor. Constructor.
*/ */
Application();
// NOTE: the default value is not yet passed, so we catch where we use this
Application(bool isStandalone = true);


/** /**
Destructor. Destructor.
@@ -62,11 +56,12 @@ public:
idle() is called at regular intervals. idle() is called at regular intervals.
@note This function is meant for standalones only, *never* call this from plugins. @note This function is meant for standalones only, *never* call this from plugins.
*/ */
void exec(int idleTime = 10);
void exec(uint idleTimeInMs = 30);


/** /**
Quit the application. Quit the application.
This stops the event-loop and closes all Windows. This stops the event-loop and closes all Windows.
@note This function is meant for standalones only, *never* call this from plugins.
*/ */
void quit(); void quit();


@@ -76,6 +71,30 @@ public:
*/ */
bool isQuiting() const noexcept; bool isQuiting() const noexcept;


/**
Add a callback function to be triggered on every idle cycle.
You can add more than one, and remove them at anytime with removeIdleCallback().
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing.
*/
void addIdleCallback(IdleCallback* callback);

/**
Remove an idle callback previously added via addIdleCallback().
*/
void removeIdleCallback(IdleCallback* callback);

/**
Set the class name of the application.

This is a stable identifier for the application, used as the window class/instance name on X11 and Windows.
It is not displayed to the user, but can be used in scripts and by window managers,
so it should be the same for every instance of the application, but different from other applications.

Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name.
*/
void setClassName(const char* name);

private: private:
struct PrivateData; struct PrivateData;
PrivateData* const pData; PrivateData* const pData;
@@ -84,7 +103,7 @@ private:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application)
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 83
- 25
dpf/dgl/Base.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,7 +20,7 @@
#include "../distrho/extra/LeakDetector.hpp" #include "../distrho/extra/LeakDetector.hpp"
#include "../distrho/extra/ScopedPointer.hpp" #include "../distrho/extra/ScopedPointer.hpp"


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Define namespace // Define namespace


#ifndef DGL_NAMESPACE #ifndef DGL_NAMESPACE
@@ -33,33 +33,46 @@


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Base DGL enums // Base DGL enums


/**
Convenience symbols for ASCII control characters.
*/
enum Char {
kCharBackspace = 0x08,
kCharEscape = 0x1B,
kCharDelete = 0x7F
};

/** /**
Keyboard modifier flags. Keyboard modifier flags.
*/ */
enum Modifier { enum Modifier {
kModifierShift = 1 << 0, /**< Shift key */
kModifierControl = 1 << 1, /**< Control key */
kModifierAlt = 1 << 2, /**< Alt/Option key */
kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */
kModifierShift = 1u << 0u, ///< Shift key
kModifierControl = 1u << 1u, ///< Control key
kModifierAlt = 1u << 2u, ///< Alt/Option key
kModifierSuper = 1u << 3u ///< Mod4/Command/Windows key
}; };


/** /**
Special (non-Unicode) keyboard keys.
Keyboard key codepoints.

All keys are identified by a Unicode code point in PuglEventKey::key. This
enumeration defines constants for special keys that do not have a standard
code point, and some convenience constants for control characters. Note
that all keys are handled in the same way, this enumeration is just for
convenience when writing hard-coded key bindings.

Keys that do not have a standard code point use values in the Private Use
Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications
must take care to not interpret these values beyond key detection, the
mapping used here is arbitrary and specific to DPF.
*/ */
enum Key { enum Key {
kKeyF1 = 1,
// Convenience symbols for ASCII control characters
kKeyBackspace = 0x08,
kKeyEscape = 0x1B,
kKeyDelete = 0x7F,

// Backwards compatibility with old DPF
kCharBackspace DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace,
kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape,
kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete,

// Unicode Private Use Area
kKeyF1 = 0xE000,
kKeyF2, kKeyF2,
kKeyF3, kKeyF3,
kKeyF4, kKeyF4,
@@ -81,30 +94,75 @@ enum Key {
kKeyEnd, kKeyEnd,
kKeyInsert, kKeyInsert,
kKeyShift, kKeyShift,
kKeyShiftL = kKeyShift,
kKeyShiftR,
kKeyControl, kKeyControl,
kKeyControlL = kKeyControl,
kKeyControlR,
kKeyAlt, kKeyAlt,
kKeySuper
kKeyAltL = kKeyAlt,
kKeyAltR,
kKeySuper,
kKeySuperL = kKeySuper,
kKeySuperR,
kKeyMenu,
kKeyCapsLock,
kKeyScrollLock,
kKeyNumLock,
kKeyPrintScreen,
kKeyPause
};

/**
Common flags for all events.
*/
enum Flag {
kFlagSendEvent = 1, ///< Event is synthetic
kFlagIsHint = 2 ///< Event is a hint (not direct user input)
};

/**
Reason for a crossing event.
*/
enum CrossingMode {
kCrossingNormal, ///< Crossing due to pointer motion
kCrossingGrab, ///< Crossing due to a grab
kCrossingUngrab ///< Crossing due to a grab release
};

/**
Scroll direction.

Describes the direction of a scroll event along with whether the scroll is a "smooth" scroll.
The discrete directions are for devices like mouse wheels with constrained axes,
while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads.
*/
enum ScrollDirection {
kScrollUp, ///< Scroll up
kScrollDown, ///< Scroll down
kScrollLeft, ///< Scroll left
kScrollRight, ///< Scroll right
kScrollSmooth ///< Smooth scroll in any direction
}; };


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Base DGL classes // Base DGL classes


/** /**
Graphics context, definition depends on build type. Graphics context, definition depends on build type.
*/ */
struct GraphicsContext;
struct GraphicsContext {};


/** /**
Idle callback. Idle callback.
*/ */
class IdleCallback
struct IdleCallback
{ {
public:
virtual ~IdleCallback() {} virtual ~IdleCallback() {}
virtual void idleCallback() = 0; virtual void idleCallback() = 0;
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL


@@ -114,6 +172,6 @@ END_NAMESPACE_DGL
using namespace DGL_NAMESPACE; using namespace DGL_NAMESPACE;
#endif #endif


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


#endif // DGL_BASE_HPP_INCLUDED #endif // DGL_BASE_HPP_INCLUDED

+ 160
- 7
dpf/dgl/Cairo.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,23 +17,176 @@
#ifndef DGL_CAIRO_HPP_INCLUDED #ifndef DGL_CAIRO_HPP_INCLUDED
#define DGL_CAIRO_HPP_INCLUDED #define DGL_CAIRO_HPP_INCLUDED


#include "Base.hpp"
#include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"


#include <cairo/cairo.h> #include <cairo/cairo.h>


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


/** /**
Graphics context.
Cairo Graphics context.
*/ */
struct GraphicsContext
struct CairoGraphicsContext : GraphicsContext
{ {
cairo_t* cairo; // FIXME proper name..
cairo_t* handle;
}; };


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

/**
Cairo Image class.

TODO ...
*/
class CairoImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
CairoImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
CairoImage(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
CairoImage(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
*/
CairoImage(const CairoImage& image);

/**
Destructor.
*/
~CairoImage() override;

/**
Load raw image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Load PNG image from memory.
Image size is read from PNG contents.
@note @a pngData must remain valid for the lifetime of this Image.
*/
void loadFromPNG(const char* pngData, uint dataSize) noexcept;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
Get the cairo surface currently associated with this image.
FIXME might be removed
*/
inline cairo_surface_t* getSurface() const noexcept
{
return surface;
}

/**
TODO document this.
*/
CairoImage& operator=(const CairoImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };

private:
cairo_surface_t* surface;
uchar* surfacedata;
int* datarefcount;
};

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

/**
CairoWidget, handy class that takes graphics context during onDisplay and passes it in a new function.
*/
template <class BaseWidget>
class CairoBaseWidget : public BaseWidget
{
public:
/**
Constructor for a CairoSubWidget.
*/
explicit CairoBaseWidget(Widget* const parentGroupWidget);

/**
Constructor for a CairoTopLevelWidget.
*/
explicit CairoBaseWidget(Window& windowToMapTo);

/**
Constructor for a CairoStandaloneWindow without parent window.
*/
explicit CairoBaseWidget(Application& app);

/**
Constructor for a CairoStandaloneWindow with parent window.
*/
explicit CairoBaseWidget(Application& app, Window& parentWindow);

/**
Destructor.
*/
virtual ~CairoBaseWidget() {}

protected:
/**
New virtual onDisplay function.
@see onDisplay
*/
virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0;

private:
/**
Widget display function.
Implemented internally to pass context into the drawing function.
*/
void onDisplay() override
{
const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext());
onCairoDisplay(context);
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget);
};

typedef CairoBaseWidget<SubWidget> CairoSubWidget;
typedef CairoBaseWidget<TopLevelWidget> CairoTopLevelWidget;
typedef CairoBaseWidget<StandaloneWindow> CairoStandaloneWindow;

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

typedef ImageBaseAboutWindow<CairoImage> CairoImageAboutWindow;
typedef ImageBaseButton<CairoImage> CairoImageButton;
typedef ImageBaseKnob<CairoImage> CairoImageKnob;
typedef ImageBaseSlider<CairoImage> CairoImageSlider;
typedef ImageBaseSwitch<CairoImage> CairoImageSwitch;

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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 13
- 8
dpf/dgl/Color.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -23,7 +23,7 @@ struct NVGcolor;


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


/** /**
A color made from red, green, blue and alpha floating-point values in [0..1] range. A color made from red, green, blue and alpha floating-point values in [0..1] range.
@@ -44,13 +44,13 @@ struct Color {


/** /**
Create a color from red, green, blue and alpha numeric values. Create a color from red, green, blue and alpha numeric values.
Values must be in [0..255] range.
All values except alpha must be in [0..255] range, with alpha in [0..1] range.
*/ */
Color(int red, int green, int blue, int alpha = 255) noexcept;
Color(int red, int green, int blue, float alpha = 1.0f) noexcept;


/** /**
Create a color from red, green, blue and alpha floating-point values. Create a color from red, green, blue and alpha floating-point values.
Values must in [0..1] range.
All values must in [0..1] range.
*/ */
Color(float red, float green, float blue, float alpha = 1.0f) noexcept; Color(float red, float green, float blue, float alpha = 1.0f) noexcept;


@@ -74,7 +74,7 @@ struct Color {
/** /**
Create a color from a HTML string like "#333" or "#112233". Create a color from a HTML string like "#333" or "#112233".
*/ */
static Color fromHTML(const char* rgb, float alpha = 1.0f);
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept;


/** /**
Linearly interpolate this color against another. Linearly interpolate this color against another.
@@ -83,7 +83,7 @@ struct Color {


/** /**
Check if this color matches another. Check if this color matches another.
@note Comparison is forced within 8-bit color values.
@note Comparison is done within 8-bit color space.
*/ */
bool isEqual(const Color& color, bool withAlpha = true) noexcept; bool isEqual(const Color& color, bool withAlpha = true) noexcept;
bool isNotEqual(const Color& color, bool withAlpha = true) noexcept; bool isNotEqual(const Color& color, bool withAlpha = true) noexcept;
@@ -95,6 +95,11 @@ struct Color {
*/ */
void fixBounds() noexcept; void fixBounds() noexcept;


/**
Set this color for use in the next drawing operation for the provided context.
*/
void setFor(const GraphicsContext& context, bool includeAlpha = false);

/** /**
@internal @internal
Needed for NanoVG compatibility. Needed for NanoVG compatibility.
@@ -103,7 +108,7 @@ struct Color {
operator NVGcolor() const noexcept; operator NVGcolor() const noexcept;
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 117
- 36
dpf/dgl/Geometry.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,7 +21,7 @@


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names // Forward class names


template<typename> class Line; template<typename> class Line;
@@ -29,7 +29,7 @@ template<typename> class Circle;
template<typename> class Triangle; template<typename> class Triangle;
template<typename> class Rectangle; template<typename> class Rectangle;


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


/** /**
DGL Point class. DGL Point class.
@@ -114,14 +114,14 @@ public:
bool operator!=(const Point<T>& pos) const noexcept; bool operator!=(const Point<T>& pos) const noexcept;


private: private:
T fX, fY;
T x, y;
template<typename> friend class Line; template<typename> friend class Line;
template<typename> friend class Circle; template<typename> friend class Circle;
template<typename> friend class Triangle; template<typename> friend class Triangle;
template<typename> friend class Rectangle; template<typename> friend class Rectangle;
}; };


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


/** /**
DGL Size class. DGL Size class.
@@ -195,7 +195,7 @@ public:


/** /**
Return true if size is not null (0x0). Return true if size is not null (0x0).
A non-null size is still invalid if its width or height is negative.
A non-null size is still invalid if its width or height are negative.
*/ */
bool isNotNull() const noexcept; bool isNotNull() const noexcept;


@@ -210,6 +210,8 @@ public:
*/ */
bool isInvalid() const noexcept; bool isInvalid() const noexcept;


Size<int> toInt() const noexcept;

Size<T> operator+(const Size<T>& size) noexcept; Size<T> operator+(const Size<T>& size) noexcept;
Size<T> operator-(const Size<T>& size) noexcept; Size<T> operator-(const Size<T>& size) noexcept;
Size<T>& operator=(const Size<T>& size) noexcept; Size<T>& operator=(const Size<T>& size) noexcept;
@@ -217,6 +219,8 @@ public:
Size<T>& operator-=(const Size<T>& size) noexcept; Size<T>& operator-=(const Size<T>& size) noexcept;
Size<T>& operator*=(double m) noexcept; Size<T>& operator*=(double m) noexcept;
Size<T>& operator/=(double d) noexcept; Size<T>& operator/=(double d) noexcept;
Size<T> operator*(double m) const noexcept;
Size<T> operator/(double m) const noexcept;
bool operator==(const Size<T>& size) const noexcept; bool operator==(const Size<T>& size) const noexcept;
bool operator!=(const Size<T>& size) const noexcept; bool operator!=(const Size<T>& size) const noexcept;


@@ -346,11 +350,6 @@ public:
*/ */
void moveBy(const Point<T>& pos) noexcept; void moveBy(const Point<T>& pos) noexcept;


/**
Draw this line using the current OpenGL state.
*/
void draw();

/** /**
Return true if line is null (start and end pos are equal). Return true if line is null (start and end pos are equal).
*/ */
@@ -361,12 +360,28 @@ public:
*/ */
bool isNotNull() const noexcept; bool isNotNull() const noexcept;


#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the provided graphics context, optionally specifying line width.
*/
void draw(const GraphicsContext& context, T width = 1);
#endif

Line<T>& operator=(const Line<T>& line) noexcept; Line<T>& operator=(const Line<T>& line) noexcept;
bool operator==(const Line<T>& line) const noexcept; bool operator==(const Line<T>& line) const noexcept;
bool operator!=(const Line<T>& line) const noexcept; bool operator!=(const Line<T>& line) const noexcept;


#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();
#endif

private: private:
Point<T> fPosStart, fPosEnd;
Point<T> posStart, posEnd;
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -461,19 +476,35 @@ public:
void setNumSegments(const uint num); void setNumSegments(const uint num);


/** /**
Draw this circle using the current OpenGL state.
Draw this circle using the provided graphics context.
*/ */
void draw();
void draw(const GraphicsContext& context);


/** /**
Draw lines (outline of this circle) using the current OpenGL state.
Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width.
*/ */
void drawOutline();
void drawOutline(const GraphicsContext& context, T lineWidth = 1);


Circle<T>& operator=(const Circle<T>& cir) noexcept; Circle<T>& operator=(const Circle<T>& cir) noexcept;
bool operator==(const Circle<T>& cir) const noexcept; bool operator==(const Circle<T>& cir) const noexcept;
bool operator!=(const Circle<T>& cir) const noexcept; bool operator!=(const Circle<T>& cir) const noexcept;


#ifndef DPF_TEST_POINT_CPP
/**
Draw this circle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw lines (outline of this circle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private: private:
Point<T> fPos; Point<T> fPos;
float fSize; float fSize;
@@ -481,9 +512,6 @@ private:


// cached values // cached values
float fTheta, fCos, fSin; float fTheta, fCos, fSin;

/** @internal */
void _draw(const bool outline);
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -541,24 +569,37 @@ public:
bool isInvalid() const noexcept; bool isInvalid() const noexcept;


/** /**
Draw this triangle using the current OpenGL state.
Draw this triangle using the provided graphics context.
*/ */
void draw();
void draw(const GraphicsContext& context);


/** /**
Draw lines (outline of this triangle) using the current OpenGL state.
Draw lines (outline of this triangle) using the provided graphics context, optionally specifying line width.
*/ */
void drawOutline();
void drawOutline(const GraphicsContext& context, T lineWidth = 1);


Triangle<T>& operator=(const Triangle<T>& tri) noexcept; Triangle<T>& operator=(const Triangle<T>& tri) noexcept;
bool operator==(const Triangle<T>& tri) const noexcept; bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept; bool operator!=(const Triangle<T>& tri) const noexcept;


private:
Point<T> fPos1, fPos2, fPos3;
#ifndef DPF_TEST_POINT_CPP
/**
Draw this triangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();


/** @internal */
void _draw(const bool outline);
/**
Draw lines (outline of this triangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> pos1, pos2, pos3;
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -712,6 +753,12 @@ public:
*/ */
bool contains(const Point<T>& pos) const noexcept; bool contains(const Point<T>& pos) const noexcept;


/**
Check if this rectangle contains the point @a pos of another type.
*/
template<typename T2>
bool contains(const Point<T2>& pos) const noexcept;

/** /**
Check if this rectangle contains X. Check if this rectangle contains X.
*/ */
@@ -723,14 +770,37 @@ public:
bool containsY(const T& y) const noexcept; bool containsY(const T& y) const noexcept;


/** /**
Draw this rectangle using the current OpenGL state.
Return true if size is null (0x0).
An null size is also invalid.
*/ */
void draw();
bool isNull() const noexcept;


/** /**
Draw lines (outline of this rectangle) using the current OpenGL state.
Return true if size is not null (0x0).
A non-null size is still invalid if its width or height are negative.
*/ */
void drawOutline();
bool isNotNull() const noexcept;

/**
Return true if size is valid (width and height are higher than zero).
*/
bool isValid() const noexcept;

/**
Return true if size is invalid (width or height are lower or equal to zero).
An invalid size might not be null under some circumstances.
*/
bool isInvalid() const noexcept;

/**
Draw this rectangle using the provided graphics context.
*/
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this rectangle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline(const GraphicsContext& context, T lineWidth = 1);


Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept;
Rectangle<T>& operator*=(double m) noexcept; Rectangle<T>& operator*=(double m) noexcept;
@@ -738,12 +808,23 @@ public:
bool operator==(const Rectangle<T>& size) const noexcept; bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept; bool operator!=(const Rectangle<T>& size) const noexcept;


private:
Point<T> fPos;
Size<T> fSize;
/**
Draw this rectangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();


/** @internal */
void _draw(const bool outline);
/**
Draw lines (outline of this rectangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();

private:
Point<T> pos;
Size<T> size;
}; };


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


+ 10
- 99
dpf/dgl/Image.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,108 +17,19 @@
#ifndef DGL_IMAGE_HPP_INCLUDED #ifndef DGL_IMAGE_HPP_INCLUDED
#define DGL_IMAGE_HPP_INCLUDED #define DGL_IMAGE_HPP_INCLUDED


#include "ImageBase.hpp"
#ifdef DGL_CAIRO
#include "Cairo.hpp"
#else
#include "OpenGL.hpp" #include "OpenGL.hpp"
#endif


START_NAMESPACE_DGL START_NAMESPACE_DGL


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

/**
OpenGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class Image : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
Image();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData,
const uint width,
const uint height,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData,
const Size<uint>& size,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using another image data.
*/
Image(const Image& image);

/**
Destructor.
*/
~Image() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData,
const uint width,
const uint height,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData,
const Size<uint>& size,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Get the image format.
*/
GLenum getFormat() const noexcept;

/**
Get the image type.
*/
GLenum getType() const noexcept;

/**
TODO document this.
*/
Image& operator=(const Image& image) noexcept;

protected:
/** @internal */
void _drawAt(const Point<int>& pos) override;

private:
GLenum fFormat;
GLenum fType;
GLuint fTextureId;
bool fIsReady;
};

// -----------------------------------------------------------------------
#ifdef DGL_CAIRO
typedef CairoImage Image;
#else
typedef OpenGLImage Image;
#endif


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 47
- 16
dpf/dgl/ImageBase.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,7 +21,16 @@


START_NAMESPACE_DGL START_NAMESPACE_DGL


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

enum ImageFormat {
kImageFormatNull,
kImageFormatGrayscale,
kImageFormatBGR,
kImageFormatBGRA,
kImageFormatRGB,
kImageFormatRGBA,
};


/** /**
Base DGL Image class. Base DGL Image class.
@@ -44,13 +53,13 @@ protected:
Constructor using raw image data. Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image. @note @a rawData must remain valid for the lifetime of this Image.
*/ */
ImageBase(const char* const rawData, const uint width, const uint height);
ImageBase(const char* rawData, uint width, uint height, ImageFormat format);


/** /**
Constructor using raw image data. Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image. @note @a rawData must remain valid for the lifetime of this Image.
*/ */
ImageBase(const char* const rawData, const Size<uint>& size);
ImageBase(const char* rawData, const Size<uint>& size, ImageFormat format);


/** /**
Constructor using another image data. Constructor using another image data.
@@ -68,6 +77,11 @@ public:
*/ */
bool isValid() const noexcept; bool isValid() const noexcept;


/**
Check if this image is not valid.
*/
bool isInvalid() const noexcept;

/** /**
Get width. Get width.
*/ */
@@ -89,19 +103,38 @@ public:
const char* getRawData() const noexcept; const char* getRawData() const noexcept;


/** /**
Draw this image at (0, 0) point.
Get the image format.
*/ */
void draw();
ImageFormat getFormat() const noexcept;


/** /**
Draw this image at (x, y) point.
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/ */
void drawAt(const int x, const int y);
void loadFromMemory(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA) noexcept;


/** /**
Draw this image at position @a pos.
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/ */
void drawAt(const Point<int>& pos);
virtual void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept;

/**
Draw this image at (0, 0) point using the current OpenGL context.
*/
void draw(const GraphicsContext& context);

/**
Draw this image at (x, y) point using the current OpenGL context.
*/
void drawAt(const GraphicsContext& context, int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
*/
virtual void drawAt(const GraphicsContext& context, const Point<int>& pos) = 0;


/** /**
TODO document this. TODO document this.
@@ -111,14 +144,12 @@ public:
bool operator!=(const ImageBase& image) const noexcept; bool operator!=(const ImageBase& image) const noexcept;


protected: protected:
/** @internal */
virtual void _drawAt(const Point<int>& pos) = 0;

const char* fRawData;
Size<uint> fSize;
const char* rawData;
Size<uint> size;
ImageFormat format;
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 223
- 0
dpf/dgl/ImageBaseWidgets.hpp View File

@@ -0,0 +1,223 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

#include "StandaloneWindow.hpp"
#include "SubWidget.hpp"

START_NAMESPACE_DGL

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

template <class ImageType>
class ImageBaseAboutWindow : public StandaloneWindow
{
public:
explicit ImageBaseAboutWindow(Window& parentWindow, const ImageType& image = ImageType());
explicit ImageBaseAboutWindow(TopLevelWidget* parentTopLevelWidget, const ImageType& image = ImageType());

void setImage(const ImageType& image);

protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;

private:
ImageType img;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow)
};

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

template <class ImageType>
class ImageBaseButton : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageBaseButton* imageButton, int button) = 0;
};

explicit ImageBaseButton(Widget* parentWidget, const ImageType& image);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown);

~ImageBaseButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseButton)
};

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

template <class ImageType>
class ImageBaseKnob : public SubWidget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0;
};

explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept;
explicit ImageBaseKnob(const ImageBaseKnob& imageKnob);
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob);
~ImageBaseKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseKnob)
};

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

// note set range and step before setting the value

template <class ImageType>
class ImageBaseSlider : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0;
};

explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept;
~ImageBaseSlider() override;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider)
};

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

template <class ImageType>
class ImageBaseSwitch : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0;
};

explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept;
explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept;
ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept;
~ImageBaseSwitch() override;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseSwitch)
};

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

END_NAMESPACE_DGL

#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

+ 13
- 255
dpf/dgl/ImageWidgets.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,266 +18,24 @@
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED #define DGL_IMAGE_WIDGETS_HPP_INCLUDED


#include "Image.hpp" #include "Image.hpp"
#include "Widget.hpp"
#include "Window.hpp"
#include "ImageBaseWidgets.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


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

#ifndef DISTRHO_OS_HAIKU
class ImageAboutWindow : public Window,
public Widget
#else
// crash when creating or opening 2nd window
class ImageAboutWindow
#endif
{
public:
explicit ImageAboutWindow(Window& parent, const Image& image = Image());
explicit ImageAboutWindow(Widget* widget, const Image& image = Image());

void setImage(const Image& image);

#ifndef DISTRHO_OS_HAIKU
protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;
void onReshape(uint width, uint height) override;
#ifdef DGL_CAIRO
typedef CairoImageAboutWindow ImageAboutWindow;
typedef CairoImageButton ImageButton;
typedef CairoImageKnob ImageKnob;
typedef CairoImageSlider ImageSlider;
typedef CairoImageSwitch ImageSwitch;
#else #else
void exec() {}
typedef OpenGLImageAboutWindow ImageAboutWindow;
typedef OpenGLImageButton ImageButton;
typedef OpenGLImageKnob ImageKnob;
typedef OpenGLImageSlider ImageSlider;
typedef OpenGLImageSwitch ImageSwitch;
#endif #endif


private:
Image fImgBackground;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow)
};

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

class ImageButton : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0;
};

explicit ImageButton(Window& parent, const Image& image);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

explicit ImageButton(Widget* widget, const Image& image);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

~ImageButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageButton)
};

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

class ImageKnob : public Widget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0;
};

explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(const ImageKnob& imageKnob);
ImageKnob& operator=(const ImageKnob& imageKnob);
~ImageKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;
bool fUsingLog;
Orientation fOrientation;

int fRotationAngle;
bool fDragging;
int fLastX;
int fLastY;

Callback* fCallback;

bool fIsImgVertical;
uint fImgLayerWidth;
uint fImgLayerHeight;
uint fImgLayerCount;
bool fIsReady;
GLuint fTextureId;

float _logscale(float value) const;
float _invlogscale(float value) const;

DISTRHO_LEAK_DETECTOR(ImageKnob)
};

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

// note set range and step before setting the value

class ImageSlider : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0;
};

explicit ImageSlider(Window& parent, const Image& image) noexcept;
explicit ImageSlider(Widget* widget, const Image& image) noexcept;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;

bool fDragging;
bool fInverted;
bool fValueIsSet;
int fStartedX;
int fStartedY;

Callback* fCallback;

Point<int> fStartPos;
Point<int> fEndPos;
Rectangle<int> fSliderArea;

void _recheckArea() noexcept;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider)
};

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

class ImageSwitch : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0;
};

explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept;
ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
Image fImageNormal;
Image fImageDown;
bool fIsDown;

Callback* fCallback;

DISTRHO_LEAK_DETECTOR(ImageSwitch)
};

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

END_NAMESPACE_DGL END_NAMESPACE_DGL


#endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED

+ 88
- 25
dpf/dgl/Makefile View File

@@ -10,8 +10,12 @@ include ../Makefile.base.mk


BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc
BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include
LINK_FLAGS += $(DGL_LIBS) LINK_FLAGS += $(DGL_LIBS)


# TODO fix these after pugl-upstream is done
BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -Wno-narrowing

# ifneq ($(MACOS_OLD),true) # ifneq ($(MACOS_OLD),true)
# needed by sofd right now, fix later # needed by sofd right now, fix later
# BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive # BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive
@@ -21,39 +25,64 @@ LINK_FLAGS += $(DGL_LIBS)


OBJS_common = \ OBJS_common = \
../build/dgl/Application.cpp.o \ ../build/dgl/Application.cpp.o \
../build/dgl/ApplicationPrivateData.cpp.o \
../build/dgl/Color.cpp.o \ ../build/dgl/Color.cpp.o \
../build/dgl/Geometry.cpp.o \ ../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \ ../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \
../build/dgl/Resources.cpp.o \ ../build/dgl/Resources.cpp.o \
../build/dgl/Widget.cpp.o

# TODO: ImageWidgets.cpp
../build/dgl/SubWidget.cpp.o \
../build/dgl/SubWidgetPrivateData.cpp.o \
../build/dgl/TopLevelWidget.cpp.o \
../build/dgl/TopLevelWidgetPrivateData.cpp.o \
../build/dgl/Widget.cpp.o \
../build/dgl/WidgetPrivateData.cpp.o \
../build/dgl/Window.cpp.o \
../build/dgl/WindowPrivateData.cpp.o
# ../build/dgl/WindowFileBrowser.cpp.o


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


OBJS_cairo = $(OBJS_common) \ OBJS_cairo = $(OBJS_common) \
../build/dgl/Cairo.cpp.cairo.o \
../build/dgl/WidgetPrivateData.cpp.cairo.o
../build/dgl/Cairo.cpp.cairo.o


ifeq ($(MACOS),true) ifeq ($(MACOS),true)
OBJS_cairo += ../build/dgl/Window.mm.cairo.o
OBJS_cairo += ../build/dgl/pugl.mm.cairo.o
else else
OBJS_cairo += ../build/dgl/Window.cpp.cairo.o
OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o
endif endif


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


OBJS_opengl = $(OBJS_common) \ OBJS_opengl = $(OBJS_common) \
../build/dgl/OpenGL.cpp.opengl.o \ ../build/dgl/OpenGL.cpp.opengl.o \
../build/dgl/Image.cpp.opengl.o \
../build/dgl/ImageWidgets.cpp.opengl.o \
../build/dgl/NanoVG.cpp.opengl.o \
../build/dgl/WidgetPrivateData.cpp.opengl.o
../build/dgl/NanoVG.cpp.opengl.o


ifeq ($(MACOS),true) ifeq ($(MACOS),true)
OBJS_opengl += ../build/dgl/Window.mm.opengl.o
OBJS_opengl += ../build/dgl/pugl.mm.opengl.o
else else
OBJS_opengl += ../build/dgl/Window.cpp.opengl.o
OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o
endif

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

OBJS_stub = $(OBJS_common)

ifeq ($(MACOS),true)
OBJS_stub += ../build/dgl/pugl.mm.o
else
OBJS_stub += ../build/dgl/pugl.cpp.o
endif

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

OBJS_vulkan = $(OBJS_common) \
../build/dgl/Vulkan.cpp.vulkan.o

ifeq ($(MACOS),true)
OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o
else
OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o
endif endif


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
@@ -68,10 +97,23 @@ TARGETS += ../build/libdgl-opengl.a
TARGETS += ../build/libdgl.a TARGETS += ../build/libdgl.a
endif endif


ifeq ($(HAVE_STUB),true)
TARGETS += ../build/libdgl-stub.a
endif

ifeq ($(HAVE_VULKAN),true)
TARGETS += ../build/libdgl-vulkan.a
endif

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


all: $(TARGETS) all: $(TARGETS)


cairo: ../build/libdgl-cairo.a
opengl: ../build/libdgl-opengl.a
stub: ../build/libdgl-stub.a
vulkan: ../build/libdgl-vulkan.a

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


../build/libdgl-cairo.a: $(OBJS_cairo) ../build/libdgl-cairo.a: $(OBJS_cairo)
@@ -86,6 +128,18 @@ all: $(TARGETS)
$(SILENT)rm -f $@ $(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^ $(SILENT)$(AR) crs $@ $^


../build/libdgl-stub.a: $(OBJS_stub)
-@mkdir -p ../build
@echo "Creating libdgl-stub.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-vulkan.a: $(OBJS_vulkan)
-@mkdir -p ../build
@echo "Creating libdgl-vulkan.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

# Compat name, to be removed soon # Compat name, to be removed soon
../build/libdgl.a: ../build/libdgl-opengl.a ../build/libdgl.a: ../build/libdgl-opengl.a
@echo "Symlinking libdgl.a" @echo "Symlinking libdgl.a"
@@ -103,6 +157,11 @@ all: $(TARGETS)
@echo "Compiling $<" @echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@


../build/dgl/%.mm.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@

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


../build/dgl/%.cpp.cairo.o: src/%.cpp ../build/dgl/%.cpp.cairo.o: src/%.cpp
@@ -110,15 +169,10 @@ all: $(TARGETS)
@echo "Compiling $< (Cairo variant)" @echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@


../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl/*
../build/dgl/%.mm.cairo.o: src/%.mm
-@mkdir -p ../build/dgl -@mkdir -p ../build/dgl
@echo "Compiling $< (Cairo variant)" @echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@

../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl/*
-@mkdir -p ../build/dgl
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@


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


@@ -127,15 +181,22 @@ all: $(TARGETS)
@echo "Compiling $< (OpenGL variant)" @echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@


../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl/*
../build/dgl/%.mm.opengl.o: src/%.mm
-@mkdir -p ../build/dgl -@mkdir -p ../build/dgl
@echo "Compiling $< (OpenGL variant)" @echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@


../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl/*
# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.vulkan.o: src/%.cpp
-@mkdir -p ../build/dgl -@mkdir -p ../build/dgl
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@

../build/dgl/%.mm.vulkan.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@


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


@@ -150,5 +211,7 @@ debug:
-include $(OBJS_common:%.o=%.d) -include $(OBJS_common:%.o=%.d)
-include $(OBJS_cairo:%.o=%.d) -include $(OBJS_cairo:%.o=%.d)
-include $(OBJS_opengl:%.o=%.d) -include $(OBJS_opengl:%.o=%.d)
-include $(OBJS_stub:%.o=%.d)
-include $(OBJS_vulkan:%.o=%.d)


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

+ 39
- 19
dpf/dgl/NanoVG.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,7 +19,9 @@


#include "Color.hpp" #include "Color.hpp"
#include "OpenGL.hpp" #include "OpenGL.hpp"
#include "Widget.hpp"
#include "SubWidget.hpp"
#include "TopLevelWidget.hpp"
#include "StandaloneWindow.hpp"


#ifndef DGL_NO_SHARED_RESOURCES #ifndef DGL_NO_SHARED_RESOURCES
# define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" # define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__"
@@ -33,7 +35,6 @@ START_NAMESPACE_DGL
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Forward class names // Forward class names


class BlendishWidget;
class NanoVG; class NanoVG;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -305,7 +306,9 @@ public:
/** /**
Constructor reusing a NanoVG context, used for subwidgets. Constructor reusing a NanoVG context, used for subwidgets.
*/ */
/*
NanoVG(NanoWidget* groupWidget); NanoVG(NanoWidget* groupWidget);
*/


/** /**
Destructor. Destructor.
@@ -851,14 +854,13 @@ public:
/** /**
Load DPF's internal shared resources for this NanoVG class. Load DPF's internal shared resources for this NanoVG class.
*/ */
virtual void loadSharedResources();
virtual bool loadSharedResources();
#endif #endif


private: private:
NVGcontext* const fContext; NVGcontext* const fContext;
bool fInFrame; bool fInFrame;
bool fIsSubWidget; bool fIsSubWidget;
friend class BlendishWidget;


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG)
}; };
@@ -873,30 +875,39 @@ private:
The drawing function onDisplay() is implemented internally but a The drawing function onDisplay() is implemented internally but a
new onNanoDisplay() needs to be overridden instead. new onNanoDisplay() needs to be overridden instead.
*/ */
class NanoWidget : public Widget,
public NanoVG
template <class BaseWidget>
class NanoBaseWidget : public BaseWidget,
public NanoVG
{ {
public: public:
/** /**
Constructor.
Constructor for a NanoSubWidget.
@see CreateFlags @see CreateFlags
*/ */
explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS);


/** /**
Constructor for a subwidget.
Constructor for a NanoTopLevelWidget.
@see CreateFlags
*/ */
explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS);


/** /**
Constructor for a subwidget, reusing a NanoVG context.
Constructor for a NanoStandaloneWindow without parent window.
@see CreateFlags
*/ */
explicit NanoWidget(NanoWidget* groupWidget);
explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS);

/**
Constructor for a NanoStandaloneWindow with parent window.
@see CreateFlags
*/
explicit NanoBaseWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS);


/** /**
Destructor. Destructor.
*/ */
virtual ~NanoWidget();
virtual ~NanoBaseWidget() {}


protected: protected:
/** /**
@@ -906,14 +917,16 @@ protected:
virtual void onNanoDisplay() = 0; virtual void onNanoDisplay() = 0;


private: private:
struct PrivateData;
PrivateData* const nData;

/** /**
Widget display function. Widget display function.
Implemented internally to wrap begin/endFrame() automatically. Implemented internally to wrap begin/endFrame() automatically.
*/ */
void onDisplay() override;
inline void onDisplay() override
{
NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight());
onNanoDisplay();
NanoVG::endFrame();
}


// these should not be used // these should not be used
void beginFrame(uint,uint) {} void beginFrame(uint,uint) {}
@@ -922,9 +935,16 @@ private:
void cancelFrame() {} void cancelFrame() {}
void endFrame() {} void endFrame() {}


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget)
}; };


typedef NanoBaseWidget<SubWidget> NanoSubWidget;
typedef NanoBaseWidget<TopLevelWidget> NanoTopLevelWidget;
typedef NanoBaseWidget<StandaloneWindow> NanoStandaloneWindow;

DISTRHO_DEPRECATED_BY("NanoSubWidget")
typedef NanoSubWidget NanoWidget;

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


END_NAMESPACE_DGL END_NAMESPACE_DGL


+ 175
- 3
dpf/dgl/OpenGL.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,7 @@
#define DGL_OPENGL_HPP_INCLUDED #define DGL_OPENGL_HPP_INCLUDED


#include "ImageBase.hpp" #include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code (part 1) // Fix OpenGL includes for Windows, based on glfw code (part 1)
@@ -108,14 +109,185 @@ START_NAMESPACE_DGL
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


/** /**
Graphics context.
OpenGL Graphics context.
*/ */
struct GraphicsContext
struct OpenGLGraphicsContext : GraphicsContext
{ {
}; };


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


static inline
ImageFormat asDISTRHOImageFormat(const GLenum format)
{
switch (format)
{
case GL_LUMINANCE:
return kImageFormatGrayscale;
case GL_BGR:
return kImageFormatBGR;
case GL_BGRA:
return kImageFormatBGRA;
case GL_RGB:
return kImageFormatRGB;
case GL_RGBA:
return kImageFormatRGBA;
}

return kImageFormatNull;
}

static inline
GLenum asOpenGLImageFormat(const ImageFormat format)
{
switch (format)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
return GL_LUMINANCE;
case kImageFormatBGR:
return GL_BGR;
case kImageFormatBGRA:
return GL_BGRA;
case kImageFormatRGB:
return GL_RGB;
case kImageFormatRGBA:
return GL_RGBA;
}

return 0x0;
}

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

/**
OpenGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class OpenGLImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
OpenGLImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, const Size<uint>& size, ImageFormat format = kImageFormatBGRA);

/**
Constructor using another image data.
*/
OpenGLImage(const OpenGLImage& image);

/**
Destructor.
*/
~OpenGLImage() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
TODO document this.
*/
OpenGLImage& operator=(const OpenGLImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, uint, uint, ImageFormat)")
explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum glFormat);

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, const Size<uint>&, ImageFormat)")
explicit OpenGLImage(const char* rawData, const Size<uint>& size, GLenum glFormat);

/**
Draw this image at (0, 0) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw this image at (x, y) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, int, int)")
void drawAt(int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, const Point<int>&)")
void drawAt(const Point<int>& pos);

/**
Get the image type.
DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE.
*/
DISTRHO_DEPRECATED
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; }

private:
GLuint textureId;
bool setupCalled;
};

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

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

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

END_NAMESPACE_DGL END_NAMESPACE_DGL


#endif #endif

+ 35
- 20
dpf/dgl/StandaloneWindow.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,39 +17,54 @@
#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED #ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED
#define DGL_STANDALONE_WINDOW_HPP_INCLUDED #define DGL_STANDALONE_WINDOW_HPP_INCLUDED


#include "Application.hpp"
#include "Widget.hpp"
#include "TopLevelWidget.hpp"
#include "Window.hpp" #include "Window.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


class StandaloneWindow : public Application,
public Window
class StandaloneWindow : public Window,
public TopLevelWidget
{ {
public: public:
/** /**
Constructor.
Constructor without parent.
*/ */
StandaloneWindow();
StandaloneWindow(Application& app)
: Window(app),
TopLevelWidget((Window&)*this) {}


/** /**
Show window and execute application.
Constructor with parent window, typically used to run as modal.
*/ */
void exec();
StandaloneWindow(Application& app, Window& parentWindow)
: Window(app, parentWindow),
TopLevelWidget((Window&)*this) {}


private:
Widget* fWidget;

/** @internal */
void onReshape(uint width, uint height) override;

/** @internal */
void _addWidget(Widget* widget) override;

/** @internal */
void _removeWidget(Widget* widget) override;
/**
Overloaded functions to ensure they apply to the Window class.
*/
bool isVisible() const noexcept { return Window::isVisible(); }
void setVisible(bool yesNo) { Window::setVisible(yesNo); }
void hide() { Window::hide(); }
void show() { Window::show(); }
uint getWidth() const noexcept { return Window::getWidth(); }
uint getHeight() const noexcept { return Window::getHeight(); }
const Size<uint> getSize() const noexcept { return Window::getSize(); }
void repaint() noexcept { Window::repaint(); }
void setWidth(uint width) { Window::setWidth(width); }
void setHeight(uint height) { Window::setHeight(height); }
void setSize(uint width, uint height) { Window::setSize(width, height); }
void setSize(const Size<uint>& size) { Window::setSize(size); }
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0)
{ return Window::addIdleCallback(callback, timerFrequencyInMs); }
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); }
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); }
double getScaleFactor() const noexcept { return Window::getScaleFactor(); }
void setGeometryConstraints(uint minimumWidth, uint minimumHeight,
bool keepAspectRatio = false, bool automaticallyScale = false)
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); }


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow)
}; };


+ 163
- 0
dpf/dgl/SubWidget.hpp View File

@@ -0,0 +1,163 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_HPP_INCLUDED
#define DGL_SUBWIDGET_HPP_INCLUDED

#include "Widget.hpp"

START_NAMESPACE_DGL

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

/**
Sub-Widget class.

This class is the main entry point for creating any reusable widgets from within DGL.
It can be freely positioned from within a parent widget, thus being named subwidget.

Many subwidgets can share the same parent, and subwidgets themselves can also have its own subwidgets.
It is subwidgets all the way down.

TODO check absolute vs relative position and see what makes more sense.

@see CairoSubWidget
*/
class SubWidget : public Widget
{
public:
/**
Constructor.
*/
explicit SubWidget(Widget* parentWidget);

/**
Destructor.
*/
virtual ~SubWidget();

/**
Check if this widget contains the point defined by @a x and @a y.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(T x, T y) const noexcept;

/**
Check if this widget contains the point @a pos.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(const Point<T>& pos) const noexcept;

/**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
Point<int> getAbsolutePos() const noexcept;

/**
Get absolute area of this subwidget.
This is the same as `Rectangle<int>(getAbsolutePos(), getSize());`
@see getConstrainedAbsoluteArea()
*/
Rectangle<int> getAbsoluteArea() const noexcept;

/**
Get absolute area of this subwidget, with special consideration for not allowing negative values.
@see getAbsoluteArea()
*/
Rectangle<uint> getConstrainedAbsoluteArea() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
*/
void setAbsolutePos(int x, int y) noexcept;

/**
Set absolute position.
*/
void setAbsolutePos(const Point<int>& pos) noexcept;

/**
Get parent Widget, as passed in the constructor.
*/
Widget* getParentWidget() const noexcept;

/**
Request repaint of this subwidget's area to the window this widget belongs to.
*/
void repaint() noexcept override;

/**
Bring this widget to the "front" of the parent widget.
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front".
*/
virtual void toFront();

/**
Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing.
*/
void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true);

/**
Indicate that this subwidget will always draw at its own internal size and needs scaling to fit target size.
*/
void setNeedsViewportScaling(bool needsViewportScaling = true, double autoScaleFactor = 0.0);

/**
Indicate that this subwidget should not be drawn on screen, typically because it is managed by something else.
*/
void setSkipDrawing(bool skipDrawing = true);

protected:
/**
A function called when the subwidget's absolute position is changed.
*/
virtual void onPositionChanged(const PositionChangedEvent&);

private:
struct PrivateData;
PrivateData* const pData;
friend class Widget;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget)
};

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

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_HPP_INCLUDED


+ 125
- 0
dpf/dgl/TopLevelWidget.hpp View File

@@ -0,0 +1,125 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

#include "Widget.hpp"

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Window;

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

/**
Top-Level Widget class.

This is the only Widget class that is allowed to be used directly on a Window.

This widget takes the full size of the Window it is mapped to.
Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent.
Doing so allows for custom position and sizes.

This class is used as the type for DPF Plugin UIs.
So anything that a plugin UI might need that does not belong in a simple Widget will go here.
*/
class TopLevelWidget : public Widget
{
public:
/**
Constructor.
*/
explicit TopLevelWidget(Window& windowToMapTo);

/**
Destructor.
*/
virtual ~TopLevelWidget();

/**
Get the application associated with this top-level widget's window.
*/
Application& getApp() const noexcept;

/**
Get the window associated with this top-level widget.
*/
Window& getWindow() const noexcept;

/**
Set width of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setWidth(uint width);

/**
Set height of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setHeight(uint height);

/**
Set size of this widget's window, using @a width and @a height values.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setSize(uint width, uint height);

/**
Set size of this widget's window.
@note This will not change the widget's size right away, but be pending on the OS resizing the window
*/
void setSize(const Size<uint>& size);

// TODO group stuff after here, convenience functions present in Window class
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);
bool removeIdleCallback(IdleCallback* callback);
double getScaleFactor() const noexcept;
void repaint() noexcept;
void repaint(const Rectangle<uint>& rect) noexcept;
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false);

DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }

DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }

private:
struct PrivateData;
PrivateData* const pData;
friend class Window;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget)
};

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

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

+ 103
- 0
dpf/dgl/Vulkan.hpp View File

@@ -0,0 +1,103 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_VULKAN_HPP_INCLUDED
#define DGL_VULKAN_HPP_INCLUDED

#include "ImageBase.hpp"

#include <vulkan/vulkan_core.h>

START_NAMESPACE_DGL

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

/**
Vulkan Graphics context.
*/
struct VulkanGraphicsContext : GraphicsContext
{
};

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

/**
Vulkan Image class.

TODO ...
*/
class VulkanImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
VulkanImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
VulkanImage(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
VulkanImage(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
*/
VulkanImage(const VulkanImage& image);

/**
Destructor.
*/
~VulkanImage() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
TODO document this.
*/
VulkanImage& operator=(const VulkanImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };
};

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

END_NAMESPACE_DGL

#endif

+ 157
- 122
dpf/dgl/Widget.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,82 +19,97 @@


#include "Geometry.hpp" #include "Geometry.hpp"


#include <vector>
START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names // Forward class names


#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Application; class Application;
class ImageSlider;
class NanoWidget;
class SubWidget;
class TopLevelWidget;
class Window; class Window;
class StandaloneWindow;


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


/** /**
Base DGL Widget class. Base DGL Widget class.


This is the base Widget class, from which all widgets are built. This is the base Widget class, from which all widgets are built.


All widgets have a parent Window where they'll be drawn.
This parent is never changed during the widget lifetime.
All widgets have a parent widget where they'll be drawn, this can be the top-level widget or a group widget.
This parent is never changed during a widget's lifetime.


Widgets receive events in relative coordinates.
(0, 0) means its top-left position.
Widgets receive events in relative coordinates. (0, 0) means its top-left position.


Windows paint widgets in the order they are constructed.
Early widgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widget gets
The top-level widget will draw subwidgets in the order they are constructed.
Early subwidgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widgets get
a chance to catch the event and stop its propagation. a chance to catch the event and stop its propagation.


All widget event callbacks do nothing by default.
All widget event callbacks do nothing by default and onDisplay MUST be reimplemented by subclasses.

@note It is not possible to subclass this Widget class directly, you must use SubWidget or TopLevelWidget instead.
*/ */
class Widget class Widget
{ {
public: public:
/** /**
Base event data. Base event data.
@a mod The currently active keyboard modifiers, @see Modifier.
@a time The timestamp (if any).
These are the fields present on all Widget events.

@a mod Currently active keyboard modifiers, @see Modifier.
@a mod Event flags, @see Flag.
@a time Event timestamp (if any).
*/ */
struct BaseEvent { struct BaseEvent {
uint mod;
uint32_t time;
uint mod;
uint flags;
uint time;


/** Constuctor */ /** Constuctor */
BaseEvent() noexcept : mod(0x0), time(0) {}
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {}
/** Destuctor */ /** Destuctor */
virtual ~BaseEvent() noexcept {} virtual ~BaseEvent() noexcept {}
}; };


/** /**
Keyboard event. Keyboard event.
@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.

This event represents low-level key presses and releases.
This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input.

Keys are represented portably as Unicode code points, using the "natural" code point for the key.
The @a key field is the code for the pressed key, without any modifiers applied.
For example, a press or release of the 'A' key will have `key` 97 ('a')
regardless of whether shift or control are being held.

Alternatively, the raw @a keycode can be used to work directly with physical keys,
but note that this value is not portable and differs between platforms and hardware.

@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.
@a keycode Raw keycode.
@see onKeyboard @see onKeyboard
*/ */
struct KeyboardEvent : BaseEvent { struct KeyboardEvent : BaseEvent {
bool press; bool press;
uint key; uint key;
uint keycode;


/** Constuctor */ /** Constuctor */
KeyboardEvent() noexcept KeyboardEvent() noexcept
: BaseEvent(), : BaseEvent(),
press(false), press(false),
key(0) {}
key(0),
keycode(0) {}
}; };


/** /**
Special keyboard event. Special keyboard event.

This event allows the use of keys that do not have unicode points.
Note that some are non-printable keys.

@a press True if the key was pressed, false if released. @a press True if the key was pressed, false if released.
@a key The key pressed. @a key The key pressed.
@see onSpecial @see onSpecial
@@ -111,54 +126,103 @@ public:
}; };


/** /**
Mouse event.
@a button The button number (1 = left, 2 = middle, 3 = right).
@a press True if the button was pressed, false if released.
@a pos The widget-relative coordinates of the pointer.
Character input event.

This event represents text input, usually as the result of a key press.
The text is given both as a Unicode character code and a UTF-8 string.

Note that this event is generated by the platform's input system,
so there is not necessarily a direct correspondence between text events and physical key presses.
For example, with some input methods a sequence of several key presses will generate a single character.

@a keycode Raw key code.
@a character Unicode character code.
@a string UTF-8 string.
@see onCharacterInput
*/
struct CharacterInputEvent : BaseEvent {
uint keycode;
uint character;
char string[8];

/** Constuctor */
CharacterInputEvent() noexcept
: BaseEvent(),
keycode(0),
character(0),
string{'\0','\0','\0','\0','\0','\0','\0','\0'} {}
};

/**
Mouse press or release event.

@a button The button number starting from 1 (1 = left, 2 = middle, 3 = right).
@a press True if the button was pressed, false if released.
@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@see onMouse @see onMouse
*/ */
struct MouseEvent : BaseEvent { struct MouseEvent : BaseEvent {
int button;
uint button;
bool press; bool press;
Point<int> pos;
Point<double> pos;
Point<double> absolutePos;


/** Constuctor */ /** Constuctor */
MouseEvent() noexcept MouseEvent() noexcept
: BaseEvent(), : BaseEvent(),
button(0), button(0),
press(false), press(false),
pos(0, 0) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0) {}
}; };


/** /**
Mouse motion event. Mouse motion event.
@a pos The widget-relative coordinates of the pointer.

@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@see onMotion @see onMotion
*/ */
struct MotionEvent : BaseEvent { struct MotionEvent : BaseEvent {
Point<int> pos;
Point<double> pos;
Point<double> absolutePos;


/** Constuctor */ /** Constuctor */
MotionEvent() noexcept MotionEvent() noexcept
: BaseEvent(), : BaseEvent(),
pos(0, 0) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0) {}
}; };


/** /**
Mouse scroll event. Mouse scroll event.
@a pos The widget-relative coordinates of the pointer.
@a delta The scroll distance.

The scroll distance is expressed in "lines",
an arbitrary unit that corresponds to a single tick of a detented mouse wheel.
For example, `delta.y` = 1.0 scrolls 1 line up.
Some systems and devices support finer resolution and/or higher values for fast scrolls,
so programs should handle any value gracefully.

@a pos The widget-relative coordinates of the pointer.
@a absolutePos The absolute coordinates of the pointer.
@a delta The scroll distance.
@a direction The direction of the scroll or "smooth".
@see onScroll @see onScroll
*/ */
struct ScrollEvent : BaseEvent { struct ScrollEvent : BaseEvent {
Point<int> pos;
Point<float> delta;
Point<double> pos;
Point<double> absolutePos;
Point<double> delta;
ScrollDirection direction;


/** Constuctor */ /** Constuctor */
ScrollEvent() noexcept ScrollEvent() noexcept
: BaseEvent(), : BaseEvent(),
pos(0, 0),
delta(0.0f, 0.0f) {}
pos(0.0, 0.0),
absolutePos(0.0, 0.0),
delta(0.0, 0.0),
direction(kScrollSmooth) {}
}; };


/** /**
@@ -193,16 +257,18 @@ public:
oldPos(0, 0) {} oldPos(0, 0) {}
}; };


private:
/** /**
Constructor.
Private constructor, reserved for TopLevelWidget class.
*/ */
explicit Widget(Window& parent);
explicit Widget(TopLevelWidget* topLevelWidget);


/** /**
Constructor for a subwidget.
Private constructor, reserved for SubWidget class.
*/ */
explicit Widget(Widget* groupWidget);
explicit Widget(Widget* widgetToGroupTo);


public:
/** /**
Destructor. Destructor.
*/ */
@@ -215,9 +281,9 @@ public:
bool isVisible() const noexcept; bool isVisible() const noexcept;


/** /**
Set widget visible (or not) according to @a yesNo.
Set widget visible (or not) according to @a visible.
*/ */
void setVisible(bool yesNo);
void setVisible(bool visible);


/** /**
Show widget. Show widget.
@@ -244,7 +310,7 @@ public:
/** /**
Get size. Get size.
*/ */
const Size<uint>& getSize() const noexcept;
const Size<uint> getSize() const noexcept;


/** /**
Set width. Set width.
@@ -267,81 +333,58 @@ public:
void setSize(const Size<uint>& size) noexcept; void setSize(const Size<uint>& size) noexcept;


/** /**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
const Point<int>& getAbsolutePos() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
Get the Id associated with this widget.
@see setId
*/ */
void setAbsolutePos(int x, int y) noexcept;
uint getId() const noexcept;


/** /**
Set absolute position.
Set an Id to be associated with this widget.
@see getId
*/ */
void setAbsolutePos(const Point<int>& pos) noexcept;
void setId(uint id) noexcept;


/** /**
Get this widget's window application.
Same as calling getParentWindow().getApp().
Get the application associated with this widget's window.
This is the same as calling `getTopLevelWidget()->getApp()`.
*/ */
Application& getParentApp() const noexcept;
Application& getApp() const noexcept;


/** /**
Get parent window, as passed in the constructor.
Get the window associated with this widget.
This is the same as calling `getTopLevelWidget()->getWindow()`.
*/ */
Window& getParentWindow() const noexcept;
Window& getWindow() const noexcept;


/** /**
Check if this widget contains the point defined by @a x and @a y.
Get the graphics context associated with this widget's window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/ */
bool contains(int x, int y) const noexcept;
const GraphicsContext& getGraphicsContext() const noexcept;


/** /**
Check if this widget contains the point @a pos.
Get top-level widget, as passed directly in the constructor
or going up the chain of group widgets until it finds the top-level one.
*/ */
bool contains(const Point<int>& pos) const noexcept;
TopLevelWidget* getTopLevelWidget() const noexcept;


/** /**
Tell this widget's window to repaint itself.
Request repaint of this widget's area to the window this widget belongs to.
On the raw Widget class this function does nothing.
*/ */
void repaint() noexcept;
virtual void repaint() noexcept;


/**
Get the Id associated with this widget.
@see setId
*/
uint getId() const noexcept;
DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }


/**
Set an Id to be associated with this widget.
@see getId
*/
void setId(uint id) noexcept;
DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }


protected: protected:
/** /**
A function called to draw the view contents with OpenGL.
A function called to draw the widget contents.
*/ */
virtual void onDisplay() = 0; virtual void onDisplay() = 0;


@@ -357,6 +400,12 @@ protected:
*/ */
virtual bool onSpecial(const SpecialEvent&); virtual bool onSpecial(const SpecialEvent&);


/**
A function called when an UTF-8 character is received.
@return True to stop event propagation, false otherwise.
*/
virtual bool onCharacterInput(const CharacterInputEvent&);

/** /**
A function called when a mouse button is pressed or released. A function called when a mouse button is pressed or released.
@return True to stop event propagation, false otherwise. @return True to stop event propagation, false otherwise.
@@ -380,30 +429,16 @@ protected:
*/ */
virtual void onResize(const ResizeEvent&); virtual void onResize(const ResizeEvent&);


/**
A function called when the widget's absolute position is changed.
*/
virtual void onPositionChanged(const PositionChangedEvent&);

private: private:
struct PrivateData; struct PrivateData;
PrivateData* const pData; PrivateData* const pData;

/** @internal */
explicit Widget(Widget* groupWidget, bool addToSubWidgets);

friend class ImageSlider;
friend class NanoWidget;
friend class Window;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif
friend class SubWidget;
friend class TopLevelWidget;


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget)
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 336
- 64
dpf/dgl/Window.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,50 +19,79 @@


#include "Geometry.hpp" #include "Geometry.hpp"


#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UIExporter;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL START_NAMESPACE_DGL


class Application;
class TopLevelWidget;

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


class Application;
class Widget;
class StandaloneWindow;
/**
DGL Window class.

This is the where all OS-related events initially happen, before being propagated to any widgets.


A Window MUST have an Application instance tied to it.
It is not possible to swap Application instances from within the lifetime of a Window.
But it is possible to completely change the Widgets that a Window contains during its lifetime.

Typically the event handling functions as following:
Application -> Window -> Top-Level-Widget -> SubWidgets

Please note that, unlike many other graphical toolkits out there,
DGL makes a clear distinction between a Window and a Widget.
You cannot directly draw in a Window, you need to create a Widget for that.

Also, a Window MUST have a single top-level Widget.
The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle.

...
*/
class Window class Window
{ {
public: public:
#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
/** /**
File browser options. File browser options.
@see Window::openFileBrowser
*/ */
struct FileBrowserOptions { struct FileBrowserOptions {
/**
File browser button state.
This allows to customize the behaviour of the file browse dialog buttons.
*/
enum ButtonState {
kButtonInvisible,
kButtonVisibleUnchecked,
kButtonVisibleChecked,
};

/** Start directory, uses current working directory if null */
const char* startDir; const char* startDir;
/** File browser dialog window title, uses "FileBrowser" if null */
const char* title; const char* title;
/** File browser dialog window width */
uint width; uint width;
/** File browser dialog window height */
uint height; uint height;
// TODO file filter


/**
File browser buttons.

0 means hidden.
1 means visible and unchecked.
2 means visible and checked.
/**
File browser buttons.
*/ */
struct Buttons { struct Buttons {
uint listAllFiles;
uint showHidden;
uint showPlaces;
/** Whether to list all files vs only those with matching file extension */
ButtonState listAllFiles;
/** Whether to show hidden files */
ButtonState showHidden;
/** Whether to show list of places (bookmarks) */
ButtonState showPlaces;


/** Constuctor for default values */ /** Constuctor for default values */
Buttons() Buttons()
: listAllFiles(2),
showHidden(1),
showPlaces(1) {}
: listAllFiles(kButtonVisibleChecked),
showHidden(kButtonVisibleUnchecked),
showPlaces(kButtonVisibleUnchecked) {}
} buttons; } buttons;


/** Constuctor for default values */ /** Constuctor for default values */
@@ -75,91 +104,334 @@ public:
}; };
#endif // DGL_FILE_BROWSER_DISABLED #endif // DGL_FILE_BROWSER_DISABLED


/**
Constructor for a regular, standalone window.
*/
explicit Window(Application& app); explicit Window(Application& app);

/**
Constructor for a modal window, by having another window as its parent.
The Application instance must be the same between the 2 windows.
*/
explicit Window(Application& app, Window& parent); explicit Window(Application& app, Window& parent);
explicit Window(Application& app, intptr_t parentId, double scaling, bool resizable);
virtual ~Window();


void show();
void hide();
void close();
void exec(bool lockWait = false);
/**
Constructor for an embed Window without known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
double scaleFactor,
bool resizable);


void focus();
void repaint() noexcept;
/**
Constructor for an embed Window with known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
uint width,
uint height,
double scaleFactor,
bool resizable);


#ifndef DGL_FILE_BROWSER_DISABLED
bool openFileBrowser(const FileBrowserOptions& options);
#endif
/**
Destructor.
*/
virtual ~Window();


/**
Whether this Window is embed into another (usually not DGL-controlled) Window.
*/
bool isEmbed() const noexcept; bool isEmbed() const noexcept;


/**
Check if this window is visible / mapped.
Invisible windows do not receive events except resize.
@see setVisible(bool)
*/
bool isVisible() const noexcept; bool isVisible() const noexcept;
void setVisible(bool yesNo);


/**
Set windows visible (or not) according to @a visible.
Only valid for standalones, embed windows are always visible.
@see isVisible(), hide(), show()
*/
void setVisible(bool visible);

/**
Show window.
This is the same as calling setVisible(true).
@see isVisible(), setVisible(bool)
*/
void show();

/**
Hide window.
This is the same as calling setVisible(false).
@see isVisible(), setVisible(bool)
*/
void hide();

/**
Hide window and notify application of a window close event.
The application event-loop will stop when all windows have been closed.
For standalone windows only, has no effect if window is embed.
@see isEmbed()

@note It is possible to hide the window while not stopping the event-loop.
A closed window is always hidden, but the reverse is not always true.
*/
void close();

/**
Check if this window is resizable (by the user or window manager).
@see setResizable
*/
bool isResizable() const noexcept; bool isResizable() const noexcept;
void setResizable(bool yesNo);


/**
Set window as resizable (by the user or window manager).
It is always possible to resize a window programmatically, which is not the same as the user being allowed to it.
@note This function does nothing for plugins, where the resizable state is set via macro.
@see DISTRHO_UI_USER_RESIZABLE
*/
void setResizable(bool resizable);

/**
Get width.
*/
uint getWidth() const noexcept; uint getWidth() const noexcept;

/**
Get height.
*/
uint getHeight() const noexcept; uint getHeight() const noexcept;

/**
Get size.
*/
Size<uint> getSize() const noexcept; Size<uint> getSize() const noexcept;

/**
Set width.
*/
void setWidth(uint width);

/**
Set height.
*/
void setHeight(uint height);

/**
Set size using @a width and @a height values.
*/
void setSize(uint width, uint height); void setSize(uint width, uint height);
void setSize(Size<uint> size);


/**
Set size.
*/
void setSize(const Size<uint>& size);

/**
Get the title of the window previously set with setTitle().
*/
const char* getTitle() const noexcept; const char* getTitle() const noexcept;
void setTitle(const char* title);


void setGeometryConstraints(uint width, uint height, bool aspect);
void setTransientWinId(uintptr_t winId);
/**
Set the title of the window, typically displayed in the title bar or in window switchers.

This only makes sense for non-embedded windows.
*/
void setTitle(const char* title);


double getScaling() const noexcept;
/**
Check if key repeat events are ignored.
*/
bool isIgnoringKeyRepeat() const noexcept;


bool getIgnoringKeyRepeat() const noexcept;
/**
Set to ignore (or not) key repeat events according to @a ignore.
*/
void setIgnoringKeyRepeat(bool ignore) noexcept; void setIgnoringKeyRepeat(bool ignore) noexcept;


/**
Add a callback function to be triggered on every idle cycle or on a specific timer frequency.
You can add more than one, and remove them at anytime with removeIdleCallback().
This can be used to perform some action at a regular interval with relatively low frequency.

If providing a timer frequency, there are a few things to note:
1. There is a platform-specific limit to the number of supported timers, and overhead associated with each,
so you should create only a few timers and perform several tasks in one if necessary.
2. This timer frequency is not guaranteed to have a resolution better than 10ms
(the maximum timer resolution on Windows) and may be rounded up if it is too short.
On X11 and MacOS, a resolution of about 1ms can usually be relied on.
*/
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);

/**
Remove an idle callback previously added via addIdleCallback().
*/
bool removeIdleCallback(IdleCallback* callback);

/**
Get the application associated with this window.
*/
Application& getApp() const noexcept; Application& getApp() const noexcept;
intptr_t getWindowId() const noexcept;


/**
Get the graphics context associated with this window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/
const GraphicsContext& getGraphicsContext() const noexcept; const GraphicsContext& getGraphicsContext() const noexcept;


void addIdleCallback(IdleCallback* const callback);
void removeIdleCallback(IdleCallback* const callback);
/**
Get the "native" window handle.
Returned value depends on the platform:
- HaikuOS: This is a pointer to a `BView`.
- MacOS: This is a pointer to an `NSView*`.
- Windows: This is a `HWND`.
- Everything else: This is an [X11] `Window`.
*/
uintptr_t getNativeWindowHandle() const noexcept;

/**
Get the scale factor requested for this window.
This is purely informational, and up to developers to choose what to do with it.

If you do not want to deal with this yourself,
consider using setGeometryConstraints() where you can specify to automatically scale the window contents.
@see setGeometryConstraints
*/
double getScaleFactor() const noexcept;

/**
Grab the keyboard input focus.
*/
void focus();

#ifndef DGL_FILE_BROWSER_DISABLED
/**
Open a file browser dialog with this window as parent.
A few options can be specified to setup the dialog.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.

This function does not block the event loop.
*/
bool openFileBrowser(const FileBrowserOptions& options);
#endif

/**
Request repaint of this window, for the entire area.
*/
void repaint() noexcept;

/**
Request partial repaint of this window, with bounds according to @a rect.
*/
void repaint(const Rectangle<uint>& rect) noexcept;

/**
Run this window as a modal, blocking input events from the parent.
Only valid for windows that have been created with another window as parent (as passed in the constructor).
Can optionally block-wait, but such option is only available if the application is running as standalone.
*/
void runAsModal(bool blockWait = false);

/**
Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically.
*/
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false);

/** DEPRECATED Use isIgnoringKeyRepeat(). */
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()")
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); }

/** DEPRECATED Use getScaleFactor(). */
DISTRHO_DEPRECATED_BY("getScaleFactor()")
inline double getScaling() const noexcept { return getScaleFactor(); }

/** DEPRECATED Use runAsModal(bool). */
DISTRHO_DEPRECATED_BY("runAsModal(bool)")
inline void exec(bool blockWait = false) { runAsModal(blockWait); }

// TESTING, DO NOT USE
void leaveContext();


protected: protected:
virtual void onDisplayBefore();
virtual void onDisplayAfter();
/**
A function called when the window is attempted to be closed.
Returning true closes the window, which is the default behaviour.
Override this method and return false to prevent the window from being closed by the user.

This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI.
For embed windows, closing is handled by the host/parent process and we have no control over it.
As such, a close action on embed windows will always succeed and cannot be cancelled.
*/
virtual bool onClose();

/**
A function called when the window gains or loses the keyboard focus.
The default implementation does nothing.
*/
virtual void onFocus(bool focus, CrossingMode mode);

/**
A function called when the window is resized.
If there is a top-level widget associated with this window, its size will be set right after this function.
The default implementation sets up drawing context where necessary.
*/
virtual void onReshape(uint width, uint height); virtual void onReshape(uint width, uint height);
virtual void onClose();

/**
A function called when scale factor requested for this window changes.
The default implementation does nothing.
WARNING function needs a proper name
*/
virtual void onScaleFactorChanged(double scaleFactor);


#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
virtual void fileBrowserSelected(const char* filename);
#endif
/**
A function called when a path is selected by the user, as triggered by openFileBrowser().
This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
The default implementation does nothing.
*/
virtual void onFileSelected(const char* filename);


// internal
void _setAutoScaling(double scaling) noexcept;
/** DEPRECATED Use onFileSelected(). */
DISTRHO_DEPRECATED_BY("onFileSelected(const char*)")
inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); }
#endif


private: private:
struct PrivateData; struct PrivateData;
PrivateData* const pData; PrivateData* const pData;
friend class Application; friend class Application;
friend class Widget;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UIExporter;
#endif

virtual void _addWidget(Widget* const widget);
virtual void _removeWidget(Widget* const widget);
void _idle();

bool handlePluginKeyboard(const bool press, const uint key);
bool handlePluginSpecial(const bool press, const Key key);
friend class TopLevelWidget;


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window);
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL


/* TODO
* add eventcrossing/enter-leave event
*/
#if 0
protected:
bool handlePluginKeyboard(const bool press, const uint key);
bool handlePluginSpecial(const bool press, const Key key);
#endif

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

#endif // DGL_WINDOW_HPP_INCLUDED #endif // DGL_WINDOW_HPP_INCLUDED

+ 33
- 30
dpf/dgl/src/Application.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,14 +15,13 @@
*/ */


#include "ApplicationPrivateData.hpp" #include "ApplicationPrivateData.hpp"
#include "../Window.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


Application::Application()
: pData(new PrivateData()) {}
Application::Application(const bool isStandalone)
: pData(new PrivateData(isStandalone)) {}


Application::~Application() Application::~Application()
{ {
@@ -31,44 +30,48 @@ Application::~Application()


void Application::idle() void Application::idle()
{ {
for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it)
{
Window* const window(*it);
window->_idle();
}

for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
pData->idle(0);
} }


void Application::exec(int idleTime)
void Application::exec(const uint idleTimeInMs)
{ {
for (; pData->doLoop;)
{
idle();
d_msleep(idleTime);
}
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,);

while (! pData->isQuitting)
pData->idle(idleTimeInMs);
} }


void Application::quit() void Application::quit()
{ {
pData->doLoop = false;
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,);


for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit)
{
Window* const window(*rit);
window->close();
}
pData->quit();
} }


bool Application::isQuiting() const noexcept bool Application::isQuiting() const noexcept
{ {
return !pData->doLoop;
return pData->isQuitting || pData->isQuittingInNextCycle;
}

void Application::addIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

pData->idleCallbacks.push_back(callback);
}

void Application::removeIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

pData->idleCallbacks.remove(callback);
}

void Application::setClassName(const char* const name)
{
pData->setClassName(name);
} }


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


END_NAMESPACE_DGL END_NAMESPACE_DGL

+ 159
- 0
dpf/dgl/src/ApplicationPrivateData.cpp View File

@@ -0,0 +1,159 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "ApplicationPrivateData.hpp"
#include "../Window.hpp"

#include "pugl.hpp"

#include <ctime>

START_NAMESPACE_DGL

typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator;

static ThreadHandle getCurrentThreadHandle() noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread();
#else
return pthread_self();
#endif
}

static bool isThisMainThread(const ThreadHandle mainThreadHandle) noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ?
#else
return pthread_equal(getCurrentThreadHandle(), mainThreadHandle) != 0;
#endif
}

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

Application::PrivateData::PrivateData(const bool standalone)
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE,
standalone ? PUGL_WORLD_THREADS : 0x0)),
isStandalone(standalone),
isQuitting(false),
isQuittingInNextCycle(false),
isStarting(true),
visibleWindows(0),
mainThreadHandle(getCurrentThreadHandle()),
windows(),
idleCallbacks()
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

puglSetWorldHandle(world, this);
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));
#ifdef HAVE_X11
sofdFileDialogSetup(world);
#endif
}

Application::PrivateData::~PrivateData()
{
DISTRHO_SAFE_ASSERT(isStarting || isQuitting);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();

if (world != nullptr)
puglFreeWorld(world);
}

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

void Application::PrivateData::oneWindowShown() noexcept
{
if (++visibleWindows == 1)
{
isQuitting = false;
isStarting = false;
}
}

void Application::PrivateData::oneWindowClosed() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,);

if (--visibleWindows == 0)
isQuitting = true;
}

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

void Application::PrivateData::idle(const uint timeoutInMs)
{
if (isQuittingInNextCycle)
{
quit();
isQuittingInNextCycle = false;
}

if (world != nullptr)
{
const double timeoutInSeconds = timeoutInMs != 0
? static_cast<double>(timeoutInMs) / 1000.0
: 0.0;

puglUpdate(world, timeoutInSeconds);
}

for (std::list<IdleCallback*>::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
}

void Application::PrivateData::quit()
{
DISTRHO_SAFE_ASSERT_RETURN(isStandalone,);

if (! isThisMainThread(mainThreadHandle))
{
if (! isQuittingInNextCycle)
{
isQuittingInNextCycle = true;
return;
}
}

isQuitting = true;

#ifndef DPF_TEST_APPLICATION_CPP
for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit)
{
DGL_NAMESPACE::Window* const window(*rit);
window->close();
}
#endif
}

void Application::PrivateData::setClassName(const char* const name)
{
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);

puglSetClassName(world, name);
}

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

END_NAMESPACE_DGL

+ 68
- 38
dpf/dgl/src/ApplicationPrivateData.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,53 +18,83 @@
#define DGL_APP_PRIVATE_DATA_HPP_INCLUDED #define DGL_APP_PRIVATE_DATA_HPP_INCLUDED


#include "../Application.hpp" #include "../Application.hpp"
#include "../../distrho/extra/Sleep.hpp"


#include <list> #include <list>


#ifdef DISTRHO_OS_WINDOWS
# include <winsock2.h>
# include <windows.h>
typedef HANDLE ThreadHandle;
#else
# include <pthread.h>
typedef pthread_t ThreadHandle;
#endif

typedef struct PuglWorldImpl PuglWorld;

START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
class Window;

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


struct Application::PrivateData { struct Application::PrivateData {
bool doLoop;
/** Pugl world instance. */
PuglWorld* const world;

/** Whether the application is running as standalone, otherwise it is part of a plugin. */
const bool isStandalone;

/** Whether the applicating is about to quit, or already stopped. Defaults to false. */
bool isQuitting;

/** Helper for safely close everything from main thread. */
bool isQuittingInNextCycle;

/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */
bool isStarting;

/** Counter of visible windows, only used in standalone mode.
If 0->1, application is starting. If 1->0, application is quitting/stopping. */
uint visibleWindows; uint visibleWindows;
std::list<Window*> windows;
std::list<IdleCallback*> idleCallbacks;

PrivateData()
: doLoop(true),
visibleWindows(0),
windows(),
idleCallbacks() {}

~PrivateData()
{
DISTRHO_SAFE_ASSERT(! doLoop);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();
}

void oneShown() noexcept
{
if (++visibleWindows == 1)
doLoop = true;
}

void oneHidden() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,);

if (--visibleWindows == 0)
doLoop = false;
}

DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)

/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */
ThreadHandle mainThreadHandle;

/** List of windows for this application. Only used during `close`. */
std::list<DGL_NAMESPACE::Window*> windows;

/** List of idle callbacks for this application. */
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks;

/** Constructor and destructor */
explicit PrivateData(bool standalone);
~PrivateData();

/** Flag one window as shown, which increments @a visibleWindows.
Sets @a isQuitting and @a isStarting as false if this is the first window.
For standalone mode only. */
void oneWindowShown() noexcept;

/** Flag one window as closed, which decrements @a visibleWindows.
Sets @a isQuitting as true if this is the last window.
For standalone mode only. */
void oneWindowClosed() noexcept;

/** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */
void idle(uint timeoutInMs);

/** Set flag indicating application is quitting, and close all windows in reverse order of registration.
For standalone mode only. */
void quit();

/** Set pugl world class name. */
void setClassName(const char* name);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 733
- 36
dpf/dgl/src/Cairo.cpp View File

@@ -1,6 +1,7 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,77 +15,148 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#include "../Geometry.hpp"
#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../Cairo.hpp" #include "../Cairo.hpp"
#include "../Color.hpp"
#include "../ImageBaseWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


static void notImplemented(const char *name)
static void notImplemented(const char* const name)
{ {
d_stderr2("cairo function not implemented: %s", name); d_stderr2("cairo function not implemented: %s", name);
} }


// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

if (includeAlpha)
cairo_set_source_rgba(handle, red, green, blue, alpha);
else
cairo_set_source_rgb(handle, red, green, blue);
}

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Line // Line


template<typename T>
void Line<T>::draw(const GraphicsContext& context, const T width)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, width);
cairo_move_to(handle, posStart.getX(), posStart.getY());
cairo_line_to(handle, posEnd.getX(), posEnd.getY());
cairo_stroke(handle);
}

template<typename T> template<typename T>
void Line<T>::draw() void Line<T>::draw()
{ {
notImplemented("Line::draw"); notImplemented("Line::draw");
} }


template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Circle // Circle


template<typename T> template<typename T>
void Circle<T>::_draw(const bool outline)
static void drawCircle(cairo_t* const handle,
const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{ {
notImplemented("Circle::draw");
}
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);


// -----------------------------------------------------------------------
// Triangle
const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;

// TODO use arc
/*
cairo_arc(handle, origx, origy, size, sin, cos);
*/

cairo_move_to(handle, x + origx, y + origy);

for (uint i=1; i<numSegments; ++i)
{
cairo_line_to(handle, x + origx, y + origy);

t = x;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

cairo_line_to(handle, x + origx, y + origy);

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}


template<typename T> template<typename T>
void Triangle<T>::_draw(const bool outline)
void Circle<T>::draw(const GraphicsContext& context)
{ {
notImplemented("Triangle::draw");
}
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;


// -----------------------------------------------------------------------
// Rectangle
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
}


template<typename T> template<typename T>
void Rectangle<T>::_draw(const bool outline)
void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{ {
notImplemented("Rectangle::draw");
}
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);


// -----------------------------------------------------------------------
// Possible template data types
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;


template class Point<double>;
template class Point<float>;
template class Point<int>;
template class Point<uint>;
template class Point<short>;
template class Point<ushort>;
cairo_set_line_width(handle, lineWidth);
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
}


template class Size<double>;
template class Size<float>;
template class Size<int>;
template class Size<uint>;
template class Size<short>;
template class Size<ushort>;
template<typename T>
void Circle<T>::draw()
{
notImplemented("Circle::draw");
}


template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;
template<typename T>
void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}


template class Circle<double>; template class Circle<double>;
template class Circle<float>; template class Circle<float>;
@@ -93,6 +165,60 @@ template class Circle<uint>;
template class Circle<short>; template class Circle<short>;
template class Circle<ushort>; template class Circle<ushort>;


// -----------------------------------------------------------------------
// Triangle

template<typename T>
static void drawTriangle(cairo_t* const handle,
const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

cairo_move_to(handle, pos1.getX(), pos1.getY());
cairo_line_to(handle, pos2.getX(), pos2.getY());
cairo_line_to(handle, pos3.getX(), pos3.getY());
cairo_line_to(handle, pos1.getX(), pos1.getY());

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}

template<typename T>
void Triangle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

drawTriangle<T>(handle, pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, lineWidth);
drawTriangle<T>(handle, pos1, pos2, pos3, true);
}

template<typename T>
void Triangle<T>::draw()
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}

template class Triangle<double>; template class Triangle<double>;
template class Triangle<float>; template class Triangle<float>;
template class Triangle<int>; template class Triangle<int>;
@@ -100,6 +226,54 @@ template class Triangle<uint>;
template class Triangle<short>; template class Triangle<short>;
template class Triangle<ushort>; template class Triangle<ushort>;


// -----------------------------------------------------------------------
// Rectangle

template<typename T>
static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline)
{
cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}

template<typename T>
void Rectangle<T>::draw(const GraphicsContext& context)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

drawRectangle(handle, *this, false);
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, lineWidth);
drawRectangle(handle, *this, true);
}

template<typename T>
void Rectangle<T>::draw()
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}

template class Rectangle<double>; template class Rectangle<double>;
template class Rectangle<float>; template class Rectangle<float>;
template class Rectangle<int>; template class Rectangle<int>;
@@ -107,6 +281,529 @@ template class Rectangle<uint>;
template class Rectangle<short>; template class Rectangle<short>;
template class Rectangle<ushort>; template class Rectangle<ushort>;


// -----------------------------------------------------------------------
// CairoImage

static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept
{
switch (format)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
return CAIRO_FORMAT_A8;
case kImageFormatBGR:
case kImageFormatRGB:
return CAIRO_FORMAT_RGB24;
case kImageFormatBGRA:
case kImageFormatRGBA:
return CAIRO_FORMAT_ARGB32;
}

return CAIRO_FORMAT_INVALID;
}

/*
static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept
{
switch (format)
{
case CAIRO_FORMAT_INVALID:
break;
case CAIRO_FORMAT_ARGB32:
break;
case CAIRO_FORMAT_RGB24:
break;
case CAIRO_FORMAT_A8:
break;
case CAIRO_FORMAT_A1:
break;
case CAIRO_FORMAT_RGB16_565:
break;
case CAIRO_FORMAT_RGB30:
break;
}

return kImageFormatNull;
}
*/

CairoImage::CairoImage()
: ImageBase(),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr) {}

CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr)
{
loadFromMemory(rdata, w, h, fmt);
}

CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr)
{
loadFromMemory(rdata, s, fmt);
}

CairoImage::CairoImage(const CairoImage& image)
: ImageBase(image.rawData, image.size, image.format),
surface(cairo_surface_reference(image.surface)),
surfacedata(image.surfacedata),
datarefcount(image.datarefcount)
{
if (datarefcount != nullptr)
++(*datarefcount);
}

CairoImage::~CairoImage()
{
cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
{
std::free(surfacedata);
std::free(datarefcount);
}
}

void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
const cairo_format_t cairoformat = asCairoImageFormat(fmt);
const int width = static_cast<int>(s.getWidth());
const int height = static_cast<int>(s.getHeight());
const int stride = cairo_format_stride_for_width(cairoformat, width);

uchar* const newdata = (uchar*)std::malloc(static_cast<size_t>(width * height * stride * 4));
DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,);

cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride);
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),);
DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),);

cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));

surface = newsurface;
surfacedata = newdata;
*datarefcount = 1;

switch (fmt)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
// Grayscale to A8
// TODO
break;
case kImageFormatBGR:
// BGR8 to CAIRO_FORMAT_RGB24
for (int h = 0; h < height; ++h)
{
for (int w = 0; w < width; ++w)
{
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*3+w*3+0]);
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*3+w*3+1]);
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*3+w*3+2]);
newdata[h*width*4+w*4+3] = 0;
}
}
break;
case kImageFormatBGRA:
// BGRA8 to CAIRO_FORMAT_ARGB32
// FIXME something is wrong here...
for (int h = 0, t; h < height; ++h)
{
for (int w = 0; w < width; ++w)
{
if ((t = rdata[h*width*4+w*4+3]) != 0)
{
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*4+w*4+0]);
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*4+w*4+1]);
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*4+w*4+2]);
newdata[h*width*4+w*4+3] = static_cast<uchar>(t);
}
else
{
// make all pixels zero, cairo does not render full transparency otherwise
memset(&newdata[h*width*4+w*4], 0, 4);
}
}
}
break;
case kImageFormatRGB:
// RGB8 to CAIRO_FORMAT_RGB24
// TODO
break;
case kImageFormatRGBA:
// RGBA8 to CAIRO_FORMAT_ARGB32
// TODO
break;
}

ImageBase::loadFromMemory(rdata, s, fmt);
}

// const GraphicsContext& context
void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept
{
struct PngReaderData
{
const char* dataPtr;
uint sizeLeft;

static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept
{
PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure);

if (readerData.sizeLeft < length)
return CAIRO_STATUS_READ_ERROR;

std::memcpy(data, readerData.dataPtr, length);
readerData.dataPtr += length;
readerData.sizeLeft -= length;
return CAIRO_STATUS_SUCCESS;
}
};

PngReaderData readerData;
readerData.dataPtr = pngData;
readerData.sizeLeft = pngSize;

cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData);
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);

const int newwidth = cairo_image_surface_get_width(newsurface);
const int newheight = cairo_image_surface_get_height(newsurface);
DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,);
DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,);

cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));

surface = newsurface;
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
*datarefcount = 1;

rawData = nullptr;
format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface));
size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight));
}

void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
{
if (surface == nullptr)
return;

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
cairo_paint(handle);
}

CairoImage& CairoImage::operator=(const CairoImage& image) noexcept
{
cairo_surface_t* newsurface = cairo_surface_reference(image.surface);
cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
{
std::free(surfacedata);
std::free(datarefcount);
}

surface = newsurface;
rawData = image.rawData;
size = image.size;
format = image.format;
surfacedata = image.surfacedata;
datarefcount = image.datarefcount;

if (datarefcount != nullptr)
++(*datarefcount);

return *this;
}

// -----------------------------------------------------------------------
// CairoSubWidget

template <>
CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent)
: SubWidget(parent) {}

template class CairoBaseWidget<SubWidget>;

// -----------------------------------------------------------------------
// CairoTopLevelWidget

template <>
CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo)
: TopLevelWidget(windowToMapTo) {}

template class CairoBaseWidget<TopLevelWidget>;

// -----------------------------------------------------------------------
// CairoStandaloneWindow

template <>
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app)
: StandaloneWindow(app) {}

template <>
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow)
: StandaloneWindow(app, parentWindow) {}

template class CairoBaseWidget<StandaloneWindow>;

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<CairoImage>::onDisplay()
{
img.draw(getGraphicsContext());
}
#endif

template class ImageBaseAboutWindow<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<CairoImage>::PrivateData::init()
{
alwaysRepaint = true;
cairoSurface = nullptr;
}

template <>
void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
{
cairo_surface_destroy((cairo_surface_t*)cairoSurface);
cairoSurface = nullptr;
}

/**
Get the pixel size in bytes.
@return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes.
*/
static int getBytesPerPixel(const cairo_format_t format) noexcept
{
switch (format)
{
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_RGB30:
return 4;
case CAIRO_FORMAT_RGB16_565:
return 2;
case CAIRO_FORMAT_A8:
return 1;
case CAIRO_FORMAT_A1:
return 0;
default:
DISTRHO_SAFE_ASSERT(false);
return 0;
}
}

static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept
{
const cairo_format_t format = cairo_image_surface_get_format(origsurface);
const int bpp = getBytesPerPixel(format);

if (bpp == 0)
return nullptr;

const int fullWidth = cairo_image_surface_get_width(origsurface);
const int fullHeight = cairo_image_surface_get_height(origsurface);
const int stride = cairo_image_surface_get_stride(origsurface);
uchar* const fullData = cairo_image_surface_get_data(origsurface);

x = (x < fullWidth) ? x : fullWidth;
y = (y < fullHeight) ? y : fullHeight;
width = (x + width < fullWidth) ? width : (fullWidth - x);
height = (x + height < fullHeight) ? height : (fullHeight - x);

uchar* const data = fullData + (x * bpp + y * stride);
return cairo_image_surface_create_for_data(data, format, width, height, stride);
}

template <>
void ImageBaseKnob<CairoImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);

cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;

if (! pData->isReady)
{
const int layerW = static_cast<int>(pData->imgLayerWidth);
const int layerH = static_cast<int>(pData->imgLayerHeight);
int layerNum = 0;

if (pData->rotationAngle == 0)
layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5);

const int layerX = pData->isImgVertical ? 0 : layerNum * layerW;
const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH;

cairo_surface_t* newsurface;

if (pData->rotationAngle == 0)
{
newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH);
}
else
{
newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH);
cairo_t* const cr = cairo_create(newsurface);
cairo_translate(cr, 0.5 * layerW, 0.5 * layerH);
cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180));
cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH);
cairo_paint(cr);
cairo_destroy(cr);
}

DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);

cairo_surface_destroy(surface);
pData->cairoSurface = surface = newsurface;
pData->isReady = true;
}

if (surface != nullptr)
{
cairo_set_source_surface(handle, surface, 0, 0);
cairo_paint(handle);
}
}

template class ImageBaseKnob<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<CairoImage>;

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

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;

bool needsResetClip = false;

cairo_matrix_t matrix;
cairo_get_matrix(handle, &matrix);

if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
{
// full viewport size
cairo_translate(handle, 0, 0);
}
else if (needsViewportScaling)
{
// limit viewport to widget bounds
// NOTE only used for nanovg for now, which is not relevant here
cairo_translate(handle, 0, 0);
}
else
{
// set viewport pos
cairo_translate(handle, absolutePos.getX(), absolutePos.getY());

// then cut the outer bounds
cairo_rectangle(handle,
0,
0,
std::round(self->getWidth() * autoScaleFactor),
std::round(self->getHeight() * autoScaleFactor));

cairo_clip(handle);
needsResetClip = true;
}

// display widget
self->onDisplay();

if (needsResetClip)
cairo_reset_clip(handle);

cairo_set_matrix(handle, &matrix);

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// FIXME anything needed here?
#if 0
// full viewport size
if (window.pData->autoScaling)
glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor);
else
glViewport(0, 0, width, height);
#endif

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
GraphicsContext& context((GraphicsContext&)graphicsContext);
((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
return context;
}

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


END_NAMESPACE_DGL END_NAMESPACE_DGL

+ 34
- 37
dpf/dgl/src/Color.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -16,10 +16,6 @@


#include "../Color.hpp" #include "../Color.hpp"


#ifndef HAVE_DCAIRO
#include "nanovg/nanovg.h"
#endif

START_NAMESPACE_DGL START_NAMESPACE_DGL


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -61,27 +57,27 @@ static uchar getFixedRange2(const float& value)
return 0; return 0;
if (value2 >= 255.0f) if (value2 >= 255.0f)
return 255; return 255;
return static_cast<uchar>(value2);
return static_cast<uchar>(value2 + 0.5f);
} }


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


Color::Color() noexcept Color::Color() noexcept
: red(1.0f),
green(1.0f),
blue(1.0f),
: red(0.0f),
green(0.0f),
blue(0.0f),
alpha(1.0f) {} alpha(1.0f) {}


Color::Color(int r, int g, int b, int a) noexcept
Color::Color(const int r, const int g, const int b, const float a) noexcept
: red(static_cast<float>(r)/255.0f), : red(static_cast<float>(r)/255.0f),
green(static_cast<float>(g)/255.0f), green(static_cast<float>(g)/255.0f),
blue(static_cast<float>(b)/255.0f), blue(static_cast<float>(b)/255.0f),
alpha(static_cast<float>(a)/255.0f)
alpha(a)
{ {
fixBounds(); fixBounds();
} }


Color::Color(float r, float g, float b, float a) noexcept
Color::Color(const float r, const float g, const float b, const float a) noexcept
: red(r), : red(r),
green(g), green(g),
blue(b), blue(b),
@@ -109,7 +105,7 @@ Color& Color::operator=(const Color& color) noexcept
return *this; return *this;
} }


Color::Color(const Color& color1, const Color& color2, float u) noexcept
Color::Color(const Color& color1, const Color& color2, const float u) noexcept
: red(color1.red), : red(color1.red),
green(color1.green), green(color1.green),
blue(color1.blue), blue(color1.blue),
@@ -136,65 +132,66 @@ Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
return col; return col;
} }


Color Color::fromHTML(const char* rgb, float alpha)
Color Color::fromHTML(const char* rgb, const float alpha) noexcept
{ {
Color fallback; Color fallback;
DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback); DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback);


if (rgb[0] == '#') ++rgb;
if (rgb[0] == '#')
++rgb;
DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback); DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback);


std::size_t rgblen(std::strlen(rgb));
std::size_t rgblen = std::strlen(rgb);
DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback); DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback);


char rgbtmp[3] = { '\0', '\0', '\0' };
char rgbtmp[5] = { '0', 'x', '\0', '\0', '\0' };
int r, g, b; int r, g, b;


if (rgblen == 3) if (rgblen == 3)
{ {
rgbtmp[0] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;


rgbtmp[0] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;


rgbtmp[0] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;
} }
else else
{ {
rgbtmp[0] = rgb[0];
rgbtmp[1] = rgb[1];
rgbtmp[2] = rgb[0];
rgbtmp[3] = rgb[1];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));


rgbtmp[0] = rgb[2];
rgbtmp[1] = rgb[3];
rgbtmp[2] = rgb[2];
rgbtmp[3] = rgb[3];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));


rgbtmp[0] = rgb[4];
rgbtmp[1] = rgb[5];
rgbtmp[2] = rgb[4];
rgbtmp[3] = rgb[5];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
} }


return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f));
return Color(r, g, b, alpha);
} }


void Color::interpolate(const Color& other, float u) noexcept void Color::interpolate(const Color& other, float u) noexcept
{ {
fixRange(u); fixRange(u);
const float oneMinusU(1.0f - u);
const float oneMinusU = 1.0f - u;


red = red * oneMinusU + other.red * u;
green = green * oneMinusU + other.green * u;
blue = blue * oneMinusU + other.blue * u;
alpha = alpha * oneMinusU + other.alpha * u;
red = (red * oneMinusU) + (other.red * u);
green = (green * oneMinusU) + (other.green * u);
blue = (blue * oneMinusU) + (other.blue * u);
alpha = (alpha * oneMinusU) + (other.alpha * u);


fixBounds(); fixBounds();
} }


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


bool Color::isEqual(const Color& color, bool withAlpha) noexcept
bool Color::isEqual(const Color& color, const bool withAlpha) noexcept
{ {
const uchar r1 = getFixedRange2(rgba[0]); const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]); const uchar g1 = getFixedRange2(rgba[1]);
@@ -212,7 +209,7 @@ bool Color::isEqual(const Color& color, bool withAlpha) noexcept
return (r1 == r2 && g1 == g2 && b1 == b2); return (r1 == r2 && g1 == g2 && b1 == b2);
} }


bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept
bool Color::isNotEqual(const Color& color, const bool withAlpha) noexcept
{ {
const uchar r1 = getFixedRange2(rgba[0]); const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]); const uchar g1 = getFixedRange2(rgba[1]);


+ 72
- 8
dpf/dgl/src/Common.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,12 +17,13 @@
#ifndef DGL_COMMON_HPP_INCLUDED #ifndef DGL_COMMON_HPP_INCLUDED
#define DGL_COMMON_HPP_INCLUDED #define DGL_COMMON_HPP_INCLUDED


#include "../ImageWidgets.hpp"
#include "../ImageBaseWidgets.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


template <class ImageType>
struct ButtonImpl { struct ButtonImpl {
enum State { enum State {
kStateNormal = 0, kStateNormal = 0,
@@ -32,11 +33,11 @@ struct ButtonImpl {


int button; int button;
int state; int state;
Widget* self;
ImageBaseButton<ImageType>* const self;


ImageButton::Callback* callback_img;
typename ImageBaseButton<ImageType>::Callback* callback_img;


ButtonImpl(Widget* const s) noexcept
explicit ButtonImpl(ImageBaseButton<ImageType>* const s) noexcept
: button(-1), : button(-1),
state(kStateNormal), state(kStateNormal),
self(s), self(s),
@@ -66,7 +67,7 @@ struct ButtonImpl {
self->repaint(); self->repaint();


if (callback_img != nullptr) if (callback_img != nullptr)
callback_img->imageButtonClicked((ImageButton*)self, button2);
callback_img->imageButtonClicked(self, button2);


return true; return true;
} }
@@ -74,7 +75,7 @@ struct ButtonImpl {
// button was pressed, wait for release // button was pressed, wait for release
if (ev.press && self->contains(ev.pos)) if (ev.press && self->contains(ev.pos))
{ {
button = ev.button;
button = static_cast<int>(ev.button);
state = kStateDown; state = kStateDown;
self->repaint(); self->repaint();
return true; return true;
@@ -114,7 +115,70 @@ struct ButtonImpl {
} }


DISTRHO_PREVENT_HEAP_ALLOCATION DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl)
DISTRHO_DECLARE_NON_COPYABLE(ButtonImpl)
};

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

template <class ImageType>
struct ImageBaseKnob<ImageType>::PrivateData {
ImageType image;
float minimum;
float maximum;
float step;
float value;
float valueDef;
float valueTmp;
bool usingDefault;
bool usingLog;
Orientation orientation;

int rotationAngle;
bool dragging;
double lastX;
double lastY;

Callback* callback;

bool alwaysRepaint;
bool isImgVertical;
uint imgLayerWidth;
uint imgLayerHeight;
uint imgLayerCount;
bool isReady;

union {
uint glTextureId;
void* cairoSurface;
};

explicit PrivateData(const ImageType& img, const Orientation o);
explicit PrivateData(PrivateData* const other);
void assignFrom(PrivateData* const other);

~PrivateData()
{
cleanup();
}

void init();
void cleanup();

inline float logscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return a * std::exp(b*v);
}

inline float invlogscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return std::log(v/a)/b;
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
}; };


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


+ 231
- 182
dpf/dgl/src/Geometry.cpp View File

@@ -14,6 +14,11 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../Geometry.hpp" #include "../Geometry.hpp"


#include <cmath> #include <cmath>
@@ -27,129 +32,129 @@ static const float M_2PIf = 3.14159265358979323846f*2.0f;


template<typename T> template<typename T>
Point<T>::Point() noexcept Point<T>::Point() noexcept
: fX(0),
fY(0) {}
: x(0),
y(0) {}


template<typename T> template<typename T>
Point<T>::Point(const T& x, const T& y) noexcept
: fX(x),
fY(y) {}
Point<T>::Point(const T& x2, const T& y2) noexcept
: x(x2),
y(y2) {}


template<typename T> template<typename T>
Point<T>::Point(const Point<T>& pos) noexcept Point<T>::Point(const Point<T>& pos) noexcept
: fX(pos.fX),
fY(pos.fY) {}
: x(pos.x),
y(pos.y) {}


template<typename T> template<typename T>
const T& Point<T>::getX() const noexcept const T& Point<T>::getX() const noexcept
{ {
return fX;
return x;
} }


template<typename T> template<typename T>
const T& Point<T>::getY() const noexcept const T& Point<T>::getY() const noexcept
{ {
return fY;
return y;
} }


template<typename T> template<typename T>
void Point<T>::setX(const T& x) noexcept
void Point<T>::setX(const T& x2) noexcept
{ {
fX = x;
x = x2;
} }


template<typename T> template<typename T>
void Point<T>::setY(const T& y) noexcept
void Point<T>::setY(const T& y2) noexcept
{ {
fY = y;
y = y2;
} }


template<typename T> template<typename T>
void Point<T>::setPos(const T& x, const T& y) noexcept
void Point<T>::setPos(const T& x2, const T& y2) noexcept
{ {
fX = x;
fY = y;
x = x2;
y = y2;
} }


template<typename T> template<typename T>
void Point<T>::setPos(const Point<T>& pos) noexcept void Point<T>::setPos(const Point<T>& pos) noexcept
{ {
fX = pos.fX;
fY = pos.fY;
x = pos.x;
y = pos.y;
} }


template<typename T> template<typename T>
void Point<T>::moveBy(const T& x, const T& y) noexcept
void Point<T>::moveBy(const T& x2, const T& y2) noexcept
{ {
fX = static_cast<T>(fX+x);
fY = static_cast<T>(fY+y);
x = static_cast<T>(x+x2);
y = static_cast<T>(y+y2);
} }


template<typename T> template<typename T>
void Point<T>::moveBy(const Point<T>& pos) noexcept void Point<T>::moveBy(const Point<T>& pos) noexcept
{ {
fX = static_cast<T>(fX+pos.fX);
fY = static_cast<T>(fY+pos.fY);
x = static_cast<T>(x+pos.x);
y = static_cast<T>(y+pos.y);
} }


template<typename T> template<typename T>
bool Point<T>::isZero() const noexcept bool Point<T>::isZero() const noexcept
{ {
return fX == 0 && fY == 0;
return x == 0 && y == 0;
} }


template<typename T> template<typename T>
bool Point<T>::isNotZero() const noexcept bool Point<T>::isNotZero() const noexcept
{ {
return fX != 0 || fY != 0;
return x != 0 || y != 0;
} }


template<typename T> template<typename T>
Point<T> Point<T>::operator+(const Point<T>& pos) noexcept Point<T> Point<T>::operator+(const Point<T>& pos) noexcept
{ {
return Point<T>(fX+pos.fX, fY+pos.fY);
return Point<T>(x+pos.x, y+pos.y);
} }


template<typename T> template<typename T>
Point<T> Point<T>::operator-(const Point<T>& pos) noexcept Point<T> Point<T>::operator-(const Point<T>& pos) noexcept
{ {
return Point<T>(fX-pos.fX, fY-pos.fY);
return Point<T>(x-pos.x, y-pos.y);
} }


template<typename T> template<typename T>
Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept
{ {
fX = pos.fX;
fY = pos.fY;
x = pos.x;
y = pos.y;
return *this; return *this;
} }


template<typename T> template<typename T>
Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept
{ {
fX = static_cast<T>(fX+pos.fX);
fY = static_cast<T>(fY+pos.fY);
x = static_cast<T>(x+pos.x);
y = static_cast<T>(y+pos.y);
return *this; return *this;
} }


template<typename T> template<typename T>
Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept
{ {
fX = static_cast<T>(fX-pos.fX);
fY = static_cast<T>(fY-pos.fY);
x = static_cast<T>(x-pos.x);
y = static_cast<T>(y-pos.y);
return *this; return *this;
} }


template<typename T> template<typename T>
bool Point<T>::operator==(const Point<T>& pos) const noexcept bool Point<T>::operator==(const Point<T>& pos) const noexcept
{ {
return (fX == pos.fX && fY == pos.fY);
return (x == pos.x && y == pos.y);
} }


template<typename T> template<typename T>
bool Point<T>::operator!=(const Point<T>& pos) const noexcept bool Point<T>::operator!=(const Point<T>& pos) const noexcept
{ {
return (fX != pos.fX || fY != pos.fY);
return (x != pos.x || y != pos.y);
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -246,6 +251,27 @@ bool Size<T>::isInvalid() const noexcept
return fWidth <= 0 || fHeight <= 0; return fWidth <= 0 || fHeight <= 0;
} }


template<typename T>
Size<int> Size<T>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth),
static_cast<int>(fHeight));
}

template<>
Size<int> Size<double>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth + 0.5),
static_cast<int>(fHeight + 0.5));
}

template<>
Size<int> Size<float>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth + 0.5f),
static_cast<int>(fHeight + 0.5f));
}

template<typename T> template<typename T>
Size<T> Size<T>::operator+(const Size<T>& size) noexcept Size<T> Size<T>::operator+(const Size<T>& size) noexcept
{ {
@@ -298,6 +324,22 @@ Size<T>& Size<T>::operator/=(double d) noexcept
return *this; return *this;
} }


template<typename T>
Size<T> Size<T>::operator*(const double m) const noexcept
{
Size<T> size(fWidth, fHeight);
size *= m;
return size;
}

template<typename T>
Size<T> Size<T>::operator/(const double m) const noexcept
{
Size<T> size(fWidth, fHeight);
size /= m;
return size;
}

template<typename T> template<typename T>
bool Size<T>::operator==(const Size<T>& size) const noexcept bool Size<T>::operator==(const Size<T>& size) const noexcept
{ {
@@ -315,162 +357,162 @@ bool Size<T>::operator!=(const Size<T>& size) const noexcept


template<typename T> template<typename T>
Line<T>::Line() noexcept Line<T>::Line() noexcept
: fPosStart(0, 0),
fPosEnd(0, 0) {}
: posStart(0, 0),
posEnd(0, 0) {}


template<typename T> template<typename T>
Line<T>::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept Line<T>::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept
: fPosStart(startX, startY),
fPosEnd(endX, endY) {}
: posStart(startX, startY),
posEnd(endX, endY) {}


template<typename T> template<typename T>
Line<T>::Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept Line<T>::Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept
: fPosStart(startX, startY),
fPosEnd(endPos) {}
: posStart(startX, startY),
posEnd(endPos) {}


template<typename T> template<typename T>
Line<T>::Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept Line<T>::Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept
: fPosStart(startPos),
fPosEnd(endX, endY) {}
: posStart(startPos),
posEnd(endX, endY) {}


template<typename T> template<typename T>
Line<T>::Line(const Point<T>& startPos, const Point<T>& endPos) noexcept Line<T>::Line(const Point<T>& startPos, const Point<T>& endPos) noexcept
: fPosStart(startPos),
fPosEnd(endPos) {}
: posStart(startPos),
posEnd(endPos) {}


template<typename T> template<typename T>
Line<T>::Line(const Line<T>& line) noexcept Line<T>::Line(const Line<T>& line) noexcept
: fPosStart(line.fPosStart),
fPosEnd(line.fPosEnd) {}
: posStart(line.posStart),
posEnd(line.posEnd) {}


template<typename T> template<typename T>
const T& Line<T>::getStartX() const noexcept const T& Line<T>::getStartX() const noexcept
{ {
return fPosStart.fX;
return posStart.x;
} }


template<typename T> template<typename T>
const T& Line<T>::getStartY() const noexcept const T& Line<T>::getStartY() const noexcept
{ {
return fPosStart.fY;
return posStart.y;
} }


template<typename T> template<typename T>
const T& Line<T>::getEndX() const noexcept const T& Line<T>::getEndX() const noexcept
{ {
return fPosEnd.fX;
return posEnd.x;
} }


template<typename T> template<typename T>
const T& Line<T>::getEndY() const noexcept const T& Line<T>::getEndY() const noexcept
{ {
return fPosEnd.fY;
return posEnd.y;
} }


template<typename T> template<typename T>
const Point<T>& Line<T>::getStartPos() const noexcept const Point<T>& Line<T>::getStartPos() const noexcept
{ {
return fPosStart;
return posStart;
} }


template<typename T> template<typename T>
const Point<T>& Line<T>::getEndPos() const noexcept const Point<T>& Line<T>::getEndPos() const noexcept
{ {
return fPosEnd;
return posEnd;
} }


template<typename T> template<typename T>
void Line<T>::setStartX(const T& x) noexcept void Line<T>::setStartX(const T& x) noexcept
{ {
fPosStart.fX = x;
posStart.x = x;
} }


template<typename T> template<typename T>
void Line<T>::setStartY(const T& y) noexcept void Line<T>::setStartY(const T& y) noexcept
{ {
fPosStart.fY = y;
posStart.y = y;
} }


template<typename T> template<typename T>
void Line<T>::setStartPos(const T& x, const T& y) noexcept void Line<T>::setStartPos(const T& x, const T& y) noexcept
{ {
fPosStart = Point<T>(x, y);
posStart = Point<T>(x, y);
} }


template<typename T> template<typename T>
void Line<T>::setStartPos(const Point<T>& pos) noexcept void Line<T>::setStartPos(const Point<T>& pos) noexcept
{ {
fPosStart = pos;
posStart = pos;
} }


template<typename T> template<typename T>
void Line<T>::setEndX(const T& x) noexcept void Line<T>::setEndX(const T& x) noexcept
{ {
fPosEnd.fX = x;
posEnd.x = x;
} }


template<typename T> template<typename T>
void Line<T>::setEndY(const T& y) noexcept void Line<T>::setEndY(const T& y) noexcept
{ {
fPosEnd.fY = y;
posEnd.y = y;
} }


template<typename T> template<typename T>
void Line<T>::setEndPos(const T& x, const T& y) noexcept void Line<T>::setEndPos(const T& x, const T& y) noexcept
{ {
fPosEnd = Point<T>(x, y);
posEnd = Point<T>(x, y);
} }


template<typename T> template<typename T>
void Line<T>::setEndPos(const Point<T>& pos) noexcept void Line<T>::setEndPos(const Point<T>& pos) noexcept
{ {
fPosEnd = pos;
posEnd = pos;
} }


template<typename T> template<typename T>
void Line<T>::moveBy(const T& x, const T& y) noexcept void Line<T>::moveBy(const T& x, const T& y) noexcept
{ {
fPosStart.moveBy(x, y);
fPosEnd.moveBy(x, y);
posStart.moveBy(x, y);
posEnd.moveBy(x, y);
} }


template<typename T> template<typename T>
void Line<T>::moveBy(const Point<T>& pos) noexcept void Line<T>::moveBy(const Point<T>& pos) noexcept
{ {
fPosStart.moveBy(pos);
fPosEnd.moveBy(pos);
posStart.moveBy(pos);
posEnd.moveBy(pos);
} }


template<typename T> template<typename T>
bool Line<T>::isNull() const noexcept bool Line<T>::isNull() const noexcept
{ {
return fPosStart == fPosEnd;
return posStart == posEnd;
} }


template<typename T> template<typename T>
bool Line<T>::isNotNull() const noexcept bool Line<T>::isNotNull() const noexcept
{ {
return fPosStart != fPosEnd;
return posStart != posEnd;
} }


template<typename T> template<typename T>
Line<T>& Line<T>::operator=(const Line<T>& line) noexcept Line<T>& Line<T>::operator=(const Line<T>& line) noexcept
{ {
fPosStart = line.fPosStart;
fPosEnd = line.fPosEnd;
posStart = line.posStart;
posEnd = line.posEnd;
return *this; return *this;
} }


template<typename T> template<typename T>
bool Line<T>::operator==(const Line<T>& line) const noexcept bool Line<T>::operator==(const Line<T>& line) const noexcept
{ {
return (fPosStart == line.fPosStart && fPosEnd == line.fPosEnd);
return (posStart == line.posStart && posEnd == line.posEnd);
} }


template<typename T> template<typename T>
bool Line<T>::operator!=(const Line<T>& line) const noexcept bool Line<T>::operator!=(const Line<T>& line) const noexcept
{ {
return (fPosStart != line.fPosStart || fPosEnd != line.fPosEnd);
return (posStart != line.posStart || posEnd != line.posEnd);
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -524,13 +566,13 @@ Circle<T>::Circle(const Circle<T>& cir) noexcept
template<typename T> template<typename T>
const T& Circle<T>::getX() const noexcept const T& Circle<T>::getX() const noexcept
{ {
return fPos.fX;
return fPos.x;
} }


template<typename T> template<typename T>
const T& Circle<T>::getY() const noexcept const T& Circle<T>::getY() const noexcept
{ {
return fPos.fY;
return fPos.y;
} }


template<typename T> template<typename T>
@@ -542,20 +584,20 @@ const Point<T>& Circle<T>::getPos() const noexcept
template<typename T> template<typename T>
void Circle<T>::setX(const T& x) noexcept void Circle<T>::setX(const T& x) noexcept
{ {
fPos.fX = x;
fPos.x = x;
} }


template<typename T> template<typename T>
void Circle<T>::setY(const T& y) noexcept void Circle<T>::setY(const T& y) noexcept
{ {
fPos.fY = y;
fPos.y = y;
} }


template<typename T> template<typename T>
void Circle<T>::setPos(const T& x, const T& y) noexcept void Circle<T>::setPos(const T& x, const T& y) noexcept
{ {
fPos.fX = x;
fPos.fY = y;
fPos.x = x;
fPos.y = y;
} }


template<typename T> template<typename T>
@@ -599,18 +641,6 @@ void Circle<T>::setNumSegments(const uint num)
fSin = std::sin(fTheta); fSin = std::sin(fTheta);
} }


template<typename T>
void Circle<T>::draw()
{
_draw(false);
}

template<typename T>
void Circle<T>::drawOutline()
{
_draw(true);
}

template<typename T> template<typename T>
Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept
{ {
@@ -640,83 +670,71 @@ bool Circle<T>::operator!=(const Circle<T>& cir) const noexcept


template<typename T> template<typename T>
Triangle<T>::Triangle() noexcept Triangle<T>::Triangle() noexcept
: fPos1(0, 0),
fPos2(0, 0),
fPos3(0, 0) {}
: pos1(0, 0),
pos2(0, 0),
pos3(0, 0) {}


template<typename T> template<typename T>
Triangle<T>::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept Triangle<T>::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept
: fPos1(x1, y1),
fPos2(x2, y2),
fPos3(x3, y3) {}
: pos1(x1, y1),
pos2(x2, y2),
pos3(x3, y3) {}


template<typename T> template<typename T>
Triangle<T>::Triangle(const Point<T>& pos1, const Point<T>& pos2, const Point<T>& pos3) noexcept
: fPos1(pos1),
fPos2(pos2),
fPos3(pos3) {}
Triangle<T>::Triangle(const Point<T>& p1, const Point<T>& p2, const Point<T>& p3) noexcept
: pos1(p1),
pos2(p2),
pos3(p3) {}


template<typename T> template<typename T>
Triangle<T>::Triangle(const Triangle<T>& tri) noexcept Triangle<T>::Triangle(const Triangle<T>& tri) noexcept
: fPos1(tri.fPos1),
fPos2(tri.fPos2),
fPos3(tri.fPos3) {}
: pos1(tri.pos1),
pos2(tri.pos2),
pos3(tri.pos3) {}


template<typename T> template<typename T>
bool Triangle<T>::isNull() const noexcept bool Triangle<T>::isNull() const noexcept
{ {
return fPos1 == fPos2 && fPos1 == fPos3;
return pos1 == pos2 && pos1 == pos3;
} }


template<typename T> template<typename T>
bool Triangle<T>::isNotNull() const noexcept bool Triangle<T>::isNotNull() const noexcept
{ {
return fPos1 != fPos2 || fPos1 != fPos3;
return pos1 != pos2 || pos1 != pos3;
} }


template<typename T> template<typename T>
bool Triangle<T>::isValid() const noexcept bool Triangle<T>::isValid() const noexcept
{ {
return fPos1 != fPos2 && fPos1 != fPos3;
return pos1 != pos2 && pos1 != pos3;
} }


template<typename T> template<typename T>
bool Triangle<T>::isInvalid() const noexcept bool Triangle<T>::isInvalid() const noexcept
{ {
return fPos1 == fPos2 || fPos1 == fPos3;
}

template<typename T>
void Triangle<T>::draw()
{
_draw(false);
}

template<typename T>
void Triangle<T>::drawOutline()
{
_draw(true);
return pos1 == pos2 || pos1 == pos3;
} }


template<typename T> template<typename T>
Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept
{ {
fPos1 = tri.fPos1;
fPos2 = tri.fPos2;
fPos3 = tri.fPos3;
pos1 = tri.pos1;
pos2 = tri.pos2;
pos3 = tri.pos3;
return *this; return *this;
} }


template<typename T> template<typename T>
bool Triangle<T>::operator==(const Triangle<T>& tri) const noexcept bool Triangle<T>::operator==(const Triangle<T>& tri) const noexcept
{ {
return (fPos1 == tri.fPos1 && fPos2 == tri.fPos2 && fPos3 == tri.fPos3);
return (pos1 == tri.pos1 && pos2 == tri.pos2 && pos3 == tri.pos3);
} }


template<typename T> template<typename T>
bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept
{ {
return (fPos1 != tri.fPos1 || fPos2 != tri.fPos2 || fPos3 != tri.fPos3);
return (pos1 != tri.pos1 || pos2 != tri.pos2 || pos3 != tri.pos3);
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -724,226 +742,257 @@ bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept


template<typename T> template<typename T>
Rectangle<T>::Rectangle() noexcept Rectangle<T>::Rectangle() noexcept
: fPos(0, 0),
fSize(0, 0) {}
: pos(0, 0),
size(0, 0) {}


template<typename T> template<typename T>
Rectangle<T>::Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept
: fPos(x, y),
fSize(width, height) {}
Rectangle<T>::Rectangle(const T& x, const T& y, const T& w, const T& h) noexcept
: pos(x, y),
size(w, h) {}


template<typename T> template<typename T>
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& size) noexcept
: fPos(x, y),
fSize(size) {}
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& s) noexcept
: pos(x, y),
size(s) {}


template<typename T> template<typename T>
Rectangle<T>::Rectangle(const Point<T>& pos, const T& width, const T& height) noexcept
: fPos(pos),
fSize(width, height) {}
Rectangle<T>::Rectangle(const Point<T>& p, const T& w, const T& h) noexcept
: pos(p),
size(w, h) {}


template<typename T> template<typename T>
Rectangle<T>::Rectangle(const Point<T>& pos, const Size<T>& size) noexcept
: fPos(pos),
fSize(size) {}
Rectangle<T>::Rectangle(const Point<T>& p, const Size<T>& s) noexcept
: pos(p),
size(s) {}


template<typename T> template<typename T>
Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept
: fPos(rect.fPos),
fSize(rect.fSize) {}
: pos(rect.pos),
size(rect.size) {}


template<typename T> template<typename T>
const T& Rectangle<T>::getX() const noexcept const T& Rectangle<T>::getX() const noexcept
{ {
return fPos.fX;
return pos.x;
} }


template<typename T> template<typename T>
const T& Rectangle<T>::getY() const noexcept const T& Rectangle<T>::getY() const noexcept
{ {
return fPos.fY;
return pos.y;
} }


template<typename T> template<typename T>
const T& Rectangle<T>::getWidth() const noexcept const T& Rectangle<T>::getWidth() const noexcept
{ {
return fSize.fWidth;
return size.fWidth;
} }


template<typename T> template<typename T>
const T& Rectangle<T>::getHeight() const noexcept const T& Rectangle<T>::getHeight() const noexcept
{ {
return fSize.fHeight;
return size.fHeight;
} }


template<typename T> template<typename T>
const Point<T>& Rectangle<T>::getPos() const noexcept const Point<T>& Rectangle<T>::getPos() const noexcept
{ {
return fPos;
return pos;
} }


template<typename T> template<typename T>
const Size<T>& Rectangle<T>::getSize() const noexcept const Size<T>& Rectangle<T>::getSize() const noexcept
{ {
return fSize;
return size;
} }


template<typename T> template<typename T>
void Rectangle<T>::setX(const T& x) noexcept void Rectangle<T>::setX(const T& x) noexcept
{ {
fPos.fX = x;
pos.x = x;
} }


template<typename T> template<typename T>
void Rectangle<T>::setY(const T& y) noexcept void Rectangle<T>::setY(const T& y) noexcept
{ {
fPos.fY = y;
pos.y = y;
} }


template<typename T> template<typename T>
void Rectangle<T>::setPos(const T& x, const T& y) noexcept void Rectangle<T>::setPos(const T& x, const T& y) noexcept
{ {
fPos.fX = x;
fPos.fY = y;
pos.x = x;
pos.y = y;
} }


template<typename T> template<typename T>
void Rectangle<T>::setPos(const Point<T>& pos) noexcept
void Rectangle<T>::setPos(const Point<T>& pos2) noexcept
{ {
fPos = pos;
pos = pos2;
} }


template<typename T> template<typename T>
void Rectangle<T>::moveBy(const T& x, const T& y) noexcept void Rectangle<T>::moveBy(const T& x, const T& y) noexcept
{ {
fPos.moveBy(x, y);
pos.moveBy(x, y);
} }


template<typename T> template<typename T>
void Rectangle<T>::moveBy(const Point<T>& pos) noexcept
void Rectangle<T>::moveBy(const Point<T>& pos2) noexcept
{ {
fPos.moveBy(pos);
pos.moveBy(pos2);
} }


template<typename T> template<typename T>
void Rectangle<T>::setWidth(const T& width) noexcept void Rectangle<T>::setWidth(const T& width) noexcept
{ {
fSize.fWidth = width;
size.fWidth = width;
} }


template<typename T> template<typename T>
void Rectangle<T>::setHeight(const T& height) noexcept void Rectangle<T>::setHeight(const T& height) noexcept
{ {
fSize.fHeight = height;
size.fHeight = height;
} }


template<typename T> template<typename T>
void Rectangle<T>::setSize(const T& width, const T& height) noexcept void Rectangle<T>::setSize(const T& width, const T& height) noexcept
{ {
fSize.fWidth = width;
fSize.fHeight = height;
size.fWidth = width;
size.fHeight = height;
} }


template<typename T> template<typename T>
void Rectangle<T>::setSize(const Size<T>& size) noexcept
void Rectangle<T>::setSize(const Size<T>& size2) noexcept
{ {
fSize = size;
size = size2;
} }


template<typename T> template<typename T>
void Rectangle<T>::growBy(double multiplier) noexcept void Rectangle<T>::growBy(double multiplier) noexcept
{ {
fSize.growBy(multiplier);
size.growBy(multiplier);
} }


template<typename T> template<typename T>
void Rectangle<T>::shrinkBy(double divider) noexcept void Rectangle<T>::shrinkBy(double divider) noexcept
{ {
fSize.shrinkBy(divider);
size.shrinkBy(divider);
} }


template<typename T> template<typename T>
void Rectangle<T>::setRectangle(const Point<T>& pos, const Size<T>& size) noexcept
void Rectangle<T>::setRectangle(const Point<T>& pos2, const Size<T>& size2) noexcept
{ {
fPos = pos;
fSize = size;
pos = pos2;
size = size2;
} }


template<typename T> template<typename T>
void Rectangle<T>::setRectangle(const Rectangle<T>& rect) noexcept void Rectangle<T>::setRectangle(const Rectangle<T>& rect) noexcept
{ {
fPos = rect.fPos;
fSize = rect.fSize;
pos = rect.pos;
size = rect.size;
} }


template<typename T> template<typename T>
bool Rectangle<T>::contains(const T& x, const T& y) const noexcept bool Rectangle<T>::contains(const T& x, const T& y) const noexcept
{ {
return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight);
return (x >= pos.x && y >= pos.y && x <= pos.x+size.fWidth && y <= pos.y+size.fHeight);
} }


template<typename T> template<typename T>
bool Rectangle<T>::contains(const Point<T>& pos) const noexcept
bool Rectangle<T>::contains(const Point<T>& p) const noexcept
{ {
return contains(pos.fX, pos.fY);
return contains(p.x, p.y);
}

template<typename T>
template<typename T2>
bool Rectangle<T>::contains(const Point<T2>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
}

template<> template<>
bool Rectangle<int>::contains(const Point<double>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
}

template<> template<>
bool Rectangle<uint>::contains(const Point<double>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
} }


template<typename T> template<typename T>
bool Rectangle<T>::containsX(const T& x) const noexcept bool Rectangle<T>::containsX(const T& x) const noexcept
{ {
return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth);
return (x >= pos.x && x <= pos.x + size.fWidth);
} }


template<typename T> template<typename T>
bool Rectangle<T>::containsY(const T& y) const noexcept bool Rectangle<T>::containsY(const T& y) const noexcept
{ {
return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight);
return (y >= pos.y && y <= pos.y + size.fHeight);
}

template<typename T>
bool Rectangle<T>::isNull() const noexcept
{
return size.isNull();
}

template<typename T>
bool Rectangle<T>::isNotNull() const noexcept
{
return size.isNotNull();
} }


template<typename T> template<typename T>
void Rectangle<T>::draw()
bool Rectangle<T>::isValid() const noexcept
{ {
_draw(false);
return size.isValid();
} }


template<typename T> template<typename T>
void Rectangle<T>::drawOutline()
bool Rectangle<T>::isInvalid() const noexcept
{ {
_draw(true);
return size.isInvalid();
} }


template<typename T> template<typename T>
Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept
{ {
fPos = rect.fPos;
fSize = rect.fSize;
pos = rect.pos;
size = rect.size;
return *this; return *this;
} }


template<typename T> template<typename T>
Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept
{ {
fSize *= m;
size *= m;
return *this; return *this;
} }


template<typename T> template<typename T>
Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept
{ {
fSize /= d;
size /= d;
return *this; return *this;
} }


template<typename T> template<typename T>
bool Rectangle<T>::operator==(const Rectangle<T>& rect) const noexcept bool Rectangle<T>::operator==(const Rectangle<T>& rect) const noexcept
{ {
return (fPos == rect.fPos && fSize == rect.fSize);
return (pos == rect.pos && size == rect.size);
} }


template<typename T> template<typename T>
bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept
{ {
return (fPos != rect.fPos || fSize != rect.fSize);
return (pos != rect.pos || size != rect.size);
} }


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


+ 0
- 150
dpf/dgl/src/Image.cpp View File

@@ -1,150 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 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 "../Image.hpp"

START_NAMESPACE_DGL

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

Image::Image()
: ImageBase(),
fFormat(0),
fType(0),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const Image& image)
: ImageBase(image),
fFormat(image.fFormat),
fType(image.fType),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type)
: ImageBase(rawData, width, height),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type)
: ImageBase(rawData, size),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::~Image()
{
if (fTextureId != 0)
{
#ifndef DISTRHO_OS_MAC // FIXME
glDeleteTextures(1, &fTextureId);
#endif
fTextureId = 0;
}
}

void Image::loadFromMemory(const char* const rawData,
const uint width,
const uint height,
const GLenum format,
const GLenum type) noexcept
{
loadFromMemory(rawData, Size<uint>(width, height), format, type);
}

void Image::loadFromMemory(const char* const rawData,
const Size<uint>& size,
const GLenum format,
const GLenum type) noexcept
{
fRawData = rawData;
fSize = size;
fFormat = format;
fType = type;
fIsReady = false;
}

GLenum Image::getFormat() const noexcept
{
return fFormat;
}

GLenum Image::getType() const noexcept
{
return fType;
}

Image& Image::operator=(const Image& image) noexcept
{
fRawData = image.fRawData;
fSize = image.fSize;
fFormat = image.fFormat;
fType = image.fType;
fIsReady = false;
return *this;
}

void Image::_drawAt(const Point<int>& pos)
{
if (fTextureId == 0 || ! isValid())
return;

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);

if (! fIsReady)
{
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_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0,
fFormat, fType, fRawData);

fIsReady = true;
}

Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

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

END_NAMESPACE_DGL

+ 57
- 31
dpf/dgl/src/ImageBase.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,82 +18,108 @@


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// protected constructors


ImageBase::ImageBase() ImageBase::ImageBase()
: fRawData(nullptr),
fSize(0, 0) {}
: rawData(nullptr),
size(0, 0),
format(kImageFormatNull) {}


ImageBase::ImageBase(const char* const rawData, const uint width, const uint height)
: fRawData(rawData),
fSize(width, height) {}
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt)
: rawData(rdata),
size(width, height),
format(fmt) {}


ImageBase::ImageBase(const char* const rawData, const Size<uint>& size)
: fRawData(rawData),
fSize(size) {}
ImageBase::ImageBase(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: rawData(rdata),
size(s),
format(fmt) {}


ImageBase::ImageBase(const ImageBase& image) ImageBase::ImageBase(const ImageBase& image)
: fRawData(image.fRawData),
fSize(image.fSize) {}
: rawData(image.rawData),
size(image.size),
format(image.format) {}


ImageBase::~ImageBase() {}
// --------------------------------------------------------------------------------------------------------------------
// public methods


// -----------------------------------------------------------------------
ImageBase::~ImageBase() {}


bool ImageBase::isValid() const noexcept bool ImageBase::isValid() const noexcept
{ {
return (fRawData != nullptr && fSize.isValid());
return (rawData != nullptr && size.isValid());
}

bool ImageBase::isInvalid() const noexcept
{
return (rawData == nullptr || size.isInvalid());
} }


uint ImageBase::getWidth() const noexcept uint ImageBase::getWidth() const noexcept
{ {
return fSize.getWidth();
return size.getWidth();
} }


uint ImageBase::getHeight() const noexcept uint ImageBase::getHeight() const noexcept
{ {
return fSize.getHeight();
return size.getHeight();
} }


const Size<uint>& ImageBase::getSize() const noexcept const Size<uint>& ImageBase::getSize() const noexcept
{ {
return fSize;
return size;
} }


const char* ImageBase::getRawData() const noexcept const char* ImageBase::getRawData() const noexcept
{ {
return fRawData;
return rawData;
} }


// -----------------------------------------------------------------------
ImageFormat ImageBase::getFormat() const noexcept
{
return format;
}

void ImageBase::loadFromMemory(const char* const rdata,
const uint width,
const uint height,
const ImageFormat fmt) noexcept
{
loadFromMemory(rdata, Size<uint>(width, height), fmt);
}


void ImageBase::draw()
void ImageBase::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{ {
_drawAt(Point<int>());
rawData = rdata;
size = s;
format = fmt;
} }


void ImageBase::drawAt(const int x, const int y)
void ImageBase::draw(const GraphicsContext& context)
{ {
_drawAt(Point<int>(x, y));
drawAt(context, Point<int>(0, 0));
} }


void ImageBase::drawAt(const Point<int>& pos)
void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y)
{ {
_drawAt(pos);
drawAt(context, Point<int>(x, y));
} }


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// public operators


ImageBase& ImageBase::operator=(const ImageBase& image) noexcept ImageBase& ImageBase::operator=(const ImageBase& image) noexcept
{ {
fRawData = image.fRawData;
fSize = image.fSize;
rawData = image.rawData;
size = image.size;
format = image.format;
return *this; return *this;
} }


bool ImageBase::operator==(const ImageBase& image) const noexcept bool ImageBase::operator==(const ImageBase& image) const noexcept
{ {
return (fRawData == image.fRawData && fSize == image.fSize);
return (rawData == image.rawData && size == image.size && format == image.format);
} }


bool ImageBase::operator!=(const ImageBase& image) const noexcept bool ImageBase::operator!=(const ImageBase& image) const noexcept
@@ -101,6 +127,6 @@ bool ImageBase::operator!=(const ImageBase& image) const noexcept
return !operator==(image); return !operator==(image);
} }


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


END_NAMESPACE_DGL END_NAMESPACE_DGL

+ 1061
- 0
dpf/dgl/src/ImageBaseWidgets.cpp
File diff suppressed because it is too large
View File


+ 0
- 1108
dpf/dgl/src/ImageWidgets.cpp
File diff suppressed because it is too large
View File


+ 56
- 68
dpf/dgl/src/NanoVG.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,7 +15,7 @@
*/ */


#include "../NanoVG.hpp" #include "../NanoVG.hpp"
#include "WidgetPrivateData.hpp"
#include "SubWidgetPrivateData.hpp"


#ifndef DGL_NO_SHARED_RESOURCES #ifndef DGL_NO_SHARED_RESOURCES
# include "Resources.hpp" # include "Resources.hpp"
@@ -53,6 +53,7 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# undef DGL_EXT # undef DGL_EXT
#endif #endif


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


static NVGcontext* nvgCreateGL_helper(int flags) static NVGcontext* nvgCreateGL_helper(int flags)
{ {
#if defined(DISTRHO_OS_WINDOWS) #if defined(DISTRHO_OS_WINDOWS)
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
static bool needsInit = true; static bool needsInit = true;
# define DGL_EXT(PROC, func) \ # define DGL_EXT(PROC, func) \
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \
@@ -111,8 +124,12 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv)
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv)
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram)
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
# undef DGL_EXT # undef DGL_EXT
needsInit = false; needsInit = false;
# if defined(__GNUC__) && (__GNUC__ >= 9)
# pragma GCC diagnostic pop
# endif
#endif #endif
return nvgCreateGL(flags); return nvgCreateGL(flags);
} }
@@ -240,11 +257,6 @@ NanoVG::NanoVG(int flags)
fInFrame(false), fInFrame(false),
fIsSubWidget(false) {} fIsSubWidget(false) {}


NanoVG::NanoVG(NanoWidget* groupWidget)
: fContext(groupWidget->fContext),
fInFrame(false),
fIsSubWidget(true) {}

NanoVG::~NanoVG() NanoVG::~NanoVG()
{ {
DISTRHO_SAFE_ASSERT(! fInFrame); DISTRHO_SAFE_ASSERT(! fInFrame);
@@ -260,8 +272,8 @@ void NanoVG::beginFrame(const uint width, const uint height, const float scaleFa
if (fContext == nullptr) return; if (fContext == nullptr) return;
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true; fInFrame = true;

nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor);
} }


@@ -269,14 +281,13 @@ void NanoVG::beginFrame(Widget* const widget)
{ {
DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true; fInFrame = true;


if (fContext == nullptr) if (fContext == nullptr)
return; return;


Window& window(widget->getParentWindow());
nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f);
if (TopLevelWidget* const tlw = widget->getTopLevelWidget())
nvgBeginFrame(fContext, static_cast<int>(tlw->getWidth()), static_cast<int>(tlw->getHeight()), 1.0f);
} }


void NanoVG::cancelFrame() void NanoVG::cancelFrame()
@@ -772,26 +783,26 @@ void NanoVG::stroke()


NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename)
{ {
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);


return nvgCreateFont(fContext, name, filename); return nvgCreateFont(fContext, name, filename);
} }


NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData)
{ {
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);


return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData); return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData);
} }


NanoVG::FontId NanoVG::findFont(const char* name) NanoVG::FontId NanoVG::findFont(const char* name)
{ {
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);


return nvgFindFont(fContext, name); return nvgFindFont(fContext, name);
} }
@@ -913,79 +924,56 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi
} }


#ifndef DGL_NO_SHARED_RESOURCES #ifndef DGL_NO_SHARED_RESOURCES
void NanoVG::loadSharedResources()
bool NanoVG::loadSharedResources()
{ {
if (fContext == nullptr) return;
if (fContext == nullptr) return false;


if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0)
return;
return true;


using namespace dpf_resources; using namespace dpf_resources;


nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0;
} }
#endif #endif


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// NanoSubWidget


struct NanoWidget::PrivateData {
NanoWidget* const self;
std::vector<NanoWidget*> subWidgets;

PrivateData(NanoWidget* const s)
: self(s),
subWidgets() {}

~PrivateData()
{
subWidgets.clear();
}
};

NanoWidget::NanoWidget(Window& parent, int flags)
: Widget(parent),
NanoVG(flags),
nData(new PrivateData(this))
template <>
NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parent, int flags)
: SubWidget(parent),
NanoVG(flags)
{ {
pData->needsScaling = true;
setNeedsViewportScaling();
} }


NanoWidget::NanoWidget(Widget* groupWidget, int flags)
: Widget(groupWidget, true),
NanoVG(flags),
nData(new PrivateData(this))
{
pData->needsScaling = true;
}
template class NanoBaseWidget<SubWidget>;


NanoWidget::NanoWidget(NanoWidget* groupWidget)
: Widget(groupWidget, false),
NanoVG(groupWidget),
nData(new PrivateData(this))
{
pData->needsScaling = true;
pData->skipDisplay = true;
groupWidget->nData->subWidgets.push_back(this);
}
// -----------------------------------------------------------------------
// NanoTopLevelWidget


NanoWidget::~NanoWidget()
{
delete nData;
}
template <>
NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags)
: TopLevelWidget(windowToMapTo),
NanoVG(flags) {}


void NanoWidget::onDisplay()
{
NanoVG::beginFrame(getWidth(), getHeight());
onNanoDisplay();
template class NanoBaseWidget<TopLevelWidget>;


for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it)
{
NanoWidget* const widget(*it);
widget->onNanoDisplay();
}
// -----------------------------------------------------------------------
// NanoStandaloneWindow


NanoVG::endFrame();
}
template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags)
: StandaloneWindow(app),
NanoVG(flags) {}

template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags)
: StandaloneWindow(app, parentWindow),
NanoVG(flags) {}

template class NanoBaseWidget<StandaloneWindow>;


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




+ 575
- 58
dpf/dgl/src/OpenGL.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,136 +14,260 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#include "../Geometry.hpp"
#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../OpenGL.hpp" #include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
}

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Line // Line


template<typename T> template<typename T>
void Line<T>::draw()
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fPosStart != fPosEnd,);
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);


glBegin(GL_LINES); glBegin(GL_LINES);


{ {
glVertex2d(fPosStart.fX, fPosStart.fY);
glVertex2d(fPosEnd.fX, fPosEnd.fY);
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
} }


glEnd(); glEnd();
} }


template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
}

// deprecated calls
template<typename T>
void Line<T>::draw()
{
drawLine<T>(posStart, posEnd);
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Circle // Circle


template<typename T> template<typename T>
void Circle<T>::_draw(const bool outline)
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fNumSegments >= 3 && fSize > 0.0f,);
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);


double t, x = fSize, y = 0.0;
const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;


glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);


for (uint i=0; i<fNumSegments; ++i)
for (uint i=0; i<numSegments; ++i)
{ {
glVertex2d(x + fPos.fX, y + fPos.fY);
glVertex2d(x + origx, y + origy);


t = x; t = x;
x = fCos * x - fSin * y;
y = fSin * t + fCos * y;
x = cos * x - sin * y;
y = sin * t + cos * y;
} }


glEnd(); glEnd();
} }


template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
}

// deprecated calls
template<typename T>
void Circle<T>::draw()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Triangle // Triangle


template<typename T> template<typename T>
void Triangle<T>::_draw(const bool outline)
static void drawTriangle(const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fPos1 != fPos2 && fPos1 != fPos3,);
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);


glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);


{ {
glVertex2d(fPos1.fX, fPos1.fY);
glVertex2d(fPos2.fX, fPos2.fY);
glVertex2d(fPos3.fX, fPos3.fY);
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
} }


glEnd(); glEnd();
} }


template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawTriangle<T>(pos1, pos2, pos3, true);
}

// deprecated calls
template<typename T>
void Triangle<T>::draw()
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline()
{
drawTriangle<T>(pos1, pos2, pos3, true);
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Rectangle // Rectangle


template<typename T> template<typename T>
void Rectangle<T>::_draw(const bool outline)
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fSize.isValid(),);
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);


glBegin(outline ? GL_LINE_LOOP : GL_QUADS); glBegin(outline ? GL_LINE_LOOP : GL_QUADS);


{ {
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f);
glVertex2d(fPos.fX, fPos.fY);
glVertex2d(x, y);


glTexCoord2f(1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f);
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY);
glVertex2d(x+w, y);


glTexCoord2f(1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight);
glVertex2d(x+w, y+h);


glTexCoord2f(0.0f, 1.0f); glTexCoord2f(0.0f, 1.0f);
glVertex2d(fPos.fX, fPos.fY+fSize.fHeight);
glVertex2d(x, y+h);
} }


glEnd(); glEnd();
} }


// -----------------------------------------------------------------------
// Possible template data types

template class Point<double>;
template class Point<float>;
template class Point<int>;
template class Point<uint>;
template class Point<short>;
template class Point<ushort>;

template class Size<double>;
template class Size<float>;
template class Size<int>;
template class Size<uint>;
template class Size<short>;
template class Size<ushort>;
template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
drawRectangle<T>(*this, false);
}


template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;
template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);


template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;
glLineWidth(static_cast<GLfloat>(lineWidth));
drawRectangle<T>(*this, true);
}


template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;
// deprecated calls
template<typename T>
void Rectangle<T>::draw()
{
drawRectangle<T>(*this, false);
}

template<typename T>
void Rectangle<T>::drawOutline()
{
drawRectangle<T>(*this, true);
}


template class Rectangle<double>; template class Rectangle<double>;
template class Rectangle<float>; template class Rectangle<float>;
@@ -152,6 +276,399 @@ template class Rectangle<uint>;
template class Rectangle<short>; template class Rectangle<short>;
template class Rectangle<ushort>; template class Rectangle<ushort>;


// -----------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

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_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

OpenGLImage::OpenGLImage()
: ImageBase(),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const OpenGLImage& image)
: ImageBase(image),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::~OpenGLImage()
{
if (textureId != 0)
glDeleteTextures(1, &textureId);
}

void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
setupCalled = false;
ImageBase::loadFromMemory(rdata, s, fmt);
}

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
setupCalled = false;
return *this;
}

// deprecated calls
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt)
: ImageBase(rdata, s, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
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_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
glPushMatrix();

const int w2 = w/2;
const int h2 = h/2;

glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);

Rectangle<int>(-w2, -h2, w, h).draw(context);

glPopMatrix();
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<OpenGLImage>;

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

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
if (skipDrawing)
return;

bool needsDisableScissor = false;

if (needsViewportScaling)
{
// limit viewport to widget bounds
const int x = absolutePos.getX();
const int w = static_cast<int>(self->getWidth());
const int h = static_cast<int>(self->getHeight());

if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0)
{
glViewport(x,
-static_cast<int>(height * viewportScaleFactor - height + absolutePos.getY() + 0.5),
static_cast<int>(width * viewportScaleFactor + 0.5),
static_cast<int>(height * viewportScaleFactor + 0.5));
}
else
{
const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY();
glViewport(x, y, w, h);
}
}
else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
{
// full viewport size
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
}
else
{
// set viewport pos
glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
-static_cast<int>(std::round((height * autoScaleFactor - height)
+ (absolutePos.getY() * autoScaleFactor))),
static_cast<int>(std::round(width * autoScaleFactor)),
static_cast<int>(std::round(height * autoScaleFactor)));

// then cut the outer bounds
glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY())
* autoScaleFactor)),
static_cast<int>(std::round(self->getWidth() * autoScaleFactor)),
static_cast<int>(std::round(self->getHeight() * autoScaleFactor)));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
}

// display widget
self->onDisplay();

if (needsDisableScissor)
glDisable(GL_SCISSOR_TEST);

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// full viewport size
if (window.pData->autoScaling)
{
glViewport(0,
-static_cast<int>(height * autoScaleFactor - height),
static_cast<int>(width * autoScaleFactor),
static_cast<int>(height * autoScaleFactor));
}
else
{
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
}

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

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


END_NAMESPACE_DGL END_NAMESPACE_DGL

+ 163
- 0
dpf/dgl/src/SubWidget.cpp View File

@@ -0,0 +1,163 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"

START_NAMESPACE_DGL

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

SubWidget::SubWidget(Widget* const parentWidget)
: Widget(parentWidget),
pData(new PrivateData(this, parentWidget)) {}

SubWidget::~SubWidget()
{
delete pData;
}

template<typename T>
bool SubWidget::contains(T x, T y) const noexcept
{
return Rectangle<double>(0, 0, getWidth(), getHeight()).contains(x, y);
}

template<typename T>
bool SubWidget::contains(const Point<T>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

int SubWidget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int SubWidget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
}

Point<int> SubWidget::getAbsolutePos() const noexcept
{
return pData->absolutePos;
}

Rectangle<int> SubWidget::getAbsoluteArea() const noexcept
{
return Rectangle<int>(getAbsolutePos(), getSize().toInt());
}

Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept
{
return Rectangle<uint>(static_cast<uint>(std::max(0, getAbsoluteX())),
static_cast<uint>(std::max(0, getAbsoluteY())),
getSize());
}

void SubWidget::setAbsoluteX(int x) noexcept
{
setAbsolutePos(Point<int>(x, getAbsoluteY()));
}

void SubWidget::setAbsoluteY(int y) noexcept
{
setAbsolutePos(Point<int>(getAbsoluteX(), y));
}

void SubWidget::setAbsolutePos(int x, int y) noexcept
{
setAbsolutePos(Point<int>(x, y));
}

void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept
{
if (pData->absolutePos == pos)
return;

PositionChangedEvent ev;
ev.oldPos = pData->absolutePos;
ev.pos = pos;

pData->absolutePos = pos;
onPositionChanged(ev);

repaint();
}

Widget* SubWidget::getParentWidget() const noexcept
{
return pData->parentWidget;
}

void SubWidget::repaint() noexcept
{
if (! isVisible())
return;

if (TopLevelWidget* const topw = getTopLevelWidget())
{
if (pData->needsFullViewportForDrawing)
topw->repaint();
else
topw->repaint(getConstrainedAbsoluteArea());
}
}

void SubWidget::toFront()
{
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);

subwidgets.remove(this);
subwidgets.push_back(this);
}

void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing)
{
pData->needsFullViewportForDrawing = needsFullViewportForDrawing;
}

void SubWidget::setNeedsViewportScaling(const bool needsViewportScaling, const double autoScaleFactor)
{
pData->needsViewportScaling = needsViewportScaling;
pData->viewportScaleFactor = autoScaleFactor;
}

void SubWidget::setSkipDrawing(const bool skipDrawing)
{
pData->skipDrawing = skipDrawing;
}

void SubWidget::onPositionChanged(const PositionChangedEvent&)
{
}

// --------------------------------------------------------------------------------------------------------------------
// Possible template data types

template<>
bool SubWidget::contains(const Point<double>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

// float, int, uint, short, ushort

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

END_NAMESPACE_DGL

+ 44
- 0
dpf/dgl/src/SubWidgetPrivateData.cpp View File

@@ -0,0 +1,44 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"

START_NAMESPACE_DGL

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

SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw)
: self(s),
selfw((Widget*)s),
parentWidget(pw),
absolutePos(),
needsFullViewportForDrawing(false),
needsViewportScaling(false),
skipDrawing(false),
viewportScaleFactor(0.0)
{
parentWidget->pData->subWidgets.push_back(self);
}

SubWidget::PrivateData::~PrivateData()
{
parentWidget->pData->subWidgets.remove(self);
}

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

END_NAMESPACE_DGL

+ 49
- 0
dpf/dgl/src/SubWidgetPrivateData.hpp View File

@@ -0,0 +1,49 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../SubWidget.hpp"

START_NAMESPACE_DGL

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

struct SubWidget::PrivateData {
SubWidget* const self;
Widget* const selfw;
Widget* const parentWidget;
Point<int> absolutePos;
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds
bool needsViewportScaling; // needed for NanoVG
bool skipDrawing; // for context reuse in NanoVG based guis
double viewportScaleFactor; // auto-scaling for NanoVG

explicit PrivateData(SubWidget* const s, Widget* const pw);
~PrivateData();

// NOTE display function is different depending on build type, must call displaySubWidgets at the end
void display(uint width, uint height, double autoScaleFactor);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

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

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 98
- 0
dpf/dgl/src/TopLevelWidget.cpp View File

@@ -0,0 +1,98 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

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

TopLevelWidget::TopLevelWidget(Window& windowToMapTo)
: Widget(this),
pData(new PrivateData(this, windowToMapTo)) {}

TopLevelWidget::~TopLevelWidget()
{
delete pData;
}

Application& TopLevelWidget::getApp() const noexcept
{
return pData->window.getApp();
}

Window& TopLevelWidget::getWindow() const noexcept
{
return pData->window;
}

void TopLevelWidget::setWidth(const uint width)
{
pData->window.setWidth(width);
}

void TopLevelWidget::setHeight(const uint height)
{
pData->window.setHeight(height);
}

void TopLevelWidget::setSize(const uint width, const uint height)
{
pData->window.setSize(width, height);
}

void TopLevelWidget::setSize(const Size<uint>& size)
{
pData->window.setSize(size);
}

bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
{
return pData->window.addIdleCallback(callback, timerFrequencyInMs);
}

bool TopLevelWidget::removeIdleCallback(IdleCallback* const callback)
{
return pData->window.removeIdleCallback(callback);
}

double TopLevelWidget::getScaleFactor() const noexcept
{
return pData->window.getScaleFactor();
}

void TopLevelWidget::repaint() noexcept
{
pData->window.repaint();
}

void TopLevelWidget::repaint(const Rectangle<uint>& rect) noexcept
{
pData->window.repaint(rect);
}

void TopLevelWidget::setGeometryConstraints(const uint minimumWidth,
const uint minimumHeight,
const bool keepAspectRatio,
const bool automaticallyScale)
{
pData->window.setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale);
}

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

END_NAMESPACE_DGL

+ 168
- 0
dpf/dgl/src/TopLevelWidgetPrivateData.cpp View File

@@ -0,0 +1,168 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"
#include "pugl.hpp"

START_NAMESPACE_DGL

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

TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w)
: self(s),
selfw(s),
window(w)
{
window.pData->topLevelWidgets.push_back(self);
}

TopLevelWidget::PrivateData::~PrivateData()
{
window.pData->topLevelWidgets.remove(self);
}

bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onKeyboard(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveKeyboardEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onSpecial(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveSpecialEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onCharacterInput(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveCharacterInputEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MouseEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onMouse(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveMouseEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MotionEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onMotion(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveMotionEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

ScrollEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor);
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor);
rev.delta.setX(ev.delta.getX() / autoScaleFactor);
rev.delta.setY(ev.delta.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onScroll(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveScrollEventForSubWidgets(rev);
}

void TopLevelWidget::PrivateData::fallbackOnResize()
{
puglFallbackOnResize(window.pData->view);
}

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

END_NAMESPACE_DGL

+ 51
- 0
dpf/dgl/src/TopLevelWidgetPrivateData.hpp View File

@@ -0,0 +1,51 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../TopLevelWidget.hpp"

#include <list>

START_NAMESPACE_DGL

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

struct TopLevelWidget::PrivateData {
TopLevelWidget* const self;
Widget* const selfw;
Window& window;

explicit PrivateData(TopLevelWidget* const s, Window& w);
~PrivateData();
void display();
bool keyboardEvent(const KeyboardEvent& ev);
bool specialEvent(const SpecialEvent& ev);
bool characterInputEvent(const CharacterInputEvent& ev);
bool mouseEvent(const MouseEvent& ev);
bool motionEvent(const MotionEvent& ev);
bool scrollEvent(const ScrollEvent& ev);
void fallbackOnResize();

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

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

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 243
- 0
dpf/dgl/src/Vulkan.cpp View File

@@ -0,0 +1,243 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Vulkan.hpp"
#include "../Color.hpp"

#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

START_NAMESPACE_DGL

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

static void notImplemented(const char* const name)
{
d_stderr2("vulkan function not implemented: %s", name);
}

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, bool)
{
notImplemented("Color::setFor");
}

// -----------------------------------------------------------------------
// Line

template<typename T>
void Line<T>::draw(const GraphicsContext&, T)
{
notImplemented("Line::draw");
}

template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
notImplemented("Circle::draw");
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Circle::drawOutline");
}

template<typename T>
void Circle<T>::draw()
{
notImplemented("Circle::draw");
}

template<typename T>
void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Triangle::drawOutline");
}

template<typename T>
void Triangle<T>::draw()
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;


// -----------------------------------------------------------------------
// Rectangle

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Rectangle::drawOutline");
}

template<typename T>
void Rectangle<T>::draw()
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// VulkanImage

VulkanImage::VulkanImage()
: ImageBase() {}

VulkanImage::VulkanImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt) {}

VulkanImage::VulkanImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt) {}

VulkanImage::VulkanImage(const VulkanImage& image)
: ImageBase(image.rawData, image.size, image.format) {}

VulkanImage::~VulkanImage() {}

void VulkanImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
ImageBase::loadFromMemory(rdata, s, fmt);
}

void VulkanImage::drawAt(const GraphicsContext&, const Point<int>&)
{
}

VulkanImage& VulkanImage::operator=(const VulkanImage& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
return *this;
}

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

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
// TODO

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// TODO

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

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

END_NAMESPACE_DGL

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

+ 46
- 94
dpf/dgl/src/Widget.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,33 +15,22 @@
*/ */


#include "WidgetPrivateData.hpp" #include "WidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"
#include "../Window.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Widget // Widget


Widget::Widget(Window& parent)
: pData(new PrivateData(this, parent, nullptr, false))
{
parent._addWidget(this);
}
Widget::Widget(TopLevelWidget* const topLevelWidget)
: pData(new PrivateData(this, topLevelWidget)) {}


Widget::Widget(Widget* groupWidget)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true))
{
pData->parent._addWidget(this);
}

Widget::Widget(Widget* groupWidget, bool addToSubWidgets)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets))
{
pData->parent._addWidget(this);
}
Widget::Widget(Widget* const parentWidget)
: pData(new PrivateData(this, parentWidget)) {}


Widget::~Widget() Widget::~Widget()
{ {
pData->parent._removeWidget(this);
delete pData; delete pData;
} }


@@ -50,13 +39,15 @@ bool Widget::isVisible() const noexcept
return pData->visible; return pData->visible;
} }


void Widget::setVisible(bool yesNo)
void Widget::setVisible(bool visible)
{ {
if (pData->visible == yesNo)
if (pData->visible == visible)
return; return;


pData->visible = yesNo;
pData->parent.repaint();
pData->visible = visible;
repaint();

// FIXME check case of hiding a previously visible widget, does it trigger a repaint?
} }


void Widget::show() void Widget::show()
@@ -79,7 +70,7 @@ uint Widget::getHeight() const noexcept
return pData->size.getHeight(); return pData->size.getHeight();
} }


const Size<uint>& Widget::getSize() const noexcept
const Size<uint> Widget::getSize() const noexcept
{ {
return pData->size; return pData->size;
} }
@@ -96,7 +87,7 @@ void Widget::setWidth(uint width) noexcept
pData->size.setWidth(width); pData->size.setWidth(width);
onResize(ev); onResize(ev);


pData->parent.repaint();
repaint();
} }


void Widget::setHeight(uint height) noexcept void Widget::setHeight(uint height) noexcept
@@ -111,7 +102,7 @@ void Widget::setHeight(uint height) noexcept
pData->size.setHeight(height); pData->size.setHeight(height);
onResize(ev); onResize(ev);


pData->parent.repaint();
repaint();
} }


void Widget::setSize(uint width, uint height) noexcept void Widget::setSize(uint width, uint height) noexcept
@@ -131,77 +122,34 @@ void Widget::setSize(const Size<uint>& size) noexcept
pData->size = size; pData->size = size;
onResize(ev); onResize(ev);


pData->parent.repaint();
}

int Widget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int Widget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
repaint();
} }


const Point<int>& Widget::getAbsolutePos() const noexcept
Application& Widget::getApp() const noexcept
{ {
return pData->absolutePos;
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getApp();
} }


void Widget::setAbsoluteX(int x) noexcept
Window& Widget::getWindow() const noexcept
{ {
setAbsolutePos(Point<int>(x, getAbsoluteY()));
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow();
} }


void Widget::setAbsoluteY(int y) noexcept
const GraphicsContext& Widget::getGraphicsContext() const noexcept
{ {
setAbsolutePos(Point<int>(getAbsoluteX(), y));
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow().getGraphicsContext();
} }


void Widget::setAbsolutePos(int x, int y) noexcept
TopLevelWidget* Widget::getTopLevelWidget() const noexcept
{ {
setAbsolutePos(Point<int>(x, y));
}

void Widget::setAbsolutePos(const Point<int>& pos) noexcept
{
if (pData->absolutePos == pos)
return;

PositionChangedEvent ev;
ev.oldPos = pData->absolutePos;
ev.pos = pos;

pData->absolutePos = pos;
onPositionChanged(ev);

pData->parent.repaint();
}

Application& Widget::getParentApp() const noexcept
{
return pData->parent.getApp();
}

Window& Widget::getParentWindow() const noexcept
{
return pData->parent;
}

bool Widget::contains(int x, int y) const noexcept
{
return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight());
}

bool Widget::contains(const Point<int>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
return pData->topLevelWidget;
} }


void Widget::repaint() noexcept void Widget::repaint() noexcept
{ {
pData->parent.repaint();
} }


uint Widget::getId() const noexcept uint Widget::getId() const noexcept
@@ -214,39 +162,43 @@ void Widget::setId(uint id) noexcept
pData->id = id; pData->id = id;
} }


bool Widget::onKeyboard(const KeyboardEvent&)
bool Widget::onKeyboard(const KeyboardEvent& ev)
{ {
return false;
return pData->giveKeyboardEventForSubWidgets(ev);
} }


bool Widget::onSpecial(const SpecialEvent&)
bool Widget::onSpecial(const SpecialEvent& ev)
{ {
return false;
return pData->giveSpecialEventForSubWidgets(ev);
} }


bool Widget::onMouse(const MouseEvent&)
bool Widget::onCharacterInput(const CharacterInputEvent& ev)
{ {
return false;
return pData->giveCharacterInputEventForSubWidgets(ev);
} }


bool Widget::onMotion(const MotionEvent&)
bool Widget::onMouse(const MouseEvent& ev)
{ {
return false;
MouseEvent rev = ev;
return pData->giveMouseEventForSubWidgets(rev);
} }


bool Widget::onScroll(const ScrollEvent&)
bool Widget::onMotion(const MotionEvent& ev)
{ {
return false;
MotionEvent rev = ev;
return pData->giveMotionEventForSubWidgets(rev);
} }


void Widget::onResize(const ResizeEvent&)
bool Widget::onScroll(const ScrollEvent& ev)
{ {
ScrollEvent rev = ev;
return pData->giveScrollEventForSubWidgets(rev);
} }


void Widget::onPositionChanged(const PositionChangedEvent&)
void Widget::onResize(const ResizeEvent&)
{ {
} }


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


END_NAMESPACE_DGL END_NAMESPACE_DGL

+ 212
- 63
dpf/dgl/src/WidgetPrivateData.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,91 +15,240 @@
*/ */


#include "WidgetPrivateData.hpp" #include "WidgetPrivateData.hpp"

#ifdef DGL_CAIRO
# include "../Cairo.hpp"
#endif
#ifdef DGL_OPENGL
# include "../OpenGL.hpp"
#endif
#include "SubWidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"


START_NAMESPACE_DGL START_NAMESPACE_DGL


#define FOR_EACH_SUBWIDGET(it) \
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)

#define FOR_EACH_SUBWIDGET_INV(rit) \
for (std::list<SubWidget*>::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit)

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


void Widget::PrivateData::display(const uint width,
const uint height,
const double scaling,
const bool renderingSubWidget)
Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw)
: self(s),
topLevelWidget(tlw),
parentWidget(nullptr),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
: self(s),
topLevelWidget(findTopLevelWidget(pw)),
parentWidget(pw),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::~PrivateData()
{ {
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible)
subWidgets.clear();
}

void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor)
{
if (subWidgets.size() == 0)
return; return;


#ifdef DGL_OPENGL
bool needsDisableScissor = false;
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
SubWidget* const subwidget(*it);

if (subwidget->isVisible())
subwidget->pData->display(width, height, autoScaleFactor);
}
}

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

bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onKeyboard(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveSpecialEventForSubWidgets(const SpecialEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onSpecial(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onCharacterInput(ev))
return true;
}

return false;
}


// reset color
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;


if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height)))
double x = ev.absolutePos.getX();
double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{ {
// full viewport size
glViewport(0,
-(height * scaling - height),
width * scaling,
height * scaling);
if (selfw->pData->needsViewportScaling)
{
x -= selfw->getAbsoluteX();
y -= selfw->getAbsoluteY();

ev.absolutePos.setX(x);
ev.absolutePos.setY(y);
}
}

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onMouse(ev))
return true;
} }
else if (needsScaling)

return false;
}

bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

double x = ev.absolutePos.getX();
double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{ {
// limit viewport to widget bounds
glViewport(absolutePos.getX(),
height - self->getHeight() - absolutePos.getY(),
self->getWidth(),
self->getHeight());
if (selfw->pData->needsViewportScaling)
{
x -= selfw->getAbsoluteX();
y -= selfw->getAbsoluteY();

ev.absolutePos.setX(x);
ev.absolutePos.setY(y);
}
} }
else

FOR_EACH_SUBWIDGET_INV(rit)
{ {
// only set viewport pos
glViewport(absolutePos.getX() * scaling,
-std::round((height * scaling - height) + (absolutePos.getY() * scaling)),
std::round(width * scaling),
std::round(height * scaling));

// then cut the outer bounds
glScissor(absolutePos.getX() * scaling,
height - std::round((self->getHeight() + absolutePos.getY()) * scaling),
std::round(self->getWidth() * scaling),
std::round(self->getHeight() * scaling));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onMotion(ev))
return true;
} }
#endif


#ifdef DGL_CAIRO
cairo_t* cr = parent.getGraphicsContext().cairo;
cairo_matrix_t matrix;
cairo_get_matrix(cr, &matrix);
cairo_translate(cr, absolutePos.getX(), absolutePos.getY());
// TODO: scaling and cropping
#endif
return false;
}


// display widget
self->onDisplay();
bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

double x = ev.absolutePos.getX();
double y = ev.absolutePos.getY();

if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self))
{
if (selfw->pData->needsViewportScaling)
{
x -= selfw->getAbsoluteX();
y -= selfw->getAbsoluteY();


#ifdef DGL_CAIRO
cairo_set_matrix(cr, &matrix);
#endif
ev.absolutePos.setX(x);
ev.absolutePos.setY(y);
}
}


#ifdef DGL_OPENGL
if (needsDisableScissor)
FOR_EACH_SUBWIDGET_INV(rit)
{ {
glDisable(GL_SCISSOR_TEST);
needsDisableScissor = false;
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onScroll(ev))
return true;
} }
#endif


displaySubWidgets(width, height, scaling);
return false;
}

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

TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw)
{
if (pw->pData->topLevelWidget != nullptr)
return pw->pData->topLevelWidget;
if (pw->pData->parentWidget != nullptr)
return findTopLevelWidget(pw->pData->parentWidget);
return nullptr;
} }


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


+ 22
- 47
dpf/dgl/src/WidgetPrivateData.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,69 +18,44 @@
#define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED #define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED


#include "../Widget.hpp" #include "../Widget.hpp"
#include "../Window.hpp"


#include <vector>
#include <list>


START_NAMESPACE_DGL START_NAMESPACE_DGL


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


struct Widget::PrivateData { struct Widget::PrivateData {
Widget* const self; Widget* const self;
Window& parent;
Point<int> absolutePos;
Size<uint> size;
std::vector<Widget*> subWidgets;

TopLevelWidget* const topLevelWidget;
Widget* const parentWidget;
uint id; uint id;
bool needsFullViewport;
bool needsScaling; bool needsScaling;
bool skipDisplay;
bool visible; bool visible;
Size<uint> size;
std::list<SubWidget*> subWidgets;


PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets)
: self(s),
parent(p),
absolutePos(0, 0),
size(0, 0),
subWidgets(),
id(0),
needsFullViewport(false),
needsScaling(false),
skipDisplay(false),
visible(true)
{
if (addToSubWidgets && groupWidget != nullptr)
{
skipDisplay = true;
groupWidget->pData->subWidgets.push_back(self);
}
}

~PrivateData()
{
subWidgets.clear();
}
// called via TopLevelWidget
explicit PrivateData(Widget* const s, TopLevelWidget* const tlw);
// called via SubWidget
explicit PrivateData(Widget* const s, Widget* const pw);
~PrivateData();


// display function is different depending on build type
void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget);
void displaySubWidgets(uint width, uint height, double autoScaleFactor);


void displaySubWidgets(const uint width, const uint height, const double scaling)
{
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
Widget* const widget(*it);
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this);
bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev);
bool giveSpecialEventForSubWidgets(const SpecialEvent& ev);
bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev);
bool giveMouseEventForSubWidgets(MouseEvent& ev);
bool giveMotionEventForSubWidgets(MotionEvent& ev);
bool giveScrollEventForSubWidgets(ScrollEvent& ev);


widget->pData->display(width, height, scaling, true);
}
}
static TopLevelWidget* findTopLevelWidget(Widget* const w);


DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
}; };


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


END_NAMESPACE_DGL END_NAMESPACE_DGL




+ 191
- 1660
dpf/dgl/src/Window.cpp
File diff suppressed because it is too large
View File


+ 1158
- 0
dpf/dgl/src/WindowPrivateData.cpp
File diff suppressed because it is too large
View File


+ 309
- 0
dpf/dgl/src/WindowPrivateData.hpp View File

@@ -0,0 +1,309 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

#include "../Window.hpp"
#include "../Widget.hpp"
#include "ApplicationPrivateData.hpp"

#include "pugl.hpp"

#include <list>

START_NAMESPACE_DGL

class TopLevelWidget;

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

struct Window::PrivateData : IdleCallback {
/** Reference to the DGL Application class this (private data) window associates with. */
Application& app;

/** Direct access to the DGL Application private data where we registers ourselves in. */
Application::PrivateData* const appData;

/** Pointer to the the DGL Window class that this private data belongs to. */
Window* const self;

/** Pugl view instance. */
PuglView* const view;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];

/** The top-level widgets associated with this Window. */
std::list<TopLevelWidget*> topLevelWidgets;

/** Whether this Window is closed (not visible or counted in the Application it is tied to).
Defaults to true unless embed (embed windows are never closed). */
bool isClosed;

/** Whether this Window is currently visible/mapped. Defaults to false. */
bool isVisible;

/** Whether this Window is embed into another (usually not DGL-controlled) Window. */
const bool isEmbed;

/** Scale factor to report to widgets on request, purely informational. */
double scaleFactor;

/** Automatic scaling to apply on widgets, implemented internally. */
bool autoScaling;
double autoScaleFactor;

/** Pugl geometry constraints access. */
uint minWidth, minHeight;
bool keepAspectRatio;

#ifdef DISTRHO_OS_WINDOWS
/** Selected file for openFileBrowser on windows, stored for fake async operation. */
const char* win32SelectedFile;
#endif

/** Modal window setup. */
struct Modal {
PrivateData* parent; // parent of this window (so we can become modal)
PrivateData* child; // child window to give focus to when modal mode is enabled
bool enabled; // wherever modal mode is enabled (only possible if parent != null)

/** Constructor for a non-modal window. */
Modal() noexcept
: parent(nullptr),
child(nullptr),
enabled(false) {}

/** Constructor for a modal window (with a parent). */
Modal(PrivateData* const p) noexcept
: parent(p),
child(nullptr),
enabled(false) {}

/** Destructor. */
~Modal() noexcept
{
DISTRHO_SAFE_ASSERT(! enabled);
}

DISTRHO_DECLARE_NON_COPYABLE(Modal)
DISTRHO_PREVENT_HEAP_ALLOCATION
} modal;

/** Constructor for a regular, standalone window. */
explicit PrivateData(Application& app, Window* self);

/** Constructor for a modal window. */
explicit PrivateData(Application& app, Window* self, PrivateData* ppData);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle,
uint width, uint height, double scaling, bool resizable);

/** Destructor. */
~PrivateData() override;

/** Helper initialization function called at the end of all this class constructors. */
void initPre(uint width, uint height, bool resizable);
/** Helper initialization function called on the Window constructor after we are done. */
void initPost();

/** Hide window and notify application of a window close event.
* Does nothing if window is embed (that is, not standalone).
* The application event-loop will stop when all windows have been closed.
*
* @note It is possible to hide the window while not stopping the event-loop.
* A closed window is always hidden, but the reverse is not always true.
*/
void close();

void show();
void hide();

void focus();

void setResizable(bool resizable);

const GraphicsContext& getGraphicsContext() const noexcept;

// idle callback stuff
void idleCallback() override;
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs);
bool removeIdleCallback(IdleCallback* callback);

#ifndef DGL_FILE_BROWSER_DISABLED
// file handling
bool openFileBrowser(const Window::FileBrowserOptions& options);
# ifdef DISTRHO_OS_MAC
static void openPanelCallback(PuglView* view, const char* path);
# endif
#endif

// modal handling
void startModal();
void stopModal();
void runAsModal(bool blockWait);

// TESTING
void leaveContext();

// pugl events
void onPuglConfigure(double width, double height);
void onPuglExpose();
void onPuglClose();
void onPuglFocus(bool focus, CrossingMode mode);
void onPuglKey(const Widget::KeyboardEvent& ev);
void onPuglSpecial(const Widget::SpecialEvent& ev);
void onPuglText(const Widget::CharacterInputEvent& ev);
void onPuglMouse(const Widget::MouseEvent& ev);
void onPuglMotion(const Widget::MotionEvent& ev);
void onPuglScroll(const Widget::ScrollEvent& ev);

// Pugl event handling entry point
static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

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

END_NAMESPACE_DGL

#if 0
// #if defined(DISTRHO_OS_HAIKU)
// BApplication* bApplication;
// BView* bView;
// BWindow* bWindow;
#if defined(DISTRHO_OS_MAC)
// NSView<PuglGenericView>* mView;
// id mWindow;
// id mParentWindow;
# ifndef DGL_FILE_BROWSER_DISABLED
NSOpenPanel* fOpenFilePanel;
id fFilePanelDelegate;
# endif
#elif defined(DISTRHO_OS_WINDOWS)
// HWND hwnd;
// HWND hwndParent;
# ifndef DGL_FILE_BROWSER_DISABLED
String fSelectedFile;
# endif
#endif
#endif

#if 0
// -----------------------------------------------------------------------
// Window Private

struct Window::PrivateData {
// -------------------------------------------------------------------

bool handlePluginSpecial(const bool press, const Key key)
{
DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);

if (fModal.childFocus != nullptr)
{
fModal.childFocus->focus();
return true;
}

int mods = 0x0;

switch (key)
{
case kKeyShift:
mods |= kModifierShift;
break;
case kKeyControl:
mods |= kModifierControl;
break;
case kKeyAlt:
mods |= kModifierAlt;
break;
default:
break;
}

if (mods != 0x0)
{
if (press)
fView->mods |= mods;
else
fView->mods &= ~(mods);
}

Widget::SpecialEvent ev;
ev.press = press;
ev.key = key;
ev.mod = static_cast<Modifier>(fView->mods);
ev.time = 0;

FOR_EACH_WIDGET_INV(rit)
{
Widget* const widget(*rit);

if (widget->isVisible() && widget->onSpecial(ev))
return true;
}

return false;
}

#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData)
{
PrivateData* pData = (PrivateData*)userData;

if (returnCode == NSOKButton)
{
NSArray* urls = [panel URLs];
NSURL* fileUrl = nullptr;

for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i)
{
NSURL* url = (NSURL*)[urls objectAtIndex:i];
if ([url isFileURL])
fileUrl = url;
}

if (fileUrl)
{
PuglView* view = pData->fView;
if (view->fileSelectedFunc)
{
const char* fileName = [fileUrl.path UTF8String];
view->fileSelectedFunc(view, fileName);
}
}
}

[pData->fOpenFilePanel release];
pData->fOpenFilePanel = nullptr;
}
#endif

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

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};
#endif

#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

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

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


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

enum FONSerrorCode { enum FONSerrorCode {
// Font atlas is full. // Font atlas is full.
FONS_ATLAS_FULL = 1, FONS_ATLAS_FULL = 1,
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
FONS_SCRATCH_FULL = 2, FONS_SCRATCH_FULL = 2,
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
FONS_STATES_OVERFLOW = 3, FONS_STATES_OVERFLOW = 3,
@@ -78,6 +83,7 @@ struct FONStextIter {
const char* next; const char* next;
const char* end; const char* end;
unsigned int utf8state; unsigned int utf8state;
int bitmapOption;
}; };
typedef struct FONStextIter FONStextIter; typedef struct FONStextIter FONStextIter;


@@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s);
void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);
// Returns current atlas size. // Returns current atlas size.
void fonsGetAtlasSize(FONScontext* s, int* width, int* height); void fonsGetAtlasSize(FONScontext* s, int* width, int* height);
// Expands the atlas size.
// Expands the atlas size.
int fonsExpandAtlas(FONScontext* s, int width, int height); int fonsExpandAtlas(FONScontext* s, int width, int height);
// Resets the whole stash. // Resets the whole stash.
int fonsResetAtlas(FONScontext* stash, int width, int height); int fonsResetAtlas(FONScontext* stash, int width, int height);


// Add fonts // Add fonts
int fonsAddFont(FONScontext* s, const char* name, const char* path);
int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData);
int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex);
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex);
int fonsGetFontByName(FONScontext* s, const char* name); int fonsGetFontByName(FONScontext* s, const char* name);


// State handling // State handling
@@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);
void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh);


// Text iterator // Text iterator
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end);
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption);
int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);


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


int fons__tt_init(FONScontext *context) int fons__tt_init(FONScontext *context)
{
{
FT_Error ftError; FT_Error ftError;
FONS_NOTUSED(context);
FONS_NOTUSED(context);
ftError = FT_Init_FreeType(&ftLibrary); ftError = FT_Init_FreeType(&ftLibrary);
return ftError == 0; return ftError == 0;
} }


int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
int fons__tt_done(FONScontext *context)
{
FT_Error ftError;
FONS_NOTUSED(context);
ftError = FT_Done_FreeType(ftLibrary);
return ftError == 0;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex)
{ {
FT_Error ftError; FT_Error ftError;
FONS_NOTUSED(context); FONS_NOTUSED(context);


//font->font.userdata = stash; //font->font.userdata = stash;
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font);
return ftError == 0; return ftError == 0;
} }


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


float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{ {
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return size / (font->font->ascender - font->font->descender); return size / (font->font->ascender - font->font->descender);
#else
return size / font->font->units_per_EM;
#endif
} }


int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
@@ -193,16 +212,28 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float
{ {
FT_Error ftError; FT_Error ftError;
FT_GlyphSlot ftGlyph; FT_GlyphSlot ftGlyph;
FT_Fixed advFixed;
FONS_NOTUSED(scale); FONS_NOTUSED(scale);


#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));
#else
ftError = FT_Set_Pixel_Sizes(font->font, 0, size);
#endif
if (ftError) return 0; if (ftError) return 0;
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
#else
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
#endif
if (ftError) return 0; if (ftError) return 0;
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance);
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
if (ftError) return 0; if (ftError) return 0;
ftGlyph = font->font->glyph; ftGlyph = font->font->glyph;
*lsb = ftGlyph->metrics.horiBearingX;
*advance = (int)advFixed;
*lsb = (int)ftGlyph->metrics.horiBearingX;
*x0 = ftGlyph->bitmap_left; *x0 = ftGlyph->bitmap_left;
*x1 = *x0 + ftGlyph->bitmap.width; *x1 = *x0 + ftGlyph->bitmap.width;
*y0 = -ftGlyph->bitmap_top; *y0 = -ftGlyph->bitmap_top;
@@ -215,7 +246,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int
{ {
FT_GlyphSlot ftGlyph = font->font->glyph; FT_GlyphSlot ftGlyph = font->font->glyph;
int ftGlyphOffset = 0; int ftGlyphOffset = 0;
int x, y;
unsigned int x, y;
FONS_NOTUSED(outWidth); FONS_NOTUSED(outWidth);
FONS_NOTUSED(outHeight); FONS_NOTUSED(outHeight);
FONS_NOTUSED(scaleX); FONS_NOTUSED(scaleX);
@@ -233,7 +264,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{ {
FT_Vector ftKerning; FT_Vector ftKerning;
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
return ftKerning.x;
return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer
} }


#else #else
@@ -256,13 +287,24 @@ int fons__tt_init(FONScontext *context)
return 1; return 1;
} }


int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
int fons__tt_done(FONScontext *context)
{
FONS_NOTUSED(context);
return 1;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex)
{ {
int stbError;
int offset, stbError;
FONS_NOTUSED(dataSize); FONS_NOTUSED(dataSize);


font->font.userdata = context; font->font.userdata = context;
stbError = stbtt_InitFont(&font->font, data, 0);
offset = stbtt_GetFontOffsetForIndex(data, fontIndex);
if (offset == -1) {
stbError = 0;
} else {
stbError = stbtt_InitFont(&font->font, data, offset);
}
return stbError; return stbError;
} }


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


float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{ {
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return stbtt_ScaleForPixelHeight(&font->font, size); return stbtt_ScaleForPixelHeight(&font->font, size);
#else
return stbtt_ScaleForMappingEmToPixels(&font->font, size);
#endif
} }


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


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


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


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


#ifdef STB_TRUETYPE_IMPLEMENTATION

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


#endif // STB_TRUETYPE_IMPLEMENTATION

// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.


@@ -751,6 +807,27 @@ static FONSstate* fons__getState(FONScontext* stash)
return &stash->states[stash->nstates-1]; return &stash->states[stash->nstates-1];
} }


int fonsAddFallbackFont(FONScontext* stash, int base, int fallback)
{
FONSfont* baseFont = stash->fonts[base];
if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) {
baseFont->fallbacks[baseFont->nfallbacks++] = fallback;
return 1;
}
return 0;
}

void fonsResetFallbackFont(FONScontext* stash, int base)
{
int i;

FONSfont* baseFont = stash->fonts[base];
baseFont->nfallbacks = 0;
baseFont->nglyphs = 0;
for (i = 0; i < FONS_HASH_LUT_SIZE; i++)
baseFont->lut[i] = -1;
}

void fonsSetSize(FONScontext* stash, float size) void fonsSetSize(FONScontext* stash, float size)
{ {
fons__getState(stash)->size = size; fons__getState(stash)->size = size;
@@ -818,7 +895,7 @@ static void fons__freeFont(FONSfont* font)
{ {
if (font == NULL) return; if (font == NULL) return;
if (font->glyphs) free(font->glyphs); if (font->glyphs) free(font->glyphs);
if (font->freeData && font->data) free((void*)font->data);
if (font->freeData && font->data) free(font->data);
free(font); free(font);
} }


@@ -849,12 +926,12 @@ error:
return FONS_INVALID; return FONS_INVALID;
} }


int fonsAddFont(FONScontext* stash, const char* name, const char* path)
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex)
{ {
FILE* fp = 0; FILE* fp = 0;
int dataSize = 0; int dataSize = 0;
size_t readed;
unsigned char* data = NULL; unsigned char* data = NULL;
size_t ignore;


// Read in the font data. // Read in the font data.
fp = fopen(path, "rb"); fp = fopen(path, "rb");
@@ -864,21 +941,20 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path)
fseek(fp,0,SEEK_SET); fseek(fp,0,SEEK_SET);
data = (unsigned char*)malloc(dataSize); data = (unsigned char*)malloc(dataSize);
if (data == NULL) goto error; if (data == NULL) goto error;
ignore = fread(data, 1, dataSize, fp);
readed = fread(data, 1, dataSize, fp);
fclose(fp); fclose(fp);
fp = 0; fp = 0;
if (readed != (size_t)dataSize) goto error;


return fonsAddFontMem(stash, name, data, dataSize, 1);
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex);


error: error:
if (data) free(data); if (data) free(data);
if (fp) fclose(fp); if (fp) fclose(fp);
return FONS_INVALID; return FONS_INVALID;

FONS_NOTUSED(ignore);
} }


int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData)
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex)
{ {
int i, ascent, descent, fh, lineGap; int i, ascent, descent, fh, lineGap;
FONSfont* font; FONSfont* font;
@@ -903,15 +979,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da


// Init font // Init font
stash->nscratch = 0; stash->nscratch = 0;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error;


// Store normalized line height. The real line height is got // Store normalized line height. The real line height is got
// by multiplying the lineh by font size. // by multiplying the lineh by font size.
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
ascent += lineGap;
fh = ascent - descent; fh = ascent - descent;
font->ascender = (float)ascent / (float)fh; font->ascender = (float)ascent / (float)fh;
font->descender = (float)descent / (float)fh; font->descender = (float)descent / (float)fh;
font->lineh = (float)(fh + lineGap) / (float)fh;
font->lineh = font->ascender - font->descender;


return idx; return idx;


@@ -1010,7 +1087,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int
} }


static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint,
short isize, short iblur)
short isize, short iblur, int bitmapOption)
{ {
int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;
float scale; float scale;
@@ -1020,6 +1097,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
int pad, added; int pad, added;
unsigned char* bdst; unsigned char* bdst;
unsigned char* dst; unsigned char* dst;
FONSfont* renderFont = font;


if (isize < 2) return NULL; if (isize < 2) return NULL;
if (iblur > 20) iblur = 20; if (iblur > 20) iblur = 20;
@@ -1032,32 +1110,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1);
i = font->lut[h]; i = font->lut[h];
while (i != -1) { while (i != -1) {
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)
return &font->glyphs[i];
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) {
glyph = &font->glyphs[i];
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) {
return glyph;
}
// At this point, glyph exists but the bitmap data is not yet created.
break;
}
i = font->glyphs[i].next; i = font->glyphs[i].next;
} }


// Could not find glyph, create it.
scale = fons__tt_getPixelHeightScale(&font->font, size);
// Create a new glyph or rasterize bitmap data for a cached glyph.
g = fons__tt_getGlyphIndex(&font->font, codepoint); g = fons__tt_getGlyphIndex(&font->font, codepoint);
fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
// Try to find the glyph in fallback fonts.
if (g == 0) {
for (i = 0; i < font->nfallbacks; ++i) {
FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];
int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);
if (fallbackIndex != 0) {
g = fallbackIndex;
renderFont = fallbackFont;
break;
}
}
// It is possible that we did not find a fallback glyph.
// In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
}
scale = fons__tt_getPixelHeightScale(&renderFont->font, size);
fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
gw = x1-x0 + pad*2; gw = x1-x0 + pad*2;
gh = y1-y0 + pad*2; gh = y1-y0 + pad*2;


// Find free spot for the rect in the atlas
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
if (added == 0 && stash->handleError != NULL) {
// Atlas is full, let the user to resize the atlas (or not), and try again.
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
// Determines the spot to draw glyph in the atlas.
if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) {
// Find free spot for the rect in the atlas
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
if (added == 0 && stash->handleError != NULL) {
// Atlas is full, let the user to resize the atlas (or not), and try again.
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
}
if (added == 0) return NULL;
} else {
// Negative coordinate indicates there is no bitmap data created.
gx = -1;
gy = -1;
} }
if (added == 0) return NULL;


// Init glyph. // Init glyph.
glyph = fons__allocGlyph(font);
glyph->codepoint = codepoint;
glyph->size = isize;
glyph->blur = iblur;
if (glyph == NULL) {
glyph = fons__allocGlyph(font);
glyph->codepoint = codepoint;
glyph->size = isize;
glyph->blur = iblur;
glyph->next = 0;

// Insert char to hash lookup.
glyph->next = font->lut[h];
font->lut[h] = font->nglyphs-1;
}
glyph->index = g; glyph->index = g;
glyph->x0 = (short)gx; glyph->x0 = (short)gx;
glyph->y0 = (short)gy; glyph->y0 = (short)gy;
@@ -1066,15 +1178,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
glyph->xadv = (short)(scale * advance * 10.0f); glyph->xadv = (short)(scale * advance * 10.0f);
glyph->xoff = (short)(x0 - pad); glyph->xoff = (short)(x0 - pad);
glyph->yoff = (short)(y0 - pad); glyph->yoff = (short)(y0 - pad);
glyph->next = 0;


// Insert char to hash lookup.
glyph->next = font->lut[h];
font->lut[h] = font->nglyphs-1;
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) {
return glyph;
}


// Rasterize // Rasterize
dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width];
fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g);
fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g);


// Make sure there is one pixel empty border. // Make sure there is one pixel empty border.
dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
@@ -1101,7 +1212,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
if (iblur > 0) { if (iblur > 0) {
stash->nscratch = 0; stash->nscratch = 0;
bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);
fons__blur(stash, bdst, gw, gh, stash->params.width, iblur);
} }


stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
@@ -1134,8 +1245,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
y1 = (float)(glyph->y1-1); y1 = (float)(glyph->y1-1);


if (stash->params.flags & FONS_ZERO_TOPLEFT) { if (stash->params.flags & FONS_ZERO_TOPLEFT) {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y + yoff);
rx = floorf(*x + xoff);
ry = floorf(*y + yoff);


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


q->x0 = rx; q->x0 = rx;
q->y0 = ry; q->y0 = ry;
@@ -1226,7 +1337,7 @@ float fonsDrawText(FONScontext* stash,
const char* str, const char* end) const char* str, const char* end)
{ {
FONSstate* state = fons__getState(stash); FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0; unsigned int utf8state = 0;
FONSglyph* glyph = NULL; FONSglyph* glyph = NULL;
FONSquad q; FONSquad q;
@@ -1263,7 +1374,7 @@ float fonsDrawText(FONScontext* stash,
for (; str != end; ++str) { for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue; continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED);
if (glyph != NULL) { if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);


@@ -1286,7 +1397,7 @@ float fonsDrawText(FONScontext* stash,
} }


int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
float x, float y, const char* str, const char* end)
float x, float y, const char* str, const char* end, int bitmapOption)
{ {
FONSstate* state = fons__getState(stash); FONSstate* state = fons__getState(stash);
float width; float width;
@@ -1326,6 +1437,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
iter->end = end; iter->end = end;
iter->codepoint = 0; iter->codepoint = 0;
iter->prevGlyphIndex = -1; iter->prevGlyphIndex = -1;
iter->bitmapOption = bitmapOption;


return 1; return 1;
} }
@@ -1346,7 +1458,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
// Get glyph and quad // Get glyph and quad
iter->x = iter->nextx; iter->x = iter->nextx;
iter->y = iter->nexty; iter->y = iter->nexty;
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur);
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption);
// If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid.
if (glyph != NULL) if (glyph != NULL)
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
@@ -1406,12 +1519,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y)
} }


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


void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr)
@@ -1594,7 +1708,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height)
height = fons__maxi(height, stash->params.height); height = fons__maxi(height, stash->params.height);


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


// Flush pending glyphs. // Flush pending glyphs.
fons__flush(stash); fons__flush(stash);


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

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


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

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


struct NVGstate { struct NVGstate {
NVGcompositeOperationState compositeOperation;
int shapeAntiAlias;
NVGpaint fill; NVGpaint fill;
NVGpaint stroke; NVGpaint stroke;
float strokeWidth; float strokeWidth;
@@ -200,6 +205,84 @@ static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio)
ctx->devicePxRatio = ratio; ctx->devicePxRatio = ratio;
} }


static NVGcompositeOperationState nvg__compositeOperationState(int op)
{
int sfactor, dfactor;

if (op == NVG_SOURCE_OVER)
{
sfactor = NVG_ONE;
dfactor = NVG_ONE_MINUS_SRC_ALPHA;
}
else if (op == NVG_SOURCE_IN)
{
sfactor = NVG_DST_ALPHA;
dfactor = NVG_ZERO;
}
else if (op == NVG_SOURCE_OUT)
{
sfactor = NVG_ONE_MINUS_DST_ALPHA;
dfactor = NVG_ZERO;
}
else if (op == NVG_ATOP)
{
sfactor = NVG_DST_ALPHA;
dfactor = NVG_ONE_MINUS_SRC_ALPHA;
}
else if (op == NVG_DESTINATION_OVER)
{
sfactor = NVG_ONE_MINUS_DST_ALPHA;
dfactor = NVG_ONE;
}
else if (op == NVG_DESTINATION_IN)
{
sfactor = NVG_ZERO;
dfactor = NVG_SRC_ALPHA;
}
else if (op == NVG_DESTINATION_OUT)
{
sfactor = NVG_ZERO;
dfactor = NVG_ONE_MINUS_SRC_ALPHA;
}
else if (op == NVG_DESTINATION_ATOP)
{
sfactor = NVG_ONE_MINUS_DST_ALPHA;
dfactor = NVG_SRC_ALPHA;
}
else if (op == NVG_LIGHTER)
{
sfactor = NVG_ONE;
dfactor = NVG_ONE;
}
else if (op == NVG_COPY)
{
sfactor = NVG_ONE;
dfactor = NVG_ZERO;
}
else if (op == NVG_XOR)
{
sfactor = NVG_ONE_MINUS_DST_ALPHA;
dfactor = NVG_ONE_MINUS_SRC_ALPHA;
}
else
{
sfactor = NVG_ONE;
dfactor = NVG_ZERO;
}

NVGcompositeOperationState state;
state.srcRGB = sfactor;
state.dstRGB = dfactor;
state.srcAlpha = sfactor;
state.dstAlpha = dfactor;
return state;
}

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

NVGcontext* nvgCreateInternal(NVGparams* params) NVGcontext* nvgCreateInternal(NVGparams* params)
{ {
FONSparams fontParams; FONSparams fontParams;
@@ -280,7 +363,7 @@ void nvgDeleteInternal(NVGcontext* ctx)
free(ctx); free(ctx);
} }


void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio)
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio)
{ {
/* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
@@ -291,8 +374,8 @@ void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float dev
nvgReset(ctx); nvgReset(ctx);


nvg__setDevicePixelRatio(ctx, devicePixelRatio); nvg__setDevicePixelRatio(ctx, devicePixelRatio);
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight);
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio);


ctx->drawCallCount = 0; ctx->drawCallCount = 0;
ctx->fillTriCount = 0; ctx->fillTriCount = 0;
@@ -383,7 +466,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
{ {
int i; int i;
float oneminu; float oneminu;
NVGcolor cint;
NVGcolor cint = {{{0}}};


u = nvg__clampf(u, 0.0f, 1.0f); u = nvg__clampf(u, 0.0f, 1.0f);
oneminu = 1.0f - u; oneminu = 1.0f - u;
@@ -391,7 +474,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
{ {
cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u;
} }
return cint; return cint;
} }


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



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

void nvgTransformIdentity(float* t) void nvgTransformIdentity(float* t)
{ {
t[0] = 1.0f; t[1] = 0.0f; t[0] = 1.0f; t[1] = 0.0f;
@@ -568,6 +645,8 @@ void nvgReset(NVGcontext* ctx)


nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255));
nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255));
state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER);
state->shapeAntiAlias = 1;
state->strokeWidth = 1.0f; state->strokeWidth = 1.0f;
state->miterLimit = 10.0f; state->miterLimit = 10.0f;
state->lineCap = NVG_BUTT; state->lineCap = NVG_BUTT;
@@ -587,6 +666,12 @@ void nvgReset(NVGcontext* ctx)
} }


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

void nvgStrokeWidth(NVGcontext* ctx, float width) void nvgStrokeWidth(NVGcontext* ctx, float width)
{ {
NVGstate* state = nvg__getState(ctx); NVGstate* state = nvg__getState(ctx);
@@ -719,7 +804,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
return image; return image;
} }


int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata)
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata)
{ {
int w, h, n, image; int w, h, n, image;
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
@@ -913,7 +998,7 @@ void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h)
} }


// Transform the current scissor rect into current transform space. // Transform the current scissor rect into current transform space.
// If there is difference in rotation, this will be approximation.
// If there is difference in rotation, this will be approximation.
memcpy(pxform, state->scissor.xform, sizeof(float)*6); memcpy(pxform, state->scissor.xform, sizeof(float)*6);
ex = state->scissor.extent[0]; ex = state->scissor.extent[0];
ey = state->scissor.extent[1]; ey = state->scissor.extent[1];
@@ -936,6 +1021,30 @@ void nvgResetScissor(NVGcontext* ctx)
state->scissor.extent[1] = -1.0f; state->scissor.extent[1] = -1.0f;
} }


// Global composite operation.
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op)
{
NVGstate* state = nvg__getState(ctx);
state->compositeOperation = nvg__compositeOperationState(op);
}

void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor)
{
nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor);
}

void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha)
{
NVGcompositeOperationState op;
op.srcRGB = srcRGB;
op.dstRGB = dstRGB;
op.srcAlpha = srcAlpha;
op.dstAlpha = dstAlpha;

NVGstate* state = nvg__getState(ctx);
state->compositeOperation = op;
}

static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
{ {
float dx = x2 - x1; float dx = x2 - x1;
@@ -1173,7 +1282,7 @@ static void nvg__tesselateBezier(NVGcontext* ctx,
{ {
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
float dx,dy,d2,d3; float dx,dy,d2,d3;
if (level > 10) return; if (level > 10) return;


x12 = (x1+x2)*0.5f; x12 = (x1+x2)*0.5f;
@@ -1205,8 +1314,8 @@ static void nvg__tesselateBezier(NVGcontext* ctx,
x1234 = (x123+x234)*0.5f; x1234 = (x123+x234)*0.5f;
y1234 = (y123+y234)*0.5f; y1234 = (y123+y234)*0.5f;


nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
} }


static void nvg__flattenPaths(NVGcontext* ctx) static void nvg__flattenPaths(NVGcontext* ctx)
@@ -1331,7 +1440,8 @@ static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w,
} }


static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
float lw, float rw, float lu, float ru, int ncap, float fringe)
float lw, float rw, float lu, float ru, int ncap,
float fringe)
{ {
int i, n; int i, n;
float dlx0 = p0->dy; float dlx0 = p0->dy;
@@ -1464,36 +1574,39 @@ static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
} }


static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, float d, float aa)
float dx, float dy, float w, float d,
float aa, float u0, float u1)
{ {
float px = p->x - dx*d; float px = p->x - dx*d;
float py = p->y - dy*d; float py = p->y - dy*d;
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, 0,0); dst++;
nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, 1,0); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++;
nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++;
nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
return dst; return dst;
} }


static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, float d, float aa)
float dx, float dy, float w, float d,
float aa, float u0, float u1)
{ {
float px = p->x + dx*d; float px = p->x + dx*d;
float py = p->y + dy*d; float py = p->y + dy*d;
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++;
nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, 0,0); dst++;
nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, 1,0); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++;
nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++;
return dst; return dst;
} }




static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, int ncap, float aa)
float dx, float dy, float w, int ncap,
float aa, float u0, float u1)
{ {
int i; int i;
float px = p->x; float px = p->x;
@@ -1504,16 +1617,17 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
for (i = 0; i < ncap; i++) { for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NVG_PI; float a = i/(float)(ncap-1)*NVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, 0,1); dst++;
nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++;
nvg__vset(dst, px, py, 0.5f,1); dst++; nvg__vset(dst, px, py, 0.5f,1); dst++;
} }
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
return dst; return dst;
} }


static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, int ncap, float aa)
float dx, float dy, float w, int ncap,
float aa, float u0, float u1)
{ {
int i; int i;
float px = p->x; float px = p->x;
@@ -1521,13 +1635,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
NVG_NOTUSED(aa); NVG_NOTUSED(aa);
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
for (i = 0; i < ncap; i++) { for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NVG_PI; float a = i/(float)(ncap-1)*NVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
nvg__vset(dst, px, py, 0.5f,1); dst++; nvg__vset(dst, px, py, 0.5f,1); dst++;
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, 0,1); dst++;
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++;
} }
return dst; return dst;
} }
@@ -1603,15 +1717,24 @@ static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float mi
} }




static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin, float miterLimit)
{
static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit)
{
NVGpathCache* cache = ctx->cache; NVGpathCache* cache = ctx->cache;
NVGvertex* verts; NVGvertex* verts;
NVGvertex* dst; NVGvertex* dst;
int cverts, i, j; int cverts, i, j;
float aa = ctx->fringeWidth;
float aa = fringe;//ctx->fringeWidth;
float u0 = 0.0f, u1 = 1.0f;
int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle.


w += aa * 0.5f;

// Disable the gradient used for antialiasing when antialiasing is not used.
if (aa == 0.0f) {
u0 = 0.5f;
u1 = 0.5f;
}

nvg__calculateJoins(ctx, w, lineJoin, miterLimit); nvg__calculateJoins(ctx, w, lineJoin, miterLimit);


// Calculate max vertex usage. // Calculate max vertex usage.
@@ -1672,42 +1795,42 @@ static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin
dy = p1->y - p0->y; dy = p1->y - p0->y;
nvg__normalize(&dx, &dy); nvg__normalize(&dx, &dy);
if (lineCap == NVG_BUTT) if (lineCap == NVG_BUTT)
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa);
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1);
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa);
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1);
else if (lineCap == NVG_ROUND) else if (lineCap == NVG_ROUND)
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa);
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1);
} }


for (j = s; j < e; ++j) { for (j = s; j < e; ++j) {
if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
if (lineJoin == NVG_ROUND) { if (lineJoin == NVG_ROUND) {
dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa);
dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa);
} else { } else {
dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa);
dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa);
} }
} else { } else {
nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), 0,1); dst++;
nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), 1,1); dst++;
nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++;
nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++;
} }
p0 = p1++; p0 = p1++;
} }


if (loop) { if (loop) {
// Loop it // Loop it
nvg__vset(dst, verts[0].x, verts[0].y, 0,1); dst++;
nvg__vset(dst, verts[1].x, verts[1].y, 1,1); dst++;
nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++;
nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++;
} else { } else {
// Add cap // Add cap
dx = p1->x - p0->x; dx = p1->x - p0->x;
dy = p1->y - p0->y; dy = p1->y - p0->y;
nvg__normalize(&dx, &dy); nvg__normalize(&dx, &dy);
if (lineCap == NVG_BUTT) if (lineCap == NVG_BUTT)
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa);
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1);
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa);
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1);
else if (lineCap == NVG_ROUND) else if (lineCap == NVG_ROUND)
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa);
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1);
} }


path->nstroke = (int)(dst - verts); path->nstroke = (int)(dst - verts);
@@ -1868,7 +1991,7 @@ void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y)
{ {
float x0 = ctx->commandx; float x0 = ctx->commandx;
float y0 = ctx->commandy; float y0 = ctx->commandy;
float vals[] = { NVG_BEZIERTO,
float vals[] = { NVG_BEZIERTO,
x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0),
x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y),
x, y }; x, y };
@@ -1950,7 +2073,7 @@ void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, in
float px = 0, py = 0, ptanx = 0, ptany = 0; float px = 0, py = 0, ptanx = 0, ptany = 0;
float vals[3 + 5*7 + 100]; float vals[3 + 5*7 + 100];
int i, ndivs, nvals; int i, ndivs, nvals;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;


// Clamp angles // Clamp angles
da = a1 - a0; da = a1 - a0;
@@ -2022,22 +2145,31 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h)


void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r)
{ {
if (r < 0.1f) {
nvgRect(ctx, x,y,w,h);
nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r);
}

void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft)
{
if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
nvgRect(ctx, x, y, w, h);
return; return;
}
else {
float rx = nvg__minf(r, nvg__absf(w)*0.5f) * nvg__signf(w), ry = nvg__minf(r, nvg__absf(h)*0.5f) * nvg__signf(h);
} else {
float halfw = nvg__absf(w)*0.5f;
float halfh = nvg__absf(h)*0.5f;
float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h);
float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h);
float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h);
float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h);
float vals[] = { float vals[] = {
NVG_MOVETO, x, y+ry,
NVG_LINETO, x, y+h-ry,
NVG_BEZIERTO, x, y+h-ry*(1-NVG_KAPPA90), x+rx*(1-NVG_KAPPA90), y+h, x+rx, y+h,
NVG_LINETO, x+w-rx, y+h,
NVG_BEZIERTO, x+w-rx*(1-NVG_KAPPA90), y+h, x+w, y+h-ry*(1-NVG_KAPPA90), x+w, y+h-ry,
NVG_LINETO, x+w, y+ry,
NVG_BEZIERTO, x+w, y+ry*(1-NVG_KAPPA90), x+w-rx*(1-NVG_KAPPA90), y, x+w-rx, y,
NVG_LINETO, x+rx, y,
NVG_BEZIERTO, x+rx*(1-NVG_KAPPA90), y, x, y+ry*(1-NVG_KAPPA90), x, y+ry,
NVG_MOVETO, x, y + ryTL,
NVG_LINETO, x, y + h - ryBL,
NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h,
NVG_LINETO, x + w - rxBR, y + h,
NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR,
NVG_LINETO, x + w, y + ryTR,
NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y,
NVG_LINETO, x + rxTL, y,
NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL,
NVG_CLOSE NVG_CLOSE
}; };
nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
@@ -2092,7 +2224,7 @@ void nvgFill(NVGcontext* ctx)
int i; int i;


nvg__flattenPaths(ctx); nvg__flattenPaths(ctx);
if (ctx->params.edgeAntiAlias)
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f);
else else
nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f);
@@ -2101,7 +2233,7 @@ void nvgFill(NVGcontext* ctx)
fillPaint.innerColor.a *= state->alpha; fillPaint.innerColor.a *= state->alpha;
fillPaint.outerColor.a *= state->alpha; fillPaint.outerColor.a *= state->alpha;


ctx->params.renderFill(ctx->params.userPtr, &fillPaint, &state->scissor, ctx->fringeWidth,
ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths);


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



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


nvg__flattenPaths(ctx); nvg__flattenPaths(ctx);


if (ctx->params.edgeAntiAlias)
nvg__expandStroke(ctx, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit);
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit);
else else
nvg__expandStroke(ctx, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit);
nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit);


ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, &state->scissor, ctx->fringeWidth,
ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
strokeWidth, ctx->cache->paths, ctx->cache->npaths); strokeWidth, ctx->cache->paths, ctx->cache->npaths);


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


// Add fonts // Add fonts
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path)
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename)
{ {
return fonsAddFont(ctx->fs, name, path);
return fonsAddFont(ctx->fs, name, filename, 0);
} }


int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData)
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex)
{ {
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData);
return fonsAddFont(ctx->fs, name, filename, fontIndex);
}

int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData)
{
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0);
}

int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex)
{
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex);
} }


int nvgFindFont(NVGcontext* ctx, const char* name) int nvgFindFont(NVGcontext* ctx, const char* name)
@@ -2170,6 +2313,28 @@ int nvgFindFont(NVGcontext* ctx, const char* name)
return fonsGetFontByName(ctx->fs, name); return fonsGetFontByName(ctx->fs, name);
} }



int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont)
{
if(baseFont == -1 || fallbackFont == -1) return 0;
return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont);
}

int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont)
{
return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont));
}

void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont)
{
fonsResetFallbackFont(ctx->fs, baseFont);
}

void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont)
{
nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont));
}

// State setting // State setting
void nvgFontSize(NVGcontext* ctx, float size) void nvgFontSize(NVGcontext* ctx, float size)
{ {
@@ -2278,7 +2443,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts)
paint.innerColor.a *= state->alpha; paint.innerColor.a *= state->alpha;
paint.outerColor.a *= state->alpha; paint.outerColor.a *= state->alpha;


ctx->params.renderTriangles(ctx->params.userPtr, &paint, &state->scissor, verts, nverts);
ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth);


ctx->drawCallCount++; ctx->drawCallCount++;
ctx->textTriCount += nverts/3; ctx->textTriCount += nverts/3;
@@ -2310,17 +2475,17 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char*
verts = nvg__allocTempVerts(ctx, cverts); verts = nvg__allocTempVerts(ctx, cverts);
if (verts == NULL) return x; if (verts == NULL) return x;


fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end);
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
float c[4*2]; float c[4*2];
if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? if (iter.prevGlyphIndex == -1) { // can not retrieve glyph?
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
if (nverts != 0) { if (nverts != 0) {
nvg__renderText(ctx, verts, nverts); nvg__renderText(ctx, verts, nverts);
nverts = 0; nverts = 0;
} }
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
iter = prevIter; iter = prevIter;
fonsTextIterNext(ctx->fs, &iter, &q); // try again fonsTextIterNext(ctx->fs, &iter, &q); // try again
if (iter.prevGlyphIndex == -1) // still can not find glyph? if (iter.prevGlyphIndex == -1) // still can not find glyph?
@@ -2343,12 +2508,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char*
} }
} }


// TODO: add back-end bit to do this just once per frame.
// TODO: add back-end bit to do this just once per frame.
nvg__flushTextTexture(ctx); nvg__flushTextTexture(ctx);


nvg__renderText(ctx, verts, nverts); nvg__renderText(ctx, verts, nverts);


return iter.x;
return iter.nextx / scale;
} }


void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end)
@@ -2407,7 +2572,7 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string,
fonsSetAlign(ctx->fs, state->textAlign); fonsSetAlign(ctx->fs, state->textAlign);
fonsSetFont(ctx->fs, state->fontId); fonsSetFont(ctx->fs, state->fontId);


fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end);
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
@@ -2431,6 +2596,7 @@ enum NVGcodepointType {
NVG_SPACE, NVG_SPACE,
NVG_NEWLINE, NVG_NEWLINE,
NVG_CHAR, NVG_CHAR,
NVG_CJK_CHAR,
}; };


int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows)
@@ -2472,7 +2638,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa


breakRowWidth *= scale; breakRowWidth *= scale;


fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end);
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
@@ -2498,7 +2664,15 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
type = NVG_NEWLINE; type = NVG_NEWLINE;
break; break;
default: default:
type = NVG_CHAR;
if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) ||
(iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) ||
(iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) ||
(iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) ||
(iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) ||
(iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF))
type = NVG_CJK_CHAR;
else
type = NVG_CHAR;
break; break;
} }


@@ -2525,12 +2699,12 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
} else { } else {
if (rowStart == NULL) { if (rowStart == NULL) {
// Skip white space until the beginning of the line // Skip white space until the beginning of the line
if (type == NVG_CHAR) {
if (type == NVG_CHAR || type == NVG_CJK_CHAR) {
// The current char is the row so far // The current char is the row so far
rowStartX = iter.x; rowStartX = iter.x;
rowStart = iter.str; rowStart = iter.str;
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX;
rowWidth = iter.nextx - rowStartX;
rowMinX = q.x0 - rowStartX; rowMinX = q.x0 - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
wordStart = iter.str; wordStart = iter.str;
@@ -2545,26 +2719,26 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
float nextWidth = iter.nextx - rowStartX; float nextWidth = iter.nextx - rowStartX;


// track last non-white space character // track last non-white space character
if (type == NVG_CHAR) {
if (type == NVG_CHAR || type == NVG_CJK_CHAR) {
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; rowWidth = iter.nextx - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
} }
// track last end of a word // track last end of a word
if (ptype == NVG_CHAR && type == NVG_SPACE) {
if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) {
breakEnd = iter.str; breakEnd = iter.str;
breakWidth = rowWidth; breakWidth = rowWidth;
breakMaxX = rowMaxX; breakMaxX = rowMaxX;
} }
// track last beginning of a word // track last beginning of a word
if (ptype == NVG_SPACE && type == NVG_CHAR) {
if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) {
wordStart = iter.str; wordStart = iter.str;
wordStartX = iter.x; wordStartX = iter.x;
wordMinX = q.x0 - rowStartX;
wordMinX = q.x0;
} }


// Break to new line when a character is beyond break width. // Break to new line when a character is beyond break width.
if (type == NVG_CHAR && nextWidth > breakRowWidth) {
if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) {
// The run length is too long, need to break to new line. // The run length is too long, need to break to new line.
if (breakEnd == rowStart) { if (breakEnd == rowStart) {
// The current word is longer than the row length, just break it from here. // The current word is longer than the row length, just break it from here.
@@ -2597,13 +2771,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
nrows++; nrows++;
if (nrows >= maxRows) if (nrows >= maxRows)
return nrows; return nrows;
// Update row
rowStartX = wordStartX; rowStartX = wordStartX;
rowStart = wordStart; rowStart = wordStart;
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; rowWidth = iter.nextx - rowStartX;
rowMinX = wordMinX;
rowMinX = wordMinX - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
// No change to the word start
} }
// Set null break point // Set null break point
breakEnd = rowStart; breakEnd = rowStart;


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

@@ -79,10 +79,46 @@ enum NVGalign {
// Vertical align // Vertical align
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle.
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
}; };


enum NVGblendFactor {
NVG_ZERO = 1<<0,
NVG_ONE = 1<<1,
NVG_SRC_COLOR = 1<<2,
NVG_ONE_MINUS_SRC_COLOR = 1<<3,
NVG_DST_COLOR = 1<<4,
NVG_ONE_MINUS_DST_COLOR = 1<<5,
NVG_SRC_ALPHA = 1<<6,
NVG_ONE_MINUS_SRC_ALPHA = 1<<7,
NVG_DST_ALPHA = 1<<8,
NVG_ONE_MINUS_DST_ALPHA = 1<<9,
NVG_SRC_ALPHA_SATURATE = 1<<10,
};

enum NVGcompositeOperation {
NVG_SOURCE_OVER,
NVG_SOURCE_IN,
NVG_SOURCE_OUT,
NVG_ATOP,
NVG_DESTINATION_OVER,
NVG_DESTINATION_IN,
NVG_DESTINATION_OUT,
NVG_DESTINATION_ATOP,
NVG_LIGHTER,
NVG_COPY,
NVG_XOR,
};

struct NVGcompositeOperationState {
int srcRGB;
int dstRGB;
int srcAlpha;
int dstAlpha;
};
typedef struct NVGcompositeOperationState NVGcompositeOperationState;

struct NVGglyphPosition { struct NVGglyphPosition {
const char* str; // Position of the glyph in the input string. const char* str; // Position of the glyph in the input string.
float x; // The x-coordinate of the logical glyph position. float x; // The x-coordinate of the logical glyph position.
@@ -100,11 +136,12 @@ struct NVGtextRow {
typedef struct NVGtextRow NVGtextRow; typedef struct NVGtextRow NVGtextRow;


enum NVGimageFlags { enum NVGimageFlags {
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction.
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction.
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear
}; };


// Begin drawing a new frame // Begin drawing a new frame
@@ -115,7 +152,7 @@ enum NVGimageFlags {
// For example, GLFW returns two dimension for an opened window: window size and // For example, GLFW returns two dimension for an opened window: window size and
// frame buffer size. In that case you would set windowWidth/Height to the window size // frame buffer size. In that case you would set windowWidth/Height to the window size
// devicePixelRatio to: frameBufferWidth / windowWidth. // devicePixelRatio to: frameBufferWidth / windowWidth.
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio);
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio);


// Cancels drawing the current frame. // Cancels drawing the current frame.
void nvgCancelFrame(NVGcontext* ctx); void nvgCancelFrame(NVGcontext* ctx);
@@ -123,6 +160,22 @@ void nvgCancelFrame(NVGcontext* ctx);
// Ends drawing flushing remaining render state. // Ends drawing flushing remaining render state.
void nvgEndFrame(NVGcontext* ctx); void nvgEndFrame(NVGcontext* ctx);


//
// Composite operation
//
// The composite operations in NanoVG are modeled after HTML Canvas API, and
// the blend func is based on OpenGL (see corresponding manuals for more info).
// The colors in the blending state have premultiplied alpha.

// Sets the composite operation. The op parameter should be one of NVGcompositeOperation.
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op);

// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor);

// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);

// //
// Color utils // Color utils
// //
@@ -183,7 +236,10 @@ void nvgReset(NVGcontext* ctx);
// Solid color is simply defined as a color value, different kinds of paints can be created // Solid color is simply defined as a color value, different kinds of paints can be created
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().
// //
// Current render style can be saved and restored using nvgSave() and nvgRestore().
// Current render style can be saved and restored using nvgSave() and nvgRestore().

// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default.
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled);


// Sets current stroke style to a solid color. // Sets current stroke style to a solid color.
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
@@ -231,7 +287,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
// Apart from nvgResetTransform(), each transformation function first creates // Apart from nvgResetTransform(), each transformation function first creates
// specific transformation matrix and pre-multiplies the current transformation by it. // specific transformation matrix and pre-multiplies the current transformation by it.
// //
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().


// Resets current transform to a identity matrix. // Resets current transform to a identity matrix.
void nvgResetTransform(NVGcontext* ctx); void nvgResetTransform(NVGcontext* ctx);
@@ -317,7 +373,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);


// Creates image by loading it from the specified chunk of memory. // Creates image by loading it from the specified chunk of memory.
// Returns handle to the image. // Returns handle to the image.
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata);
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);


// Creates image from specified image data. // Creates image from specified image data.
// Returns handle to the image. // Returns handle to the image.
@@ -368,7 +424,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey
// Scissoring // Scissoring
// //
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various // Scissoring allows you to clip the rendering into a rectangle. This is useful for various
// user interface cases like rendering a text edit or a timeline.
// user interface cases like rendering a text edit or a timeline.


// Sets the current scissor rectangle. // Sets the current scissor rectangle.
// The scissor rectangle is transformed by the current transform. // The scissor rectangle is transformed by the current transform.
@@ -423,7 +479,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad
// Closes current sub-path with a line segment. // Closes current sub-path with a line segment.
void nvgClosePath(NVGcontext* ctx); void nvgClosePath(NVGcontext* ctx);


// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
void nvgPathWinding(NVGcontext* ctx, int dir); void nvgPathWinding(NVGcontext* ctx, int dir);


// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
@@ -437,10 +493,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);
// Creates new rounded rectangle shaped sub-path. // Creates new rounded rectangle shaped sub-path.
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r);


// Creates new rounded rectangle shaped sub-path with varying radii for each corner.
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft);

// Creates new ellipse shaped sub-path. // Creates new ellipse shaped sub-path.
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);


// Creates new circle shaped sub-path.
// Creates new circle shaped sub-path.
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);


// Fills the current path with current fill style. // Fills the current path with current fill style.
@@ -487,13 +546,31 @@ void nvgStroke(NVGcontext* ctx);
// Returns handle to the font. // Returns handle to the font.
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);


// Creates image by loading it from the specified memory chunk.
// fontIndex specifies which font face to load from a .ttf/.ttc file.
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex);

// Creates font by loading it from the specified memory chunk.
// Returns handle to the font. // Returns handle to the font.
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData);
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData);

// fontIndex specifies which font face to load from a .ttf/.ttc file.
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex);


// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
int nvgFindFont(NVGcontext* ctx, const char* name); int nvgFindFont(NVGcontext* ctx, const char* name);


// Adds a fallback font by handle.
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont);

// Adds a fallback font by name.
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont);

// Resets fallback fonts by handle.
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont);

// Resets fallback fonts by name.
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont);

// Sets the font size of current text style. // Sets the font size of current text style.
void nvgFontSize(NVGcontext* ctx, float size); void nvgFontSize(NVGcontext* ctx, float size);


@@ -503,7 +580,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur);
// Sets the letter spacing of current text style. // Sets the letter spacing of current text style.
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);


// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);


// Sets the text align of current text style, see NVGalign for options. // Sets the text align of current text style, see NVGalign for options.
@@ -588,12 +665,12 @@ struct NVGparams {
int (*renderDeleteTexture)(void* uptr, int image); int (*renderDeleteTexture)(void* uptr, int image);
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data);
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);
void (*renderViewport)(void* uptr, int width, int height);
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
void (*renderCancel)(void* uptr); void (*renderCancel)(void* uptr);
void (*renderFlush)(void* uptr); void (*renderFlush)(void* uptr);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe);
void (*renderDelete)(void* uptr); void (*renderDelete)(void* uptr);
}; };
typedef struct NVGparams NVGparams; typedef struct NVGparams NVGparams;


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

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


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

#endif #endif


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


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

#endif #endif


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


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

#endif #endif


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


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

#endif #endif


// These are additional flags on top of NVGimageFlags. // These are additional flags on top of NVGimageFlags.
@@ -87,10 +99,6 @@ enum NVGimageFlagsGL {
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.
}; };


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


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


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

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


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

int dummyTex;
}; };
typedef struct GLNVGcontext GLNVGcontext; typedef struct GLNVGcontext GLNVGcontext;


@@ -298,7 +319,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
if ((gl->stencilFunc != func) || if ((gl->stencilFunc != func) ||
(gl->stencilFuncRef != ref) || (gl->stencilFuncRef != ref) ||
(gl->stencilFuncMask != mask)) { (gl->stencilFuncMask != mask)) {
gl->stencilFunc = func; gl->stencilFunc = func;
gl->stencilFuncRef = ref; gl->stencilFuncRef = ref;
gl->stencilFuncMask = mask; gl->stencilFuncMask = mask;
@@ -308,6 +329,21 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
glStencilFunc(func, ref, mask); glStencilFunc(func, ref, mask);
#endif #endif
} }
static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend)
{
#if NANOVG_GL_USE_STATE_FILTER
if ((gl->blendFunc.srcRGB != blend->srcRGB) ||
(gl->blendFunc.dstRGB != blend->dstRGB) ||
(gl->blendFunc.srcAlpha != blend->srcAlpha) ||
(gl->blendFunc.dstAlpha != blend->dstAlpha)) {

gl->blendFunc = *blend;
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha);
}
#else
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha);
#endif
}


static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
{ {
@@ -331,10 +367,10 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
} }
tex = &gl->textures[gl->ntextures++]; tex = &gl->textures[gl->ntextures++];
} }
memset(tex, 0, sizeof(*tex)); memset(tex, 0, sizeof(*tex));
tex->id = ++gl->textureId; tex->id = ++gl->textureId;
return tex; return tex;
} }


@@ -363,8 +399,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id)


static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type)
{ {
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetShaderInfoLog(shader, 512, &len, str); glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512; if (len > 512) len = 512;
str[len] = '\0'; str[len] = '\0';
@@ -373,8 +409,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char*


static void glnvg__dumpProgramError(GLuint prog, const char* name) static void glnvg__dumpProgramError(GLuint prog, const char* name)
{ {
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetProgramInfoLog(prog, 512, &len, str); glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512; if (len > 512) len = 512;
str[len] = '\0'; str[len] = '\0';
@@ -466,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader)
#endif #endif
} }


static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);

static int glnvg__renderCreate(void* uptr) static int glnvg__renderCreate(void* uptr)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -514,7 +552,7 @@ static int glnvg__renderCreate(void* uptr)
" gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n"
"}\n"; "}\n";


static const char* fillFragShader =
static const char* fillFragShader =
"#ifdef GL_ES\n" "#ifdef GL_ES\n"
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"
" precision highp float;\n" " precision highp float;\n"
@@ -592,6 +630,7 @@ static int glnvg__renderCreate(void* uptr)
" float scissor = scissorMask(fpos);\n" " float scissor = scissorMask(fpos);\n"
"#ifdef EDGE_AA\n" "#ifdef EDGE_AA\n"
" float strokeAlpha = strokeMask();\n" " float strokeAlpha = strokeMask();\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#else\n" "#else\n"
" float strokeAlpha = 1.0;\n" " float strokeAlpha = 1.0;\n"
"#endif\n" "#endif\n"
@@ -631,9 +670,6 @@ static int glnvg__renderCreate(void* uptr)
" color *= scissor;\n" " color *= scissor;\n"
" result = color * innerCol;\n" " result = color * innerCol;\n"
" }\n" " }\n"
"#ifdef EDGE_AA\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#endif\n"
"#ifdef NANOVG_GL3\n" "#ifdef NANOVG_GL3\n"
" outColor = result;\n" " outColor = result;\n"
"#else\n" "#else\n"
@@ -663,11 +699,15 @@ static int glnvg__renderCreate(void* uptr)
#if NANOVG_GL_USE_UNIFORMBUFFER #if NANOVG_GL_USE_UNIFORMBUFFER
// Create UBOs // Create UBOs
glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING);
glGenBuffers(1, &gl->fragBuf);
glGenBuffers(1, &gl->fragBuf);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align);
#endif #endif
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align;


// Some platforms does not allow to have samples to unset textures.
// Create empty one which is bound when there's no texture specified.
gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL);

glnvg__checkError(gl, "create done"); glnvg__checkError(gl, "create done");


glFinish(); glFinish();
@@ -690,7 +730,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h);
imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
} }
// No mips.
// No mips.
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h);
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS;
@@ -722,7 +762,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
if (type == NVG_TEXTURE_RGBA) if (type == NVG_TEXTURE_RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else else
#if defined(NANOVG_GLES2)
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#elif defined(NANOVG_GLES3) #elif defined(NANOVG_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
@@ -731,11 +771,24 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
#endif #endif


if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
} else {
if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}

if (imageFlags & NVG_IMAGE_NEAREST) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else { } else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


if (imageFlags & NVG_IMAGE_REPEATX) if (imageFlags & NVG_IMAGE_REPEATX)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -801,7 +854,7 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w
if (tex->type == NVG_TEXTURE_RGBA) if (tex->type == NVG_TEXTURE_RGBA)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
else else
#ifdef NANOVG_GLES2
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#else #else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);
@@ -887,19 +940,30 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai
tex = glnvg__findTexture(gl, paint->image); tex = glnvg__findTexture(gl, paint->image);
if (tex == NULL) return 0; if (tex == NULL) return 0;
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {
float flipped[6];
nvgTransformScale(flipped, 1.0f, -1.0f);
nvgTransformMultiply(flipped, paint->xform);
nvgTransformInverse(invxform, flipped);
float m1[6], m2[6];
nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f);
nvgTransformMultiply(m1, paint->xform);
nvgTransformScale(m2, 1.0f, -1.0f);
nvgTransformMultiply(m2, m1);
nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f);
nvgTransformMultiply(m1, m2);
nvgTransformInverse(invxform, m1);
} else { } else {
nvgTransformInverse(invxform, paint->xform); nvgTransformInverse(invxform, paint->xform);
} }
frag->type = NSVG_SHADER_FILLIMG; frag->type = NSVG_SHADER_FILLIMG;


#if NANOVG_GL_USE_UNIFORMBUFFER
if (tex->type == NVG_TEXTURE_RGBA) if (tex->type == NVG_TEXTURE_RGBA)
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
else else
frag->texType = 2; frag->texType = 2;
#else
if (tex->type == NVG_TEXTURE_RGBA)
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f;
else
frag->texType = 2.0f;
#endif
// printf("frag->texType = %d\n", frag->texType); // printf("frag->texType = %d\n", frag->texType);
} else { } else {
frag->type = NSVG_SHADER_FILLGRAD; frag->type = NSVG_SHADER_FILLGRAD;
@@ -917,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i);


static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
{ {
GLNVGtexture* tex = NULL;
#if NANOVG_GL_USE_UNIFORMBUFFER #if NANOVG_GL_USE_UNIFORMBUFFER
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms));
#else #else
@@ -925,19 +990,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
#endif #endif


if (image != 0) { if (image != 0) {
GLNVGtexture* tex = glnvg__findTexture(gl, image);
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);
glnvg__checkError(gl, "tex paint tex");
} else {
glnvg__bindTexture(gl, 0);
tex = glnvg__findTexture(gl, image);
} }
// If no image is set, use empty texture
if (tex == NULL) {
tex = glnvg__findTexture(gl, gl->dummyTex);
}
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);
glnvg__checkError(gl, "tex paint tex");
} }


static void glnvg__renderViewport(void* uptr, int width, int height)
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio)
{ {
NVG_NOTUSED(devicePixelRatio);
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
gl->view[0] = (float)width;
gl->view[1] = (float)height;
gl->view[0] = width;
gl->view[1] = height;
} }


static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
@@ -979,7 +1047,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
// Draw fill // Draw fill
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);
glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount);


glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
} }
@@ -992,12 +1060,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call)
glnvg__setUniforms(gl, call->uniformOffset, call->image); glnvg__setUniforms(gl, call->uniformOffset, call->image);
glnvg__checkError(gl, "convex fill"); glnvg__checkError(gl, "convex fill");


for (i = 0; i < npaths; i++)
for (i = 0; i < npaths; i++) {
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
if (gl->flags & NVG_ANTIALIAS) {
// Draw fringes // Draw fringes
for (i = 0; i < npaths; i++)
if (paths[i].strokeCount > 0) {
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
}
} }
} }


@@ -1026,7 +1094,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call)
for (i = 0; i < npaths; i++) for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);


// Clear stencil buffer.
// Clear stencil buffer.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
@@ -1064,6 +1132,50 @@ static void glnvg__renderCancel(void* uptr) {
gl->nuniforms = 0; gl->nuniforms = 0;
} }


static GLenum glnvg_convertBlendFuncFactor(int factor)
{
if (factor == NVG_ZERO)
return GL_ZERO;
if (factor == NVG_ONE)
return GL_ONE;
if (factor == NVG_SRC_COLOR)
return GL_SRC_COLOR;
if (factor == NVG_ONE_MINUS_SRC_COLOR)
return GL_ONE_MINUS_SRC_COLOR;
if (factor == NVG_DST_COLOR)
return GL_DST_COLOR;
if (factor == NVG_ONE_MINUS_DST_COLOR)
return GL_ONE_MINUS_DST_COLOR;
if (factor == NVG_SRC_ALPHA)
return GL_SRC_ALPHA;
if (factor == NVG_ONE_MINUS_SRC_ALPHA)
return GL_ONE_MINUS_SRC_ALPHA;
if (factor == NVG_DST_ALPHA)
return GL_DST_ALPHA;
if (factor == NVG_ONE_MINUS_DST_ALPHA)
return GL_ONE_MINUS_DST_ALPHA;
if (factor == NVG_SRC_ALPHA_SATURATE)
return GL_SRC_ALPHA_SATURATE;
return GL_INVALID_ENUM;
}

static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op)
{
GLNVGblend blend;
blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB);
blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB);
blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha);
blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha);
if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM)
{
blend.srcRGB = GL_ONE;
blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA;
blend.srcAlpha = GL_ONE;
blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA;
}
return blend;
}

static void glnvg__renderFlush(void* uptr) static void glnvg__renderFlush(void* uptr)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1074,7 +1186,6 @@ static void glnvg__renderFlush(void* uptr)
// Setup require GL state. // Setup require GL state.
glUseProgram(gl->shader.prog); glUseProgram(gl->shader.prog);


glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
@@ -1093,6 +1204,10 @@ static void glnvg__renderFlush(void* uptr)
gl->stencilFunc = GL_ALWAYS; gl->stencilFunc = GL_ALWAYS;
gl->stencilFuncRef = 0; gl->stencilFuncRef = 0;
gl->stencilFuncMask = 0xffffffff; gl->stencilFuncMask = 0xffffffff;
gl->blendFunc.srcRGB = GL_INVALID_ENUM;
gl->blendFunc.srcAlpha = GL_INVALID_ENUM;
gl->blendFunc.dstRGB = GL_INVALID_ENUM;
gl->blendFunc.dstAlpha = GL_INVALID_ENUM;
#endif #endif


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


for (i = 0; i < gl->ncalls; i++) { for (i = 0; i < gl->ncalls; i++) {
GLNVGcall* call = &gl->calls[i]; GLNVGcall* call = &gl->calls[i];
glnvg__blendFuncSeparate(gl,&call->blendFunc);
if (call->type == GLNVG_FILL) if (call->type == GLNVG_FILL)
glnvg__fill(gl, call); glnvg__fill(gl, call);
else if (call->type == GLNVG_CONVEXFILL) else if (call->type == GLNVG_CONVEXFILL)
@@ -1136,9 +1252,9 @@ static void glnvg__renderFlush(void* uptr)
glDisableVertexAttribArray(1); glDisableVertexAttribArray(1);
#if defined NANOVG_GL3 #if defined NANOVG_GL3
glBindVertexArray(0); glBindVertexArray(0);
#endif
#endif
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
glnvg__bindTexture(gl, 0); glnvg__bindTexture(gl, 0);
} }
@@ -1237,7 +1353,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
vtx->v = v; vtx->v = v;
} }


static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
const float* bounds, const NVGpath* paths, int npaths) const float* bounds, const NVGpath* paths, int npaths)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1249,16 +1365,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
if (call == NULL) return; if (call == NULL) return;


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


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


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount;
offset = glnvg__allocVerts(gl, maxverts); offset = glnvg__allocVerts(gl, maxverts);
if (offset == -1) goto error; if (offset == -1) goto error;


@@ -1280,20 +1401,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
} }
} }


// Quad
call->triangleOffset = offset;
call->triangleCount = 6;
quad = &gl->verts[call->triangleOffset];
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);

glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);

// Setup uniforms for draw calls // Setup uniforms for draw calls
if (call->type == GLNVG_FILL) { if (call->type == GLNVG_FILL) {
// Quad
call->triangleOffset = offset;
quad = &gl->verts[call->triangleOffset];
glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f);
glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f);

call->uniformOffset = glnvg__allocFragUniforms(gl, 2); call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
// Simple shader for stencil // Simple shader for stencil
@@ -1318,7 +1435,7 @@ error:
if (gl->ncalls > 0) gl->ncalls--; if (gl->ncalls > 0) gl->ncalls--;
} }


static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
float strokeWidth, const NVGpath* paths, int npaths) float strokeWidth, const NVGpath* paths, int npaths)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1332,6 +1449,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor
if (call->pathOffset == -1) goto error; if (call->pathOffset == -1) goto error;
call->pathCount = npaths; call->pathCount = npaths;
call->image = paint->image; call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths); maxverts = glnvg__maxVertCount(paths, npaths);
@@ -1373,8 +1491,8 @@ error:
if (gl->ncalls > 0) gl->ncalls--; if (gl->ncalls > 0) gl->ncalls--;
} }


static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor,
const NVGvertex* verts, int nverts)
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor,
const NVGvertex* verts, int nverts, float fringe)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
GLNVGcall* call = glnvg__allocCall(gl); GLNVGcall* call = glnvg__allocCall(gl);
@@ -1384,6 +1502,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis


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


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
call->triangleOffset = glnvg__allocVerts(gl, nverts); call->triangleOffset = glnvg__allocVerts(gl, nverts);
@@ -1396,7 +1515,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
frag = nvg__fragUniformPtr(gl, call->uniformOffset); frag = nvg__fragUniformPtr(gl, call->uniformOffset);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f);
frag->type = NSVG_SHADER_IMG; frag->type = NSVG_SHADER_IMG;


return; return;
@@ -1499,7 +1618,15 @@ void nvgDeleteGLES3(NVGcontext* ctx)
nvgDeleteInternal(ctx); nvgDeleteInternal(ctx);
} }


int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#if defined NANOVG_GL2
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GL3
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES2
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES3
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#endif
{ {
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__allocTexture(gl); GLNVGtexture* tex = glnvg__allocTexture(gl);
@@ -1515,7 +1642,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i
return tex->id; return tex->id;
} }


GLuint nvglImageHandle(NVGcontext* ctx, int image)
#if defined NANOVG_GL2
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image)
#elif defined NANOVG_GL3
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES2
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES3
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image)
#endif
{ {
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__findTexture(gl, image); GLNVGtexture* tex = glnvg__findTexture(gl, image);


+ 29
- 7
dpf/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. // Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb); void nvgluBindFramebuffer(NVGLUframebuffer* fb);
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb);
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);


#endif // NANOVG_GL_UTILS_H #endif // NANOVG_GL_UTILS_H


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


fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);
fb->texture = nvglImageHandle(ctx, fb->image);

#if defined NANOVG_GL2
fb->texture = nvglImageHandleGL2(ctx, fb->image);
#elif defined NANOVG_GL3
fb->texture = nvglImageHandleGL3(ctx, fb->image);
#elif defined NANOVG_GLES2
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
#elif defined NANOVG_GLES3
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
#endif

fb->ctx = ctx;


// frame buffer object // frame buffer object
glGenFramebuffers(1, &fb->fbo); glGenFramebuffers(1, &fb->fbo);
@@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);


if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
#ifdef GL_DEPTH24_STENCIL8
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
// Some graphics cards require a depth buffer along with a stencil.
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
#endif // GL_DEPTH24_STENCIL8
goto error;
}


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


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


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


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


dpf/dgl/src/pugl/pugl.h → dpf/dgl/src/pugl-custom/pugl.h View File


dpf/dgl/src/pugl/pugl_haiku.cpp → dpf/dgl/src/pugl-custom/pugl_haiku.cpp View File


dpf/dgl/src/pugl/pugl_internal.h → dpf/dgl/src/pugl-custom/pugl_internal.h View File


dpf/dgl/src/pugl/pugl_osx.m → dpf/dgl/src/pugl-custom/pugl_osx.m View File


dpf/dgl/src/pugl/pugl_win.cpp → dpf/dgl/src/pugl-custom/pugl_win.cpp View File


dpf/dgl/src/pugl/pugl_x11.c → dpf/dgl/src/pugl-custom/pugl_x11.c View File

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

+ 29
- 0
dpf/dgl/src/pugl-extra/extras.c View File

@@ -0,0 +1,29 @@
/*
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.

THIS 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.
*/

/**
@file extras.c pugl extra implementations for DPF.
*/

#include "extras.h"

#include "../pugl-upstream/src/implementation.h"

const char*
puglGetWindowTitle(const PuglView* view)
{
return view->title;
}

+ 50
- 0
dpf/dgl/src/pugl-extra/extras.h View File

@@ -0,0 +1,50 @@
/*
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.

THIS 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.
*/

/**
@file pugl.h pugl extra API for DPF.
*/

#ifndef PUGL_EXTRAS_PUGL_H
#define PUGL_EXTRAS_PUGL_H

#include "../pugl-upstream/include/pugl/pugl.h"

PUGL_BEGIN_DECLS

PUGL_API const char*
puglGetWindowTitle(const PuglView* view);

PUGL_API int
puglGetViewHint(const PuglView* view, PuglViewHint hint);

PUGL_API void
puglRaiseWindow(PuglView* view);

PUGL_API void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height);

PUGL_API void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect);

#ifdef DISTRHO_OS_WINDOWS
PUGL_API void
puglWin32SetWindowResizable(PuglView* view, bool resizable);
#endif

PUGL_END_DECLS

#endif // PUGL_EXTRAS_PUGL_H

+ 81
- 0
dpf/dgl/src/pugl-extra/haiku.cpp View File

@@ -0,0 +1,81 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-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.

THIS 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.
*/

/**
@file mac.cpp HaikuOS implementation.
*/

#include "haiku.h"

#include "pugl/detail/implementation.h"

PuglStatus
puglGrabFocus(PuglView* view)
{
view->impl->bView->MakeFocus(true);
return PUGL_SUCCESS;
}

// extras follow

void
puglRaiseWindow(PuglView* view)
{
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
bView->ResizeTo(width, height);

if (bWindow != nullptr && bWindow->LockLooper())
{
bWindow->MoveTo(50, 50);
bWindow->ResizeTo(width, height);

if (! forced)
bWindow->Flush();

bWindow->UnlockLooper();
}
// TODO resizable
}

void setVisible(const bool yesNo)
{
if (bWindow != nullptr)
{
if (bWindow->LockLooper())
{
if (yesNo)
bWindow->Show();
else
bWindow->Hide();

// TODO use flush?
bWindow->Sync();
bWindow->UnlockLooper();
}
}
else
{
if (yesNo)
bView->Show();
else
bView->Hide();
}
}

+ 35
- 0
dpf/dgl/src/pugl-extra/haiku.h View File

@@ -0,0 +1,35 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-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.

THIS 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.
*/

/**
@file haiku.h Shared definitions for HaikuOS implementation.
*/

#include "pugl/pugl.h"

#include <Application.h>
#include <Window.h>
// using? interface/

struct PuglWorldInternalsImpl {
BApplication* app;
};

struct PuglInternalsImpl {
BViewType* view;
BWindow* window;
};

+ 48
- 0
dpf/dgl/src/pugl-extra/mac.m View File

@@ -0,0 +1,48 @@
/*
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.

THIS 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.
*/

/**
@file mac.m MacOS extra implementation for DPF.
*/

#include "pugl/detail/mac.h"

void
puglRaiseWindow(PuglView* view)
{
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
// NOTE: pugl mac code does nothing with x and y
const PuglRect frame = { 0.0, 0.0, (double)width, (double)height };
puglSetFrame(view, frame);
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio
view->minWidth = width;
view->minHeight = height;

[view->impl->window setContentMinSize:sizePoints(view, width, height)];

if (aspect) {
[view->impl->window setContentAspectRatio:sizePoints(view, width, height)];
}
}

+ 118
- 0
dpf/dgl/src/pugl-extra/win.c View File

@@ -0,0 +1,118 @@
/*
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.

THIS 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.
*/

/**
@file win.c Windows extra implementation for DPF.
*/

#include "pugl/detail/win.h"

#include "pugl/detail/implementation.h"

void
puglRaiseWindow(PuglView* view)
{
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
return PUGL_SUCCESS;
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
view->frame.width = width;
view->frame.height = height;

// NOTE the following code matches upstream pugl, except we add SWP_NOMOVE flag
if (view->impl->hwnd) {
RECT rect = { (long)frame.x,
(long)frame.y,
(long)frame.x + (long)frame.width,
(long)frame.y + (long)frame.height };

AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view),
FALSE,
puglWinGetWindowExFlags(view));

SetWindowPos(view->impl->hwnd,
HWND_TOP,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio, but stilL TODO on pugl
Display* display = view->world->impl->display;

view->minWidth = width;
view->minHeight = height;

if (aspect) {
view->minAspectX = width;
view->minAspectY = height;
view->maxAspectX = width;
view->maxAspectY = height;
}
}

void
puglWin32RestoreWindow(PuglView* view)
{
PuglInternals* impl = view->impl;

ShowWindow(impl->hwnd, SW_RESTORE);
SetFocus(impl->hwnd);
}

void
puglWin32ShowWindowCentered(PuglView* view)
{
PuglInternals* impl = view->impl;

RECT rectChild, rectParent;

if (impl->transientParent != 0 &&
GetWindowRect(impl->hwnd, &rectChild) &&
GetWindowRect(impl->transientParent, &rectParent))
{
SetWindowPos(impl->hwnd, (HWND)impl->transientParent,
rectParent.left + (rectChild.right-rectChild.left)/2,
rectParent.top + (rectChild.bottom-rectChild.top)/2,
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
}
else
{
ShowWindow(hwnd, SW_SHOWNORMAL);
}

SetFocus(impl->hwnd);
}

void
puglWin32SetWindowResizable(PuglView* view, bool resizable)
{
PuglInternals* impl = view->impl;

const int winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
: GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
SetWindowLong(impl->hwnd, GWL_STYLE, winFlags);
}

+ 111
- 0
dpf/dgl/src/pugl-extra/x11.c View File

@@ -0,0 +1,111 @@
/*
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.

THIS 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.
*/

/**
@file x11.c X11 extra implementation for DPF.
*/

// NOTE can't import this file twice!
#ifndef PUGL_DETAIL_X11_H_INCLUDED
#include "../pugl-upstream/src/x11.h"
#endif

#include "../pugl-upstream/src/implementation.h"

#include <sys/types.h>
#include <unistd.h>

void
puglRaiseWindow(PuglView* view)
{
XRaiseWindow(view->impl->display, view->impl->win);
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
view->frame.width = width;
view->frame.height = height;

if (view->impl->win) {
#if 0
if (! fResizable)
{
XSizeHints sizeHints;
memset(&sizeHints, 0, sizeof(sizeHints));

sizeHints.flags = PSize|PMinSize|PMaxSize;
sizeHints.width = static_cast<int>(width);
sizeHints.height = static_cast<int>(height);
sizeHints.min_width = static_cast<int>(width);
sizeHints.min_height = static_cast<int>(height);
sizeHints.max_width = static_cast<int>(width);
sizeHints.max_height = static_cast<int>(height);

XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
}
#endif

XResizeWindow(view->world->impl->display, view->impl->win, width, height);
}
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio
Display* display = view->world->impl->display;

view->minWidth = width;
view->minHeight = height;

if (aspect) {
view->minAspectX = width;
view->minAspectY = height;
view->maxAspectX = width;
view->maxAspectY = height;
}

#if 0
if (view->impl->win) {
XSizeHints sizeHints = getSizeHints(view);
XSetNormalHints(display, view->impl->win, &sizeHints);
// NOTE old code used this instead
// XSetWMNormalHints(display, view->impl->win, &sizeHints);
}
#endif
}

void
puglExtraSetWindowTypeAndPID(PuglView* view)
{
PuglInternals* const impl = view->impl;

const pid_t pid = getpid();
const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False);
XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);

const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False);

// Setting the window to both dialog and normal will produce a decorated floating dialog
// Order is important: DIALOG needs to come before NORMAL
const Atom _wts[2] = {
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False),
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False)
};

XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
}

+ 28
- 0
dpf/dgl/src/pugl-upstream/.clang-format View File

@@ -0,0 +1,28 @@
---
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: true
BasedOnStyle: Mozilla
BraceWrapping:
AfterNamespace: false
AfterClass: true
AfterEnum: false
AfterExternBlock: false
AfterFunction: true
AfterStruct: false
SplitEmptyFunction: false
SplitEmptyRecord: false
BreakBeforeBraces: Custom
Cpp11BracedListStyle: true
IndentCaseLabels: false
IndentPPDirectives: AfterHash
KeepEmptyLinesAtTheStartOfBlocks: false
SpacesInContainerLiterals: false
StatementMacros:
- PUGL_API
- PUGL_CONST_API
- PUGL_CONST_FUNC
- PUGL_DEPRECATED_BY
- PUGL_UNUSED
- _Pragma
...

+ 4
- 0
dpf/dgl/src/pugl-upstream/.clang-tidy View File

@@ -0,0 +1,4 @@
Checks: >
*,
FormatStyle: file
WarningsAsErrors: '*'

+ 13
- 0
dpf/dgl/src/pugl-upstream/.clant.json View File

@@ -0,0 +1,13 @@
{
"version": "1.0.0",
"include_dirs": [
"bindings/cpp/include",
"include"
],
"exclude_patterns": [
"glad\\.c"
],
"mapping_files": [
".includes.imp"
]
}

+ 19
- 0
dpf/dgl/src/pugl-upstream/.editorconfig View File

@@ -0,0 +1,19 @@
root = true

[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true

[*.{c,h,m,cpp,hpp,mm,glsl,frag,vert}]
indent_style = space
indent_size = 2

[*.py]
indent_style = space
indent_size = 4

[doc/**.{html,xml,css}]
indent_style = space
indent_size = 2

+ 3
- 0
dpf/dgl/src/pugl-upstream/.gitignore View File

@@ -0,0 +1,3 @@
build/
__pycache__
*.pyc

+ 153
- 0
dpf/dgl/src/pugl-upstream/.gitlab-ci.yml View File

@@ -0,0 +1,153 @@
stages:
- build
- deploy

.build_template: &build_definition
stage: build

arm32_dbg:
<<: *build_definition
image: lv2plugin/debian-arm32
script:
- meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

arm32_rel:
<<: *build_definition
image: lv2plugin/debian-arm32
script:
- meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


arm64_dbg:
<<: *build_definition
image: lv2plugin/debian-arm64
script:
- meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

arm64_rel:
<<: *build_definition
image: lv2plugin/debian-arm64
script:
- meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


x64_dbg:
<<: *build_definition
image: lv2plugin/debian-x64
script:
- meson setup build -Dbuildtype=debug -Ddocs=enabled -Dstrict=true -Dwerror=true
- ninja -C build
artifacts:
paths:
- build/doc

x64_rel:
<<: *build_definition
image: lv2plugin/debian-x64
script:
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


x64_static:
<<: *build_definition
image: lv2plugin/debian-x64
script:
- meson setup build -Ddefault_library=static -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


x64_sanitize:
<<: *build_definition
image: lv2plugin/debian-x64-clang
script:
- meson setup build -Db_lundef=false -Dbuildtype=plain -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build
variables:
CC: "clang"
CXX: "clang++"
CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability"
CXXFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability"
LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability"


mingw32_dbg:
<<: *build_definition
image: lv2plugin/debian-mingw32
script:
- meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

mingw32_rel:
<<: *build_definition
image: lv2plugin/debian-mingw32
script:
- meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


mingw64_dbg:
<<: *build_definition
image: lv2plugin/debian-mingw64
script:
- meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

mingw64_rel:
<<: *build_definition
image: lv2plugin/debian-mingw64
script:
- meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


mac_dbg:
<<: *build_definition
tags: [macos]
script:
- meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

mac_rel:
<<: *build_definition
tags: [macos]
script:
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build


win_dbg:
<<: *build_definition
tags: [windows,meson]
script:
- meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

win_rel:
<<: *build_definition
tags: [windows,meson]
script:
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true
- ninja -C build

pages:
stage: deploy
script:
- mkdir public
- mkdir public/c
- mkdir public/cpp
- mv build/doc/c/singlehtml/ public/c/singlehtml/
- mv build/doc/cpp/singlehtml/ public/cpp/singlehtml/
- mv build/doc/c/html/ public/c/html/
- mv build/doc/cpp/html/ public/cpp/html/
dependencies:
- x64_dbg
artifacts:
paths:
- public
only:
- master

+ 15
- 0
dpf/dgl/src/pugl-upstream/.includes.imp View File

@@ -0,0 +1,15 @@
[
{ "include": [ "<ext/alloc_traits.h>", "private", "<string>", "public", ] },
{ "include": [ "<ext/alloc_traits.h>", "private", "<vector>", "public", ] },
{ "symbol": [ "bool", "private", "<stdbool.h>", "public" ] },
{ "symbol": [ "int32_t", "private", "<stdint.h>", "public" ] },
{ "symbol": [ "int64_t", "private", "<stdint.h>", "public" ] },
{ "symbol": [ "timespec", "private", "<time.h>", "public" ] },
{ "symbol": [ "timeval", "private", "<time.h>", "public" ] },
{ "symbol": [ "uint32_t", "private", "<stdint.h>", "public" ] },
{ "symbol": [ "uint64_t", "private", "<stdint.h>", "public" ] },
{ "symbol": [ "uint8_t", "private", "<stdint.h>", "public" ] },
{ "symbol": [ "uintptr_t", "private", "<stdint.h>", "public" ] },

{ "symbol": [ "uintptr_t", "private", "<cstdint>", "public" ] }
]

+ 13
- 0
dpf/dgl/src/pugl-upstream/AUTHORS View File

@@ -0,0 +1,13 @@
Pugl is primarily written and maintained by David Robillard <d@drobilla.net>
with contributions from (in increasing chronological order):

Ben Loftis <ben@harrisonconsoles.com>
Robin Gareus <robin@gareus.org>
Erik Ă…ldstedt Sund <erikalds@gmail.com>
Hanspeter Portner <dev@open-music-kontrollers.ch>
Stefan Westerfeld <stefan@space.twc.de>
Jordan Halase <jordan@halase.me>
Oliver Schmidt <oliver@luced.de>
Zoë Sparks <zoe@milky.flowers>
Jean Pierre Cimalando <jp-dev@inbox.ru>
Thomas Brand <tom@trellis.ch>

+ 13
- 0
dpf/dgl/src/pugl-upstream/COPYING View File

@@ -0,0 +1,13 @@
Copyright 2011-2021 David Robillard <d@drobilla.net>

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.

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

+ 98
- 0
dpf/dgl/src/pugl-upstream/README.md View File

@@ -0,0 +1,98 @@
Pugl
====

Pugl (PlUgin Graphics Library) is a minimal portable API for GUIs which is
suitable for use in plugins. It works on X11, MacOS, and Windows, and
optionally supports Vulkan, OpenGL, and Cairo graphics contexts.

Pugl is vaguely similar to libraries like GLUT and GLFW, but with some
distinguishing features:

* Minimal in scope, providing only a thin interface to isolate
platform-specific details from applications.

* Zero dependencies, aside from standard system libraries.

* Support for embedding in native windows, for example as a plugin or
component within a larger application that is not based on Pugl.

* Simple and extensible event-based API that makes dispatching in application
or toolkit code easy with minimal boilerplate.

* Suitable not only for continuously rendering applications like games, but
also event-driven applications that only draw when necessary.

* Explicit context and no static data whatsoever, so that several instances
can be used within a single program at once.

* Small, liberally licensed Free Software implementation that is suitable for
vendoring and/or static linking. Pugl can be installed as a library, or
used by simply copying the headers into a project.

Stability
---------

Pugl is currently being developed towards a long-term stable API. For the time
being, however, the API may break occasionally. Please report any relevant
feedback, or file feature requests, so that we can ensure that the released API
is stable for as long as possible.

Documentation
-------------

Pugl is a C library that includes C++ bindings.
Each API is documented separately:

* [C Documentation (single page)](https://lv2.gitlab.io/pugl/c/singlehtml/)
* [C Documentation (paginated)](https://lv2.gitlab.io/pugl/c/html/)
* [C++ Documentation (single page)](https://lv2.gitlab.io/pugl/cpp/singlehtml/)
* [C++ Documentation (paginated)](https://lv2.gitlab.io/pugl/cpp/html/)

The documentation will also be built from the source if the `docs`
configuration option is enabled, and both Doxygen and Sphinx are available.

Testing
-------

Some unit tests are included, but unfortunately manual testing is still
required. The tests and example programs are built by default. You can run
all the tests at once via ninja:

meson setup build
cd build
ninja test

The `examples` directory contains several programs that serve as both manual
tests and demonstrations:

* `pugl_embed_demo` shows a view embedded in another, and also tests
requesting attention (which happens after 5 seconds), keyboard focus
(switched by pressing tab), view moving (with the arrow keys), and view
resizing (with the arrow keys while shift is held). This program uses only
very old OpenGL and should work on any system.

* `pugl_window_demo` demonstrates multiple top-level windows.

* `pugl_shader_demo` demonstrates using more modern OpenGL (version 3 or 4)
where dynamic loading and shaders are required. It can also be used to test
performance by passing the number of rectangles to draw on the command line.

* `pugl_cairo_demo` demonstrates using Cairo on top of the native windowing
system (without OpenGL), and partial redrawing.

* `pugl_print_events` is a utility that prints all received events to the
console in a human readable format.

* `pugl_cpp_demo` is a simple cube demo that uses the C++ API.

* `pugl_vulkan_demo` is a simple example of using Vulkan in C that simply
clears the window.

* `pugl_vulkan_cpp_demo` is a more advanced Vulkan demo in C++ that draws many
animated rectangles like `pugl_shader_demo`.

All example programs support several command line options to control various
behaviours, see the output of `--help` for details. Please file an issue if
any of these programs do not work as expected on your system.

-- David Robillard <d@drobilla.net>

+ 14
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/.clang-tidy View File

@@ -0,0 +1,14 @@
Checks: >
*,
-*-uppercase-literal-suffix,
-clang-diagnostic-unused-macros,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-reinterpret-cast,
-google-runtime-references,
-hicpp-named-parameter,
-llvmlibc-*,
-modernize-use-trailing-return-type,
-readability-implicit-bool-conversion,
-readability-named-parameter,
FormatStyle: file
HeaderFilterRegex: 'pugl/.*'

+ 12
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/meson.build View File

@@ -0,0 +1,12 @@
cpp_headers = [
'pugl/pugl.hpp',

'pugl/cairo.hpp',
'pugl/gl.hpp',
'pugl/stub.hpp',
'pugl/vulkan.hpp',
]

cpp_header_files = files(cpp_headers)

install_headers(cpp_headers, subdir: 'puglpp' + version_suffix / 'pugl')

+ 45
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/cairo.hpp View File

@@ -0,0 +1,45 @@
/*
Copyright 2012-2020 David Robillard <d@drobilla.net>

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.

THIS 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 PUGL_CAIRO_HPP
#define PUGL_CAIRO_HPP

#include "pugl/cairo.h"
#include "pugl/pugl.h"

namespace pugl {

/**
@defgroup cairopp Cairo
Cairo graphics support.
@ingroup puglpp
@{
*/

/// @copydoc puglCairoBackend
inline const PuglBackend*
cairoBackend() noexcept
{
return puglCairoBackend();
}

/**
@}
*/

} // namespace pugl

#endif // PUGL_CAIRO_HPP

+ 70
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/gl.hpp View File

@@ -0,0 +1,70 @@
/*
Copyright 2012-2020 David Robillard <d@drobilla.net>

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.

THIS 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 PUGL_GL_HPP
#define PUGL_GL_HPP

#include "pugl/gl.h"
#include "pugl/pugl.h"
#include "pugl/pugl.hpp"

namespace pugl {

/**
@defgroup glpp OpenGL
OpenGL graphics support.
@ingroup puglpp
@{
*/

/// @copydoc PuglGlFunc
using GlFunc = PuglGlFunc;

/// @copydoc puglGetProcAddress
inline GlFunc
getProcAddress(const char* name) noexcept
{
return puglGetProcAddress(name);
}

/// @copydoc puglEnterContext
inline Status
enterContext(View& view) noexcept
{
return static_cast<Status>(puglEnterContext(view.cobj()));
}

/// @copydoc puglLeaveContext
inline Status
leaveContext(View& view) noexcept
{
return static_cast<Status>(puglLeaveContext(view.cobj()));
}

/// @copydoc puglGlBackend
inline const PuglBackend*
glBackend() noexcept
{
return puglGlBackend();
}

/**
@}
*/

} // namespace pugl

#endif // PUGL_GL_HPP

+ 734
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/pugl.hpp View File

@@ -0,0 +1,734 @@
/*
Copyright 2012-2020 David Robillard <d@drobilla.net>

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.

THIS 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 PUGL_PUGL_HPP
#define PUGL_PUGL_HPP

#include "pugl/pugl.h"

#include <cstdint>

#if defined(PUGL_HPP_THROW_FAILED_CONSTRUCTION)
# include <exception>
#elif defined(PUGL_HPP_ASSERT_CONSTRUCTION)
# include <cassert>
#endif

namespace pugl {

/**
@defgroup puglpp Pugl C++ API
Pugl C++ API wrapper.
@{
*/

namespace detail {

/// Free function for a C object
template<typename T>
using FreeFunc = void (*)(T*);

/// Generic C++ wrapper for a C object
template<class T, FreeFunc<T> Free>
class Wrapper
{
public:
Wrapper(const Wrapper&) = delete;
Wrapper& operator=(const Wrapper&) = delete;

Wrapper(Wrapper&& wrapper) noexcept
: _ptr{wrapper._ptr}
{
wrapper._ptr = nullptr;
}

Wrapper& operator=(Wrapper&& wrapper) noexcept
{
_ptr = wrapper._ptr;
wrapper._ptr = nullptr;
return *this;
}

~Wrapper() noexcept { Free(_ptr); }

T* cobj() noexcept { return _ptr; }
const T* cobj() const noexcept { return _ptr; }

protected:
explicit Wrapper(T* ptr) noexcept
: _ptr{ptr}
{}

private:
T* _ptr;
};

} // namespace detail

using Rect = PuglRect; ///< @copydoc PuglRect

/**
@defgroup eventspp Events
@{
*/

/**
A strongly-typed analogue of PuglEvent.

This is bit-for-bit identical to the corresponding PuglEvent.

@tparam t The `type` field of the corresponding PuglEvent.

@tparam Base The specific struct type of the corresponding PuglEvent.
*/
template<PuglEventType t, class Base>
struct Event final : Base {
/// The type of the corresponding C event structure
using BaseEvent = Base;

/// The `type` field of the corresponding C event structure
static constexpr const PuglEventType type = t;

explicit Event(Base base)
: Base{base}
{}

template<class... Args>
explicit Event(const PuglEventFlags f, Args... args)
: Base{t, f, args...}
{}
};

using Mod = PuglMod; ///< @copydoc PuglMod
using Mods = PuglMods; ///< @copydoc PuglMods
using Key = PuglKey; ///< @copydoc PuglKey
using EventType = PuglEventType; ///< @copydoc PuglEventType
using EventFlag = PuglEventFlag; ///< @copydoc PuglEventFlag
using EventFlags = PuglEventFlags; ///< @copydoc PuglEventFlags
using CrossingMode = PuglCrossingMode; ///< @copydoc PuglCrossingMode

/// @copydoc PuglCreateEvent
using CreateEvent = Event<PUGL_CREATE, PuglCreateEvent>;

/// @copydoc PuglDestroyEvent
using DestroyEvent = Event<PUGL_DESTROY, PuglDestroyEvent>;

/// @copydoc PuglConfigureEvent
using ConfigureEvent = Event<PUGL_CONFIGURE, PuglConfigureEvent>;

/// @copydoc PuglMapEvent
using MapEvent = Event<PUGL_MAP, PuglMapEvent>;

/// @copydoc PuglUnmapEvent
using UnmapEvent = Event<PUGL_UNMAP, PuglUnmapEvent>;

/// @copydoc PuglUpdateEvent
using UpdateEvent = Event<PUGL_UPDATE, PuglUpdateEvent>;

/// @copydoc PuglExposeEvent
using ExposeEvent = Event<PUGL_EXPOSE, PuglExposeEvent>;

/// @copydoc PuglCloseEvent
using CloseEvent = Event<PUGL_CLOSE, PuglCloseEvent>;

/// @copydoc PuglFocusEvent
using FocusInEvent = Event<PUGL_FOCUS_IN, PuglFocusEvent>;

/// @copydoc PuglFocusEvent
using FocusOutEvent = Event<PUGL_FOCUS_OUT, PuglFocusEvent>;

/// @copydoc PuglKeyEvent
using KeyPressEvent = Event<PUGL_KEY_PRESS, PuglKeyEvent>;

/// @copydoc PuglKeyEvent
using KeyReleaseEvent = Event<PUGL_KEY_RELEASE, PuglKeyEvent>;

/// @copydoc PuglTextEvent
using TextEvent = Event<PUGL_TEXT, PuglTextEvent>;

/// @copydoc PuglCrossingEvent
using PointerInEvent = Event<PUGL_POINTER_IN, PuglCrossingEvent>;

/// @copydoc PuglCrossingEvent
using PointerOutEvent = Event<PUGL_POINTER_OUT, PuglCrossingEvent>;

/// @copydoc PuglButtonEvent
using ButtonPressEvent = Event<PUGL_BUTTON_PRESS, PuglButtonEvent>;

/// @copydoc PuglButtonEvent
using ButtonReleaseEvent = Event<PUGL_BUTTON_RELEASE, PuglButtonEvent>;

/// @copydoc PuglMotionEvent
using MotionEvent = Event<PUGL_MOTION, PuglMotionEvent>;

/// @copydoc PuglScrollEvent
using ScrollEvent = Event<PUGL_SCROLL, PuglScrollEvent>;

/// @copydoc PuglClientEvent
using ClientEvent = Event<PUGL_CLIENT, PuglClientEvent>;

/// @copydoc PuglTimerEvent
using TimerEvent = Event<PUGL_TIMER, PuglTimerEvent>;

/// @copydoc PuglLoopEnterEvent
using LoopEnterEvent = Event<PUGL_LOOP_ENTER, PuglLoopEnterEvent>;

/// @copydoc PuglLoopLeaveEvent
using LoopLeaveEvent = Event<PUGL_LOOP_LEAVE, PuglLoopLeaveEvent>;

/**
@}
@defgroup statuspp Status
@{
*/

/// @copydoc PuglStatus
enum class Status {
success, ///< @copydoc PUGL_SUCCESS
failure, ///< @copydoc PUGL_FAILURE
unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR
badBackend, ///< @copydoc PUGL_BAD_BACKEND
badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION
badParameter, ///< @copydoc PUGL_BAD_PARAMETER
backendFailed, ///< @copydoc PUGL_BACKEND_FAILED
registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED
realizeFailed, ///< @copydoc PUGL_REALIZE_FAILED
setFormatFailed, ///< @copydoc PUGL_SET_FORMAT_FAILED
createContextFailed, ///< @copydoc PUGL_CREATE_CONTEXT_FAILED
unsupportedType, ///< @copydoc PUGL_UNSUPPORTED_TYPE
};

static_assert(Status(PUGL_UNSUPPORTED_TYPE) == Status::unsupportedType, "");

/// @copydoc puglStrerror
inline const char*
strerror(const Status status) noexcept
{
return puglStrerror(static_cast<PuglStatus>(status));
}

/**
@}
@defgroup worldpp World
@{
*/

/// @copydoc PuglWorldType
enum class WorldType {
program, ///< @copydoc PUGL_PROGRAM
module, ///< @copydoc PUGL_MODULE
};

static_assert(WorldType(PUGL_MODULE) == WorldType::module, "");

/// @copydoc PuglWorldFlag
enum class WorldFlag {
threads = PUGL_WORLD_THREADS, ///< @copydoc PUGL_WORLD_THREADS
};

static_assert(WorldFlag(PUGL_WORLD_THREADS) == WorldFlag::threads, "");

using WorldFlags = PuglWorldFlags; ///< @copydoc PuglWorldFlags

#if defined(PUGL_HPP_THROW_FAILED_CONSTRUCTION)

/// An exception thrown when construction fails
class FailedConstructionError : public std::exception
{
public:
FailedConstructionError(const char* const msg) noexcept
: _msg{msg}
{}

virtual const char* what() const noexcept override;

private:
const char* _msg;
};

# define PUGL_CHECK_CONSTRUCTION(cond, msg) \
do { \
if (!(cond)) { \
throw FailedConstructionError(msg); \
} \
} while (0)

#elif defined(PUGL_HPP_ASSERT_CONSTRUCTION)
# define PUGL_CHECK_CONSTRUCTION(cond, msg) assert(cond);
#else
/**
Configurable macro for handling construction failure.

If `PUGL_HPP_THROW_FAILED_CONSTRUCTION` is defined, then this throws a
`pugl::FailedConstructionError` if construction fails.

If `PUGL_HPP_ASSERT_CONSTRUCTION` is defined, then this asserts if
construction fails.

Otherwise, this does nothing.
*/
# define PUGL_CHECK_CONSTRUCTION(cond, msg)
#endif

/// @copydoc PuglWorld
class World : public detail::Wrapper<PuglWorld, puglFreeWorld>
{
public:
World(const World&) = delete;
World& operator=(const World&) = delete;

World(World&&) = delete;
World& operator=(World&&) = delete;

~World() = default;

World(WorldType type, WorldFlag flag)
: Wrapper{puglNewWorld(static_cast<PuglWorldType>(type),
static_cast<PuglWorldFlags>(flag))}
{
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::World");
}

World(WorldType type, WorldFlags flags)
: Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)}
{
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::World");
}

explicit World(WorldType type)
: World{type, WorldFlags{}}
{}

/// @copydoc puglGetNativeWorld
void* nativeWorld() noexcept { return puglGetNativeWorld(cobj()); }

/// @copydoc puglSetClassName
Status setClassName(const char* const name) noexcept
{
return static_cast<Status>(puglSetClassName(cobj(), name));
}

/// @copydoc puglGetTime
double time() const noexcept { return puglGetTime(cobj()); }

/// @copydoc puglUpdate
Status update(const double timeout) noexcept
{
return static_cast<Status>(puglUpdate(cobj(), timeout));
}
};

/**
@}
@defgroup viewpp View
@{
*/

using Backend = PuglBackend; ///< @copydoc PuglBackend
using NativeView = PuglNativeView; ///< @copydoc PuglNativeView

/// @copydoc PuglViewHint
enum class ViewHint {
useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE
useDebugContext, ///< @copydoc PUGL_USE_DEBUG_CONTEXT
contextVersionMajor, ///< @copydoc PUGL_CONTEXT_VERSION_MAJOR
contextVersionMinor, ///< @copydoc PUGL_CONTEXT_VERSION_MINOR
redBits, ///< @copydoc PUGL_RED_BITS
greenBits, ///< @copydoc PUGL_GREEN_BITS
blueBits, ///< @copydoc PUGL_BLUE_BITS
alphaBits, ///< @copydoc PUGL_ALPHA_BITS
depthBits, ///< @copydoc PUGL_DEPTH_BITS
stencilBits, ///< @copydoc PUGL_STENCIL_BITS
samples, ///< @copydoc PUGL_SAMPLES
doubleBuffer, ///< @copydoc PUGL_DOUBLE_BUFFER
swapInterval, ///< @copydoc PUGL_SWAP_INTERVAL
resizable, ///< @copydoc PUGL_RESIZABLE
ignoreKeyRepeat, ///< @copydoc PUGL_IGNORE_KEY_REPEAT
refreshRate, ///< @copydoc PUGL_REFRESH_RATE
};

static_assert(ViewHint(PUGL_REFRESH_RATE) == ViewHint::refreshRate, "");

using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue

/// @copydoc PuglCursor
enum class Cursor {
arrow, ///< @copydoc PUGL_CURSOR_ARROW
caret, ///< @copydoc PUGL_CURSOR_CARET
crosshair, ///< @copydoc PUGL_CURSOR_CROSSHAIR
hand, ///< @copydoc PUGL_CURSOR_HAND
no, ///< @copydoc PUGL_CURSOR_NO
leftRight, ///< @copydoc PUGL_CURSOR_LEFT_RIGHT
upDown, ///< @copydoc PUGL_CURSOR_UP_DOWN
};

static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, "");

/// @copydoc PuglView
class View : protected detail::Wrapper<PuglView, puglFreeView>
{
public:
/**
@name Setup
Methods for creating and destroying a view.
@{
*/

explicit View(World& world)
: Wrapper{puglNewView(world.cobj())}
, _world(world)
{
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::View");
}

const World& world() const noexcept { return _world; }
World& world() noexcept { return _world; }

/**
Set the object that will be called to handle events.

This is a type-safe wrapper for the C functions puglSetHandle() and
puglSetEventFunc() that will automatically dispatch events to the
`onEvent` method of `handler` that takes the appropriate event type.
The handler must have such a method defined for every event type, but if
the handler is the view itself, a `using` declaration can be used to
"inherit" the default implementation to avoid having to define every
method. For example:

@code
class MyView : public pugl::View
{
public:
explicit MyView(pugl::World& world)
: pugl::View{world}
{
setEventHandler(*this);
}

using pugl::View::onEvent;

pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept;
pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept;
};
@endcode

This facility is just a convenience, applications may use the C API
directly to set a handle and event function to set up a different
approach for event handling.
*/
template<class Handler>
Status setEventHandler(Handler& handler)
{
puglSetHandle(cobj(), &handler);
return static_cast<Status>(puglSetEventFunc(cobj(), eventFunc<Handler>));
}

/// @copydoc puglSetBackend
Status setBackend(const PuglBackend* backend) noexcept
{
return static_cast<Status>(puglSetBackend(cobj(), backend));
}

/// @copydoc puglSetViewHint
Status setHint(ViewHint hint, int value) noexcept
{
return static_cast<Status>(
puglSetViewHint(cobj(), static_cast<PuglViewHint>(hint), value));
}

/// @copydoc puglGetViewHint
int getHint(ViewHint hint) noexcept
{
return puglGetViewHint(cobj(), static_cast<PuglViewHint>(hint));
}

/**
@}
@name Frame
Methods for working with the position and size of a view.
@{
*/

/// @copydoc puglGetFrame
Rect frame() const noexcept { return puglGetFrame(cobj()); }

/// @copydoc puglSetFrame
Status setFrame(Rect frame) noexcept
{
return static_cast<Status>(puglSetFrame(cobj(), frame));
}

/// @copydoc puglSetDefaultSize
Status setDefaultSize(int width, int height) noexcept
{
return static_cast<Status>(puglSetDefaultSize(cobj(), width, height));
}

/// @copydoc puglSetMinSize
Status setMinSize(int width, int height) noexcept
{
return static_cast<Status>(puglSetMinSize(cobj(), width, height));
}

/// @copydoc puglSetMaxSize
Status setMaxSize(int width, int height) noexcept
{
return static_cast<Status>(puglSetMaxSize(cobj(), width, height));
}

/// @copydoc puglSetAspectRatio
Status setAspectRatio(int minX, int minY, int maxX, int maxY) noexcept
{
return static_cast<Status>(
puglSetAspectRatio(cobj(), minX, minY, maxX, maxY));
}

/**
@}
@name Windows
Methods for working with top-level windows.
@{
*/

/// @copydoc puglSetWindowTitle
Status setWindowTitle(const char* title) noexcept
{
return static_cast<Status>(puglSetWindowTitle(cobj(), title));
}

/// @copydoc puglSetParentWindow
Status setParentWindow(NativeView parent) noexcept
{
return static_cast<Status>(puglSetParentWindow(cobj(), parent));
}

/// @copydoc puglSetTransientFor
Status setTransientFor(NativeView parent) noexcept
{
return static_cast<Status>(puglSetTransientFor(cobj(), parent));
}

/// @copydoc puglRealize
Status realize() noexcept { return static_cast<Status>(puglRealize(cobj())); }

/// @copydoc puglShow
Status show() noexcept { return static_cast<Status>(puglShow(cobj())); }

/// @copydoc puglHide
Status hide() noexcept { return static_cast<Status>(puglHide(cobj())); }

/// @copydoc puglGetVisible
bool visible() const noexcept { return puglGetVisible(cobj()); }

/// @copydoc puglGetNativeWindow
NativeView nativeWindow() noexcept { return puglGetNativeWindow(cobj()); }

/**
@}
@name Graphics
Methods for working with the graphics context and scheduling
redisplays.
@{
*/

/// @copydoc puglGetContext
void* context() noexcept { return puglGetContext(cobj()); }

/// @copydoc puglPostRedisplay
Status postRedisplay() noexcept
{
return static_cast<Status>(puglPostRedisplay(cobj()));
}

/// @copydoc puglPostRedisplayRect
Status postRedisplayRect(const Rect rect) noexcept
{
return static_cast<Status>(puglPostRedisplayRect(cobj(), rect));
}

/**
@}
@name Interaction
Methods for interacting with the user and window system.
@{
*/

/// @copydoc puglGrabFocus
Status grabFocus() noexcept
{
return static_cast<Status>(puglGrabFocus(cobj()));
}

/// @copydoc puglHasFocus
bool hasFocus() const noexcept { return puglHasFocus(cobj()); }

/// @copydoc puglSetCursor
Status setCursor(const Cursor cursor) noexcept
{
return static_cast<Status>(
puglSetCursor(cobj(), static_cast<PuglCursor>(cursor)));
}

/// @copydoc puglRequestAttention
Status requestAttention() noexcept
{
return static_cast<Status>(puglRequestAttention(cobj()));
}

/**
Activate a repeating timer event.

This starts a timer which will send a timer event to `view` every
`timeout` seconds. This can be used to perform some action in a view at a
regular interval with relatively low frequency. Note that the frequency
of timer events may be limited by how often update() is called.

If the given timer already exists, it is replaced.

@param id The identifier for this timer. This is an application-specific
ID that should be a low number, typically the value of a constant or `enum`
that starts from 0. There is a platform-specific limit to the number of
supported timers, and overhead associated with each, so applications should
create only a few timers and perform several tasks in one if necessary.

@param timeout The period, in seconds, of this timer. This is not
guaranteed to have a resolution better than 10ms (the maximum timer
resolution on Windows) and may be rounded up if it is too short. On X11
and MacOS, a resolution of about 1ms can usually be relied on.

@return #PUGL_FAILURE if timers are not supported by the system,
#PUGL_UNKNOWN_ERROR if setting the timer failed.
*/
Status startTimer(const uintptr_t id, const double timeout) noexcept
{
return static_cast<Status>(puglStartTimer(cobj(), id, timeout));
}

/**
Stop an active timer.

@param id The ID previously passed to startTimer().

@return #PUGL_FAILURE if timers are not supported by this system,
#PUGL_UNKNOWN_ERROR if stopping the timer failed.
*/
Status stopTimer(const uintptr_t id) noexcept
{
return static_cast<Status>(puglStopTimer(cobj(), id));
}

template<PuglEventType t, class Base>
Status sendEvent(const Event<t, Base>& event) noexcept
{
PuglEvent cEvent{{t, 0}};

*reinterpret_cast<Base*>(&cEvent) = event;

return static_cast<Status>(puglSendEvent(cobj(), &cEvent));
}

/**
@}
*/

PuglView* cobj() noexcept { return Wrapper::cobj(); }
const PuglView* cobj() const noexcept { return Wrapper::cobj(); }

private:
template<class Target>
static Status dispatch(Target& target, const PuglEvent* event)
{
switch (event->type) {
case PUGL_NOTHING:
return Status::success;
case PUGL_CREATE:
return target.onEvent(CreateEvent{event->any});
case PUGL_DESTROY:
return target.onEvent(DestroyEvent{event->any});
case PUGL_CONFIGURE:
return target.onEvent(ConfigureEvent{event->configure});
case PUGL_MAP:
return target.onEvent(MapEvent{event->any});
case PUGL_UNMAP:
return target.onEvent(UnmapEvent{event->any});
case PUGL_UPDATE:
return target.onEvent(UpdateEvent{event->any});
case PUGL_EXPOSE:
return target.onEvent(ExposeEvent{event->expose});
case PUGL_CLOSE:
return target.onEvent(CloseEvent{event->any});
case PUGL_FOCUS_IN:
return target.onEvent(FocusInEvent{event->focus});
case PUGL_FOCUS_OUT:
return target.onEvent(FocusOutEvent{event->focus});
case PUGL_KEY_PRESS:
return target.onEvent(KeyPressEvent{event->key});
case PUGL_KEY_RELEASE:
return target.onEvent(KeyReleaseEvent{event->key});
case PUGL_TEXT:
return target.onEvent(TextEvent{event->text});
case PUGL_POINTER_IN:
return target.onEvent(PointerInEvent{event->crossing});
case PUGL_POINTER_OUT:
return target.onEvent(PointerOutEvent{event->crossing});
case PUGL_BUTTON_PRESS:
return target.onEvent(ButtonPressEvent{event->button});
case PUGL_BUTTON_RELEASE:
return target.onEvent(ButtonReleaseEvent{event->button});
case PUGL_MOTION:
return target.onEvent(MotionEvent{event->motion});
case PUGL_SCROLL:
return target.onEvent(ScrollEvent{event->scroll});
case PUGL_CLIENT:
return target.onEvent(ClientEvent{event->client});
case PUGL_TIMER:
return target.onEvent(TimerEvent{event->timer});
case PUGL_LOOP_ENTER:
return target.onEvent(LoopEnterEvent{event->any});
case PUGL_LOOP_LEAVE:
return target.onEvent(LoopLeaveEvent{event->any});
}

return Status::failure;
}

template<class Target>
static PuglStatus eventFunc(PuglView* view, const PuglEvent* event) noexcept
{
auto* target = static_cast<Target*>(puglGetHandle(view));

#ifdef __cpp_exceptions
try {
return static_cast<PuglStatus>(dispatch(*target, event));
} catch (...) {
return PUGL_UNKNOWN_ERROR;
}
#else
return static_cast<PuglStatus>(pugl::dispatch(*target, event));
#endif
}

World& _world;
};

/**
@}
@}
*/

} // namespace pugl

#endif // PUGL_PUGL_HPP

+ 45
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/stub.hpp View File

@@ -0,0 +1,45 @@
/*
Copyright 2012-2020 David Robillard <d@drobilla.net>

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.

THIS 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 PUGL_STUB_HPP
#define PUGL_STUB_HPP

#include "pugl/pugl.h"
#include "pugl/stub.h"

namespace pugl {

/**
@defgroup stubpp Stub
Stub graphics support.
@ingroup puglpp
@{
*/

/// @copydoc puglStubBackend
inline const PuglBackend*
stubBackend() noexcept
{
return puglStubBackend();
}

/**
@}
*/

} // namespace pugl

#endif // PUGL_STUB_HPP

+ 168
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/include/pugl/vulkan.hpp View File

@@ -0,0 +1,168 @@
/*
Copyright 2012-2020 David Robillard <d@drobilla.net>

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.

THIS 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.
*/

/*
Note that this header includes Vulkan headers, so if you are writing a
program or plugin that dynamically loads vulkan, you should first define
`VK_NO_PROTOTYPES` before including it.
*/

#ifndef PUGL_VULKAN_HPP
#define PUGL_VULKAN_HPP

#include "pugl/pugl.h"
#include "pugl/pugl.hpp"
#include "pugl/vulkan.h"

#include <vulkan/vulkan_core.h>

#include <cstdint>

namespace pugl {

/**
@defgroup vulkanpp Vulkan
Vulkan graphics support.

Note that the Pugl C++ wrapper does not use vulkan-hpp because it is a
heavyweight dependency which not everyone uses, and its design is not very
friendly to dynamic loading in plugins anyway. However, if you do use
vulkan-hpp smart handles, it is relatively straightforward to wrap the
result of createSurface() manually.

@ingroup puglpp
@{
*/

/// @copydoc PuglVulkanLoader
class VulkanLoader final
: public detail::Wrapper<PuglVulkanLoader, puglFreeVulkanLoader>
{
public:
/**
Create a new dynamic loader for Vulkan functions.

This dynamically loads the Vulkan library and gets the load functions
from it.

Note that this constructor does not throw exceptions, though failure is
possible. To check if the Vulkan library failed to load, test this
loader, which is explicitly convertible to `bool`. It is safe to use a
failed loader, but the accessors will always return null.
*/
explicit VulkanLoader(World& world) noexcept
: Wrapper{puglNewVulkanLoader(world.cobj())}
{}

/**
Return the `vkGetInstanceProcAddr` function.

@return Null if the Vulkan library failed to load, or does not contain
this function (which is unlikely and indicates a broken system).
*/
PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc() const noexcept
{
return cobj() ? puglGetInstanceProcAddrFunc(cobj()) : nullptr;
}

/**
Return the `vkGetDeviceProcAddr` function.

@return Null if the Vulkan library failed to load, or does not contain
this function (which is unlikely and indicates a broken system).
*/
PFN_vkGetDeviceProcAddr getDeviceProcAddrFunc() const noexcept
{
return cobj() ? puglGetDeviceProcAddrFunc(cobj()) : nullptr;
}

/// Return true if this loader is valid to use
explicit operator bool() const noexcept { return cobj(); }
};

/**
A simple wrapper for an array of static C strings.

This provides a minimal API that supports iteration, like `std::vector`, but
avoids allocation, exceptions, and a dependency on the C++ standard library.
*/
class StaticStringArray final
{
public:
using value_type = const char*;
using const_iterator = const char* const*;
using size_type = uint32_t;

StaticStringArray(const char* const* strings, const uint32_t size) noexcept
: _strings{strings}
, _size{size}
{}

const char* const* begin() const noexcept { return _strings; }
const char* const* end() const noexcept { return _strings + _size; }
const char* const* data() const noexcept { return _strings; }
uint32_t size() const noexcept { return _size; }

private:
const char* const* _strings;
uint32_t _size;
};

/**
Return the Vulkan instance extensions required to draw to a PuglView.

If successful, the returned array always contains "VK_KHR_surface", along
with whatever other platform-specific extensions are required.

@return An array of extension name strings.
*/
inline StaticStringArray
getInstanceExtensions() noexcept
{
uint32_t count = 0;
const char* const* const extensions = puglGetInstanceExtensions(&count);

return StaticStringArray{extensions, count};
}

/// @copydoc puglCreateSurface
inline VkResult
createSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr,
View& view,
VkInstance instance,
const VkAllocationCallbacks* const allocator,
VkSurfaceKHR* const surface) noexcept
{
const VkResult r = puglCreateSurface(
vkGetInstanceProcAddr, view.cobj(), instance, allocator, surface);

return (!r && !surface) ? VK_ERROR_INITIALIZATION_FAILED : r;
}

/// @copydoc puglVulkanBackend
inline const PuglBackend*
vulkanBackend() noexcept
{
return puglVulkanBackend();
}

/**
@}
*/

} // namespace pugl

#endif // PUGL_VULKAN_HPP

+ 7
- 0
dpf/dgl/src/pugl-upstream/bindings/cpp/meson.build View File

@@ -0,0 +1,7 @@
subdir('include')

pkg.generate(name: 'Pugl++',
filebase: 'puglpp-@0@'.format(major_version),
subdirs: ['puglpp-@0@'.format(major_version)],
version: meson.project_version(),
description: 'Pugl GUI library C++ bindings')

+ 2
- 0
dpf/dgl/src/pugl-upstream/doc/_static/meson.build View File

@@ -0,0 +1,2 @@
configure_file(copy: true, input: '../../resources/pugl.svg', output: 'pugl.svg')


+ 32
- 0
dpf/dgl/src/pugl-upstream/doc/c/Doxyfile.in View File

@@ -0,0 +1,32 @@
PROJECT_NAME = Pugl
PROJECT_BRIEF = "A minimal portable API for embeddable GUIs"

QUIET = YES
WARN_AS_ERROR = YES
WARN_IF_UNDOCUMENTED = NO
WARN_NO_PARAMDOC = NO

JAVADOC_AUTOBRIEF = YES

FULL_PATH_NAMES = NO
CASE_SENSE_NAMES = YES
HIDE_IN_BODY_DOCS = YES
REFERENCES_LINK_SOURCE = NO

GENERATE_HTML = NO
GENERATE_LATEX = NO
GENERATE_XML = YES
XML_PROGRAMLISTING = NO
SHOW_FILES = NO

MACRO_EXPANSION = YES
PREDEFINED = PUGL_API \
PUGL_DISABLE_DEPRECATED \
PUGL_CONST_API= \
PUGL_CONST_FUNC=

RECURSIVE = YES
STRIP_FROM_PATH = @PUGL_SRCDIR@
INPUT = @PUGL_SRCDIR@/include

OUTPUT_DIRECTORY = @DOX_OUTPUT@

+ 5
- 0
dpf/dgl/src/pugl-upstream/doc/c/api/meson.build View File

@@ -0,0 +1,5 @@
c_pugl_rst = custom_target(
'C API ReST Documentation',
command: [dox_to_sphinx, '-f', '@INPUT0@', 'doc/c/api'],
input: [c_index_xml] + c_rst_files,
output: 'pugl.rst')

+ 101
- 0
dpf/dgl/src/pugl-upstream/doc/c/event-loop.rst View File

@@ -0,0 +1,101 @@
.. default-domain:: c
.. highlight:: c

######################
Driving the Event Loop
######################

Pugl does not contain any threads or other event loop "magic".
For flexibility, the event loop is driven explicitly by repeatedly calling :func:`puglUpdate`,
which processes events from the window system and dispatches them to views when necessary.

The exact use of :func:`puglUpdate` depends on the application.
Plugins should call it with a ``timeout`` of 0 in a callback driven by the host.
This avoids blocking the main loop,
since other plugins and the host itself need to run as well.

A program can use whatever timeout is appropriate:
event-driven applications may wait forever by using a ``timeout`` of -1,
while those that draw continuously may use a significant fraction of the frame period
(with enough time left over to render).

*********
Redrawing
*********

Occasional redrawing can be requested by calling :func:`puglPostRedisplay` or :func:`puglPostRedisplayRect`.
After these are called,
a :struct:`PuglExposeEvent` will be dispatched on the next call to :func:`puglUpdate`.

For continuous redrawing,
call :func:`puglPostRedisplay` while handling a :struct:`PuglUpdateEvent` event.
This event is sent just before views are redrawn,
so it can be used as a hook to expand the update region right before the view is exposed.
Anything else that needs to be done every frame can be handled similarly.

*****************
Event Dispatching
*****************

Ideally, pending events are dispatched during a call to :func:`puglUpdate`,
directly within the scope of that call.

Unfortunately, this is not universally true due to differences between platforms.

MacOS
=====

On MacOS, drawing is handled specially and not by the normal event queue mechanism.
This means that configure and expose events,
and possibly others,
may be dispatched to a view outside the scope of a :func:`puglUpdate` call.
In general, you can not rely on coherent event dispatching semantics on MacOS:
the operating system can call into application code at "random" times,
and these calls may result in Pugl events being dispatched.

An application that follows the Pugl guidelines should work fine,
but there is one significant inconsistency you may encounter on MacOS:
posting a redisplay will not wake up a blocked :func:`puglUpdate` call.

Windows
=======

On Windows, the application has relatively tight control over the event loop,
so events are typically dispatched explicitly by :func:`puglUpdate`.
Drawing is handled by events,
so posting a redisplay will wake up a blocked :func:`puglUpdate` call.

However, it is possible for the system to dispatch events at other times.
So,
it is possible for events to be dispatched outside the scope of a :func:`puglUpdate` call,
but this does not happen in normal circumstances and can largely be ignored.

X11
===

On X11, the application strictly controls event dispatching,
and there is no way for the system to call into application code at surprising times.
So, all events are dispatched in the scope of a :func:`puglUpdate` call.

*********************
Recursive Event Loops
*********************

On Windows and MacOS,
the event loop is stalled while the user is resizing the window or,
on Windows,
has displayed the window menu.
This means that :func:`puglUpdate` will block until the resize is finished,
or the menu is closed.

Pugl dispatches :struct:`PuglLoopEnterEvent` and :struct:`PuglLoopLeaveEvent` events to notify the application of this situation.
If you want to continuously redraw during resizing on these platforms,
you can schedule a timer with :func:`puglStartTimer` when the recursive loop is entered,
and post redisplays when handling the :struct:`PuglTimerEvent`.
Be sure to remove the timer with :func:`puglStopTimer` when the recursive loop is finished.

On X11, there are no recursive event loops,
and everything works as usual while the user is resizing the window.
There is nothing special about a "live resize" on X11,
and the above loop events will never be dispatched.


+ 84
- 0
dpf/dgl/src/pugl-upstream/doc/c/events.rst View File

@@ -0,0 +1,84 @@
.. default-domain:: c
.. highlight:: c

***************
Handling Events
***************

Events are sent to a view when it has received user input,
must be drawn, or in other situations that may need to be handled such as resizing.

Events are sent to the event handler as a :union:`PuglEvent` union.
The ``type`` field defines the type of the event and which field of the union is active.
The application must handle at least :enumerator:`PUGL_CONFIGURE <PuglEventType.PUGL_CONFIGURE>`
and :enumerator:`PUGL_EXPOSE <PuglEventType.PUGL_EXPOSE>` to draw anything,
but there are many other :enum:`event types <PuglEventType>`.

For example, a basic event handler might look something like this:

.. code-block:: c

static PuglStatus
onEvent(PuglView* view, const PuglEvent* event)
{
MyApp* app = (MyApp*)puglGetHandle(view);

switch (event->type) {
case PUGL_CREATE:
return setupGraphics(app);
case PUGL_DESTROY:
return teardownGraphics(app);
case PUGL_CONFIGURE:
return resize(app, event->configure.width, event->configure.height);
case PUGL_EXPOSE:
return draw(app, view);
case PUGL_CLOSE:
return quit(app);
case PUGL_BUTTON_PRESS:
return onButtonPress(app, view, event->button);
default:
break;
}

return PUGL_SUCCESS;
}

Using the Graphics Context
==========================

Drawing
-------

Note that Pugl uses a different drawing model than many libraries,
particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_.

In that style of code, drawing is performed imperatively in the main loop,
but with Pugl, the application must draw only while handling an expose event.
This is because Pugl supports event-driven applications that only draw the damaged region when necessary,
and handles exposure internally to provide optimized and consistent behavior across platforms.

Cairo Context
-------------

A Cairo context is created for each :struct:`PuglExposeEvent`,
and only exists during the handling of that event.
Null is returned by :func:`puglGetContext` at any other time.

OpenGL Context
--------------

The OpenGL context is only active during the handling of these events:

- :struct:`PuglCreateEvent`
- :struct:`PuglDestroyEvent`
- :struct:`PuglConfigureEvent`
- :struct:`PuglExposeEvent`

As always, drawing is only possible during an expose.

Vulkan Context
--------------

With Vulkan, the graphics context is managed by the application rather than Pugl.
However, drawing must still only be performed during an expose.


+ 11
- 0
dpf/dgl/src/pugl-upstream/doc/c/index.rst View File

@@ -0,0 +1,11 @@
####
Pugl
####

.. include:: summary.rst

.. toctree::

deployment
overview
api/pugl

+ 48
- 0
dpf/dgl/src/pugl-upstream/doc/c/meson.build View File

@@ -0,0 +1,48 @@
config = configuration_data()
config.set('PUGL_VERSION', meson.project_version())

conf_py = configure_file(configuration: config,
input: '../conf.py.in',
output: 'conf.py')

configure_file(copy: true, input: '../deployment.rst', output: 'deployment.rst')
configure_file(copy: true, input: '../summary.rst', output: 'summary.rst')

c_rst_files = files(
'index.rst',
'overview.rst',
'world.rst',
'view.rst',
'events.rst',
'event-loop.rst',
'shutting-down.rst'
)

foreach f : c_rst_files
configure_file(copy: true, input: f, output: '@PLAINNAME@')
endforeach

subdir('xml')
subdir('api')

docs = custom_target(
'singlehtml C documentation for pugl',
command: [sphinx_build, '-M', 'singlehtml',
meson.current_build_dir(), meson.current_build_dir(),
'-E', '-q', '-t', 'singlehtml'],
input: [c_rst_files, c_pugl_rst, c_index_xml],
output: 'singlehtml',
build_by_default: true,
install: true,
install_dir: docdir / 'pugl-0')

docs = custom_target(
'html C documentation for pugl',
command: [sphinx_build, '-M', 'html',
meson.current_build_dir(), meson.current_build_dir(),
'-E', '-q', '-t', 'html'],
input: [c_rst_files, c_pugl_rst, c_index_xml],
output: 'html',
build_by_default: true,
install: true,
install_dir: docdir / 'pugl-0')

+ 26
- 0
dpf/dgl/src/pugl-upstream/doc/c/overview.rst View File

@@ -0,0 +1,26 @@
.. default-domain:: c
.. highlight:: c

########
Overview
########

The Pugl API revolves around two main objects: the `world` and the `view`.
An application creates a world to manage top-level state,
then creates one or more views to display.

The core API (excluding backend-specific components) is declared in ``pugl.h``:

.. code-block:: c

#include <pugl/pugl.h>

.. toctree::

world
view
events
event-loop
shutting-down

.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/

+ 20
- 0
dpf/dgl/src/pugl-upstream/doc/c/shutting-down.rst View File

@@ -0,0 +1,20 @@
.. default-domain:: c
.. highlight:: c

#############
Shutting Down
#############

When a view is closed,
it will receive a :struct:`PuglCloseEvent`.
An application may also set a flag based on user input or other conditions,
which can be used to break out of the main loop and stop calling :func:`puglUpdate`.

When the main event loop has finished running,
any views and the world need to be destroyed, in that order.
For example:

.. code-block:: c

puglFreeView(view);
puglFreeWorld(world);

+ 321
- 0
dpf/dgl/src/pugl-upstream/doc/c/view.rst View File

@@ -0,0 +1,321 @@
.. default-domain:: c
.. highlight:: c

###############
Creating a View
###############

A view is a drawable region that receives events.
You may think of it as a window,
though it may be embedded and not represent a top-level system window. [#f1]_

Creating a visible view is a multi-step process.
When a new view is created with :func:`puglNewView`,
it does not yet represent a "real" system view:

.. code-block:: c

PuglView* view = puglNewView(world);

*********************
Configuring the Frame
*********************

Before display,
the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set.
These allow the window system (or plugin host) to arrange the view properly.
For example:

.. code-block:: c

const double defaultWidth = 1920.0;
const double defaultHeight = 1080.0;

puglSetWindowTitle(view, "My Window");
puglSetDefaultSize(view, defaultWidth, defaultHeight);
puglSetMinSize(view, defaultWidth / 4.0, defaultHeight / 4.0);
puglSetAspectRatio(view, 1, 1, 16, 9);

There are also several :enum:`hints <PuglViewHint>` for basic attributes that can be set:

.. code-block:: c

puglSetViewHint(view, PUGL_RESIZABLE, PUGL_TRUE);
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);

*********
Embedding
*********

To embed the view in another window,
you will need to somehow get the :type:`native view handle <PuglNativeView>` for the parent,
then set it with :func:`puglSetParentWindow`.
If the parent is a Pugl view,
the native handle can be accessed with :func:`puglGetNativeWindow`.
For example:

.. code-block:: c

puglSetParentWindow(view, puglGetNativeWindow(parent));

************************
Setting an Event Handler
************************

In order to actually do anything, a view must process events from the system.
Pugl dispatches all events to a single :type:`event handling function <PuglEventFunc>`,
which is set with :func:`puglSetEventFunc`:

.. code-block:: c

puglSetEventFunc(view, onEvent);

See :doc:`events` for details on writing the event handler itself.

*****************
Setting View Data
*****************

Since the event handler is called with only a view pointer and an event,
there needs to be some way to access application data associated with the view.
Similar to :ref:`setting application data <setting-application-data>`,
this is done by setting an opaque handle on the view with :func:`puglSetHandle`,
for example:

.. code-block:: c

puglSetHandle(view, myViewData);

The handle can be later retrieved,
likely in the event handler,
with :func:`puglGetHandle`:

.. code-block:: c

MyViewData* data = (MyViewData*)puglGetHandle(view);

All non-constant data should be accessed via this handle,
to avoid problems associated with static mutable data.

If data is also associated with the world,
it can be retrieved via the view using :func:`puglGetWorld`:

.. code-block:: c

PuglWorld* world = puglGetWorld(view);
MyApp* app = (MyApp*)puglGetWorldHandle(world);

*****************
Setting a Backend
*****************

Before being realized, the view must have a backend set with :func:`puglSetBackend`.

The backend manages the graphics API that will be used for drawing.
Pugl includes backends and supporting API for
:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`.

Using Cairo
===========

Cairo-specific API is declared in the ``cairo.h`` header:

.. code-block:: c

#include <pugl/cairo.h>

The Cairo backend is provided by :func:`puglCairoBackend()`:

.. code-block:: c

puglSetBackend(view, puglCairoBackend());

No additional configuration is required for Cairo.
To draw when handling an expose event,
the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`puglGetContext`:

.. code-block:: c

cairo_t* cr = (cairo_t*)puglGetContext(view);

Using OpenGL
============

OpenGL-specific API is declared in the ``gl.h`` header:

.. code-block:: c

#include <pugl/gl.h>

The OpenGL backend is provided by :func:`puglGlBackend()`:

.. code-block:: c

puglSetBackend(view, puglGlBackend());

Some hints must also be set so that the context can be set up correctly.
For example, to use OpenGL 3.3 Core Profile:

.. code-block:: c

puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MINOR, 3);

If you need to perform some setup using the OpenGL API,
there are two ways to do so.

The OpenGL context is active when
:enumerator:`PUGL_CREATE <PuglEventType.PUGL_CREATE>` and
:enumerator:`PUGL_DESTROY <PuglEventType.PUGL_DESTROY>`
events are dispatched,
so things like creating and destroying shaders and textures can be done then.

Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler,
:func:`puglEnterContext` and :func:`puglLeaveContext` can be used to manually activate the OpenGL context during application setup.
Note, however, that unlike many other APIs, these functions must not be used for drawing.
It is only valid to use the OpenGL API for configuration in a manually entered context,
rendering will not work.
For example:

.. code-block:: c

puglEnterContext(view);
setupOpenGL(myApp);
puglLeaveContext(view);

while (!myApp->quit) {
puglUpdate(world, 0.0);
}

puglEnterContext(view);
teardownOpenGL(myApp);
puglLeaveContext(view);

Using Vulkan
============

Vulkan-specific API is declared in the ``vulkan.h`` header.
This header includes Vulkan headers,
so if you are dynamically loading Vulkan at runtime,
you should define ``VK_NO_PROTOTYPES`` before including it.

.. code-block:: c

#define VK_NO_PROTOTYPES

#include <pugl/vulkan.h>

The Vulkan backend is provided by :func:`puglVulkanBackend()`:

.. code-block:: c

puglSetBackend(view, puglVulkanBackend());

Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly.
Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API.

Loading Vulkan
--------------

For maximum compatibility,
it is best to not link to Vulkan at compile-time,
but instead load the Vulkan API at run-time.
To do so, first create a :struct:`PuglVulkanLoader`:

.. code-block:: c

PuglVulkanLoader* loader = puglNewVulkanLoader(world);

The loader manages the dynamically loaded Vulkan library,
so it must be kept alive for as long as the application is using Vulkan.
You can get the function used to load Vulkan functions with :func:`puglGetInstanceProcAddrFunc`:

.. code-block:: c

PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
puglGetInstanceProcAddrFunc(loader);

This vkGetInstanceProcAddr_ function can be used to load the rest of the Vulkan API.
For example, you can use it to get the vkCreateInstance_ function,
then use that to create your Vulkan instance.
In practice, you will want to use some loader or wrapper API since there are many Vulkan functions.

For advanced situations,
there is also :func:`puglGetDeviceProcAddrFunc` which retrieves the vkGetDeviceProcAddr_ function instead.

The Vulkan loader is provided for convenience,
so that applications to not need to write platform-specific code to load Vulkan.
Its use it not mandatory and Pugl can be used with Vulkan loaded by some other method.

Linking with Vulkan
-------------------

If you do want to link to the Vulkan library at compile time,
note that the Pugl Vulkan backend does not depend on it,
so you will have to do so explicitly.

Creating a Surface
------------------

The details of using Vulkan are far beyond the scope of this documentation,
but Pugl provides a portable function, :func:`puglCreateSurface`,
to get the Vulkan surface for a view.
Assuming you have somehow created your ``VkInstance``,
you can get the surface for a view using :func:`puglCreateSurface`:

.. code-block:: c

VkSurfaceKHR* surface = NULL;
puglCreateSurface(puglGetDeviceProcAddrFunc(loader),
view,
vulkanInstance,
NULL,
&surface);

****************
Showing the View
****************

Once the view is configured, it can be "realized" with :func:`puglRealize`.
This creates a "real" system view, for example:

.. code-block:: c

PuglStatus status = puglRealize(view);
if (status) {
fprintf(stderr, "Error realizing view (%s)\n", puglStrerror(status));
}

Note that realizing a view can fail for many reasons,
so the return code should always be checked.
This is generally the case for any function that interacts with the window system.
Most functions also return a :enum:`PuglStatus`,
but these checks are omitted for brevity in the rest of this documentation.

A realized view is not initially visible,
but can be shown with :func:`puglShow`:

.. code-block:: c

puglShow(view);

To create an initially visible view,
it is also possible to simply call :func:`puglShow` right away.
The view will be automatically realized if necessary.

.. rubric:: Footnotes

.. [#f1] MacOS has a strong distinction between
`views <https://developer.apple.com/documentation/appkit/nsview>`_,
which may be nested, and
`windows <https://developer.apple.com/documentation/appkit/nswindow>`_,
which may not.
On Windows and X11, everything is a nestable window,
but top-level windows are configured differently.

.. _vkCreateInstance: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html

.. _vkGetDeviceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetDeviceProcAddr.html

.. _vkGetInstanceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html

+ 65
- 0
dpf/dgl/src/pugl-upstream/doc/c/world.rst View File

@@ -0,0 +1,65 @@
################
Creating a World
################

.. default-domain:: c
.. highlight:: c

The world is the top-level object which represents an instance of Pugl.
It handles the connection to the window system,
and manages views and the event loop.

An application typically has a single world,
which is constructed once on startup and used to drive the main event loop.

************
Construction
************

A world must be created before any views, and it must outlive all of its views.
A world is created with :func:`puglNewWorld`, for example:

.. code-block:: c

PuglWorld* world = puglNewWorld(PUGL_PROGRAM, 0);

For a plugin, specify :enumerator:`PUGL_MODULE <PuglWorldType.PUGL_MODULE>` instead.
In some cases, it is necessary to pass additional flags.
For example, Vulkan requires thread support:

.. code-block:: c

PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS)

It is a good idea to set a class name for your project with :func:`puglSetClassName`.
This allows the window system to distinguish different applications and,
for example, users to set up rules to manage their windows nicely:

.. code-block:: c

puglSetClassName(world, "MyAwesomeProject")

.. _setting-application-data:

************************
Setting Application Data
************************

Pugl will call an event handler in the application with only a view pointer and an event,
so there needs to be some way to access the data you use in your application.
This is done by setting an opaque handle on the world with :func:`puglSetWorldHandle`,
for example:

.. code-block:: c

puglSetWorldHandle(world, myApp);

The handle can be later retrieved with :func:`puglGetWorldHandle`:

.. code-block:: c

MyApp* app = (MyApp*)puglGetWorldHandle(world);

All non-constant data should be accessed via this handle,
to avoid problems associated with static mutable data.


+ 19
- 0
dpf/dgl/src/pugl-upstream/doc/c/xml/meson.build View File

@@ -0,0 +1,19 @@
doxygen = find_program('doxygen')

c_doxygen_input = []
foreach h : c_headers
c_doxygen_input += ['..' / h]
endforeach

config = configuration_data()
config.set('PUGL_SRCDIR', pugl_src_root)
config.set('DOX_OUTPUT', meson.current_build_dir() / '..')

c_doxyfile = configure_file(configuration: config,
input: '../Doxyfile.in',
output: 'Doxyfile')

c_index_xml = custom_target('c-index.xml',
command: [doxygen, '@INPUT0@'],
input: [c_doxyfile] + c_header_files,
output: 'index.xml')

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save